日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用STM32测量脉宽可变的PWM波的脉冲宽度

發布時間:2023/12/29 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用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
該部分是在正點原子的源碼基礎上改動的,改動的地方我都標注了

#include "sys.h" #include "usart.h" #include "stm32f10x.h" #include "stdlib.h"//我自己加的 #include "string.h"//我自己加的 // //如果使用ucos,則包括下面的頭文件即可. #if SYSTEM_SUPPORT_OS #include "includes.h" //ucos 使用 #endif // //本程序只供學習使用,未經作者許可,不得用于其它任何用途 //ALIENTEK STM32開發板 //串口1初始化 //正點原子@ALIENTEK //技術論壇:www.openedv.com //修改日期:2012/8/18 //版本:V1.5 //版權所有,盜版必究。 //Copyright(C) 廣州市星翼電子科技有限公司 2009-2019 //All rights reserved //******************************************************************************** //V1.3修改說明 //支持適應不同頻率下的串口波特率設置. //加入了對printf的支持 //增加了串口接收命令功能. //修正了printf第一個字符丟失的bug //V1.4修改說明 //1,修改串口初始化IO的bug //2,修改了USART_RX_STA,使得串口最大接收字節數為2的14次方 //3,增加了USART_REC_LEN,用于定義串口最大允許接收的字節數(不大于2的14次方) //4,修改了EN_USART1_RX的使能方式 //V1.5修改說明 //1,增加了對UCOSII的支持 // // //加入以下代碼,支持printf函數,而不需要選擇use MicroLIB #if 1 #pragma import(__use_no_semihosting) //標準庫需要的支持函數 struct __FILE { int handle; }; FILE __stdout; //定義_sys_exit()以避免使用半主機模式 _sys_exit(int x) { x = x; } //重定義fputc函數 int fputc(int ch, FILE *f) { while((USART1->SR&0X40)==0);//循環發送,直到發送完畢 USART1->DR = (u8) ch; return ch; } #endif /*使用microLib的方法*//* int fputc(int ch, FILE *f) {USART_SendData(USART1, (uint8_t) ch);while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {} return ch; } int GetKey (void) { while (!(USART1->SR & USART_FLAG_RXNE));return ((int)(USART1->DR & 0x1FF)); } */#if EN_USART1_RX //如果使能了接收 //串口1中斷服務程序 //注意,讀取USARTx->SR能避免莫名其妙的錯誤 u8 USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節. //接收狀態 //bit15, 接收完成標志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字節數目 u16 USART_RX_STA=0; //接收狀態標記 void uart_init(u32 bound){//GPIO端口設置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘//USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶占優先級3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器//USART 初始化設置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串口接受中斷USART_Cmd(USART1, ENABLE); //使能串口1 }int temp;//我自己加的,非正點原子源碼void USART1_IRQHandler(void) //串口1中斷服務程序{u8 Res; #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.OSIntEnter(); #endifif(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾){Res =USART_ReceiveData(USART1); //讀取接收到的數據if((USART_RX_STA&0x8000)==0)//接收未完成{if(USART_RX_STA&0x4000)//接收到了0x0d{if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始else {//以下為我自己加的,非正點原子的源碼temp=atoi(USART_RX_BUF);if(temp>=1&&temp<=999){TIM_SetCompare2(TIM3,temp);printf("PWM ARR寄存器為999,OC寄存器為%d\n",temp);}USART_RX_STA|=0x8000; //接收完成了memset(USART_RX_BUF,0,sizeof(USART_RX_BUF));//以上為我自己加的,非正點原子的源碼}}else //還沒收到0X0D{ if(Res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收數據錯誤,重新開始接收 } }} } #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支持OS.OSIntExit(); #endif } #endif

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波的脉冲宽度的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。