專注差異化嵌入式產(chǎn)品解決方案 給智能產(chǎn)品定制注入靈魂給予生命
提供開發(fā)工具、應(yīng)用測試 完善的開發(fā)代碼案例庫分享
從全面的產(chǎn)品導(dǎo)入到強(qiáng)大技術(shù)支援服務(wù) 全程貼心伴隨服務(wù),創(chuàng)造無限潛能!
提供新的芯片及解決方案,提升客戶產(chǎn)品競爭力
提供最新的單片機(jī)資訊,行業(yè)消息以及公司新聞動態(tài)
單片機(jī)方案開發(fā)商深圳英銳恩分享PIC單片機(jī)學(xué)習(xí)知識點(diǎn),菜鳥學(xué)PIC單片機(jī)(三)LCD時鐘的總結(jié),并由中斷暫禁的后果說開去。
上回說到剛接觸PIC沒20天的菜鳥碧水長天準(zhǔn)備"野心勃勃"寫一段LCD顯示精確時鐘的但遭到無情狙擊的故事,幸好得到這里行家的點(diǎn)撥,方能理清一點(diǎn)頭緒,于是,今天就接著上回的故事,總結(jié)一些通用的注意事項(xiàng),并對LCD顯示精確時鐘進(jìn)行功能實(shí)現(xiàn)上的分析.
一、先總結(jié)一些細(xì)節(jié)的問題,再分析功能實(shí)現(xiàn)上的缺陷:
1. 關(guān)于中斷現(xiàn)場的保護(hù)和恢復(fù)的問題
由于movf指令可以影響STATUS,而W又要在現(xiàn)場保護(hù)過程中起中轉(zhuǎn)寄存器的作用,因此,應(yīng)先保護(hù)W,再保護(hù)STATUS,最后是保存其他現(xiàn)場變
量。保存的時候應(yīng)注意,如果W的備份寄存器w_temp若不是位于快速存取區(qū)70H~7FH,假如w_temp定位為0x20,那么還需保證bank1,bank2,
bank3中的0xA0,0x120,0x1A0出的單元沒有被派做他用。如果fsr_temp,pclath_temp等也不是定義在快速存取區(qū)的話,那么,需注意在備份FSR
,PCLATH之前,要確保當(dāng)前操作在bank0處(當(dāng)然,在其他bank也可,但必須注意在恢復(fù)現(xiàn)場的時候,也要保證在相同的bank中對備份積存器進(jìn)
行操作,為了方便起見,建議控制在bank0處進(jìn)行保存和恢復(fù)操作)。
至于,備份寄存器若定位與快取區(qū)中,那么對bank沒有要求,但對次序的要求仍然存在的。
這是經(jīng)過改進(jìn)后的恢復(fù)和保存現(xiàn)場代碼:
ORG 0x000 ; processor reset vector
nop ; ICD need
goto main ; go to beginning of program
ORG 0x004 ; interrupt vector location
movwf w_temp ; 先保存W
movfw STATUS ; 再保存STATUS到W中
clrf STATUS ; 注意該指令,確保對status_temp,pclath_temp的操作在bank0中
; (如果備份寄存器定義在快取區(qū)中,可無取消此條clrf及恢復(fù)現(xiàn)場那條clrf指令)
movwf status_temp ; 保存上上條指令備份在W中的STATUS
movfw PCLATH ; 備份PCLATH
movwf pclath_temp
movfw FSR ; 備份FSR
movwf fsr_temp
; 可添加其他欲保護(hù)的變量
;******************** 中斷服務(wù)代碼
btfss INTCON,T0IE ; 判斷是否為T0中斷
goto other_int
btfss INTCON,T0IF ; it 's the time of T0 int
goto other_int
bcf INTCON,T0IF ; 是T0中斷,清除中斷標(biāo)志
movlw 0x10 ; 微秒的高位字節(jié)加上定時時間 256x16分頻=4096=0x1000的高位(0x10)
addwf us+1
goto end_int
other_int ; 可添加其他中斷服務(wù)代碼
nop ; other isr code can be added
;**********************************
end_int ; 恢復(fù)現(xiàn)場
clrf STATUS ; 確?;謴?fù)現(xiàn)場的操作在bank0中(如果備份寄存器定義在快取區(qū)中,可無取消此條指令)
; 可添加恢復(fù)其他變量
movfw fsr_temp ; 恢復(fù)FSR
movwf FSR
movfw pclath_temp ; 恢復(fù)PCLATH(FSR和PCLATH的恢復(fù)無先后之分)
movwf PCLATH
movfw status_temp ; 先恢復(fù)STATUS
movwf STATUS ;
swapf w_temp,f
swapf w_temp,w ; 最后恢復(fù)W,采用swapf是因?yàn)槠洳粫绊慡TATUS
retfie ; 中斷返回
;*********
2.(保留區(qū)域,待添加)
--------------------------------------------
二、分析功能實(shí)現(xiàn)上的缺陷,并由中斷響應(yīng)及子程序暫禁中斷所引起的問題說開去
先將昨天貼的源程序的main部分的代碼拿出來分析:
主程序要實(shí)現(xiàn)的功能是顯示時鐘:
HH MM SS
00:00:00
定時中斷每次產(chǎn)生4096us的增量,在中斷服務(wù)中,將此時間增量累加在(us+1:us)兩個相鄰的字節(jié)中,由_clock子程序
對(us+1:us)進(jìn)行及時判斷,超出50ms即取走一個50ms的增量,并保留余量在(us+1:us)中以保證長時間定時精確.
主程序流程:
main
nop
call _init ; 調(diào)用初始化子程序,清緩沖區(qū),實(shí)現(xiàn)液晶顯示器和TMR0的初始化操作.
call _disp1 ; 調(diào)用顯示字符" HH MM SS "的子程序
loop
call _clock ; 調(diào)用時間更新子程序,更新定時中斷產(chǎn)生的時間累加值
call _convert ; 調(diào)用時鐘的小時,分,秒的BCD碼轉(zhuǎn)換子程序,并換成字符對應(yīng)的ASCII碼
call _disp2 ; 調(diào)用轉(zhuǎn)換后的小時:分:秒字符的顯示子程序
goto loop ; 執(zhí)行主循環(huán)
分析如下:
由于_clcok和_convert碼制字符轉(zhuǎn)換子程序與時間顯示_disp2子程序是前后的順序關(guān)系的,在時間顯示時,前兩個子程序是不工作的,由于
LCM的慢顯特性,使得該子程序執(zhí)行時間較長,這樣,即使中斷定時時間已經(jīng)累計到應(yīng)改變顯示結(jié)果的條件,但此刻_disp2若仍在顯示上一時間
,使得_clcok不能及時更新時間,并且_convert不能轉(zhuǎn)換代碼,那么顯示結(jié)果仍然
沒有變化。當(dāng)loop循環(huán)執(zhí)行一次完畢之后,_clock和_convert才開始更新.
但是這里可能會有個疑問:既然如此,計算_disp2的執(zhí)行時間大概為500ms,當(dāng)_disp2子程序執(zhí)行完畢之后,那么也開始循環(huán)執(zhí)行_clock和
_convert,然后LCM再顯示,此刻應(yīng)該顯示的是更新的時間了吧,總時間也大概為1s多一點(diǎn),為何執(zhí)行結(jié)果大概等到1分鐘左右,秒?yún)^(qū)數(shù)字才加1呢?
問題提得很好。
思考原因可能為 :由于_clock不能及時更新時間,及不能及時取走(us+1:us)中大于50ms時的50ms量,但中斷服務(wù)代碼中始終嚴(yán)格執(zhí)行下面兩
條指令:
movlw 0x10 ; 256x16分頻=4096=0x1000的高位(0x10)
addwf us+1 ; 微秒的高位字節(jié)加上定時時間
多次累加后(15次累加令us+1單元的內(nèi)容為從00H到F0H)令us+1單元溢出,丟失定時的時間增量,若當(dāng)_clock更新時,(us+1:us)發(fā)生溢出使得其
值小于50ms(代數(shù)值50000),因此也不能使得變量ms50的值增加,那么秒鐘變量sec也不會變化,轉(zhuǎn)換后時間顯示仍然保持不變.
注意: 當(dāng)_clock更新時間時,(us+1:us)若滿足大于50 000的條件,則ms50變量加一,在main主程序中_clock循環(huán)更新時,若捕捉到20次
(us+1:us)單元大于50000(50ms)時,sec的值才能加1。而這個在多次更新過程中捕捉該條件的周期,就是秒?yún)^(qū)顯示加1的周期,我認(rèn)為這個周
期是固定的,也許是30秒,也許是1分鐘,也許更長,只要程序長度和結(jié)構(gòu)沒有發(fā)生變化。后來在程序中,我增加延時子程序的時間,結(jié)果秒?yún)^(qū)數(shù)字加1的間隔時間也跟著延長了。
到了這里,知道了問題所在,那么在基于原程序的框架下,我對幾種解決方案都嘗試了一下:
方案1:
[既然癥結(jié)是在_clock不能真實(shí)捕捉到每一次中斷時間累加增量(us+1:us)值大于50ms(50000)的條件,那么,將_clock內(nèi)嵌中斷中去,中
斷每一次改變us+1的值然后馬上進(jìn)行時間更新,這樣,使得_clock能真實(shí)捕捉每一次(us+1:us)值大于50ms(50000)的條件,也能真實(shí)更新系統(tǒng)時
間。]
方案1分析:這樣確實(shí)可以保證每一次都可以捕捉us時間增量,不考慮運(yùn)行的結(jié)果問題,該方案有幾個缺點(diǎn):
1) 中斷服務(wù)代碼由于調(diào)用了_clock子程序,顯得異常臃腫;
2) 每次中斷(4096us)都調(diào)用_clock,判斷其是否到50ms(值為50000),增加了程序的開銷,效率較低;
3) 由于LCM慢顯示特性的原因,可能使得結(jié)果仍然不能令人滿意:
關(guān)于3) 我描述一下一下:雖然此刻,秒?yún)^(qū)的數(shù)字能基本上每秒鐘跳變一次了,但是調(diào)試過程中出現(xiàn)了一個問題: 秒?yún)^(qū)數(shù)字跳變有時會忽略下
一個值,而跳到下下一個值去,比如,當(dāng)前顯示12,然后馬上顯示14。
那么問題出在什么地方呢? 試想,若_convert在進(jìn)行格式轉(zhuǎn)換時,發(fā)生中斷,且更改了sec變量,那么,_convert會按新的值進(jìn)行轉(zhuǎn)換,這
樣,本來這次要轉(zhuǎn)換并送顯示的舊值被新值給覆蓋了,所以,_disp2在顯示的時候,也就根據(jù)_convert的轉(zhuǎn)換結(jié)果,忠實(shí)地顯示了一個新值,將
本來應(yīng)該顯示的值給忽略了。
既然如此,有什么辦法來解決呢?兩個方法:
(a) _convert在對時間變量進(jìn)行格式轉(zhuǎn)換時,暫時禁止TMR0中斷,轉(zhuǎn)換后再開啟TMR0中斷;
(b) 將_conver也歸并到中斷代碼中去,規(guī)定次序,使得_clock更新時間后,_convert再進(jìn)行轉(zhuǎn)換,這樣,格式轉(zhuǎn)換區(qū)的變量不用擔(dān)心被
_clock修改;
**那么方法(a)會存在什么問題呢?試想:當(dāng)_convert在轉(zhuǎn)換時,TMR0定時時間到,TMR0向內(nèi)核提交中斷,但由于TMR0中斷請求被禁止,即使
_convert轉(zhuǎn)換完畢之后,允許TMR0中斷,那么TMR0的中斷請求會不會被丟棄呢? 顯然,根據(jù)PIC的中斷系統(tǒng),當(dāng)TMR0定時時間到后,首先將
T0IF置1,并由T0IF向內(nèi)核提出中斷請求,如果該中斷請求被禁止,那么只要其中斷標(biāo)志T0IF仍然保持為1,當(dāng)該中斷響應(yīng)解禁之后,內(nèi)核根據(jù)
T0IF立即響應(yīng)其中斷。
因此,方法(a)中"TMR0的中斷請求可能會被遺棄的擔(dān)心"是多余的.
并且,由于_convert的執(zhí)行時間少于一個中斷周期,所以它對中斷的暫禁操作不會出現(xiàn)在一個暫禁中斷的過程中,中斷標(biāo)志T0IF的多次被置一
的現(xiàn)象,所以不會發(fā)生中斷響應(yīng)被沖掉的不良后果。同樣,_clock子程序在沒有加載到中斷服務(wù)代碼中去時,其對TMR0的暫禁影響與_convert分
析的結(jié)果相同.
那么,既然如此,我認(rèn)為這樣的話,由于_disp2的執(zhí)行時間也不會超過1秒,因此,不會出現(xiàn)當(dāng)秒跳變時,_convert來不及轉(zhuǎn)換而丟棄上一次待
轉(zhuǎn)換的字符。所以,結(jié)果應(yīng)該是正常.
于是按照這種方法修改程序,結(jié)果發(fā)現(xiàn)秒?yún)^(qū)每次都跳變,最小增量為2,最多為為3(跳變周期大約1.2秒)。于是將延遲子程序的外循環(huán)值由
64H-〉40H(大概右25ms變成16ms),結(jié)果仍然如此,秒?yún)^(qū)每次都跳變,只是跳變節(jié)奏比未修改延時子程序前變快很多(跳變周期大約0.6s),但最
小跳變增量1,最多為2。
[正在分析其根源,也請有興趣的兄弟一起思考一下.....]
那么那試試方法(b).我按方法(b)修改了程序,結(jié)果發(fā)現(xiàn),仍然出現(xiàn)秒?yún)^(qū)數(shù)字跳變的情況。
究其原因,跟3)類似:當(dāng)_disp2運(yùn)行的時候,準(zhǔn)備從顯示緩沖區(qū)取字符來顯示,如果發(fā)生中斷,_clock,_convert更改了顯示緩沖區(qū)的內(nèi)容
,使得本來即將待顯示的內(nèi)容被替換成下一次顯示的內(nèi)容。所以,該方法依然存在,而且,由于_disp執(zhí)行時間大于一次中斷的255us,如果在
_disp執(zhí)行過程暫禁TMR0中斷將會丟棄中斷請求(即:TMR0的中斷請求被自己下一次中斷請求覆蓋,上一次中斷請求被忽略,顯示時間將變慢)。
----------------------------------------------------------------
方案2:
[中斷服務(wù)仍然只改變us+1的值,但是格式轉(zhuǎn)換及顯示功能內(nèi)嵌到_clock子程序中去,主程序執(zhí)行_clock循環(huán)。]
下午我按這種方式更改了程序,在軟件模擬時發(fā)現(xiàn)程序跑飛。原因是:內(nèi)嵌了這些功能之后,代碼由400行變成500多行,在_disp1查表顯示
字符時,_table已經(jīng)超過PCL的256字ROM空間,而查表時未注意PCLATH內(nèi)容,以致跑飛。解決此問題后,下載到ICD中運(yùn)行,發(fā)現(xiàn)結(jié)果倒是正常
了,但是感覺時間好像有一點(diǎn)點(diǎn)慢。
呵呵,細(xì)心的站友想必已經(jīng)看出來了,由于加了顯示功能的_clock子程序中依然是暫時禁止TMR0中斷的,雖然該時間顯示功能只是在時間跳
變時刷新LCD屏幕,但是正是由于在時間跳變時執(zhí)行時間刷新的周期過長(大于4096us),TMR0 的多次中斷請求最后只被響應(yīng)一次,即T0IF多次
被置1后,卻只能在_clock子程序末對TMR0解禁時得到一次中斷響應(yīng),未被響應(yīng)的累積時間被丟棄了,沒有加到(us+1:us)中去,引起時鐘顯示變慢了.
方案3:
由于前兩個方案均存在不近人意的問題,難道在用TMR0做秒表時,且當(dāng)"定時中斷的周期小于LCD慢顯器件的驅(qū)動刷新周期的情況下",就沒有一個完美的解決方案么?
留在這里和有興趣的站友一起思考...
深圳市英銳恩科技有限公司(www.traavell.com)為單片機(jī)技術(shù)服務(wù)\開發(fā)設(shè)計和產(chǎn)品代理商,授權(quán)MDT(麥肯 MICON)單片機(jī)A級代理商,MICROCHIP產(chǎn)品全系列單片機(jī)與模擬器件授權(quán)推廣商。同時A級代理分銷NOVACAP、Syfer、Voltronics精密可調(diào)電容、DLI寬帶隔直微波電容,專注分銷AIC沛亨半導(dǎo)體(電源管理IC)、IR(場效應(yīng)管)。
如:
MDT2020/MDT10P20(完全兼容PIC16C57C、PIC16F57、CF775,直接替換,不要任何硬軟與軟件修改)
特性:ROM:2K,腳位:28PIN,I/O:22PIN,復(fù)位時間極快.2V,低電壓工作.低功耗,溫度范圍寬。
MDT2030 (完全兼容PIC16C58B,直接替換,不要任何硬軟與軟件修改)
特性:ROM:2K,腳位:18PIN,I/O:13PIN,復(fù)位時間極快.2V,低電壓工作.低功耗,溫度范圍寬。