• PWM DAC实验


    PWM DAC 简介

    有时候,STM32F4 自带的 2 路 DAC 可能不够用,需要多路 DAC,外扩 DAC 成本又会 高不少。此时,我们可以利用 STM32F4 的 PWM+简单的 RC 滤波来实现 DAC 输出,从而节省成本。 在精度要求不是很高的时候,PWM+RC 滤波的 DAC 输出方式,是一种非常廉 价的解决方案。

    PWM 本质上其实就是是一种周期一定,而高低电平占空比可调的方波。

    PWM 波形可以用分段函数表示:

     其中:T 是单片机中计数脉冲的基本周期,也就是 STM32F4 定时器的计数频率的倒数。 N 是 PWM 波一个周期的计数脉冲个数,也就是 STM32F4 的 ARR-1 的值。n 是 PWM 波一 个周期中高电平的计数脉冲个数,也就是 STM32F4 的 CCRx 的值。VH 和 VL 分别是 PWM 波的高低电平电压值,k 为谐波次数,t 为时间。我们将①式展开成傅里叶级数,得到公式 ②:

    从②式可以看出,式中第 1 个方括弧为直流分量,第 2 项为 1 次谐波分量,第 3 项为大 于 1 次的高次谐波分量。式②中的直流分量与 n 成线性关系,并随着 n 从 0 到 N,直流分量 从 VL 到 VL+VH 之间变化。这正是电压输出的 DAC 所需要的。因此,如果能把式②中除 直流分量外的谐波过滤掉,则可以得到从 PWM 波到电压输出 DAC 的转换,即:PWM 波 可以通过一个低通滤波器进行解调。 

    式②中的第 2 项的幅度和相角与 n 有关,频率为 1/(NT), 其实就是 PWM 的输出频率。该频率是设计低通滤波器的依据。如果能把 1 次谐波很好过滤 掉,则高次谐波就应该基本不存在了。 通过上面的了解,可以得到 PWM DAC 的分辨率,计算公式如下:

    分辨率=log2(N)

    这里假设 n 的最小变化为 1,当 N=256 的时候,分辨率就是 8 位。而 STM32F4 的定时器大部分都是 16 位的(TIM2 和 TIM5 是 32 位),可以很容易得到更高的分辨率,分辨率越高,速度就越慢。不过在本实验要设计的 DAC 分辨率为 8 位。 在 8 位分辨条件下,我们一般要求 1 次谐波对输出电压的影响不要超过 1 个位的精度, 也就是 3.3/256=0.01289V。假设 VH 为 3.3V,VL 为 0V,那么一次谐波的最大值是 2*3.3/ π=2.1V,这就要求我们的 RC 滤波电路提供至少-20lg(2.1/0.01289)=-44dB 的衰减。 STM32F4 的定时器最快的计数频率是 168Mhz,某些定时器只能到 84M,以 84M 频率为例,8 为分辨率的时候,PWM 频率为 84M/256=328.125Khz。如果是 1 阶 RC 滤波,则要求截止频率 2.07Khz,如果为 2 阶 RC 滤波,则要求截止频率为 26.14Khz。

    探索者 STM32F4 开发板的 PWM DAC 输出采用二阶 RC 滤波。

     二阶 RC 滤波截止频率计算公式为: f=1/2πRC 以上公式要求 R28*C37=R29*C38=RC。根据这个公式,计算出截止频 率为:33.8Khz 超过了 26.14Khz,这个和前面提到的要求有点出入,原因是该电路我们还需要用作 PWM DAC 音频输出,而音频信号带宽是 22.05Khz,为了让音频信号能够通过该低通滤波,同时为了标准化参数选取,所以确定了这样的参数。实测精度在 0.5LSB 左右。

    硬件设计

    我们使用 STM32F4 的 TIM9_CH2(PA3)输出 PWM,经过二阶 RC 滤波后,转换 为直流输出,实现 PWM DAC。同上一章一样,我们通过 ADC1 的通道 5(PA5)读取 PWM DAC 的输出,并在 LCD 模块上显示相关数值,通过按键和 USMART 控制 PWM DAC 的输 出值。我们需要用到 ADC 采集 DAC 的输出电压,所以需要在硬件上将 PWM DAC 和 ADC 短接起来

    从上图可知 PWM_DAC 的连接关系,但是这里有个特别需要注意的地方:因为 PWM_DAC 和 USART2_RX 共用了 PA3 引脚,所以在做本例程的时候,必须拔了 P9 上面 PA3(RX)的跳线帽(左侧跳线帽),否则会影响 PWM 转换结果。 在硬件上,我们还需要用跳线帽短接多功能端口的 PDC 和 ADC。 

     

    程序

    PWM初始化

    1. void TIM9_CH2_PWM_Init(u16 arr,u16 psc)
    2. {
    3. GPIO_InitTypeDef GPIO_InitStructure;
    4. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    5. TIM_OCInitTypeDef TIM_OCInitStructure;
    6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9,ENABLE);
    7. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    10. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    11. GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    12. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    13. GPIO_Init(GPIOA,&GPIO_InitStructure);
    14. GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_TIM9);
    15. TIM_TimeBaseStructure.TIM_Prescaler=psc;
    16. TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
    17. TIM_TimeBaseStructure.TIM_Period=arr;
    18. TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    19. TIM_TimeBaseInit(TIM9,&TIM_TimeBaseStructure);
    20. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    21. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    22. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    23. TIM_OCInitStructure.TIM_Pulse=0;
    24. TIM_OC2Init(TIM9, &TIM_OCInitStructure);
    25. TIM_OC2PreloadConfig(TIM9, TIM_OCPreload_Enable);
    26. TIM_ARRPreloadConfig(TIM9,ENABLE);
    27. TIM_Cmd(TIM9, ENABLE);
    28. }

    该初始化函数与PWM输出实验几乎一模一样,只有定时器和通道不同。

    main函数

    1. int main(void)
    2. {
    3. u16 adcx;
    4. float temp;
    5. u8 t=0;
    6. u16 pwmval=0;
    7. u8 key;
    8. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    9. delay_init(168); //初始化延时函数
    10. uart_init(115200); //初始化串口波特率为115200
    11. LED_Init(); //初始化LED
    12. Adc_Init(); //adc初始化
    13. KEY_Init(); //按键初始化
    14. TIM9_CH2_PWM_Init(255,0);
    15. TIM_SetCompare2(TIM9,pwmval); //初始值
    16. while(1)
    17. {
    18. t++;
    19. key=KEY_Scan(0);
    20. if(key==4)
    21. {
    22. if(pwmval<250)pwmval+=10;
    23. TIM_SetCompare2(TIM9,pwmval);
    24. }else if(key==2)
    25. {
    26. if(pwmval>10)pwmval-=10;
    27. else pwmval=0;
    28. TIM_SetCompare2(TIM9,pwmval);
    29. }
    30. if(t==10||key==2||key==4)
    31. {
    32. adcx=TIM_GetCapture2(TIM9);;
    33. printf("PWM DAC:%d\t",adcx);
    34. temp=(float)adcx*(3.3/256);;
    35. printf("PWM DAC_V:%.2f\t",temp);
    36. adcx=Get_Adc_Average(ADC_Channel_5,20);
    37. temp=(float)adcx*(3.3/4096);
    38. printf("ADC_V:%.2f\r\n",temp);
    39. t=0;
    40. LED0=!LED0;
    41. }
    42. delay_ms(10);
    43. }
    44. }

    运行视频

    PWM DAC实验

  • 相关阅读:
    Linux系统配置及服务管理-06-存储管理
    龙蜥开发者说:海纳百川,有容乃大,我在龙蜥社区的升级之旅 | 第 11 期
    bpftrace:简便输出调试信息
    华为OD机考算法题:数字加减游戏
    zip包解压时报malformed input off : 0, length : 1
    ES6中的class对象和它的家人们
    数学基础之博弈论
    大模型应用:Prompt-Engineering优化原则
    二十八、CANdelaStudio实践-10服务(SessionControl)
    阿里云 短信服务——开启验证码防盗刷监控
  • 原文地址:https://blog.csdn.net/qq_64531765/article/details/126407667