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