使用STM32测量脉宽可变的PWM波的脉冲宽度
最近受疫情影響導致我莫得辦法出去玩,打游戲一不小心又給打通關了就只能找點東西玩玩了,所以就有了下面這篇文章。。。。。。搞這個東西的時候遇見一些好玩的問題,我寫在第6部分,希望能幫到看到這篇小文章的同志們。
1.硬件平臺:stm32f103zet6,正點原子的精英板
2.使用到的硬件:定時器3,定時器5,串口1,按鍵
3.描述:使用定時器3產生一個pwm波,占空比可通過串口調試助手調整。把pwm波通過IO和杜邦線輸入到定時器5的輸入捕獲通道以測量pwm波的高電平持續時間,單位為us
4.主要代碼
(1).main.c
//1.main.c #include "stm32f10x.h" #include "timer.h" #include "usart.h" #include "delay.h" int main(void) {NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);uart_init(115200);timer3_ch2_pwm_out(999,7199);//定時器2時鐘10khz,溢出時間為100ms,pwm波周期為100msTIM_SetCompare2(TIM3,499);//占空比50%,高電平時間為50mstimer5_ch1_capture(0XFFFF,71);//定時器3時鐘1Mhz,記一個數為1us,溢出時間為65536uswhile(1){if(USART_RX_STA&0x8000) {USART_RX_STA=0;//請注意這里,當字符串接收完成后(即USART_RX_STA&0x8000=1,在system下的usart.c內有相關代碼)請在接收中斷函數或main內將USART_RX_STA置零,否則將無法完成下次接收}//一般情況下在這里都會寫個delay_ms(10),但是這里延時之后會導致上面的if來不及執行,就會使得無法完成下次的串口接收} }(2).timer.c
#include "timer.h" #include "sys.h" void timer3_ch2_pwm_out(u16 arr,u16 psc) {GPIO_InitTypeDef GPIO_InitType;TIM_TimeBaseInitTypeDef TIM_TimeBaseInitType;TIM_OCInitTypeDef TIM_OCInitType;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);//部分重映射對應PB5,完全重映射對應PC7,不映射對應PA7,但是PA7并不能生成PWM波,其余兩個都可以GPIO_InitType.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitType.GPIO_Pin=GPIO_Pin_7;GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOC,&GPIO_InitType);TIM_TimeBaseInitType.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitType.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitType.TIM_Period=arr;TIM_TimeBaseInitType.TIM_Prescaler=psc;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitType);TIM_OCInitType.TIM_OCMode=TIM_OCMode_PWM2;TIM_OCInitType.TIM_OCPolarity=TIM_OCPolarity_High;TIM_OCInitType.TIM_OutputState = TIM_OutputState_Enable;TIM_OC2Init(TIM3,&TIM_OCInitType);TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);TIM_ARRPreloadConfig(TIM3,ENABLE);//使占空比的調整在本周期立即生效TIM_Cmd(TIM3,ENABLE);}void timer5_ch1_capture(u16 arr,u16 psc) {GPIO_InitTypeDef GPIO_InitType;TIM_TimeBaseInitTypeDef TIM_TimeBaseInitType;TIM_ICInitTypeDef TIM_ICInitType;NVIC_InitTypeDef NVIC_InitType;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitType.GPIO_Mode=GPIO_Mode_IPD;GPIO_InitType.GPIO_Pin=GPIO_Pin_0;GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitType);TIM_TimeBaseInitType.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseInitType.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseInitType.TIM_Period=arr;TIM_TimeBaseInitType.TIM_Prescaler=psc;TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitType);TIM_ICInitType.TIM_Channel=TIM_Channel_1;TIM_ICInitType.TIM_ICFilter=0x0;TIM_ICInitType.TIM_ICPolarity=TIM_ICPolarity_Rising;TIM_ICInitType.TIM_ICPrescaler=TIM_ICPSC_DIV1;TIM_ICInitType.TIM_ICSelection=TIM_ICSelection_DirectTI;TIM_ICInit(TIM5,&TIM_ICInitType);NVIC_InitType.NVIC_IRQChannel=TIM5_IRQn;NVIC_InitType.NVIC_IRQChannelCmd=ENABLE;NVIC_InitType.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitType.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitType);TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);TIM_Cmd(TIM5,ENABLE);}u8 timer3_ch2_capture_sta=0;//該變量的位0指示上次是否檢測到了高電平,1代表檢測到高電平;位1表示是否完成了一次先檢測高電平再檢測一次低電平的過程,1代表完成了 u8 overFlowTimer=0; u16 timer3_ch2_capture_val;void TIM5_IRQHandler() {if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET)//定時器3溢出{if(timer3_ch2_capture_sta&0x01){if(overFlowTimer>=0xFF)//溢出次數到最大值,強制結束本次高電平維持時間測量{timer3_ch2_capture_sta|=0x02;//標記本次捕獲完成,當然本行可以不要timer3_ch2_capture_val=0xffff;timer3_ch2_capture_sta=0;printf("高電平為%d微秒,到達最大時間范圍\n",256*65536+timer3_ch2_capture_val);}else{overFlowTimer++;}}}if(TIM_GetITStatus(TIM5,TIM_IT_CC1)==SET){if(timer3_ch2_capture_sta&0x01)//符合條件的話說明上次捕獲了高電平,那么這次捕獲的一定是低電平{timer3_ch2_capture_sta|=0x02;//標記本次捕獲完成,當然本行可以不要timer3_ch2_capture_val=TIM_GetCapture1(TIM5);TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);timer3_ch2_capture_sta=0;//清掉標志位準備開始下一次上升沿和下降沿檢測printf("高電平為%d微秒\n",overFlowTimer*65536+timer3_ch2_capture_val);}else{//timer3_ch2_capture_sta=0;overFlowTimer=0;timer3_ch2_capture_val=0;TIM_SetCounter(TIM5,0);//以上為清零timer3_ch2_capture_sta|=0x01;//高電平指示被賦值TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //當捕獲上升沿后改為捕獲下降沿}}TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中斷標志位,一定不要忘,要不然下次進不了中斷 }(3).usart.c
該部分是在正點原子的源碼基礎上改動的,改動的地方我都標注了
MDK5工程結構如下(使用的是正點原子提供的新建工程模板)
5.測試
程序最初設定的PWM波的周期是100ms,占空比為50%,也就是說理論上高電平持續時間為50000us。初始狀態下的測試結果如下(PC7連接PA0):
與理論值還是很接近的。接下來再使用串口調試助手改變占空比試試看,最開始時CCR2的值是499,這里我設定的能輸入的范圍是1到999。測試結果如下:
可以看到當輸出比較值被設定為1后高電平幾乎就是100ms,與理論很接近。
如上圖,當輸出比較寄存器的值被設定為999后高電平脈寬持續時間幾乎為0,與理論相符合。
我使用的捕獲通道是定時器5的通道1,連接在PA0,恰好PA0上面還連接一個按下為高電平輸入的按鍵,所以按按鍵也能測試。在前面的測試中考慮了高電平持續時間過長會溢出的問題,所以專門準備了一個變量來記錄溢出的次數,根據溢出的次數和捕獲通道計時器的值之和能得到準確的持續時間。定時器5的溢出時間是63356us,接下來試試看在有溢出的情況下能否測準:
圖中倒數第二次我一直按下按鍵不松手,到了大概十六秒多的時候自動結束測量,這是因為記錄溢出次數的變量是八位的,定時器的值是16位的,那么最大測量時間就是256*65536+0xffff,理論值是16842752us,與實測數據僅相差1us。如果換個16位的變量記錄溢出次數那記錄的時間范圍將大大延長。大家如果要試驗的話可以使用別的定時器或串口,修改起來也是很簡單的
6.遇到的一些問題
(1.)當pwm波出不來的時候先檢查兩遍用的定時器的配置,當找不著問題的時候不要懷疑自己,有可能代碼沒寫錯,可能只是硬件上由于IO連接了其他片內外設導致的。就比如我使用的定時器3的通道2,如果不重映射或者不部分重映射的話對應的IO引腳輸出不了pwm波。遇見這種情況建議換個通道,或者換個定時器,或者是映射一下試試看,總比看著代碼干瞪眼強。
(2.)測時間的時候不要只讀取一個捕獲通道的計數器值就完事了,如果不溢出的話那還是準確的,如果高電平比較長,那就不準確了。
(3.)定時器5的中斷服務函數里一定要手動清除中斷標志位,要不然下次就進不去中斷了。
(4.)一般我們習慣在main.c的while(1)里放一個延時,但這個延時可能會導致某些代碼來不及執行。就比如把我的while(1)里放個delay_ms(10)會導致上面的if語句來不及執行從而導致在第一次接收字符串完成后下次能進接收中斷但卻無法接收,因為USART_RX_STA沒有被清掉(大概是因為定時器5的時鐘頻率比較高導致的吧)
這些我在注釋里也有寫,大家看代碼的時候希望能注意一下
關于代碼有哪里看不懂的話可以留言,如果我看到的話會盡量回復。(我也只是個STM32的半吊子,如果能幫到大家的話還是會盡量幫的)
完整工程:https://download.csdn.net/download/naruhina/12124097
總結
以上是生活随笔為你收集整理的使用STM32测量脉宽可变的PWM波的脉冲宽度的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ISIS(中间系统到中间系统)
- 下一篇: 基于网页的个人音乐播放器系统 毕业设计毕