天天看點

外設驅動庫開發筆記4:AD9833函數發生器驅動

  很多時候我們需要輸出某種函數信号,如方波、三角波、正弦波等,但想要獲得這樣的函數信号,不論是硬體電路還是軟體實作,卻并不是一件簡單的事情。不過AD9833這類函數生成晶片可以簡化這方面的操作,這一節我們就來設計并實作AD9833的驅動。

1、功能概述

  各種類型的檢測、信号激勵和時域反射(TDR)應用都需要波形發生器。而AD9833就是一款低功耗、可程式設計波形發生器,能夠産生正弦波、三角波和方波輸出。

1.1、硬體配置及功能描述

  AD9833無需額外的外部元件就能夠産生正弦波、三角波和方波輸出。輸出頻率和相位可通過軟體進行程式設計,調整簡單。AD9833通過一個三線式串行接口寫入資料。該串行接口能夠以最高40 MHz的時鐘速率工作,并且與DSP和微控制器标準相容。該器件采用2.3 V至5.5 V電源供電。

外設驅動庫開發筆記4:AD9833函數發生器驅動

1.2、内部寄存器

  AD9833包含一個16位控制寄存器,讓使用者可以配置AD9833的操作。mode位之外的所有控制位均在MCLK的内部下降沿采樣。

外設驅動庫開發筆記4:AD9833函數發生器驅動

  控制寄存器各位的含義如下:

外設驅動庫開發筆記4:AD9833函數發生器驅動

  AD9833包含兩個頻率寄存器和兩個相位寄存器,頻率寄存器為28位:時鐘速率為25 MHz時,可以實作0.1 Hz的分辨率;而時鐘速率為1 MHz時,則可以實作0.004 Hz的分辨率。

外設驅動庫開發筆記4:AD9833函數發生器驅動

  每次寫資料時,都是從寫控制寄存器器開始,每次寫的16為資料的高兩位用以決定所寫的寄存器。

外設驅動庫開發筆記4:AD9833函數發生器驅動

       如上圖所示,寫不同寄存器時高兩位需根據寄存器的不同設定不同的值。

2、驅動設計與實作

  我們已經了解了AD9833的基本情況。接下來我們就據此實作AD9833波形發生器驅動的設計及實作。

2.1、對象定義

  AD9833波形發生器的驅動依然采用基于對象的操作,是以我們需要先得到AD9833波形發生器的對象。

2.1.1、抽象對象類型

  一個對象最起碼包含屬性和操作兩方面内容,我們先來分析一下AD9833波形發生器對象需要包含哪些屬性和操作。

  對于AD9833波形發生器來說,控制寄存器的狀态決定了下一步的操作,是以我們将控制寄存器的狀态抽象為對象的屬性,以便随時掌握操作的目标。此外,作為函數發生器,輸出的信号具有周期性,在輸出頻率固定的情況下,計算有一個常數,我們将其作為屬性已确認輸出型号的頻率。

  進而我們考慮AD9833波形發生器對象的操作。首先我們要操作AD9833波形發生器則需要向其傳送資料,是以我們将向AD9833波形發生器寫資料作為對象的一個操作。AD9833波形發生器采用SPI通訊接口,有時需要在軟體中對片選信号進行操作,是以我們将片選型号的操作作為對象的另一個操作。在一些情況下,有些針對對象的活動需要延時進行,而在不同的平台中采取的延時方式不盡相同,為了操作友善我們将延時操作作為對象的一個操作。

  據以上的分析我們可以抽象AD9833波形發生器的對象類型如下:

1 /* 定義AD9833對象類型 */
2 typedef struct Ad9833Object{
3        uint16_t ctlRegister;                //控制寄存器
4        float freqConstant;                          //頻率計算常數
5        void (*WriteData)(uint8_t *tData,uint16_t tSize);        //向DAC發送資料
6        void (*ChipSelcet)(AD9833CSType en);     //片選信号
7        void (*Delayms)(volatile uint32_t nTime);       //ms延時操作指針
8 }Ad9833ObjectType;      

2.1.2、對象初始化

  我們雖然得到了AD9833的對象,但對象不能直接使用,我們需要對其進行初始化方能使用。是以接下來我們考慮AD9833波形發生器對象的初始化函數。

  初始化函數至少包含有2方面内容:一是為對象變量賦必要的初值;二是檢查這些初值是否是有效的。特别是一些操作指針錯誤的話可能産生嚴重的後果。基于這一原則,我們設計AD9833波形發生器的對象初始化函數如下:

