电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
2021电赛预测—手把手带你玩转DDS模块AD9833
分 享
扫描二维码分享
2021电赛预测—手把手带你玩转DDS模块AD9833
DDS
AD9833
电赛
果果小师弟
关注
发布时间: 2021-07-30
丨
阅读: 2371
# 1、电赛预测 摘要:原定于7月28日才发布的器件清单,提前2天在26号就发布。我感觉大家现在应该猜题预测,果果觉得与其猜题,还不如静下心来做题。盲目猜题是没有必要的,熟悉相关器件倒尤为重要。不要到处去水群聊天,听风就是雨,题目要是能被猜中了,那只能说题目出的很差,都能被我们猜到。 ![ ](https://img-blog.csdnimg.cn/cac94e2bd07d474db11722f3baa9f1bf.jpg) 既然猜不到,那就别浪费时间,相反,把这些时间好好利用起来,把手上的事情继续完成,把该买的元器件买好,买晚了就贵了。买回来之后每个组件尽可能的去熟悉使用,准备好相关程序。 # 2、电赛清单 其实每年的带电赛主要元器件以及器材清单基本都差不多,只有很小的改动。2019年国赛年同样有DDS模块,今年照样有DDS模块。 ![ ](https://img-blog.csdnimg.cn/ac81d58483ee44e384de38842cc9102e.png) # 3、什么是DDS DDS直接数字式频率综合器 DDS(Direct Digital Synthesizer),实际上是一种分频器:通过编程频率控制字来分频系统时钟(SYSTEM CLOCK)以产生所需要的频率。DDS有两个突出的特点,一方面,DDS工作在数字域,一旦更新频率控制字,输出的频率就相应改变,其跳频速率高;另一方面,由于频率控制字的宽度宽(48bit 或者更高),频率分辨率高。你可以把他理解为一个信号源即信号发生器。 ![高配版DDS模块](https://img-blog.csdnimg.cn/42c7413e777b44fca95f7b8e9008ddf3.png) 电赛肯定不会让你自己搞这样的仪器做信号源,用你的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模块吧! ![聪明如我](https://img-blog.csdnimg.cn/adb47caee8cb4e9daef6a60313c01988.png) ## 如何选择DDS 怎么选择具体的哪一款DDS芯片还是要看你自己的预算和你的需求。今天主要讲的DDS模块是安富莱家的AD9833这一款DDS模块。至于为啥选择一款,因为19年电赛购买过这一款,价格也还便宜,电路和编程相对来说还是比较简单的。**强调一点这不是打广告啊!** ![ ](https://img-blog.csdnimg.cn/02036342ccc0423883f102493c578837.png) # 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模块电路图 ![波形发生电路](https://img-blog.csdnimg.cn/e9bac50ab9c742a9870452f7d199547b.png)![运算放大输出电路](https://img-blog.csdnimg.cn/9c5c5d0791a94f76a035d22a76443209.png)为了使大家比较好理解,我直接截取的成品模块原理图。 可以看到AD9833是一块完全集成的DDS,仅需要1个外部参考时钟、1个低精度电阻器和一个解耦电容器就能产生高达12.5Mz的正弦波。AD933的核心是28位的相位累加器,它由加法器和相位寄存器组成,每来1个时钟,相位寄存器以步长增加,相位寄存器的输岀与相位控制字相加后输入到正弦査询表地址中。正弦査询表包含1个周期正弦波的数字幅度信息,每个地址对应正弦波中0°-360°范围内的1个相位点。 下面这张图来自AD9833的数据手册,可以看到每个引脚的功能说明都非常详细,再配合上图的电路原理图就可以一目了然了! ![管脚功能描述](https://img-blog.csdnimg.cn/454ee419c69e4d91ba18087c59fc723c.png) 接下来就是单片机如何与芯片的引脚相连,以及如何写驱动代码了。 # 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 ```c /@@* 定义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选择合适的引脚进行控制。 ## 写数据 ```c /@@* ******************************************************** * 函 数 名: 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循环将数据发送出去。 ## 选择输出波形 ```c 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`就输出正弦波,那就要看芯片的数据手册对于寄存器这一张章节的说明了。 ![芯片手册第九页 ](https://img-blog.csdnimg.cn/bd90b5f6537a41689ea88550c028e3d9.png) ![芯片手册第十页](https://img-blog.csdnimg.cn/8e4685a8e4e1472ea1b01df71dcb4a85.png)看起来是不是感觉无从下手,但是我们不需要了解这些,因为具体要输出哪种波形配置什么样的参数已经告诉你了,直接用就可以。 ## 设置频率值 接下来就是编写配置输出各种频率的函数了,具体操作如下: ```c 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操作,如果对寄存器操作比较熟悉得小伙伴一眼就能看出来,这里就不过多解释了。 ```c lsb_14bit &= ~(1U<<15);//0111 1111 1111 1111 先把第15位清0,其他位不变 lsb_14bit |= 1<<14; //0100 0000 0000 0000 再把第14位置1,其他位不变 结果就是01xx xxxx xxxx xxxx ``` ![计算公式](https://img-blog.csdnimg.cn/c20dbde56ec4400baef68cd7caa6d250.png) ![ ](https://img-blog.csdnimg.cn/a9e5f7d57c5244f89b596e31b56d2791.png)到这里底层驱动函数已经基本完成了,接下来就是写main函数了,在主函数中我们可以增加几个按键进行调频操作。 ```c 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**。 ![ ](https://img-blog.csdnimg.cn/6c12ed7a97e849d3ba63f93b8a4b4836.png)通过这个工具,你可以根据自己要实现的指标,来确定需要的滤波器的阶数,然后借助滤波器的设计工具、仿真软件就可以设计出满足你系统系统要求的模拟电路部分。 巧妙利用DAC的镜像,还可以实现通信中的上变频功能,从而省去了本振、上变频、滤波器等复杂的模拟电路。比如你要产生一个140MHz载频的FM信号,可以使用150MHz的主时钟,产生一个10MHz的FM信号,自然就会通过DAC镜像得到一个140MHz和一个160MHz的FM信号,在140MHz处加一个带通滤波器就可以得到你需要的FM信号。电路将变得非常简单。利用这个方法,可以获得更高频率的调制信号。**点击下方原文链接,即可打开该在线工具**。 公众号后台回复:DDS,即可获取本文的驱动代码以及相关文档资料。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
1
)
果果小师弟
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交