1、電賽預測
摘要:原定于7月28日才發布的器件清單,提前2天在26號就發布,感覺大家現在應該猜題預測,與其猜題,還不如靜下心來做題,盲目猜題是沒有必要的,熟悉相關器件倒尤為重要,不要到處去水群聊天,聽風就是雨,題目要是能被猜中了,那只能說題目出的很差,都能被我們猜到,

既然猜不到,那就別浪費時間,相反,把這些時間好好利用起來,把手上的事情繼續完成,把該買的元器件買好,買晚了就貴了,買回來之后每個組件盡可能的去熟悉使用,準備好相關程式,
2、電賽清單
其實每年的帶電賽主要元器件以及器材清單基本都差不多,只有很小的改動,2019年國賽年同樣有DDS模塊,今年照樣有DDS模塊,

3、什么是DDS
DDS直接數字式頻率綜合器 DDS(Direct Digital Synthesizer),實際上是一種分頻器:通過編程頻率控制字來分頻系統時鐘(SYSTEM CLOCK)以產生所需要的頻率,DDS有兩個突出的特點,一方面,DDS作業在數字域,一旦更新頻率控制字,輸出的頻率就相應改變,其跳頻速率高;另一方面,由于頻率控制字的寬度寬(48bit 或者更高),頻率解析度高,你可以把他理解為一個信號源即信號發生器,

電賽肯定不會讓你自己搞這樣的儀器做信號源,用你的DDS代替它,
4、DDS作業原理
DDS主要分成3 部分:相位累加器 , 相位幅度轉換 , 數模轉換器(DAC),
- 相位累加器
一個正弦波,雖然它的幅度不是線性的,但是它的相位卻是線性增加的,DDS 正是利用了這一特點來產生正弦信號,根據DDS的頻率控制字的位數N,把 360° 平均分成了2的N次方等份, - 相位幅度轉換
通過相位累加器,我們已經得到了合成Fout 頻率所對應的相位資訊,然后相位幅度轉換器把 0°~360°的相位轉換成相應相位的幅度值,比如當DDS選擇為2Vp-p的輸出時,45°對應的幅度值為 0.707V,這個數值以二進制的形式被送入DAC, 這個相位到幅度的轉換是通過查表完成的, - DAC 輸出
代表幅度的二進制數字信號被送入DAC中,并轉換成為模擬信號輸出,DAC的位數并不影響輸出頻率的解析度,輸出頻率的解析度是由頻率控制字的位數決定的,
5、怎么做出一個DDS
注意電賽清單說的是:DDS芯片或模塊,也就是意味著你可以買芯片自己設計電路板,也可以自己買DDS模塊,如果你有能力當然是直接買芯片自己畫板子,這樣你做出來的DDS肯定你那些直接買DDS模塊的同學更有優勢,當然如果你覺得難度比較大還是買一個DDS模塊吧!

如何選擇DDS
怎么選擇具體的哪一款DDS芯片還是要看你自己的預算和你的需求,今天主要講的DDS模塊是安富萊家的AD9833這一款DDS模塊,至于為啥選擇一款,因為19年電賽購買過這一款,價格也還便宜,電路和編程相對來說還是比較簡單的,強調一點這不是打廣告啊!

