本文作者:Miler Shao
近日有個(gè)工程師網(wǎng)絡(luò)留言反映:
最近在做一個(gè)項(xiàng)目關(guān)于輸入捕捉的,使用的單片機(jī)為STM8S105,用到STM8的輸入捕捉功能。利用庫(kù)函數(shù)TIME1CH1 捕捉PE0的頻率很準(zhǔn),但此程序基礎(chǔ)上將PE0口的頻率改為由 TIME2產(chǎn)生150HZPWM波形,同樣使用TIME1CH1 捕捉,捕捉到的頻率卻不對(duì)。用示波器查看TIME2 產(chǎn)生的PWM很準(zhǔn)的。怎么回事呢? 上面是該工程師的留言,咋看之下,紅色語(yǔ)句貌似就是互相矛盾的表述。呵呵將就下,很多人表述個(gè)東西就是任性,讓你猜。 順便提醒下,如果在類似論壇或郵件咨詢STMCU的技術(shù)問(wèn)題時(shí),最基本的一條要先把芯片型號(hào)說(shuō)完整,不要說(shuō)STM8,STM32,或者STM32F1,STM32F4等。對(duì)于STM8一定要說(shuō)出包括數(shù)字在內(nèi)的前10字符,對(duì)于STM32一定要說(shuō)出包括數(shù)字在內(nèi)的前11字符。這樣人家才能看出該芯片的管腳數(shù)和FLASH容量大小,很多問(wèn)題跟二者息息相關(guān)。再就是盡可能把問(wèn)題描述清楚。 
拉回來(lái)繼續(xù)剛才的話題。他提供的信息除了上面一段話外,再就是系統(tǒng)時(shí)鐘配置和定時(shí)器初始化代碼以及輸入捕捉測(cè)量的函數(shù)。他的意思應(yīng)該是說(shuō)在利用TIM1的CH1的捕捉功能對(duì)外來(lái)脈沖做頻率測(cè)量時(shí),當(dāng)待測(cè)信號(hào)頻率低到一定程度時(shí)就測(cè)不準(zhǔn),頻率較高時(shí)則正常。
void TimeCapture(void) { TIM1_ICPolarity = TIM1_ICPOLARITY_FALLING TIM1_ICPrescaler = TIM1_ICPSC_DIV8 。。!緸榱斯(jié)省篇幅,此處省卻部分初始化代碼】 /* wait a capture on CC1 */ while((TIM1->SR1 & TIM1_FLAG_CC1) !=TIM1_FLAG_CC1); /* Get CCR1 value*/ ICValue1 = TIM1_GetCapture1(); TIM1_ClearFlag(TIM1_FLAG_CC1); /* wait a capture on cc1 */ while((TIM1->SR1 & TIM1_FLAG_CC1) !=TIM1_FLAG_CC1); /* Get CCR1 value*/ ICValue2 = TIM1_GetCapture1(); TIM1_ClearFlag(TIM1_FLAG_CC1); /* Compute clock frequency */ ClockFreq = (TIM1_ICPSC_DIV8* TIM1ClockFreq) / (ICValue2 - ICValue1); } 從代碼來(lái)看,他使用的STM8S TIM1輸入捕捉的例程,下降沿捕捉。結(jié)合他的描述和現(xiàn)象分析,代碼配置應(yīng)該沒(méi)啥問(wèn)題了。我看到他的初始化代碼里使用16M HIS做系統(tǒng)時(shí)鐘,供給TIM1的時(shí)鐘沒(méi)有分頻,即16M。他對(duì)輸入捕捉事件做了8分頻,就是上面代碼中的紅色語(yǔ)句。  他做這個(gè)8分頻,意味著每8個(gè)下降沿才捕捉1次。150Hz本來(lái)就夠慢了的,還要每8個(gè)脈沖才捕捉一次,意味著相鄰兩次捕捉動(dòng)作的間隔就更長(zhǎng)。而這個(gè)例程代碼并沒(méi)有考慮到計(jì)數(shù)器溢出問(wèn)題,或者說(shuō)它是假設(shè)中間不會(huì)發(fā)生計(jì)數(shù)器溢出而設(shè)計(jì)的。
直覺(jué)和經(jīng)驗(yàn)告訴我,很可能兩次捕捉間隔過(guò)長(zhǎng)導(dǎo)致中途有計(jì)數(shù)器溢出事件發(fā)生,如果這樣自然測(cè)算不準(zhǔn)了。于是提醒該他在做150Hz信號(hào)輸入捕捉測(cè)量時(shí)中途可能發(fā)生計(jì)數(shù)器溢出,建議其提高待測(cè)信號(hào)頻率驗(yàn)證。后來(lái)他反饋當(dāng)待測(cè)信號(hào)頻率提高到2.4K以上后就正確了【當(dāng)然他這個(gè)數(shù)字是隨意選定的】,意思是說(shuō)頻率高于一定數(shù)字后就OK了。 看來(lái)的確是因?yàn)橄噜弮纱尾蹲絼?dòng)作的間隔過(guò)長(zhǎng)導(dǎo)致中途有計(jì)數(shù)器溢出事件。我們可以大致計(jì)算下,假設(shè)定時(shí)器時(shí)鐘為16M,不發(fā)生溢出情況下能捕捉的最大間隔,即最慢頻率應(yīng)該是16M/65536=24.4Hz, 這是最理想的情況,如果剛好錯(cuò)過(guò)了第一個(gè)捕捉沿后就得等到下個(gè)周期的捕捉沿,要想不發(fā)生溢出,待測(cè)頻率最少也得48Hz以上。  現(xiàn)在他的待測(cè)信號(hào)頻率為150Hz時(shí),加上做了捕捉事件8分頻,實(shí)測(cè)信號(hào)頻率相當(dāng)于18.7Hz,遠(yuǎn)低于不發(fā)生溢出要求的最慢頻率48Hz,不可避免的會(huì)發(fā)生溢出,導(dǎo)致測(cè)量錯(cuò)誤。
在代碼邏輯不變的情況下,我們可以考慮將定時(shí)器時(shí)鐘適當(dāng)做分頻處理,當(dāng)然不可無(wú)限制分頻,得兼顧測(cè)量精度需求。至于那個(gè)捕捉預(yù)分頻參數(shù)可以根據(jù)待測(cè)信號(hào)特征和測(cè)量需求調(diào)整。 總之,上面STM8S定時(shí)器捕捉參考例程有其使用前提或局限性的。例程是通過(guò)查詢方式實(shí)現(xiàn),每次開(kāi)啟定時(shí)器后必須立馬進(jìn)入捕捉,測(cè)量過(guò)程中還不可發(fā)生溢出,否則測(cè)量就出問(wèn)題。如果有些應(yīng)用場(chǎng)合中途溢出難免,那就得對(duì)代碼做些調(diào)整。 其實(shí),在STM32定時(shí)器應(yīng)用時(shí)也可能碰到過(guò)類似問(wèn)題。曾經(jīng)有人反映用參考例程里的輸入捕捉代碼測(cè)量頻率時(shí)發(fā)現(xiàn)待測(cè)信號(hào)低到一定程度時(shí)就出現(xiàn)測(cè)量不正確的問(wèn)題。 下面是STM32F1系列固件庫(kù)輸入捕捉項(xiàng)目例程里的代碼: STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\TIM\InputCapture voidTIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET) { /* Clear TIM3 Capture compare interruptpending bit */ TIM_ClearITPendingBit(TIM3, TIM_IT_CC2); if(CaptureNumber == 0) { /* Get the Input Capture value */ IC3ReadValue1 = TIM_GetCapture2(TIM3); CaptureNumber = 1; } else if(CaptureNumber == 1) { /* Get the Input Capture value */ IC3ReadValue2 = TIM_GetCapture2(TIM3); /* Capture computation */ if(IC3ReadValue2 > IC3ReadValue1) { Capture = (IC3ReadValue2 - IC3ReadValue1); //N=0,未發(fā)生溢出; } else { Capture = ((0xFFFF - IC3ReadValue1) +IC3ReadValue2); //N=1發(fā)生1次溢出; } /* Frequency computation */ TIM3Freq = (uint32_t) SystemCoreClock /Capture; CaptureNumber = 0; } } } 這個(gè)例程代碼使用了捕捉中斷,程序也是基于一個(gè)前提:待測(cè)信號(hào)時(shí)間長(zhǎng)度不會(huì)長(zhǎng)于65536個(gè)定時(shí)器計(jì)數(shù)脈沖,相比上面的STM8S的例程應(yīng)該說(shuō)有所改進(jìn),中途最多允許溢出一次。偶爾有些人對(duì)上面例程紅色代碼的第2條感到疑惑,那就是指中途發(fā)生過(guò)一次溢出的情形。比如第一次捕捉到的計(jì)數(shù)器的值是0Xeb3f, 中途發(fā)生過(guò)一次溢出后計(jì)數(shù)器重新開(kāi)始計(jì)數(shù),再第二次捕捉到的計(jì)算器的值完全比前一個(gè)捕捉值小。那有無(wú)可能中途只溢出一次,第2次捕捉到的數(shù)據(jù)比第一次還大呢?不可能,不信你可以把問(wèn)題簡(jiǎn)化下。在紙上書(shū)寫(xiě)從0~9選擇任一個(gè)數(shù)字開(kāi)始的10個(gè)連續(xù)的數(shù)看看。如果測(cè)量時(shí)中途發(fā)生1次以上的溢出事件時(shí),該例程就有問(wèn)題了。 
也就是說(shuō),STM32參考代碼里該例程也是使用前提的,盡管說(shuō)它能滿足大部分的應(yīng)用,作為代碼還是其局限性,當(dāng)然代碼有局限性是絕對(duì)的,沒(méi)局限性的代碼猶如沒(méi)缺陷的人一樣不復(fù)存在。[就此打住,別扯太遠(yuǎn)了。] STM32定時(shí)器的工作頻率高,很多情況下完全可以先做時(shí)鐘預(yù)分頻再做測(cè)量操作。如果個(gè)別應(yīng)用情形下,計(jì)數(shù)器會(huì)不可避免地可能溢出多次怎么辦呢?其實(shí)只要搞清了原理,代碼怎么寫(xiě)完全你自己掌握。在兩次捕捉之間,我們可以開(kāi)啟溢出中斷,并對(duì)溢出中斷次數(shù)計(jì)數(shù),假設(shè)中間發(fā)生了N次溢出中斷,始、止兩個(gè)捕捉數(shù)據(jù)value1和value2。除第一個(gè)溢出不做滿量程計(jì)算外,其它N-1 個(gè)溢出中斷為滿量程計(jì)數(shù)。 整個(gè)計(jì)數(shù)脈沖個(gè)數(shù)=(65536-value1)+(N-1)*65536+value2 上面等式中的N 可以是任意非負(fù)整數(shù)。不難看出當(dāng)N=0和N=1時(shí),整個(gè)計(jì)數(shù)脈沖個(gè)數(shù)跟上面ST官方參考例程里兩處紅色代碼描述的是吻合一致的。 或許有細(xì)心的人看到,官方例程里用的是65535,我這里是65536。我認(rèn)為這里應(yīng)該是65536,不過(guò)具體到應(yīng)用上,這1個(gè)脈沖誤差完全可以忽略不計(jì)了。 上面提到的定時(shí)器都是假設(shè)其為16位的,所以計(jì)數(shù)器滿量程計(jì)數(shù)個(gè)數(shù)為65536。其實(shí)STM32家族中有不少系列都有32位的定時(shí)器,比如STM32F0,STM32F3,STM32F4,STM32F7,STM32L4系列中都帶32位定時(shí)器。 搞清了原理,具體的代碼實(shí)現(xiàn)也就不難了,多了個(gè)溢出中斷次數(shù)計(jì)數(shù),兩次捕捉間的計(jì)數(shù)脈沖個(gè)數(shù)統(tǒng)一用上面的等式即可。弄清了原理,就能自主地對(duì)參考性的東西做甄別和借鑒,然后自行調(diào)整相關(guān)軟硬件設(shè)計(jì)達(dá)到滿足應(yīng)用需求之目的。 |