作者:Miler Shao 近日,從ST MCU技術(shù)論壇看到一個(gè)貼子,覺得有點(diǎn)意思,拿過來(lái)稍作整理交流下。 發(fā)帖者問: “我利用stm32f103要做PWM輸出,利用timer1 對(duì) GPIO PE8, PE9 做輸出程式碼如下,當(dāng)我將PE8,9設(shè)定為out_pp時(shí)利用示波器可以看到波形輸出,但是一設(shè)定成AF_PP時(shí),示波器就看不到任何輸出了!所以想要請(qǐng)問,我下面的程式碼哪裡出錯(cuò)了呢?”
從上面的繁體字和措辭不難看出發(fā)帖者極可能是港臺(tái)同胞。文字信息就這么多。另外發(fā)帖者還附加了下面一些程序配置代碼。 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; } }
可以看出,發(fā)帖者的代碼是基于ST 官方的傳統(tǒng)標(biāo)準(zhǔn)外設(shè)庫(kù)來(lái)寫的。上面代碼對(duì)TIM1的ARR、CCR1、CCR2及相關(guān)捕捉中斷做了配置并使能。對(duì)PE8/PE9做了GPIO及復(fù)用配置,開啟了TIM1復(fù)用腳的部分REMAP功能。針對(duì)上述問題,下面擬出幾點(diǎn)一起交流下。
1、看看上面紅色的捕捉中斷代碼。竟然發(fā)現(xiàn)有對(duì)GPIO口PE9的翻轉(zhuǎn)操作,感覺上他是希望利用捕捉中斷做GPIO翻轉(zhuǎn)來(lái)實(shí)現(xiàn)PWM輸出? 誠(chéng)然,在使用有些其它品牌MCU芯片的時(shí)候,要實(shí)現(xiàn)PWM輸出可能不太方便,需借助定時(shí)中斷和GPIO翻轉(zhuǎn)來(lái)實(shí)現(xiàn)。這個(gè)過程實(shí)現(xiàn)起來(lái)往往并不是很方便,也有諸多局限性。對(duì)于ST MCU,不論STM8還是STM32所有芯片都能利用內(nèi)部的定時(shí)器輕松實(shí)現(xiàn)PWM輸出。只需做些基本配置,給定信號(hào)周期和脈寬就好,無(wú)須借助CPU中斷來(lái)協(xié)助實(shí)現(xiàn)。
2、發(fā)帖者的描述信息中沒有給出完整的STM32芯片型號(hào),不說(shuō)完整芯片型號(hào)經(jīng)常是個(gè)麻煩事。STM32有9大系列,幾百個(gè)料號(hào)。其實(shí)不少問題是跟具體料號(hào)息息相關(guān)的。所以,如果通過郵件或網(wǎng)絡(luò)咨詢時(shí),提供完整的信息是必須的。  上圖是STM32F1系列參考手冊(cè)里關(guān)于TIM1復(fù)用功能REMAP的表格。
發(fā)帖者配置了PE8/PE9卻又只是做了PARTIAL REMAP, 結(jié)合上圖表格得知TIM1的OC1/OC2輸出只能出現(xiàn)在PA8/PA9。不知怎么又扯到PE8/PE9了。如果用PE8、PE9,那對(duì)應(yīng)的OC輸出應(yīng)該是OC1和OC1N這對(duì)互補(bǔ)輸出,這跟OC1/OC2又并不一樣,而且還得做FULL REMAP操作才行。也就是說(shuō),按照他的配置,在PE口是看不到TIM1_CH1/2的OC輸出的,具體到這里就是不能在PE口看到PWM輸出。
3、發(fā)帖者說(shuō)當(dāng)他把PE9的GPIO模式配置為OUT_PP時(shí)能看到脈沖,配置為AF_PP時(shí)又看不到,這是怎么回事呢? 在標(biāo)準(zhǔn)庫(kù)里對(duì)GPIO輸出模式有相關(guān)定義,這里的OUT_PP、AF_PP分別是指GPIO_MODE_OUT_PP和GPIO_Mode_AF_PP。前者指GPIO不做復(fù)用時(shí)的輸出配置;后者是指GPIO做復(fù)用輸出時(shí)的配置。 盡管發(fā)帖者的代碼有點(diǎn)混亂,但他的定時(shí)器1的基本配置還是能工作的,導(dǎo)致捕捉中斷能進(jìn)入。前面提過,在捕捉中斷里他做了PE9的IO翻轉(zhuǎn)。作為普通GPIO口,即配置為GPIO_MODE_OUT_PP,當(dāng)然能用示波器看到該腳翻轉(zhuǎn)的脈沖,但這并等同于來(lái)自STM32定時(shí)器硬件實(shí)現(xiàn)的PWM信號(hào),純粹IO翻轉(zhuǎn)脈沖。而當(dāng)PE9被配置為GPIO_Mode_AF_PP時(shí),意味著它要輸出其它復(fù)用信號(hào),而不是本身IO通道的信號(hào)。前面說(shuō)了,按他現(xiàn)有配置,PWM輸出是到不了PE9的,此時(shí)看不到IO翻轉(zhuǎn)信號(hào)也就不難理解了。 下面是STM32F1系列GPIO管腳復(fù)用時(shí)的原理框圖 
4、發(fā)帖者的代碼混亂還有個(gè)地方,那就是關(guān)于PWM的脈寬和信號(hào)周期的設(shè)置。上面的代碼里信號(hào)周期設(shè)置為8個(gè)預(yù)分頻時(shí)鐘,而2個(gè)通道的脈寬卻配置為120和680個(gè),即CCR比ARR大得多。按照這樣的配置,即使其它有關(guān)GPIO復(fù)用及REMAP的地方配置無(wú)誤,它也無(wú)法在相應(yīng)管腳看到PWM跳變脈沖。因?yàn)?span style="color: red; font-size: 16px;">兩個(gè)通道都沒有電平翻轉(zhuǎn)的機(jī)會(huì),輸出一定是個(gè)固定電平。具體是高還是低跟PWM輸出模式與CCR的值有關(guān)。經(jīng)常有人在參考庫(kù)代碼基礎(chǔ)上,機(jī)械地對(duì)個(gè)別數(shù)據(jù)一通神改,結(jié)果發(fā)現(xiàn)PWM出不來(lái)了。還比如,對(duì)DEADTIME參數(shù)的隨意修改,也會(huì)導(dǎo)致同樣問題。
5、STM32F1系列是目前STM32 九大系列中推出得最早的,其有關(guān)管腳復(fù)用配置個(gè)人覺得是最啰嗦的,沒有后面推出的STM32F0、F4、F3等系列的配置簡(jiǎn)潔。另外印象中STM32F1系列也是唯一沒有二級(jí)加密保護(hù)的芯片。如果可能的話,在新品選型時(shí)不一定要拘泥于STM32F1,其實(shí)STM32家族中有很多性價(jià)比很好的型號(hào)可以選擇。再就是對(duì)于初學(xué)者的STM32開發(fā),建議使用STM32CUBEMX做初始化配置。尤其涉及到管腳復(fù)用和重映射的地方,操作簡(jiǎn)單快捷,不易出錯(cuò)。也建議盡量使用ST官方推出CUBE固件庫(kù),里面資源比傳統(tǒng)固件庫(kù)更為豐富。
6、程序代碼的正確,終究離不開對(duì)原理的清晰理解。不論有多好、多方便的工具,它不論代替你對(duì)原理的理解和把握。 |