6、AD9833簡介
AD9833是ADI公司生產的一款低功耗,可編程波形發生器,能夠產生正弦波、三角波、方波輸出,波形發生器廣泛應用于各種測量、激勵和時域回應領域,AD9833無需外接元件,輸出頻率和相位都可通過軟體編程,易于調節,頻率暫存器是28位的,主頻時鐘為25MHz時,精度為0.1Hz,主頻時鐘為1MHz時,精度可以達到0.004Hz,
可以通過3個串行介面將資料寫入AD983,這3個串口的最高作業頻率可以達到40MHz,易于與DSP和各種主流微控制器兼容,AD9833的作業電壓范圍為2.3V-5.5V,
AD9833還具有休眠功能,可使沒被使用的部分休眠,減少該部分的電流損耗,例如,若利用AD9833輸岀作為時鐘源,就可以讓DAC休眠,以減小功耗,該電路采用10引腳MSOP型表面貼片封裝,體積很小,
AD9833特點
- 頻率和相位可數字編程
- 作業電壓為3V時,功耗僅為20mW
- 輸出頻率范圍為OHz-12.5MHz
- 頻率暫存器為28位(在25Mz的參考時鐘下,精度為0.1Hz)
- 可選擇正弦波、三角波、方波輸出
- 無需外界元件
- 3線SPI介面
- 溫度范圍為-40℃-+105℃
總結一下就是:這個模塊與單片機之間是通過SPI通信的方式,通過對芯片內部暫存器的操作可以調節模塊的資料頻率和相位,可輸出的頻率范圍是0—13.5MHZ,可以輸出正弦波、三角波和方波,
AD9833模塊電路圖

為了使大家比較好理解,我直接截取的成品模塊原理圖,
可以看到AD9833是一塊完全集成的DDS,僅需要1個外部參考時鐘、1個低精度電阻器和一個解耦電容器就能產生高達12.5Mz的正弦波,AD933的核心是28位的相位累加器,它由加法器和相位暫存器組成,每來1個時鐘,相位暫存器以步長增加,相位暫存器的輸岀與相位控制字相加后輸入到正弦査詢表地址中,正弦査詢表包含1個周期正弦波的數字幅度資訊,每個地址對應正弦波中0°-360°范圍內的1個相位點,
下面這張圖來自AD9833的資料手冊,可以看到每個引腳的功能說明都非常詳細,再配合上圖的電路原理圖就可以一目了然了!

