|
本帖最后由 gujiamao 于 2015-5-3 10:41 编辑 最近要用到正弦波,就用STM32内部的DAC产生。 思路主要有以下三个: 1.用matlab或者excel产生一个N长度的正弦码表,如果要产生频率为f的正弦波,则每隔fclk / (N * f)个定时器中断给DAC的数据寄存器赋值一次(fclk是定时器中断频率); 2.根据定时器频率fclk和信号频率f计算出一个周期内点的个数N = fclk / f,动态生成正弦码表,则每个定时器中断给DAC的数据寄存器赋值一次; 先总结下,思路1和思路2的方法差不多,在频率的范围上,f = fclk / (N * M)(M是多少个中断赋值DAC),思路2的M为1,频率范围应该更大些; 在波形的美观上,思路1更好些,因为频率大的时候,2中的点数会少,导致波形中高频能量比较大; 但是,都是时钟的分频,比如说fclk / 8, fclk / 16,两者之间的频率是肯定产生不了的。 以上的方法都不尽如人意。 想到在学校里参加电子设计比赛时,用过AD9850产生过正弦波,为何不用DDS的原理实现呢? 1.高速dac; 2.正弦码表;3.相位累加器;4.低通滤波器,STM32都有,试一试呗。 3.DDS的方法:以下是个人的理解,不知道对不对。sin(ΔΦ) = sin(w*t) = sin(2*pi*f*Ts),其中f是要得的信号频率,Ts = 1 / fclk, fclk是定时器中断频率,相位Φ就是ΔΦ的不断累积。 程序里是这么做的,当串口输入f时,计算出delta_phase_temp = 2.0 * f / fclk,显然这是一个浮点数,总不能在中断内部计算浮点数吧,听同事说有一个Q15的数据格式,将浮点数 转化成short类型的方法,网上查了下,发现STM32的DSP库里有这样的东西,能够快速计算出某相位的正弦值(下面会介绍),接下来把delta_phase_temp 转换成Q15格式, delta_phase = float_to_Q15(delta_phase_temp),这样基本工作就OK了,接下来打开定时器中断,写中断服务程序,主要就是相位累加和计算出给DAC赋值的数字量, phase += delta_phase,相位累加够简单吧,给DAC赋值的数字量就是此相位对应的正弦值啦,这就用到了CMSIS的DSP库里面的东东了,第一次接触,感慨,聪明人真的太多了, Q15的格式不说了,避免浮点运算,选用查表法,自己怎么想不出来呢。 下面的函数就是把Q15格式的theta算成Q15格式的正弦值,函数我就不分析了,论坛里大大们一看就懂。 void arm_sin_q15(q15_t theta, q15_t* pSinVal) { q15_t x0; q15_t y0,y1; q15_t xSpacing = 0xB6; unsigned int i = 0; q15_t oneByXSpacing; q15_t out; unsigned int sign_bits; short firstX = 0x8000; i = (unsigned int)(theta - firstX) / (unsigned int)xSpacing; if(i < 0){ i = 0; } else if(i >= 358){ i = 358; } x0 = (q15_t)firstX + ((q15_t)i * xSpacing); y0 = SinTabQ15; y1 = SinTabQ15[i + 1u]; sign_bits = 8u; oneByXSpacing = 0x5A00; out =(((q15_t) (((int) (theta - x0) * oneByXSpacing) >> 16)) << sign_bits); *pSinVal = y0 + ((q15_t) (((int) (y1 - y0) * out) >> 15)); } 算出Q15格式的正弦值之后,当然还是转成0~1之间的浮点数啦,之后再进行DAC赋值,dac_ch2.write((int)(Q15_to_float(sin) * 4095)); 下午看了下效果还不错,当然没有AD9850产生的波形准而好看了。等去公司了,上点图看看。 相比思路1和2,思路3的明显方法好多了。 |
| 沙发,支持原创 |
| 路过看看。。。。。。。 |
微信公众号
手机版