先來了解一下DAC083x的一些基本介紹:

在DAC0830的晶片手冊中可以瞭解到,輸出IOUT1是與施加的參考電壓和數字輸入字的乘積成正比的電流。 為了實現應用的多功能性,第二個輸出IOUT2作為電流提供,與數字輸入的補碼成正比。

【Proteus】使用DAC0832輸出可調正弦波

數字輸入為所施加的8位二進位制字(0至255)的十進位制等效值(以10為基數),VREF是引腳8上的電壓,而15kΩ是R的內部電阻R的標稱值 -2R梯形網路。而在DAC083x系列的晶片手冊中,也可以很方便的找到參考電路,如下:

【Proteus】使用DAC0832輸出可調正弦波

按照這個參考電路,以及看了一些網上其他人設計的電路,DAC0832部分的電路如下:

【Proteus】使用DAC0832輸出可調正弦波

完整的電路如圖:

【Proteus】使用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可以控制正弦波的週期,也就是後面需要做的按鍵調頻功能。這個先不用管,現在就可以看到模擬中的正弦波效果:

【Proteus】使用DAC0832輸出可調正弦波

看到了正弦效果之後,就可以按照上面說的,檢測按鍵,來調節正弦波的週期,先來畫幾個按鍵:

【Proteus】使用DAC0832輸出可調正弦波

將按鍵檢測寫成一個函式,並且做好分類與註釋,先填寫好週期按鍵的相關內容,如下:

/**********************************

按鍵檢測函式

***********************************/

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

();

//波形發生函式

}

}

點個贊嘍!