[原创] 用GD32做了个迷你DDS信号发生器
本帖最后由 tomzbj 于 2022-6-22 10:43 编辑大约10年前, 某次看到一个用AVR做的DDS信号源, 记得作者是Jasper Hansen, 感觉挺不错.
搜了一下, 链接居然还在:
http://www.radanpro.com/Radan2400/mikrokontroleri/Jesper's%20AVR%20pages%20-%20MiniDDS.htm
大概的原理:
用单片机的一组8位GPIO搭成R-2R DAC
建立一个256点的正弦波形表
执行这个累加器循环, 就可以输出任意频率的正弦波了.
void cloop(uint16_t step)
{
registeruint16_t counter = 0;
while(1){
counter += step;
PORTC = waveform;
}
return;
}
这个循环大概需要11个时钟周期, 用20M主频的ATTiny2313可以做到的最高DDS时钟是是20M/11=1.82MHz左右, 最高输出频率要再除以2, 不到0.91MHz. 把这个循环写成汇编的话可以优化到7个时钟周期, 最高能输出1.43MHz左右.
缺点么, 也很明显, CPU占用率是100%, 完全干不了别的了, 想加个屏幕加个按键什么的, 基本不可能.
从那之后我陆续用AVR和STM32各做了几次, STM32虽然比AVR快得多, 但在这个场合也没有太大优势, 循环体需要12个时钟周期, 72M的STM32F103也只能做到6M的DDS时钟.
某天突然想到, GD32F30x/F3x0能超频到280MHz, 用来做DDS岂不是很有优势? 最好不要用死循环的形式, 这样可以把屏幕按键都加上了.
先试试GPIO输出到底能有多快, 在开发板上用DMA按M2M方式直接写GPIO, 实测可以做到大约6.5个时钟周期更新一次, 果然不错. 不过更新周期不确定, 大概是因为DMA用M2M方式需要竞争总线, 能不能抢到要看运气. 看来得另想办法.
先画个小板, 原理图如下, GD32F350G8U6的PA0-PA7接成R2R DAC, 用高速运放GS8092缓冲输出(输出端忘了串50欧电阻了). 电荷泵SGM3204用于给运放提供负压. 此外就是128*32的OLED屏和两个按键, 没了.
支持国货, 除了不重要的AMS1117-3.3, 其他都是国产.
这个累加器循环怎么改呢? 先用定时器中断试试, 在中断服务程序里更新DAC. 结果呢, 不太理想, 中断服务程序需要37个时钟周期, 如果还要屏幕/按键/串口能响应,即使超频到240M,DDS时钟只能做到240M/50=4.8M左右, 不太实用.
另一个办法就是用DMA了, 用定时器触发DMA写GPIOA, 在DMA的半满和全满中断里重写波形数据表. 实测了一下, 刷新半张表也就是128个点, 需要1000个时钟周期多点, 这样可以做到每10个时钟周期输出一次, 同样超频240M, 可以做到24M的DDS时钟.
不过实测发现剩余200多个时钟周期不太够, 处理串口屏幕按键还是卡顿, 怎么办呢? 把中断这里的循环展开试试, 再测, 只需要700多个时钟周期, 这次操作比较流畅了.
操作么, 就俩按键, 短按上键切换输出幅度, 长按上键切换波形, 短按下键切换频率(可以预设4个频率), 长按下键进入设置菜单.
实测输出2M/3.58M/7.023M的频谱, 还可以吧? 7M时能看到右边17M的镜像峰了, sunzx给了个好办法, 后面缓冲级用自带6阶10M LPF的视频放大器即可.
原理图及程序见github链接:
https://github.com/tomzbj/diy/tree/master/2022/gd32_dds
操作视频
本帖最后由 Rabbitoose 于 2022-6-22 00:22 编辑
所以说你的工作经常要用到信号发生器吗?我一直想买一个信号发生器但是一直没有认真了解过。
以前翻帖子就看到过R2R,这次趁机学习一下 这是超了2.6倍吗。。有点狠啊。。不过24M的话很有诱惑力了,差不多五百块机器的频率了 cne53102 发表于 2022-6-22 01:30
这是超了2.6倍吗。。有点狠啊。。不过24M的话很有诱惑力了,差不多五百块机器的频率了 ...
(引用自4楼)
以前测试的极限, GD32F350, F303, FFPR都是336M左右, 不太稳定, 280M就很稳了.
GD32F103可以超到192M.
GD32F450ZET6可以超到400M.
见我的测试 https://github.com/tomzbj/dhrystone_score
上面这几个, 除了F450, 超频后性能基本都是STC8的100倍以上, 价格也就是10元上下, 真不知道STC那伙人有什么好吹的 GD的flash是有一块SRAM模拟区的,这一点确实非常有利于超频 tomzbj 发表于 2022-6-22 09:42
以前测试的极限, GD32F350, F303, FFPR都是336M左右, 不太稳定, 280M就很稳了.
GD32F103可以超到192M.
GD ...
(引用自5楼)
都是国货,人家有大火炉加持,你这个没有。{:titter:} cne53102 发表于 2022-6-22 01:30
这是超了2.6倍吗。。有点狠啊。。不过24M的话很有诱惑力了,差不多五百块机器的频率了 ...
(引用自4楼)
DDS时钟24M, 实际输出频率能到10M左右吧... 不加LPF的话, 频谱看着还好, 用示波器看波形已经很杂乱了. 楼主位第一个链接需要爱国吗?我点开就是404 liyang121316 发表于 2022-6-22 10:36
楼主位第一个链接需要爱国吗?我点开就是404
(引用自9楼)
奇怪, 我再粘贴一个
http://www.radanpro.com/Radan2400/mikrokontroleri/Jesper's%20AVR%20pages%20-%20MiniDDS.htm tomzbj 发表于 2022-6-22 10:43
奇怪, 我再粘贴一个
http://www.radanpro.com/Radan2400/mikrokontroleri/Jesper's%20AVR%20pages%20-%2 ...
(引用自10楼)
点开不行, 复制粘贴到地址栏可以...是老莫的bug么 网址没错, 应该是论坛的问题, 帖子外连断在 ' 符号, 可以手动复制去看 用DMA是最好的竞争总线的问题如何解决或者降低影响呢 用FSMC来实现会不会快很多? wowangru 发表于 2022-6-22 11:57
用DMA是最好的竞争总线的问题如何解决或者降低影响呢
(引用自13楼)
不用M2M呗
其实是移花接木了一下, 用TIMER触发DMA写DAC, 但是没有真的写到DAC, 而是写到了GPIOA
不然没法实现用TIMER触发写GPIO
GD32本身的DAC虽然比STM32快, 但和GPIO+R2R比还是太慢了 pspice 发表于 2022-6-22 12:03
用FSMC来实现会不会快很多?
(引用自14楼)
FSMC应该也是6个周期输出一次吧? 我忘了
关键是重写查找表,这个时间没法省 tomzbj 发表于 2022-6-22 12:07
不用M2M呗
其实是移花接木了一下, 用TIMER触发DMA写DAC, 但是没有真的写到DAC, 而是写到了GPIOA
不然没法 ...
(引用自15楼)
我就是用的APB2的tim1触发 DMA输出GPIO 但是也不是很稳定, IO时间精度不稳定
DMA_InitTypeDefDMA_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2ʱÖÓʹÄÜ
DMA_DeInit(DMA2_Stream5);
while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE){}
DMA_InitStructure.DMA_Channel = DMA_Channel_6;
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(GPIOC->ODR);
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)g_EXC_DATA;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = POINT_NUM2;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//
DMA_Init(DMA2_Stream5, &DMA_InitStructure);//³õʼ»¯DMA Stream
DMA_Cmd(DMA2_Stream5, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
TIM_TimeBaseInitStructure.TIM_Period=6; //
TIM_TimeBaseInitStructure.TIM_Prescaler=1;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure);
TIM_DMACmd(TIM1,TIM_DMA_Update,ENABLE);
TIM_Cmd(TIM1, ENABLE); 正弦波的最大输出频率是不是DDS时钟除以256? koon 发表于 2022-6-22 15:07
正弦波的最大输出频率是不是DDS时钟除以256?
(引用自18楼)
不是, DDS时钟除以2
不过这时波形基本没法看了 tomzbj 发表于 2022-6-22 15:35
不是, DDS时钟除以2
不过这时波形基本没法看了
(引用自19楼)
DDS时钟除以2,又没有滤波网络,这不是方波吗,这个正弦波不是256个点吗 tomzbj 发表于 2022-6-22 12:07
不用M2M呗
其实是移花接木了一下, 用TIMER触发DMA写DAC, 但是没有真的写到DAC, 而是写到了GPIOA
不然没法 ...
(引用自15楼)
那不还是存在DMA竞争总线吗
楼主厉害耶 thank you chenchaoting 发表于 2022-6-22 16:53
那不还是存在DMA竞争总线吗
(引用自21楼)
m2m的总线利用率低啊
不是m2m的话,别把总线全占完就行,输出频率是准的 直接十块钱dds芯片不香? NJ8888 发表于 2022-6-22 19:48
直接十块钱dds芯片不香?
(引用自24楼)
AD9833? 现在10元也只能买拆机件了吧, 新的起码30+了吧...
不知道有没有国内厂家克隆? koon 发表于 2022-6-22 16:44
DDS时钟除以2,又没有滤波网络,这不是方波吗,这个正弦波不是256个点吗
(引用自20楼)
是啊, 不能刚好到2, 得稍微低点.
你看7M多的频谱不是还可以么, 右边17M的镜像也好办, 弄个12M左右的LPF就滤掉了.
用MS1651之类的视频放大器, 内置10M的6阶LPF, 刚合适. koon 发表于 2022-6-22 16:44
DDS时钟除以2,又没有滤波网络,这不是方波吗,这个正弦波不是256个点吗
(引用自20楼)
三角波,就波峰波谷两个点 还是用FPGA来做最好。 厉害,膜拜一下 lyl520719 发表于 2022-6-23 02:17
还是用FPGA来做最好。
(引用自28楼)
有个国产DAC模块就是FPGA外加R2R电阻网络来实现的; 打不开 github了,以前在早上这个时间段还可以打开的。
页:
[1]