這個(gè)寄存器多余了嗎?
上次寫(xiě)過(guò)一篇筆記《介紹一個(gè)高效無(wú)隱患輸出 IO 的方法》,介紹了如何避免直接操作 ODR 導(dǎo)致的隱患問(wèn)題,然后有道友留言對(duì)以下代碼提出了疑問(wèn):
void out_data(uint8_t byte) { GPIOA->BSRR = ((uint16_t)byte << 8); // set byte = ~byte; // 打斷后續(xù)運(yùn)行 GPIOA->BRR = ((uint16_t)byte << 8); // reset }
那就是兩條操作寄存器的代碼間如果產(chǎn)生中斷或者任務(wù)切換了,會(huì)不會(huì)產(chǎn)生影響,它的電平是否處于不穩(wěn)定狀態(tài)。
這里魚(yú)鷹來(lái)解釋一下,并給出具體的解決方案。
首先中斷影響,因?yàn)橹袛嗵幚頃r(shí)間幾乎在微妙級(jí)別,如果你的中斷處理在毫秒級(jí)別,那么你的系統(tǒng)實(shí)時(shí)性一定不怎么高。
所以中斷在這里造成的影響比較小。
為什么明明分開(kāi)操作了,影響還是比較小呢,這是因?yàn)槿绻氵@個(gè)代碼用于并口總線驅(qū)動(dòng),那么總線驅(qū)動(dòng)一般會(huì)用另外的 IO 變化來(lái)確定并口數(shù)據(jù)的穩(wěn)定性。
比如說(shuō) SPI 總線(非并口),會(huì)定義 CLK 上升沿或下降沿才開(kāi)始采集數(shù)據(jù),并口一般也有這樣的規(guī)定,這樣就保證了即使并口數(shù)據(jù)沒(méi)有一次性輸出,因?yàn)榱硗獾男盘?hào)線沒(méi)有產(chǎn)生下降沿或下降沿,從機(jī)也不會(huì)對(duì)并口上的數(shù)據(jù)采樣的。
但是不能保證有些并口總線規(guī)范會(huì)定義最長(zhǎng)的時(shí)間,但即使有,微秒級(jí)別也差不多沒(méi)什么問(wèn)題。
但還有一種情況是系統(tǒng)使用了 RTOS 。
這樣會(huì)導(dǎo)致切換到另一個(gè)線程,而這個(gè)線程的執(zhí)行時(shí)間根本不確定,執(zhí)行毫秒級(jí)別是正常的事情,所以,這種情況該如何處理呢?
兩種方法,關(guān)中斷或關(guān)調(diào)度器。(關(guān)于這些內(nèi)容可以看歷史文章,比如《信號(hào)量保護(hù)之禁止中斷》,《嵌入式系統(tǒng)優(yōu)先級(jí)詳解》等)
但是對(duì)于幾行代碼就要使用這些代碼,還是太奢侈了一些,雖然對(duì)系統(tǒng)的效率影響不大,但畢竟還是不爽,那么是否有更好的辦法可以解決這個(gè)問(wèn)題。
有個(gè)道友的留言提醒了魚(yú)鷹,就是有一個(gè)寄存器是可以同時(shí)操作 set 和 reset 的。
以前初學(xué) STM32 的時(shí)候,看到這個(gè)寄存器可以同時(shí)操作 set 和 reset,而另一個(gè)寄存器也可以操作 reset,以為功能重復(fù)了,誰(shuí)知道在這里等著魚(yú)鷹呢。
這個(gè)寄存器就是 BSRR。
通過(guò)它,就能用一條語(yǔ)句完成多個(gè) IO 的同步操作。
(這里有個(gè)錯(cuò)誤,應(yīng)該是操作 32 bit,畢竟它在庫(kù)函數(shù)中可是 32 bit 數(shù)據(jù)。另外特別注意框出來(lái)的地方,不過(guò)除非你的代碼有問(wèn)題,不然沒(méi)人會(huì)沒(méi)事同時(shí)操作一個(gè) IO 的 BSy、BRy)。
在這里特別感謝這幾位道友的留言提醒。
上面的代碼可以改成這樣:
// 僅用于 8 位數(shù)據(jù) 假設(shè)使用 PA8~15 void out_data(uint8_t byte) { GPIOA->BSRR = ((uint32_t)((uint8_t)~byte) << (0 + 16)) | ((uint32_t)byte << 0); // GPIOA->BSRR = ((uint32_t)(~byte) << (8 + 16)) | ((uint32_t)byte << 8); // 錯(cuò)誤寫(xiě)法 } // 使用宏,更高效,任意位數(shù) #define GPIOA_RESET_SET(data, offset, msk) GPIOA->BSRR = ((uint32_t)(~(data) & (msk)) << ((offset) + 16)) | ((uint32_t)((data) & (msk)) << (offset)) // 等效代碼 PA8~PA15 輸出 #define GPIOA_RESET_SET_BYTE(byte) GPIOA_RESET_SET(byte, 8, 0xff)
如果你的代碼對(duì)性能要求比較高,建議使用宏,但同時(shí)你需要知道里面還有一個(gè) & 的計(jì)算,這個(gè)會(huì)在運(yùn)行時(shí)計(jì)算,而對(duì)于固定的 8 bit 數(shù)據(jù),這個(gè)計(jì)算可以通過(guò)強(qiáng)制轉(zhuǎn)化去掉(就像前面的代碼一樣,并沒(méi)有 & 計(jì)算),這樣效率會(huì)更高,另一種方法是使用內(nèi)聯(lián)函數(shù),在使用上應(yīng)該會(huì)比宏更加安全(優(yōu)化級(jí)別提高的情況下)。
希望本期筆記對(duì)各位道友開(kāi)發(fā)項(xiàng)目有所幫助,如有幫助,歡迎轉(zhuǎn)發(fā)支持魚(yú)鷹。
*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。
逆變器相關(guān)文章:逆變器原理
逆變器相關(guān)文章:逆變器工作原理
光伏發(fā)電相關(guān)文章:光伏發(fā)電原理