【Proteus】使用DAC0832輸出可調正弦波
先來了解一下DAC083x的一些基本介紹:
在DAC0830的晶片手冊中可以瞭解到,輸出IOUT1是與施加的參考電壓和數字輸入字的乘積成正比的電流。 為了實現應用的多功能性,第二個輸出IOUT2作為電流提供,與數字輸入的補碼成正比。
數字輸入為所施加的8位二進位制字(0至255)的十進位制等效值(以10為基數),VREF是引腳8上的電壓,而15kΩ是R的內部電阻R的標稱值 -2R梯形網路。而在DAC083x系列的晶片手冊中,也可以很方便的找到參考電路,如下:
按照這個參考電路,以及看了一些網上其他人設計的電路,DAC0832部分的電路如下:
完整的電路如圖:
在手冊中對於DAC晶片的時序介紹比較詳細,不過程式寫起來並不會那麼複雜,因為我們只需要在初始化之後往資料口裡傳資料就可以了,初始化也很簡單,就直接把一些埠給0:
/**********************************
外設初始化函式
***********************************/
void
da_init
()
{
DA_WR
=
0
;
DA_CS
=
0
;
DA_DATA
=
0
;
key0
=
1
;
//按鍵置高
key1
=
1
;
//按鍵置高
key2
=
1
;
//按鍵置高
key3
=
1
;
//按鍵置高
}
先用程式碼實現正弦資料的產生,先不用管幅值的調節,直接就最大幅值12V,也就是正弦資料中的255對應12V,0對應-12V。
我們使用C語言中的math庫生成sin函式,有個地方需要注意,math庫中的sin函式輸入資料是弧度制的,假如說,我們需要30度的sin值,那麼需要將30度轉換成弧度制:30 * PI / 180。現在我們需要迴圈一個週期的sin值,那麼就使用一個變數,一直自加,從0加到360度,然後求出對應的sin值,然後再將sin值轉換到0-255之間傳輸到DAC晶片就可以了:
/**********************************
波形發生函式
***********************************/
void
wave
()
{
float
sinAngle
=
0。0f
;
Angle
++
;
//角度自加
if
(
Angle
>=
360
)
Angle
=
0
;
sinAngle
=
sin
(((
float
)
Angle
*
PI
/
180。0f
));
//求角度對應的正弦值 返回[-1,1]
DA_DATA
=
(
int
)(((
sinAngle
+
1。0f
)
/
2。0f
)
*
255。0f
);
}
上面程式碼中需要注意的是sinAngle即是求取出來的正弦值,範圍是[-1,1],而DAC晶片的輸入是0-255,因此需要將正弦值整體加1,整體上移,然後除以2得到比例,然後再乘以255,便可以直接賦值給DAC晶片。
現在我們有了生成正弦值的函式,那麼就要控制正弦值輸入給DAC的時間,使用一個定時器來計時,在定時器中斷中控制呼叫wave函式來控制生成正弦波的時間間隔,
定時器初始化程式如下:
/**********************************
定時器初始化函式
***********************************/
void
init
()
{
TMOD
=
0x01
;
//設定定時器工作方式,第二位為定時器0
TH0
=
(
65536
-
T
)
/
256
;
//定時器0高8位 定時時間1ms
TL0
=
(
65536
-
T
)
/
256
;
//定時器0低8位
TR0
=
1
;
//開啟定時器
ET0
=
1
;
//開定時器中斷
EA
=
1
;
//開總中斷
}
定時器中斷程式如下:
void
T0_time
()
interrupt
1
{
TH0
=
(
65536
-
T
)
/
256
;
//定時器0高8位 定時時間1ms
TL0
=
(
65536
-
T
)
/
256
;
//定時器0低8位
time
++
;
if
(
time
>
(
cycle_T
/
T
))
//0。1ms
{
time
=
0
;
wave
();
//波形發生函式
}
}
這裡我配置的定時器中斷時間是1ms,也就是T,即1ms進一次中斷,透過調節cycle_T可以控制正弦波的週期,也就是後面需要做的按鍵調頻功能。這個先不用管,現在就可以看到模擬中的正弦波效果:
看到了正弦效果之後,就可以按照上面說的,檢測按鍵,來調節正弦波的週期,先來畫幾個按鍵:
將按鍵檢測寫成一個函式,並且做好分類與註釋,先填寫好週期按鍵的相關內容,如下:
/**********************************
按鍵檢測函式
***********************************/
void
key_check
()
{
/*按鍵埠置高*/
key0
=
1
;
key1
=
1
;
key2
=
1
;
key3
=
1
;
/*增大週期*/
if
(
key0
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key0
==
0
)
{
cycle_T
=
cycle_T
+
2000
;
//每次增加2000us
/*限幅*/
if
(
cycle_T
>
CYCLE_MAX
)
{
cycle_T
=
CYCLE_MAX
;
}
}
while
(
!
key0
);
//等待按鍵鬆開
}
/*減小週期*/
if
(
key1
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key1
==
0
)
{
cycle_T
=
cycle_T
-
2000
;
//每次減小2000us
/*限幅*/
if
(
cycle_T
<
CYCLE_MIN
)
{
cycle_T
=
CYCLE_MIN
;
}
}
while
(
!
key1
);
//等待按鍵鬆開
}
/*增大幅值*/
if
(
key2
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key2
==
0
)
{
}
while
(
!
key2
);
//等待按鍵鬆開
}
/*減小幅值*/
if
(
key3
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key3
==
0
)
{
}
while
(
!
key3
);
//等待按鍵鬆開
}
}
這裡我每按一次按鍵,就調節那個cycle_T的值,並且對這個值做了一次限幅,避免出現一些溢位之類的情況。
之後就可以看到按鍵調頻的效果,如下:
之後是修改幅值,修改幅值其實就是修改傳輸到DAC的0-255的區間範圍,如果是佔滿0-255就是最大幅值,那樣的話,就可以修改wave函式,使用另外一個單獨變數Amp來確定幅值,除此之外,我們需要確定一個最大幅值為12V,Amp不能超過12,修改如下:
/**********************************
波形發生函式
***********************************/
void
wave
()
{
float
sinAngle
=
0。0f
;
Angle
++
;
//角度自加
if
(
Angle
>=
360
)
Angle
=
0
;
sinAngle
=
sin
(((
float
)
Angle
*
PI
/
180。0f
));
//求角度對應的正弦值 返回[-1,1]
sinAngle
=
sinAngle
*
Amp
/
AMP_MAX
;
//按照幅值範圍等比例放大縮小
DA_DATA
=
(
int
)(((
sinAngle
+
1。0f
)
/
2。0f
)
*
255。0f
);
}
需要定義三個宏:
#define AMP_DEFAULT 5。0f
//預設幅值 單位:V
#define AMP_MAX 12。0f
//最大幅值為12
#define AMP_MIN 0。0f //最小幅值
之後修改按鍵的部分:
/*增大幅值*/
if
(
key2
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key2
==
0
)
{
Amp
=
Amp
+
1。0f
;
if
(
Amp
>=
AMP_MAX
)
Amp
=
AMP_MAX
;
}
while
(
!
key2
);
//等待按鍵鬆開
}
/*減小幅值*/
if
(
key3
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key3
==
0
)
{
Amp
=
Amp
-
1。0f
;
if
(
Amp
<=
AMP_MIN
)
Amp
=
AMP_MIN
;
}
while
(
!
key3
);
//等待按鍵鬆開
}
之後就可以看到效果啦:
完整程式碼如下:
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define DA_DATA P1
//DAC0832資料口
#define AMP_DEFAULT 5。0f
//預設幅值 單位:V
#define AMP_MAX 12。0f
//最大幅值為12
#define AMP_MIN 0。0f
//最小幅值
#define T 1000
//計時週期 單位:us
#define CYCLE_DEFAULT 1000
//預設正弦週期 單位:us
#define CYCLE_MAX 20000
//最大週期
#define CYCLE_MIN 1000
#define PI 3。1415926f
/****************************變數*******************************/
uint
Angle
=
0
;
//角度 0-360
int
time
=
0
;
//計時
int
cycle_T
=
0
;
//正弦週期
float
Amp
=
0。0f
;
//定義DAC0832介面
sbit
DA_WR
=
P2
^
1
;
//寫輸入訊號
sbit
DA_CS
=
P2
^
0
;
//片選訊號
//定義按鍵介面
sbit
key0
=
P2
^
2
;
//增大週期
sbit
key1
=
P2
^
3
;
//減小週期
sbit
key2
=
P2
^
4
;
//增大幅值
sbit
key3
=
P2
^
5
;
//減小幅值
//延時
void
delayms
(
int
x
)
{
uint
i
,
j
;
for
(
i
=
x
;
i
>
0
;
i
——
)
for
(
j
=
110
;
j
>
0
;
j
——
);
}
/**********************************
外設初始化函式
***********************************/
void
da_init
()
{
DA_WR
=
0
;
DA_CS
=
0
;
DA_DATA
=
0
;
key0
=
1
;
//按鍵置高
key1
=
1
;
//按鍵置高
key2
=
1
;
//按鍵置高
key3
=
1
;
//按鍵置高
}
/**********************************
定時器初始化函式
***********************************/
void
init
()
{
TMOD
=
0x01
;
//設定定時器工作方式,第二位為定時器0
TH0
=
(
65536
-
T
)
/
256
;
//定時器0高8位 定時時間1ms
TL0
=
(
65536
-
T
)
/
256
;
//定時器0低8位
TR0
=
1
;
//開啟定時器
ET0
=
1
;
//開定時器中斷
EA
=
1
;
//開總中斷
}
/**********************************
按鍵檢測函式
***********************************/
void
key_check
()
{
/*按鍵埠置高*/
key0
=
1
;
key1
=
1
;
key2
=
1
;
key3
=
1
;
/*增大週期*/
if
(
key0
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key0
==
0
)
{
cycle_T
=
cycle_T
+
2000
;
//每次增加2000us
/*限幅*/
if
(
cycle_T
>
CYCLE_MAX
)
{
cycle_T
=
CYCLE_MAX
;
}
}
while
(
!
key0
);
//等待按鍵鬆開
}
/*減小週期*/
if
(
key1
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key1
==
0
)
{
cycle_T
=
cycle_T
-
2000
;
//每次減小2000us
/*限幅*/
if
(
cycle_T
<
CYCLE_MIN
)
{
cycle_T
=
CYCLE_MIN
;
}
}
while
(
!
key1
);
//等待按鍵鬆開
}
/*增大幅值*/
if
(
key2
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key2
==
0
)
{
Amp
=
Amp
+
1。0f
;
if
(
Amp
>=
AMP_MAX
)
Amp
=
AMP_MAX
;
}
while
(
!
key2
);
//等待按鍵鬆開
}
/*減小幅值*/
if
(
key3
==
0
)
{
delayms
(
5
);
//按鍵消抖
if
(
key3
==
0
)
{
Amp
=
Amp
-
1。0f
;
if
(
Amp
<=
AMP_MIN
)
Amp
=
AMP_MIN
;
}
while
(
!
key3
);
//等待按鍵鬆開
}
}
/**********************************
波形發生函式
***********************************/
void
wave
()
{
float
sinAngle
=
0。0f
;
Angle
++
;
//角度自加
if
(
Angle
>=
360
)
Angle
=
0
;
sinAngle
=
sin
(((
float
)
Angle
*
PI
/
180。0f
));
//求角度對應的正弦值 返回[-1,1]
sinAngle
=
sinAngle
*
Amp
/
AMP_MAX
;
//按照幅值範圍等比例放大縮小
DA_DATA
=
(
int
)(((
sinAngle
+
1。0f
)
/
2。0f
)
*
255。0f
);
}
void
main
()
{
init
();
//定時器初始化
cycle_T
=
CYCLE_DEFAULT
;
//初始化為預設正弦週期
Amp
=
AMP_DEFAULT
;
//初始化為預設幅值
da_init
();
//DA初始化
while
(
1
)
{
key_check
();
}
}
void
T0_time
()
interrupt
1
{
TH0
=
(
65536
-
T
)
/
256
;
//定時器0高8位 定時時間1ms
TL0
=
(
65536
-
T
)
/
256
;
//定時器0低8位
time
++
;
if
(
time
>
(
cycle_T
/
T
))
//0。1ms
{
time
=
0
;
wave
();
//波形發生函式
}
}
點個贊嘍!