zoukankan      html  css  js  c++  java
  • 【STM32】PWM DAC基本原理(实验:PWM实现DAC)

    虽然STM32F103ZET6具有内部DAC,但是也仅仅只有两条DAC通道,并且STM32还有其他的很多型号是没有DAC的。通常情况下,采用专用的D/A芯片来实现,但是这样就会带来成本的增加。

    不过STM32所有的芯片都有PWM输出,并且PWM输出通道很多,资源丰富。因此,我们可以使用PWM+简单的RC滤波来实现DAC的输出从而节省成本。

    PWM DAC
    PWM DAC的构成原理
    PWM本质上其实就是是一种周期一定,而高低电平占空比可调的方波。实际电路的典型PWM波形,如下图所示:

     

    针对PWM的波形进行以下分析:

    高电平阶段:计数器当前值从0-CCRx阶段(总时间=CCRx*每两个计数之间的间隔时间);
    低电平阶段:计数器当前值从CCRx-ARR-1阶段(总时间=(ARR-1-CCRx)*每两个计数之间的间隔时间)。
    如果PWM内容如果不太懂,可以参考链接:【STM32】通用定时器的PWM输出(实例:PWM输出)。

    根据PWM的波形,可以用分段函数来进行表示:

     

    其中:T是STM32中计数脉冲的基本周期,也就是STM32定时器的计数频率的倒数;N是PWM波一个周期的计数脉冲个数,也就是STM32的ARR-1的值;n是PWM波一个周期中高电平的计数脉冲个数,也就是STM32的CCRx的值;VH和VL分别是PWM波的高低电平电压值;k为谐波次数;t为时间。

    我们将分段函数①式展开成傅里叶级数,得到公式②:

     

    从②式可以看出,式中第1个方括弧为直流分量,第2项为1次谐波分量,第3项为大于1次的高次谐波分量。

    式②中的直流分量与n成线性关系,并随着n从0到N,直流分量从VL到VL+VH之间变化。而STM32的DAC功能也就是电压输出,这正是电压输出的DAC所需要的。

    因此,如果能把式②中除直流分量外的谐波过滤掉,则可以得到从PWM波到电压输出DAC的转换,即:PWM波可以通过一个低通滤波器进行解调。式②中的第2项的幅度和相角与n有关,频率为1/(NT),其实就是PWM的输出频率。该频率是设计低通滤波器的依据。如果能把1次谐波很好过滤掉,则高次谐波就应该基本不存在了。

    PWM DAC的具体实现
    通过上面的了解,我们可以得到PWM DAC的分辨率,计算公式如下:

    分辨率=log2(N)

    这里假设n的最小变化为1,当N=256的时候,分辨率就是8位。而STM32的定时器都是16位的,可以很容易得到更高的分辨率,分辨率越高,速度就越慢。不过我们在本章要设计的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的衰减。

    STM32的定时器最快的计数频率是72Mhz,8为分辨率的时候,PWM频率为72M/256=281.25Khz。如果是1阶RC滤波,则要求截止频率为1.77Khz,如果为2阶RC滤波,则要求截止频率为22.34Khz。

    二阶RC滤波截止频率计算公式为:

    f=1/2πRC

    以上公式要求R55=R56=R,C63=C64=C(R55*C63=R56*C64=RC)。根据这个公式,我们计算出图25.1.2的截止频率为:33.8Khz超过了22.34Khz,这个和我们前面提到的要求有点出入,原因是该电路我们还需要用作PWM DAC音频输出,而音频信号带宽是22.05Khz,为了让音频信号能够通过该低通滤波,同时为了标准化参数选取,所以确定了这样的参数。实测精度在0.5LSB以内。

    PWM DAC实例
    硬件连接
    单片机:STM32F103ZET6
    硬件资源:指示灯DS0,WK_UP和KEY1按键,ADC,PWM DAC
    具体的硬件连接的图如下所示:

     

    STM32控制程序
    //设置输出电压
    //vol:0~330,代表0~3.3V
    void PWM_DAC_Set(u16 vol)
    {
    float temp=vol;
    temp/=100;
    temp=temp*256/3.3;
    TIM_SetCompare1(TIM1,temp);
    }
    int main(void)
    {     
    u16 adcx;
    float temp;
    u8 t=0;     
    u16 pwmval=0;
    u8 key;
    delay_init();    //延时函数初始化    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    uart_init(115200);    //串口初始化为115200
    KEY_Init();    //KEY初始化
    LED_Init();    //LED端口初始化
    usmart_dev.init(72);    //初始化USMART    
    LCD_Init();    //LCD初始化
    Adc_Init();    //ADC初始化
    TIM1_PWM_Init(255,0);    //TIM1 PWM初始化, Fpwm=72M/256=281.25Khz.
    TIM_SetCompare1(TIM1,100);//初始值为0    
    
    
    POINT_COLOR=RED;//设置字体为红色 
    LCD_ShowString(60,50,200,16,16,"WarShip STM32");    
    LCD_ShowString(60,70,200,16,16,"PWM DAC TEST");    
    LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
    LCD_ShowString(60,110,200,16,16,"2015/1/15");    
    LCD_ShowString(60,130,200,16,16,"WK_UP:+ KEY1:-");    
    //显示提示信息    
    POINT_COLOR=BLUE;//设置字体为蓝色
    LCD_ShowString(60,150,200,16,16,"PWM VAL:");    
    LCD_ShowString(60,170,200,16,16,"DAC VOL:0.000V");    
    LCD_ShowString(60,190,200,16,16,"ADC VOL:0.000V");
    
    TIM_SetCompare1(TIM1,pwmval);//初始值    
    while(1)
    {
    t++;
    key=KEY_Scan(0);    
    if(key==WKUP_PRES)
    {    
    if(pwmval<250)pwmval+=10;
    TIM_SetCompare1(TIM1,pwmval); //输出
    }else if(key==KEY1_PRES)    
    {
    if(pwmval>10)pwmval-=10;
    else pwmval=0;
    TIM_SetCompare1(TIM1,pwmval); //输出
    }     
    if(t==10||key==KEY1_PRES||key==WKUP_PRES) //WKUP/KEY1按下了,或者定时时间到了
    {    
    adcx=TIM_GetCapture1(TIM1);
    LCD_ShowxNum(124,150,adcx,4,16,0); //显示DAC寄存器值
    temp=(float)adcx*(3.3/256);    //得到DAC电压值
    adcx=temp;
    LCD_ShowxNum(124,170,temp,1,16,0); //显示电压值整数部分
    temp-=adcx;
    temp*=1000;
    LCD_ShowxNum(140,170,temp,3,16,0x80); //显示电压值的小数部分
    adcx=Get_Adc_Average(ADC_Channel_1,20); //得到ADC转换值    
    temp=(float)adcx*(3.3/4096);    //得到ADC电压值
    adcx=temp;
    LCD_ShowxNum(124,190,temp,1,16,0); //显示电压值整数部分
    temp-=adcx;
    temp*=1000;
    LCD_ShowxNum(140,190,temp,3,16,0x80); //显示电压值的小数部分
    t=0;
    LED0=!LED0;    
    }    
    delay_ms(10);    
    
    }

    ---------------------
    作者:Yngz_Miao
    来源:CSDN
    原文:https://blog.csdn.net/qq_38410730/article/details/80113841
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    为什么你应该(从现在开始就)写博客
    ASP.net 中使用Flexigrid详细教程之二直接使用数据库数据(有图有真相)
    保护眼睛的方法 (眼睛累了吗 来看看吧)
    程序员不如快递员?
    项目管理界面
    地址栏射击游戏!对,你没看错,就是在地址栏上玩的游戏,有图有真相!
    书写是为了更好的思考
    IT人员如何找到自己的时间?
    std::mem_fun_ref,mem_fun1_ref分析
    __declspec(selectany) 的作用是什么
  • 原文地址:https://www.cnblogs.com/isAndyWu/p/9910998.html
Copyright © 2011-2022 走看看