作者:Miler Shao 近日,從ST MCU技術論壇看到一個貼子,覺得有點意思,拿過來稍作整理交流下。 發帖者問: “我利用stm32f103要做PWM輸出,利用timer1 對 GPIO PE8, PE9 做輸出程式碼如下,當我將PE8,9設定為out_pp時利用示波器可以看到波形輸出,但是一設定成AF_PP時,示波器就看不到任何輸出了!所以想要請問,我下面的程式碼哪裡出錯了呢?”
從上面的繁體字和措辭不難看出發帖者極可能是港臺同胞。文字信息就這么多。另外發帖者還附加了下面一些程序配置代碼。 void Time_init(void) { TIM_TimeBaseInitTypeDef TIM1_TimeBaseInitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); RCC_APB2PeriphResetCmd(RCC_APB2Periph_TIM1,DISABLE); TIM1_TimeBaseInitStruct.TIM_Prescaler = 999; TIM1_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM1_TimeBaseInitStruct.TIM_Period = 8; TIM1_TimeBaseInitStruct.TIM_ClockDivision = 0x0; TIM1_TimeBaseInitStruct.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1,&TIM1_TimeBaseInitStruct); TIM_ClearITPendingBit(TIM1,TIM_IT_CC1); TIM_ITConfig(TIM1,TIM_IT_CC1,ENABLE); TIM_Cmd(TIM1,ENABLE); }
void pwm_init(void) { TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse = 120; TIM_OC1Init(TIM1,&TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 680; TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM1, ENABLE); TIM_ClearITPendingBit(TIM1, TIM_IT_CC1 );
TIM_ITConfig(TIM1,TIM_IT_CC1,ENABLE); TIM_Cmd(TIM1, ENABLE); }
void GPIO_Configuration(void) { GPIO_InitTypeDef g; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE| RCC_APB2Periph_AFIO, ENABLE);
g.GPIO_Pin = GPIO_Pin_9; g.GPIO_Mode = GPIO_Mode_AF_PP; g.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &g);
g.GPIO_Pin = GPIO_Pin_8 |GPIO_Pin_9 ; g.GPIO_Mode = GPIO_Mode_AF_PP; g.GPIO_Speed =GPIO_Speed_50MHz; GPIO_Init(GPIOE, &g); GPIO_PinRemapConfig(GPIO_PartialRemap_TIM1, ENABLE); }
int main(void) { RCC_Configuration(); NVIC_Configuration(); GPIO_Configuration(); Time_init(); pwm_init(); while(1); } void TIM1_CC_IRQHandler(void) //Interruptroutine { if (TIM_GetITStatus(TIM1, TIM_IT_CC1) != RESET) { TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
GPIOE-> ODR ^= GPIO_Pin_9; } }
可以看出,發帖者的代碼是基于ST 官方的傳統標準外設庫來寫的。上面代碼對TIM1的ARR、CCR1、CCR2及相關捕捉中斷做了配置并使能。對PE8/PE9做了GPIO及復用配置,開啟了TIM1復用腳的部分REMAP功能。針對上述問題,下面擬出幾點一起交流下。
1、看看上面紅色的捕捉中斷代碼。竟然發現有對GPIO口PE9的翻轉操作,感覺上他是希望利用捕捉中斷做GPIO翻轉來實現PWM輸出? 誠然,在使用有些其它品牌MCU芯片的時候,要實現PWM輸出可能不太方便,需借助定時中斷和GPIO翻轉來實現。這個過程實現起來往往并不是很方便,也有諸多局限性。對于ST MCU,不論STM8還是STM32所有芯片都能利用內部的定時器輕松實現PWM輸出。只需做些基本配置,給定信號周期和脈寬就好,無須借助CPU中斷來協助實現。
2、發帖者的描述信息中沒有給出完整的STM32芯片型號,不說完整芯片型號經常是個麻煩事。STM32有9大系列,幾百個料號。其實不少問題是跟具體料號息息相關的。所以,如果通過郵件或網絡咨詢時,提供完整的信息是必須的。  上圖是STM32F1系列參考手冊里關于TIM1復用功能REMAP的表格。
發帖者配置了PE8/PE9卻又只是做了PARTIAL REMAP, 結合上圖表格得知TIM1的OC1/OC2輸出只能出現在PA8/PA9。不知怎么又扯到PE8/PE9了。如果用PE8、PE9,那對應的OC輸出應該是OC1和OC1N這對互補輸出,這跟OC1/OC2又并不一樣,而且還得做FULL REMAP操作才行。也就是說,按照他的配置,在PE口是看不到TIM1_CH1/2的OC輸出的,具體到這里就是不能在PE口看到PWM輸出。
3、發帖者說當他把PE9的GPIO模式配置為OUT_PP時能看到脈沖,配置為AF_PP時又看不到,這是怎么回事呢? 在標準庫里對GPIO輸出模式有相關定義,這里的OUT_PP、AF_PP分別是指GPIO_MODE_OUT_PP和GPIO_Mode_AF_PP。前者指GPIO不做復用時的輸出配置;后者是指GPIO做復用輸出時的配置。 盡管發帖者的代碼有點混亂,但他的定時器1的基本配置還是能工作的,導致捕捉中斷能進入。前面提過,在捕捉中斷里他做了PE9的IO翻轉。作為普通GPIO口,即配置為GPIO_MODE_OUT_PP,當然能用示波器看到該腳翻轉的脈沖,但這并等同于來自STM32定時器硬件實現的PWM信號,純粹IO翻轉脈沖。而當PE9被配置為GPIO_Mode_AF_PP時,意味著它要輸出其它復用信號,而不是本身IO通道的信號。前面說了,按他現有配置,PWM輸出是到不了PE9的,此時看不到IO翻轉信號也就不難理解了。 下面是STM32F1系列GPIO管腳復用時的原理框圖 
4、發帖者的代碼混亂還有個地方,那就是關于PWM的脈寬和信號周期的設置。上面的代碼里信號周期設置為8個預分頻時鐘,而2個通道的脈寬卻配置為120和680個,即CCR比ARR大得多。按照這樣的配置,即使其它有關GPIO復用及REMAP的地方配置無誤,它也無法在相應管腳看到PWM跳變脈沖。因為兩個通道都沒有電平翻轉的機會,輸出一定是個固定電平。具體是高還是低跟PWM輸出模式與CCR的值有關。經常有人在參考庫代碼基礎上,機械地對個別數據一通神改,結果發現PWM出不來了。還比如,對DEADTIME參數的隨意修改,也會導致同樣問題。
5、STM32F1系列是目前STM32 九大系列中推出得最早的,其有關管腳復用配置個人覺得是最啰嗦的,沒有后面推出的STM32F0、F4、F3等系列的配置簡潔。另外印象中STM32F1系列也是唯一沒有二級加密保護的芯片。如果可能的話,在新品選型時不一定要拘泥于STM32F1,其實STM32家族中有很多性價比很好的型號可以選擇。再就是對于初學者的STM32開發,建議使用STM32CUBEMX做初始化配置。尤其涉及到管腳復用和重映射的地方,操作簡單快捷,不易出錯。也建議盡量使用ST官方推出CUBE固件庫,里面資源比傳統固件庫更為豐富。
6、程序代碼的正確,終究離不開對原理的清晰理解。不論有多好、多方便的工具,它不論代替你對原理的理解和把握。 |