【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
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM