加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

見(jiàn)鬼!PWM 沒(méi)有輸出和串口有啥關(guān)系?

2021/04/27
517
閱讀需 8 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

魚(yú)鷹在做一個(gè)項(xiàng)目時(shí),曾經(jīng)遇到一個(gè)問(wèn)題,8 路 PWM 輸出,有一個(gè)高級(jí)定時(shí)器死活無(wú)法輸出PWM,另一個(gè)高級(jí)定時(shí)器卻可以順利輸出,初始化配置完全是一樣的。

根據(jù)魚(yú)鷹的經(jīng)驗(yàn),定時(shí)器沒(méi)有輸出有幾個(gè)方面:

1、如果通過(guò)中斷翻轉(zhuǎn)電平輸出PWM,那么需要檢查是否進(jìn)入中斷(檢查中斷控制器是否開(kāi)啟中斷,外設(shè)相應(yīng)中斷是否開(kāi)啟)。

2、如果使用 PWM模式,一般問(wèn)題出在 IO復(fù)用功能和重映射上。如果沒(méi)有使用 IO的復(fù)用功能,那么它是不可能被定時(shí)器外設(shè)所驅(qū)動(dòng)的。而如果你的 IO不是該定時(shí)器默認(rèn)的輸出 IO,那么就需要進(jìn)行重映射。而STM32F1 和 STM32F4 的重映射機(jī)制是不一樣的。

 

可以看到 F4的重映射比較簡(jiǎn)單,直接和 GPIO綁定,不需要另外開(kāi)啟時(shí)鐘,并且我們可以從該函數(shù)的參數(shù)表中直接找到我們想要的對(duì)應(yīng) IO復(fù)用功能(上面的代碼代表 GPIOD_12的 TIM4 復(fù)用功能)。

但是 F1 分為部分映射,全映射,還可能有部分映射2……

沒(méi)有參考手冊(cè),你根本不知道這些映射對(duì)應(yīng)的到底是哪個(gè)引腳。此時(shí),我們打開(kāi)相應(yīng)參考手冊(cè),找到 GPIO那一章節(jié),AFIO 小節(jié),找到定時(shí)器部分,你可以看到所有定時(shí)器的IO映射關(guān)系。

從這張圖,我們可以得到以下幾點(diǎn)信息:

1)如果不開(kāi)啟重映射(沒(méi)有重映射),定時(shí)器2默認(rèn)輸出 IO為:PA0、PA1、PA2、PA3。2)部分重映射1 :PA15、PB3、PA2、PA3。3)部分重映射2:PA0、PA1、PB10、PB11。4)完全重映射:PA15、PB3、PB10、PB11。

從中我們也可以看到另一個(gè)坑,那就是可能端口換了,而你相應(yīng)的時(shí)鐘并沒(méi)有打開(kāi),導(dǎo)致初始化配置失敗,最終導(dǎo)致無(wú)法輸出。而使用 PB3 時(shí)必須禁用部分 JTAG引腳才行。

而在重映射上,魚(yú)鷹也確實(shí)踩了一個(gè)坑,到現(xiàn)在我也沒(méi)明白為什么。當(dāng)時(shí)在程序開(kāi)始關(guān)閉了部分 JTAG引腳功能,然后再初始化定時(shí)器,并且開(kāi)啟相應(yīng)重映射,最終還是沒(méi)有任何輸出,魚(yú)鷹甚至直接查看最終的 AFIO寄存器的值,但情況就是配置的值一樣,但還是無(wú)法輸出,所幸的是,當(dāng)我把復(fù)用功能在禁用部分 JTAG引腳后再開(kāi)啟所有定時(shí)器的重映射功能,發(fā)現(xiàn)能用了……

3、GPIO 的時(shí)鐘沒(méi)有打開(kāi),或者打開(kāi)錯(cuò)誤。

RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOA ENABLE);

比如像這樣,用 APB1的函數(shù)打開(kāi) APB2外設(shè)的時(shí)鐘,當(dāng)然會(huì)出現(xiàn)問(wèn)題,當(dāng)然這種坑還是蠻容易檢查出來(lái)的

4、高級(jí)定時(shí)器的坑:

要想高級(jí)定時(shí)器輸出脈沖,必須在初始化后增加一條語(yǔ)句:TIM_CtrlPWMOutputs(TIM8, ENABLE);沒(méi)有這個(gè)函數(shù),定時(shí)器是不可能輸出脈沖的。畢竟是高級(jí)定時(shí)器,很傲嬌,也很任性。

而因?yàn)樗厥庑?,魚(yú)鷹也是在這里載了一個(gè)大跟頭,這也是魚(yú)鷹為什么要寫(xiě)這篇筆記的原因了。

定時(shí)器1 和定時(shí)器 8 同屬于高級(jí)定時(shí)器,但定時(shí)器 8 可以正常輸出,而定時(shí)器 1 卻沒(méi)法輸出,這是怎么回事?為了解決這個(gè)問(wèn)題,魚(yú)鷹專門(mén)把定時(shí)器初始化部分封裝成了一個(gè)函數(shù),只留一個(gè)定時(shí)器作為參數(shù)部分:

 