1 /* 初始化AD9833對象 */
 2 void AD9833Initialization(Ad9833ObjectType *dev,
 3                                            float mclk,
 4                                            AD9833WriteData write,
 5                                            AD9833ChipSelcet cs,
 6                                            AD9833Delayms delayms)
 7 {
 8        if((dev==NULL)||(write==NULL)||(delayms==NULL))
 9        {
10               return;
11        }
12       
13        dev->ctlRegister=0x0000;
14       
15        if(mclk>0)
16        {
17               dev->freqConstant=268.435456/mclk;
18        }
19        else
20        {
21               dev->freqConstant=10.73741824; //預設是25M
22        }
23  
24        dev->WriteData=write;
25        dev->Delayms=delayms;
26       
27        if(cs!=NULL)
28        {
29               dev->ChipSelcet=cs;
30        }
31        else
32        {
33               dev->ChipSelcet=DefaultChipSelcet;
34        }
35 }      

2.2、對象操作

  我們已知AD9833波形發生器包含3類寄存器:控制寄存器、頻率寄存器和相位寄存器。接下來我們就實作對這三個寄存器的操作。

2.2.1、操作控制寄存器

  AD9833波形發生器有一個16位的控制寄存用于配置各種操作。其中DB13(B28)、DB12(HLB)、DB11(FSELECT)、DB10(PSELECT)、DB8(RESET)、DB7(SLEEP1)、DB6(SLEEP12)、DB5(OPBITEN)、DB3(DIV2)、DB1(MODE)等位是可以操作的。與頻率寄存器和相位寄存器相關的配置我們在後續說明,這裡先看看複位、休眠及輸出模式的配置。

  AD9833上電時,器件應複位。要使AD9833複位, 應将DB8(RESET)位置1。要使器件退出複位,應将該位清0。在reset 置0後的8個MCLK周期内,DAC輸出端會出現信号。複位功能可使相應的内部寄存器複位至0,以提供中間電平的模拟輸出。複位操作不會使相位、頻率或控制寄存器複位。

1 /* 複位AD9833對象 */
 2 void ResetAD9833Object(Ad9833ObjectType *dev)
 3 {
 4        uint16_t regValue=dev->ctlRegister;
 5       
 6        regValue|=AD9833_CTRLRESET;
 7        SendToAD9833(dev,regValue);
 8       
 9        dev->Delayms(1);
10       
11        regValue&=(~AD9833_CTRLRESET);
12        SendToAD9833(dev,regValue);
13       
14        dev->ctlRegister=regValue;
15 }      

  SLEEP功能可關斷AD9833中不使用的部分,以将功耗降至最低。可關斷的晶片部分是内部時鐘和DAC。休眠功能需要操作DB7(SLEEP1)和DB6(SLEEP12)位。具體配置如下:

外設驅動庫開發筆記4:AD9833函數發生器驅動
1 /* 設定AD9833休眠狀态 */
 2 void SetAD9833SleepMode(Ad9833ObjectType *dev,Ad9833SleepMode mode)
 3 {
 4        uint16_t regValue=dev->ctlRegister;
 5  
 6        regValue&=(~(AD9833_CTRLSLEEP1|AD9833_CTRLSLEEP12));
 7       
 8        switch(mode)
 9        {
10               case DACTurnOff:
11               {
12                      regValue|=AD9833_CTRLSLEEP12;
13                      break;
14               }
15               case MCLKTurnOff:
16               {
17                      regValue|=AD9833_CTRLSLEEP1;
18                      break;
19               }
20               case DACMCLKTurnOff:
21               {
22                      regValue|=(AD9833_CTRLSLEEP1|AD9833_CTRLSLEEP12);
23                      break;
24               }
25               default:
26               {
27                      break;
28               }
29        }
30        SendToAD9833(dev,regValue);
31       
32        dev->ctlRegister=regValue;
33 }      

  AD9833可從晶片提供各種輸出,所有這些輸出均通過VOUT引腳提供。輸出選項包括DAC資料的MSB、正弦波 輸出或三角波輸出。控制寄存器的DB5(OPBITEN)、DB3(DIV2)和DB1(MODE)決定 AD9833将提供的輸出。具體如下:

外設驅動庫開發筆記4:AD9833函數發生器驅動
1 /* 設定AD9833的輸出模式 */
 2 void SetAD9833OutputMode(Ad9833ObjectType *dev,Ad9833OutMode mode)
 3 {
 4        uint16_t regValue=dev->ctlRegister;
 5       
 6        regValue&=(~(AD9833_CTRLOPBITEN|AD9833_CTRLDIV2|AD9833_CTRLMODE));
 7       
 8        switch(mode)
 9        {
10               case triangular:
11               {
12                      regValue|=AD9833_CTRLMODE;
13                      break;
14               }
15               case square_msb_2:
16               {
17                      regValue|=AD9833_CTRLOPBITEN;
18                      break;
19               }
20               case square_msb:
21               {
22                      regValue|=(AD9833_CTRLOPBITEN|AD9833_CTRLDIV2);
23                      break;
24               }
25               default:
26               {
27                      break;
28               }
29        }
30  
31        SendToAD9833(dev,regValue);
32       
33        dev->ctlRegister=regValue;
34 }      

2.2.2、操作頻率寄存器

  寫頻率寄存器時,Bit D15和Bit D14設定為01或10。控制寄存DB13(B28)和DB12(HLB)位決定操作的頻率寄存器。如果希望更改某個頻率寄存器的全部内容,則必須向 同一位址執行兩次連續寫入,因為頻率寄存器是28位寬。 第一次寫入包含14個LSB,第二次寫入則包含14個MSB。 對于此工作模式,B28(D13)控制位應置1。在某些應用中,使用者無需更新頻率寄存器的全部28個位。 在粗調情況下,隻需更新14個MSB,而在精調情況下,則隻需更新14個LSB。通過将B28 (D13)控制位清0時,28位頻率寄存器用作兩個14位寄存器,其中一個包含14個MSB,另一個則包含14個LSB。這意味着,可單獨更新頻率字的 14個MSB而不影響14個LSB,反之亦然。控制寄存器中的 Bit HLB (D12)确定要更新的具體14個位。資料結構如下:

外設驅動庫開發筆記4:AD9833函數發生器驅動
1 /* 設定頻率寄存器的值 */
 2 void SetAD9833FreqRegister(Ad9833ObjectType *dev,WriteAd9833FreqReg reg,uint32_t freqValue)
 3 {
 4        uint16_t msbFreq,lsbFreq;
 5        uint32_t freqReg;
 6       
 7        freqReg =(uint32_t)(dev->freqConstant*freqValue);
 8        lsbFreq = (freqReg & 0x0003FFF);
 9        msbFreq = ((freqReg & 0xFFFC000) >> 14);
10       
11        ConfigFreqRegisterStyle(dev,reg);
12       
13        switch(reg)
14        {
15               case FREQ0_B28:
16               {
17                      lsbFreq |=FREQ0_Address;
18                      SendToAD9833(dev,lsbFreq);
19                      msbFreq |=FREQ0_Address;
20                      SendToAD9833(dev,msbFreq);
21                      break;
22               }
23               case FREQ0_B14_LSB:
24               {
25                      lsbFreq |=FREQ0_Address;
26                      SendToAD9833(dev,lsbFreq);
27                      break;
28               }
29               case FREQ0_B14_MSB:
30               {
31                      msbFreq |=FREQ0_Address;
32                      SendToAD9833(dev,msbFreq);
33                      break;
34               }
35               case FREQ1_B28:
36               {
37                      lsbFreq |=FREQ1_Address;
38                      SendToAD9833(dev,lsbFreq);
39                      msbFreq |=FREQ1_Address;
40                      SendToAD9833(dev,msbFreq);
41                      break;
42               }
43               case FREQ1_B14_LSB:
44               {
45                      lsbFreq |=FREQ1_Address;
46                      SendToAD9833(dev,lsbFreq);
47                      break;
48               }
49               case FREQ1_B14_MSB:
50               {
51                      msbFreq |=FREQ1_Address;
52                      SendToAD9833(dev,msbFreq);
53                      break;
54               }
55               default:
56               {
57                      break;
58               }
59        }
60 }      

2.2.3、操作相位寄存器

  寫入相位寄存器時,Bit D15和Bit D14設定為11。Bit D13确定将載入的相位寄存器。具體結構如下:

外設驅動庫開發筆記4:AD9833函數發生器驅動
1 /* 設定相位寄存器的值 */
 2 void SetAD9833PhaseRegister(Ad9833ObjectType *dev,Ad9833PhaseReg reg,float phaseValue)
 3 {
 4        uint16_t phaseReg=0;
 5        float phaseConstant=651.8986469;
 6       
 7        phaseReg=(uint16_t)(phaseValue*phaseConstant);
 8        phaseReg&=0x0FFF;
 9       
10        if(reg==PHASE0)
11        {
12               phaseReg|=PHASE0_Address;
13        }
14        else
15        {
16               phaseReg|=PHASE1_Address;
17        }
18       
19        SendToAD9833(dev,phaseReg);
20 }      