接下來就是單片機如何與芯片的引腳相連,以及如何寫驅動代碼了,
7、AD9833驅動代碼
一般你在網上買到模塊后賣家一般都會送你實體代碼,可能實體代碼與你所用的單片機型號不同,但是大致的思路框架是一樣的,下面就以安富萊家的AD9833代碼為例,
說明:他家的平臺是STM32F407的平臺,也許你用的F103系列或者MSP430,但是啟動代碼都是的,你完全可以把驅動代碼的.c和.h檔案匯入到你的專案中即可,
功能描述
AD9833有3根串行介面線,與SPI、QSPI、DSP介面標準兼容,在串口時鐘SCLK的作用下,資料是以16位的方式加載到設備上,FSYNC引腳是片選使能引腳,電平觸發方式,低電平有效,進行串行資料傳輸時,FSYNC引腳必須置低,要注意 FSYNC有效到SCLK下降沿的建立時間的最小值,FSYNC置低后,在16個SCLK的下降沿資料被送到AD9833的輸入移位暫存器,在第16個SCLK的下降沿FSYNC可以被置高,但要注意在SCLK下降沿到FSYC上升沿的資料保持時間的最小和最大值,當然,也可以在 FSYNC為低電平的時候,連續加載多個16位資料,僅在最后一個資料的第16個SCLK的下降沿的時將 FSYNC置高,最后要注意的是,寫資料時SCLK時鐘為高低電平脈沖,但是,在 FSYNC剛開始變為低時,(即將開始寫資料時),SCLK必須為高電平(注意t11這個引數),
當AD9833初始化時,為了避免DAC產生虛假輸出,RESET必須置為1(RESET不會復位頻率、相位和控制暫存器),直到配置完畢,需要輸出時才將 RESET置為0;RESET為0后的8-9個MCLK時鐘周期可在DAC的輸出端觀察到波形,
AD9833寫入資料到輸出端得到回應,中間有一定的回應時間,每次給頻率或相位暫存器加載新的資料,都會有7-8個MCIK時鐘周期的延時之后,輸出端的波形才會產生改變,有1個MCIK時鐘周期的不確定性,因為資料加載到目的暫存器時,MCLK的上升沿位置不確定,
既然模塊要與單片機相連那肯定首先要確定使用那幾個引腳,因為他們之間是通過3線的SPI方式通信的
初始化GPIO
/* 定義GPIO埠 */
#define RCC_SCLK RCC_AHB1Periph_GPIOB
#define PORT_SCLK GPIOB
#define PIN_SCLK GPIO_Pin_3
#define RCC_SDATA RCC_AHB1Periph_GPIOB
#define PORT_SDATA GPIOB
#define PIN_SDATA GPIO_Pin_5
/* 片選 */
#define RCC_FSYNC RCC_AHB1Periph_GPIOF
#define PORT_FSYNC GPIOF
#define PIN_FSYNC GPIO_Pin_7
/* 定義口線置0和置1的宏 */
#define FSYNC_0() PORT_FSYNC->BSRRH = PIN_FSYNC
#define FSYNC_1() PORT_FSYNC->BSRRL = PIN_FSYNC
#define SCLK_0() PORT_SCLK->BSRRH = PIN_SCLK
#define SCLK_1() PORT_SCLK->BSRRL = PIN_SCLK
#define SDATA_0() PORT_SDATA->BSRRH = PIN_SDATA
#define SDATA_1() PORT_SDATA->BSRRL = PIN_SDATA
void bsp_InitAD9833(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
FSYNC_1(); /* FSYNC = 1 */
/* 打開GPIO時鐘 */
RCC_AHB1PeriphClockCmd(RCC_SCLK | RCC_SDATA | RCC_FSYNC, ENABLE);
/* 配置幾個推挽輸出IO */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; /* 設為輸出口 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; /* 設為推挽模式 */
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; /* 上下拉電阻不使能 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; /* IO口最大速度 */
GPIO_InitStructure.GPIO_Pin = PIN_SCLK;
GPIO_Init(PORT_SCLK, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = PIN_SDATA;
GPIO_Init(PORT_SDATA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = PIN_FSYNC;
GPIO_Init(PORT_FSYNC, &GPIO_InitStructure);
}
這幾句話大家應該不陌生,首先是使用宏定義來定義使用的GPIO口的時鐘、埠和輸出的高低電平,然后就是最基本的初始化相關的GPIO口了,這里使用的是PB3、PB5、PF3作為控制引腳,你需要根據的MCU選擇合適的引腳進行控制,
寫資料
/*
********************************************************
* 函 數 名: AD9833_Write_16Bits
* 功能說明: 向SPI總線發送16個bit資料 發送控制字
* 形 參: _cmd : 資料
* 返 回 值: 無
*******************************************************
*/
void AD9833_Write_16Bits(uint16_t _cmd)
{
uint8_t i;
SCLK_1(); //在時鐘上升沿下操作
FSYNC_0(); //片選打開
/* AD9833 SCLK時鐘高達40M,因此可以不延遲 */
for(i = 0; i < 16; i++)
{
if (_cmd & 0x8000)
{
SDATA_1();
}
else
{
SDATA_0();
}
SCLK_0();
_cmd <<= 1;
SCLK_1();
}
FSYNC_1();//片選關閉
}
FSYNC置低后,在16個SCLK的下降沿資料被送到AD9833的輸入移位暫存器,采用for回圈將資料發送出去,
選擇輸出波形
void AD9833_SelectWave(uint8_t _Type)
{
FSYNC_1(); //宏定義
SCLK_1();
if(_Type == 0)
{
AD9833_Write_16Bits(0x2028); /*頻率暫存器輸出方波*/
}
else if(_Type == 1)
{
AD9833_Write_16Bits(0x2002); /*頻率暫存器輸出三角波*/
}
else if(_Type == 2)
{
AD9833_Write_16Bits(0x2000); /*頻率暫存器輸出正弦波*/
}
else if(_Type == 3)
{
AD9833_Write_16Bits(0x00C0); /*無輸出*/
}
}
這里根據輸入的形參來選擇輸出的波形,至于這里為什么寫0x2002就輸出三角波,寫0x2000就輸出正弦波,那就要看芯片的資料手冊對于暫存器這一張章節的說明了,

看起來是不是感覺無從下手,但是我們不需要了解這些,因為具體要輸出哪種波形配置什么樣的引數已經告訴你了,直接用就可以,
設定頻率值
接下來就是撰寫配置輸出各種頻率的函式了,具體操作如下:
void AD9833_SetFreq(uint32_t _freq)
{
uint32_t freq;
uint16_t lsb_14bit;
uint16_t msb_14bit;
uint8_t freq_number = 0;
freq = (uint32_t)(268435456.0 / AD9833_SYSTEM_CLOCK * _freq);
lsb_14bit = (uint16_t)freq;
msb_14bit = (uint16_t)(freq >> 14);
if(freq_number == FREQ_0)
{
lsb_14bit &= ~(1U<<15);//0111 1111 1111 1111 先把第15位清0,其他位不變
lsb_14bit |= 1<<14; //0100 0000 0000 0000 再把第14位置1,其他位不變 結果就是01xx xxxx xxxx xxxx
msb_14bit &= ~(1U<<15); //同上
msb_14bit |= 1<<14;
}
else
{
lsb_14bit &= ~(1<<14); //1011 1111 1111 1111 先把第14位清0,其他位不變
lsb_14bit |= 1U<<15; //1000 0000 0000 0000 再把第15位置1,其他位不變 結果就是10xx xxxx xxxx xxxx
msb_14bit &= ~(1<<14); //同上
msb_14bit |= 1U<<15;
}
AD9833_Write_16Bits(lsb_14bit);
AD9833_Write_16Bits(msb_14bit);
}
這里面有一句關鍵的代碼就是freq = (uint32_t)(268435456.0 / AD9833_SYSTEM_CLOCK * _freq);結合芯片的資料手冊知道,另外這里面用到了暫存器操作的置位和清0操作,如果對暫存器操作比較熟悉得小伙伴一眼就能看出來,這里就不過多解釋了,
lsb_14bit &= ~(1U<<15);//0111 1111 1111 1111 先把第15位清0,其他位不變
lsb_14bit |= 1<<14; //0100 0000 0000 0000 再把第14位置1,其他位不變 結果就是01xx xxxx xxxx xxxx

到這里底層驅動函式已經基本完成了,接下來就是寫main函式了,在主函式中我們可以增加幾個按鍵進行調頻操作,
int main(void)
{
uint8_t ucKeyCode;
uint8_t ucChange;
uint32_t freq;
/*
由于ST韌體庫的啟動檔案已經執行了CPU系統時鐘的初始化,所以不必再次重復配置系統時鐘,
啟動檔案配置了CPU主時鐘頻率、內部Flash訪問速度和可選的外部SRAM FSMC初始化,
系統時鐘預設配置為72MHz,如果需要更改,可以修改:
\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\system_stm32f10x.c
中配置系統時鐘的宏,
*/
bsp_Init();//先初始化各種外設,自己寫
freq = 100000;
AD9833_SetFreq(freq); /*頻率freq單位HZ*/
ucChange = 1;
while (1)
{
if (ucChange == 1)
{
ucChange = 0;
AD9833_SetFreq(freq); /*設定頻率值 */
/* 列印當前的頻率值 */
if (freq < 1000)
{
printf("freq =%8dHz\r", freq);
}
else if (freq >= 1000 && freq < 1000000)
{
printf("freq =%3d.%03dKHz\r", freq / 1000, (freq % 1000) );
}
else if (freq >= 1000000)
{
printf("freq =%3d.%03d %03d %dMHz\r", freq / 1000000, (freq % 100000) / 1000,
((freq % 1000000) / 1000) / 100, freq % 10) ;
}
}
/* 按鍵濾波和檢測由后臺systick中斷服務程式實作,我們只需要呼叫bsp_GetKey讀取鍵值即可, */
ucKeyCode = bsp_GetKey(); /* 讀取鍵值, 無鍵按下時回傳 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
switch (ucKeyCode)
{
case KEY_DOWN_K1: /* K1鍵按下,輸出方波 */
AD9833_SelectWave(0);
ucChange = 1;
break;
case KEY_DOWN_K2: /* K2鍵按下,輸出三角波 */
AD9833_SelectWave(1);
ucChange = 1;
break;
case KEY_DOWN_K3: /* K3鍵按下,輸出正弦波 */
AD9833_SelectWave(2);
ucChange = 1;
break;
case KEY_DOWN_K4: /* K4鍵按下 */
if (freq >= 1000000) /* 1MHz 以上 */
{
if (freq < 25000000)
{
freq += 1000000;
ucChange = 1;
}
}
else if (freq > 1000) /* 1KHz 以上 */
{
freq += 1000;
ucChange = 1;
}
else if (freq > 100) /* 100Hz 以上 */
{
freq += 100;
ucChange = 1;
}
else
{
freq += 1;
ucChange = 1;
}
break;
case KEY_DOWN_K5: /* K5鍵按下 */
if (freq >= 1000000) /* 1MHz 以上 */
{
freq -= 1000000;
ucChange = 1;
}
else if (freq > 1000) /* 1KHz 以上 */
{
freq -= 1000;
ucChange = 1;
}
else if (freq > 100) /* 100Hz 以上 */
{
freq -= 100;
ucChange = 1;
}
else if (freq > 0) /*(0,100HZ)*/
{
freq -= 1;
ucChange = 1;
}
break;
case KEY_DOWN_K6: /* K6鍵按下*/
if (freq > 1000)
{
freq -= 100;
ucChange = 1;
}
else if (freq > 100) /* 100Hz 以上 */
{
freq -= 10;
ucChange = 1;
}
else if (freq > 0) /*(0,100HZ)*/
{
freq -= 1;
ucChange = 1;
}
break;
case KEY_DOWN_K7: /* K7鍵按下 */
if (freq > 1000) /*1KHZ以上*/
{
freq += 100;
ucChange = 1;
}
else if (freq > 100) /* 100Hz 以上 */
{
freq += 10;
ucChange = 1;
}
else if (freq > 0) /*(0,100HZ)*/
{
freq += 1;
ucChange = 1;
}
break;
case KEY_DOWN_K8: /* K8鍵按下 */
AD9833_SelectWave(3);
ucChange = 1;
break;
default:
/* 其它的鍵值不處理 */
break;
}
}
}
}
這里面的邏輯就很簡單了,按下對應的按鍵執行相應的操作,按鍵的個數與步進值可以根據自己的情況自己改寫,
8、DDS在線工具
最后給大家推薦一個幫助理解和用好DDS的一個在線工具—simDDS,
通過這個工具,你可以根據自己要實作的指標,來確定需要的濾波器的階數,然后借助濾波器的設計工具、仿真軟體就可以設計出滿足你系統系統要求的模擬電路部分,
巧妙利用DAC的鏡像,還可以實作通信中的上變頻功能,從而省去了本振、上變頻、濾波器等復雜的模擬電路,比如你要產生一個140MHz載頻的FM信號,可以使用150MHz的主時鐘,產生一個10MHz的FM信號,自然就會通過DAC鏡像得到一個140MHz和一個160MHz的FM信號,在140MHz處加一個帶通濾波器就可以得到你需要的FM信號,電路將變得非常簡單,利用這個方法,可以獲得更高頻率的調制信號,點擊下方原文鏈接,即可打開該在線工具,
公眾號后臺回復:DDS,即可獲取本文的驅動代碼以及相關檔案資料,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/291365.html
標籤:其他
上一篇:很多人說單片機很簡單,有些本專業學生為什么學起來這么吃力?
下一篇:#1~nginx安裝