但最終的結(jié)果還是沒(méi)法輸出,這不應(yīng)該的啊,都是一樣的函數(shù),一樣的配置,怎么一個(gè)成功,一個(gè)失敗。認(rèn)真檢查了初始化函數(shù)的每一條語(yǔ)句,參數(shù)并沒(méi)有任何問(wèn)題。

那到底是什么問(wèn)題導(dǎo)致的?

最終魚(yú)鷹只能上網(wǎng)查找相關(guān)問(wèn)題了,有可能就有前輩踩過(guò)這種坑呢。經(jīng)過(guò)多方查找,魚(yú)鷹嘗試了各種辦法也沒(méi)有效果,最終死馬當(dāng)活馬醫(yī)的試了一個(gè)完全沒(méi)有道理的可能:把串口初始化函數(shù)屏蔽了。

 

試了之后,發(fā)現(xiàn)定時(shí)器真的有輸出了。那么這是怎么一回事?為什么串口還能干擾到定時(shí)器的輸出。這個(gè)時(shí)候就要說(shuō)一說(shuō)我們的棧了(關(guān)于棧,魚(yú)鷹寫(xiě)過(guò)一篇筆記《今天,你的棧溢出了嗎?》)。

很多關(guān)于棧的話題,基本都是棧溢出,但事實(shí)上,還有一個(gè)容易忽略的話題是,棧的值不確定。我們都知道,函數(shù)進(jìn)入時(shí)會(huì)進(jìn)行壓棧操作(用于保持寄存器的值),同時(shí)如果函數(shù)有局部變量,也可能會(huì)從棧中申請(qǐng)空間。

因?yàn)榫植孔兞坑猛昙礆?,不?huì)占用我們寶貴的 RAM資源,所以很多時(shí)候我們會(huì)選擇使用局部變量。而大部分網(wǎng)上參考例程在使用局部變量時(shí)并不規(guī)范,導(dǎo)致問(wèn)題的發(fā)生。

現(xiàn)在魚(yú)鷹解釋一下為什么串口函數(shù)會(huì)影響高級(jí)定時(shí)器的輸出,而其他普通定時(shí)器并沒(méi)有受影響。當(dāng)串口函數(shù)執(zhí)行時(shí),使用的棧比較大,而在定時(shí)器函數(shù)執(zhí)行時(shí),剛好使用了這部分已被修改的棧空間,并且使用時(shí)沒(méi)有初始化它,導(dǎo)致出現(xiàn)了問(wèn)題。

那為什么屏蔽了串口就沒(méi)有問(wèn)題呢?那是因?yàn)?a class="article-link" target="_blank" href="/tag/%E5%8D%95%E7%89%87%E6%9C%BA/">單片機(jī)開(kāi)始運(yùn)行時(shí),__main 函數(shù)會(huì)將??臻g全部清零(在運(yùn)行到main前完成該工作),如果不運(yùn)行串口函數(shù),那么棧中的臟數(shù)據(jù)就不會(huì)很多,那么定時(shí)器函數(shù)的局部變量即使不初始化,也可認(rèn)為就是 0,而這正是定時(shí)器需要的默認(rèn)值。

那么為什么普通定時(shí)器不受影響呢?拿 TIM_TimeBaseInitTypeDef 結(jié)構(gòu)體來(lái)舉例。

基本定時(shí)器一般會(huì)初始化這幾個(gè)變量,但是你查看該結(jié)構(gòu)體的定義時(shí)你會(huì)發(fā)現(xiàn):

該結(jié)構(gòu)體還有一個(gè)變量是專為高級(jí)定時(shí)器準(zhǔn)備的,你并沒(méi)有對(duì)它進(jìn)行初始化,此時(shí)它可能是任何值,并會(huì)直接賦值給高級(jí)定時(shí)器的寄存器中。而關(guān)于輸出的結(jié)構(gòu)體 TIM_OCInitTypeDef 更是如此,很多變量在基本定時(shí)器中不會(huì)使用,卻會(huì)在高級(jí)定時(shí)器中影響它的輸出功能。

所以為了解決這個(gè)問(wèn)題,有兩個(gè)辦法:1、使用庫(kù)函數(shù)提前初始化局部變量:

這樣后續(xù)就不用關(guān)心不需要的變量了。2、直接在初始化時(shí),將所有的結(jié)構(gòu)體成員變量都初始化一遍,確定沒(méi)有任何一個(gè)變量遺漏:

魚(yú)鷹推薦第二種辦法,這樣高級(jí)定時(shí)器和普通定時(shí)器的代碼可以統(tǒng)一,也能更直觀的看出函數(shù)提供的功能。當(dāng)然,兩種方法同時(shí)使用也是沒(méi)有任何問(wèn)題的。

以上就是魚(yú)鷹踩的坑,希望對(duì)各位道友有所幫助。咱們下期再見(jiàn)!

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

六年開(kāi)發(fā)經(jīng)驗(yàn),豐富的KEIL調(diào)試經(jīng)驗(yàn),STM32使用經(jīng)驗(yàn),C語(yǔ)言運(yùn)用經(jīng)驗(yàn)。