3、驅動的使用

  我們已經設計并實作了AD9833波形發生器的驅動,接下來我們考慮如何使用這一驅動程式實作AD9833波形發生器的應用。

3.1、聲明并初始化對象

  驅動是基于對象的操作設計的,是以我們先要使用Ad9833ObjectType聲明對象變量。形如:

  Ad9833ObjectType ad9833;

  聲明了這個對象變量并不能用于操作AD9833波形發生器,我們還需要使用初始化函數對對象變量進行初始化。初始換函數所需參數如下:

  Ad9833ObjectType *dev,所要初始化的AD9833對象裝置

  float mclk,AD9833采用的數字時鐘,預設為25M

  AD9833WriteData write,寫AD9833對象函數

  AD9833ChipSelcet cs,AD9833片選信号操作函數

  AD9833Delayms delayms,操作ms延時函數

  對于這些參數,對象變量我們已經定義了。AD9833采用的數字時鐘則根據我們的實際使用情況輸入。主要的是我們需要定義幾個函數,并将函數指針作為參數。這幾個函數的類型如下:

1 /* 定義AD9833寫資料指針類型 */
2 typedef void (*AD9833WriteData)(uint8_t *tData,uint16_t tSize);
3 
4 /* 定義AD9833片選操作指針類型 */
5 typedef void (*AD9833ChipSelcet)(AD9833CSType en);
6 
7 /* 定義AD9833 ms延時操作指針類型 */
8 typedef void (*AD9833Delayms)(volatile uint32_t nTime);      

  對于這幾個函數我們根據樣式定義就可以了,具體的操作可能與使用的硬體平台有關系。片選操作函數用于多裝置需要軟體操作時,如采用硬體片選可以傳入NULL即可。具體函數定義如下:

1 /*定義片選信号函數*/
 2 void AD9833CS(AD9833CSType en)
 3 {
 4        if(AD9833CS_ENABLE==en)
 5        {
 6               HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_RESET);
 7        }
 8        else
 9        {
10               HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_SET);
11        }
12 }
13  
14 /*定義發送資料函數*/
15 void AD9833TransmitData(uint8_t *wData,uint16_t wSize)
16 {
17        HAL_SPI_Transmit (&ad9833hspi, wData, wSize, 1000);
18 }      

  對于延時函數我們可以采用各種方法實作。我們采用的STM32平台和HAL庫則可以直接使用HAL_Delay()函數。于是我們可以調用初始化函數如下:

  AD9833Initialization(&ad9833,25.0,AD9833TransmitData,AD9833CS,HAL_Delay);

3.2、基于對象進行操作

  接下來我們将操作對象生成我們想要的波形。如我們想要生成頻率為10MHz,相位為0的正弦波,編碼如下:

1 /* 生成波形 */
 2 void SignalGenerator(void)
 3 {
 4        SetAD9833FreqRegister(&ad9833,FREQ0_B28,10000000);
 5        SetAD9833PhaseRegister(&ad9833,PHASE0,0.0);
 6       
 7        SelectAD9833FregRegister(&ad9833,FREQ0);
 8        SelectAD9833PhaseRegister(&ad9833,PHASE0);
 9       
10        SetAD9833OutputMode(&ad9833,sinusoid);
11 }      

  在這段程式中我們使用的是頻率寄存器0和相位寄存器0,并且頻率寄存器采用的是修改28位的形式。對于其他的操作方式我們我們可以作相應的更改。

4、應用總結

  我們已經實作AD9833波形發生器的驅動及基于此驅動的應用。我們輸出正弦波,三角波及方波均得到了與我們預期一緻的結果,說明驅動的設計是符合需求的。

  控制寄存器的DB11(FSELECT)和DB10(PSELECT)位決定所使用的頻率寄存器和相位寄存器,預設是FREQ0寄存器和PHASE0寄存器。若需要修改則可以調用SelectAD9833FregRegister和SelectAD9833PhaseRegister函數進行配置。

  在使用驅動時需注意,采用SPI接口的器件需要考慮片選操作的問題。如果片選信号是通過硬體電路來實作的,我們在初始化時給其傳遞NULL值。如果是軟體操作片選則傳遞我們編寫的片選操作函數。

  完整的源代碼可在GitHub下載下傳:​​https://github.com/foxclever/ExPeriphDriver​​

歡迎關注:

如果您希望更友善且及時的閱讀相關文章,關注我的微信公衆号【木南創智】