如何在 8051 單片機(jī)上編程一個(gè)阻塞延遲函數(shù)?
大家可能熟悉Arduino IDE 中的 delay() 函數(shù)。這是一個(gè)簡單的函數(shù),它提供了一個(gè)適用于Arduino 微控制器系列中所有成員的阻塞延遲。當(dāng)你過渡到裸機(jī)微控制器編程時(shí),你可能會(huì)發(fā)現(xiàn)自己在尋找類似的代碼。不幸的是,你不太可能 在8051 “標(biāo)準(zhǔn)庫”中找到這樣一個(gè)延遲函數(shù)。
本文引用地址:http://cafeforensic.com/article/202403/456719.htm在本文中,我們將簡要探討硬件延遲方法,然后使用一組嚴(yán)格定義的假設(shè)提出 8051 Busy Bee 解決方案。
這種明顯的 delay() 遺漏的最大原因可能是靈活性。了解 Arduino 代碼控制了底層微控制器的很大一部分。時(shí)鐘速度和專用計(jì)時(shí)器都是預(yù)定義的,隱藏在后臺(tái)。這種隱藏的一致性允許簡單的編程體驗(yàn),delay() 函數(shù)在所有平臺(tái)上執(zhí)行相同。否則需要 Arduino 程序員對(duì)外設(shè)的體系結(jié)構(gòu)和操作有大量的了解。這樣的行為違背了 Arduino 為初學(xué)者和業(yè)余愛好者提供可訪問性的目的。
在典型的 8051 環(huán)境中,情況就大不相同了。沒有什么是隱藏的,你通常需要配置所有必要的外圍設(shè)備。你還可以負(fù)責(zé)時(shí)鐘,并可以自由選擇高速外部,內(nèi)部,32.678 kHz 甚至深度睡眠,只有看門狗計(jì)時(shí)器定期喚醒微控制器。這些選項(xiàng)中的每一個(gè)都會(huì)損壞甚至停止一個(gè)函數(shù),如 delay() 按預(yù)期運(yùn)行。
找到一份 Busy Bee 參考手冊,讓我們開始吧。
技術(shù)貼士:術(shù)語阻塞意味著微控制器的主代碼在整個(gè)延遲期間被阻塞(什么都不做)。對(duì)于小的延遲和簡單的問題,這通常是可以接受的,但可能導(dǎo)致不可接受的操作。例如,當(dāng)阻塞延遲正在進(jìn)行時(shí),微控制器將對(duì)按鈕按下無響應(yīng)。這個(gè)問題的替代方案包括中斷和非阻塞延遲。
許多延遲選項(xiàng)是可用的
讓我們首先認(rèn)識(shí)到,在微控制器中構(gòu)造延遲函數(shù)的方法有很多種。一個(gè)簡短的列表可能包括:
■ 以 NOP (什么都不做指令)為特征的精心構(gòu)造的匯編代碼。在這里,程序員將根據(jù)每個(gè)匯編命令的特征計(jì)算微控制器的時(shí)鐘周期。
■ 帶有矢量中斷的硬件定時(shí)器。與延遲相關(guān)的操作可以嵌入到中斷服務(wù)例程( ISR )中,或者中斷可以維持類似于 Arduino millis() 函數(shù)的系統(tǒng)時(shí)間。
■ 免費(fèi)運(yùn)行硬件定時(shí)器而不中斷。這里硬件定時(shí)器是不斷運(yùn)行的。這類似于看一個(gè)帶模數(shù)60運(yùn)算的掛鐘。假設(shè)當(dāng)前時(shí)間是50秒,而你想要一個(gè)20秒的延遲。然后你會(huì)一直等到秒針到達(dá)10秒。在 8051 中,這樣的解決方案將根據(jù)選擇的計(jì)時(shí)器類型對(duì) 256 或 25536 取模。操作的“速度”取決于系統(tǒng)時(shí)鐘和定時(shí)器的預(yù)量程配置。
■ 不使用中斷的受控硬件定時(shí)器。使用這種方法,用戶程序?qū)⑼V苟〞r(shí)器,預(yù)加載定時(shí)器到一個(gè)已知值,啟用定時(shí)器,并等待溢出發(fā)生。
每種方法都有優(yōu)點(diǎn)和缺點(diǎn)。選擇在很大程度上取決于單個(gè)項(xiàng)目的需求、程序員的技能、未來的可維護(hù)性以及可用的硬件資源。例如,如果項(xiàng)目需要一致的心跳,帶有矢量中斷的硬件計(jì)時(shí)器就是一個(gè)非常好的解決方案。這種精確的周期性定時(shí)對(duì)于比例積分導(dǎo)數(shù)控制器或使用 DAC 生成波形來說是很好的。
阻塞延遲
在這篇文章中,我們將介紹一個(gè)使用無中斷硬件計(jì)時(shí)器的解決方案。回想一下,微控制器的硬件計(jì)時(shí)器通常就像時(shí)鐘的秒針一樣計(jì)數(shù)。在此代碼開發(fā)中使用的 Silicon Labs 的 EFM8BB1 8051 具有一個(gè)定時(shí)器集合,其中包括四個(gè)16位通用硬件定時(shí)器,向后兼容標(biāo)準(zhǔn) 8051。
16位定時(shí)器具有模數(shù)2^16運(yùn)算。與從0到60計(jì)數(shù)的時(shí)鐘一樣,16位計(jì)時(shí)器在回滾到 0 之前也會(huì)從 0計(jì)數(shù)到 65535。這個(gè)溢出事件是特殊的,因?yàn)橛布?huì)自動(dòng)設(shè)置一個(gè)中斷標(biāo)志。這個(gè)操作建議在高度簡化的圖1框圖中。
圖 1 : Busy Bee 定時(shí)器 #2 的簡化框圖。
技術(shù)貼士:外設(shè)的中斷標(biāo)志不會(huì)自動(dòng)啟動(dòng)中斷。只有在中斷或擴(kuò)展中斷使能電阻中設(shè)置了相關(guān)的中斷使能位時(shí),它才會(huì)這樣做。
這個(gè)中斷標(biāo)志是本文所附代碼中描述的阻塞延遲的關(guān)鍵。延時(shí)的操作描述如下:
■ 將合適預(yù)售和模式的定時(shí)器分別配置為 1:1 和16位自動(dòng)重載。還要使用sbit操作符別名運(yùn)行控制和中斷標(biāo)志。這些操作在while(1)超級(jí)循環(huán)之前執(zhí)行一次。
■ 關(guān)閉定時(shí)器并清除中斷標(biāo)志。
■ 用期望的延遲值加載計(jì)時(shí)器。
■ 啟用計(jì)時(shí)器的運(yùn)行位。
■ 在 while 循環(huán)中旋轉(zhuǎn)(什么都不做),直到中斷標(biāo)志。此操作將阻塞所有其他 main() 代碼,直到設(shè)置該標(biāo)志。
技術(shù)提示:最初的8051是一個(gè)獨(dú)特的架構(gòu),能夠在位級(jí)寄存器上操作。這導(dǎo)致了快速的代碼,而不需要使用掩碼來選擇寄存器內(nèi)的特定位。這是使用Keil C51特殊功能寄存器(Sbit)匯編器語句完成的。
在EFM8BB1等衍生產(chǎn)品中保留了這種向后能力。然而,較新的產(chǎn)品比最初的8051包含了更多的外設(shè)和相關(guān)的SFRs。不幸的是,并非所有的寄存器都是位可尋址的。只有以0x0或0x8結(jié)尾的寄存器才能使用這種方便快捷的位操作。仔細(xì)查看參考手冊,可以發(fā)現(xiàn)地址為0xC8的TMR2CN0是位可尋址的。
調(diào)用和數(shù)字開銷
在結(jié)束本文之前,我們需要考慮與調(diào)用函數(shù)和計(jì)算重載值相關(guān)的開銷。例如,這一小段代碼帶來了一個(gè)嚴(yán)重的問題:
tmr_load = -((n_us * 49) >> 1);
它試圖聰明地解釋每微秒24.5個(gè)時(shí)鐘節(jié)拍。它沒有使用float類型來解釋半比特,而是先乘以49,然后使用右移操作除以2;一個(gè)為高位,一個(gè)為低位,帶進(jìn)位。
最后一步是用216減去裝彈值。簡寫就是對(duì)結(jié)果求反。換一種說法,在模65536環(huán)境中,65536 - x提供的答案與0 - x相同,因?yàn)?5536 = 0(模65536)。這和60 = 0(模60)的時(shí)鐘是一樣的。
在下一行代碼中,我們將100添加到tmr_load變量中。這是一個(gè)粗略的方法,用于計(jì)算函數(shù)調(diào)用開銷以及計(jì)算tmr_load所需的時(shí)間?;叵胍幌?,Busy Bee沒有硬件乘法器。因此,通過8位ALU執(zhí)行8位乘16位乘法需要時(shí)間。
這個(gè)函數(shù)開銷的一個(gè)犧牲品是接受小延遲的能力。它的開銷大約為4us,因此不可能延遲更小的東西。如果需要這些小延遲,應(yīng)該使用前面提到的帶有NOP操作的手工編碼匯編程序。
真實(shí)世界的結(jié)果如圖2所示,我們看到一個(gè)引腳為5 us高,5 us低,然后又高。對(duì)于相關(guān)的blk_ms_delay也獲得了類似的結(jié)果,其中blk_ms_delay(2000)的調(diào)用在0.01 ms內(nèi),由 Digilent 的 471-060 (Analog Discovery)測量。你是否同意阻塞函數(shù)提供了合理的性能?
圖 2 :示波器測量現(xiàn)實(shí)世界的信號(hào),編程5路開和5路關(guān)。
結(jié)論
如前所述,所附代碼高度依賴于Busy Bee的時(shí)鐘、預(yù)分頻和定時(shí)器配置。你將需要修改代碼,以解釋任何偏離此代碼開發(fā)中所做假設(shè)的條件。
也許你會(huì)修改代碼,使用較慢的振蕩器來節(jié)省能量。還有一些技巧可以讓微控制器進(jìn)入深度睡眠狀態(tài)。然后,你可能希望以盡可能高的速度喚醒微控制器,以便在返回睡眠之前快速執(zhí)行操作。這種靈活性使得微控制器裸機(jī)編程比你過去可能使用的高級(jí)編程更加困難。這也是解鎖性能的關(guān)鍵。
評(píng)論