![]() |
發布時間: 2021-10-24 12:27
正文摘要:先上單片機代碼 #include <reg52.h> sbit LED=P3^5; unsigned short TMR_XX_OT; sfr AUXR = 0x8E; void Timer0Init(void) //1毫秒@12.00 ... |
老董 發表于 2024-7-7 16:42 不知道為啥舊帖子被推上來了 問題已經解決很久了 就是8樓wulin 說的原因 8位機要避免同時在中斷和主循環運算和比較多于8位的變量 |
引腳設置了 嗎?P0M0 P0M1--P7M0 P7M1 |
大神過招,我只能看來看去,啥也看不懂!! |
受教了,還以為是13位的方式0呢! |
sunny118 發表于 2022-1-5 22:23 因為是 自動重裝 |
怎么后面的幾個程序中定時器中斷響應后沒有重復初值了? |
188610329 發表于 2021-10-26 17:59 受教了 ![]() |
lkc8210 發表于 2021-10-26 01:14 雖然,你這個修正解決了你目前的問題,但是,這個方案是有缺陷的:如果你程序比較長,定時器定時比較短,會出現掉幀的問題,即:當定時器把 INT0_EN 置1之后,你的主程序還沒有來的及判斷 INT0_EN 是否為1,你的定時器再次觸發,又一次把 INT0_EN 置一, 這個時候,你的主程序 才開始第一次判斷,那么就會少減一次(那么頻率就會不對)。所以這時候,通常會用傳char來避免這樣的情況發生。 代碼如下: unsigned char INT0_Times,Temp; void timer0_int (void) interrupt 1 { INT0_Times++; } void main() { Timer0Init(); ET0 = 1; EA = 1; while(1) { if(INT0_Times != 0) { Temp = INT0_Times; INT0_Times -= Temp; //防止這個時候中斷觸發又改寫了 INT0_Times 所以不直接 = 0,而用減法 TMR_01_OT -= Temp; if(CY) //有借位則反轉LED以及賦新值 { TMR_01_OT +=270; LED1 = !LED1; } TMR_02_OT -= Temp; if(CY) { TMR_02_OT +=273; LED2 = !LED2; } TMR_03_OT -= Temp; if(CY) { TMR_03_OT +=277; LED3 = !LED3; } TMR_04_OT -= Temp; if(CY) { TMR_04_OT +=281; LED4 = !LED4; } } } } 這樣,就能避免掉幀的問題,并且如果這次少了1拍,下次會補回來,總的頻率不會發生變化,你可以參考一下。 |
npn 發表于 2021-10-25 19:38 有點覆雜 借鑒了你, 188610329總和wulin總的回覆 得出改善的代碼如下
|
yzwzfyz 發表于 2021-10-25 17:31 你是不認真看貼子的結果 就算是用了13位定時器也不會出現閃爍不均勻的情況 |
Jiang_YY 發表于 2021-10-25 16:07 不懂如何插入匯編 試了賦值兩次,都是重覆那個問題 已找到解決方法 |
188610329 發表于 2021-10-25 13:56 因為一個TMR_XX就加一個傳標志 如果有幾個TMR_XX就要加幾個傳標志 借鑒了你的代碼 已找到解決方法 |
你遇到的問題和這個程序一樣,很多新人都會遇到:
|
都是不認真讀手冊的結果: 15. TMOD &= 0xF0; //設置定時器模式 在89C52中,這是13位定時器模式(注意不是16位),TH0TL0=FC18,中只有13位起作用,即時常數不是真正的FC18! 在STC中,設計者將定時器做了改進,增加了一個TH0TL0,的影子TH0TL0’,它們共用一個地址,這樣做的好處是,可以進行16自動重裝,所以在 在STC中,它是16位自動重裝模式,時常數是真正的16位:FC18! |
lkc8210 發表于 2021-10-25 11:22 TR0 = 0;TMR_XX_OT=270;TR0 = 1; 你在關閉定時器后清除一下定時器中斷標志,然后加2個NOP,看看。 按你10樓回復,你把TMR_XX.Dat_i=270;改成插入匯編,然后先賦值低字節的,然后賦值高字節的, MOV 0x09,#0x0E MOV TMR_XX(0x08),#0x01 或者直接TMR_XX.Dat_i=270;TMR_XX.Dat_i=270;寫2次, 看看是什么情況? |
lkc8210 發表于 2021-10-25 11:12 說了,不是賦值的問題,是判斷的問題, 你要關定時器的話,得在 if 之前關,然后,整個 if 結束之后開才能徹底杜絕這個問題,而且,在if之前關,因為是15系列,要在ET0=0 之后放一個NOP, 其實,一個簡單的傳標志搞定的事情,不知道為啥你非要傳short…… |
TTQ001 發表于 2021-10-25 08:50 在STC89上可以正常運行 應該不會是代碼或邏輯的問題 |
wulin 發表于 2021-10-24 21:16 10樓有新發現但審批遲了 我試過停止定時器再賦值再運行定時器 TR0 = 0;TMR_XX_OT=270;TR0 = 1; 已經確保定時器中斷不會打斷變量的賦值 也會出現這種情況 停止運行定時器和禁止定時中斷在防止打斷賦值的功能上是等價的吧? |
188610329 發表于 2021-10-25 00:24 10樓有新發現但審批遲了 我試過停止定時器再賦值再運行定時器 TR0 = 0;TMR_XX_OT=270;TR0 = 1; 已經確保定時器中斷不會打斷變量的賦值 也會出現這種情況 |
應該是代碼的原因。 請仔細檢查程序。 |
lkc8210 發表于 2021-10-24 21:38 這跟什么T的沒有關系 12T能正常只是運氣好而已,程序本身就是錯誤的 |
lkc8210 發表于 2021-10-24 21:38 不是 1T 的問題, 12T也會有這個問題,只是相比1T不容易出現(或者說不容易顯現出來)。而且你原程序,不是那么簡單的幾句吧?應該有更多的內容,這次是為了測試才變那么“迷你”的吧?這是好事,以后程序復雜了,你沒有注意這點的話,發生“詭異”事件,反而更麻煩。 其實道理很簡單,中斷和主程序 或者 高級中斷和低級中斷之間傳參的話,8位機(51屬于8位機)必須避免16位傳參,因為不可必免會發生這樣的事情(程序越簡單發生概率越高):主程序在處理16位的變量,處理了其中的8位,這個時候中斷打斷,改寫了整個16位,這個時候返回主程序,主程序繼續處理剩下沒處理的8位(這8位已經是新的數據了),那么這個結果會錯的離譜。 所以,一旦有16位甚至32位的數據在中斷里累加累減計數,達到你預期值的時候,必須轉換成一個標志(bit),然后讓主程序按這個標志去執行。 |
本帖最后由 lkc8210 于 2021-10-24 23:01 編輯 改用聯合體又正常了
編譯出來發現if(!TMR_XX_OT)是用ORL
if(!TMR_XX.Dat_c[0] && !TMR_XX.Dat_c[1]) 是用兩個JNZ
是ORL有問題嗎? |
188610329 發表于 2021-10-24 21:08 改了后可以是可以,但是為什么1T會這樣,12T就正常? |
從邏輯上看程序沒有問題,但同一個16位變量在主函數和中斷中都可以操作容易出錯,有前輩就此問題詳細闡述過。擇錄如下: /* 注釋二: * ET0=0;uiTimeCnt=0;ET0=1;----在清零 uiTimeCnt 之前,為什么要先禁止定時中斷? * 因為 uiTimeCnt 是 unsigned int 類型,本質上是由兩個字節組成。 * 在 C 語言中 uiTimeCnt=0 看似一條指令,實際上經過編譯之后它不只一條匯編指令。 * 由于定時中斷函數里也對這個變量進行累加操作,如果不禁止定時中斷, * 那么 uiTimeCnt 這個變量在 main()函數中還沒被完全清零的時候,如果這個時候 * 突然來一個定時中斷,并且在中斷里又更改了此變量,這種情況在某些要求高的 * 項目上會是一個不容易察覺的漏洞,為項目帶來隱患。當然,大部分的普通項目, * 都可以不用那么嚴格,可以不用禁止定時中斷。在這里只是提醒各位初學者有這種情況。 */ 建議改寫為: void timer0_int (void) interrupt 1 { TL0 = 0x18; //設置定時初始值 TH0 = 0xFC; //設置定時初始值 if(--TMR_XX_OT==0) { TMR_XX_OT=270; flag=1; } } void main() { Timer0Init(); IE = 0; ET0 = 1; EA = 1; while(1) { if(flag) { flag=0; LED = !LED; } } } |
你按我寫的改了之后,不就知道了? |
188610329 發表于 2021-10-24 19:25 為什么? |
lkc8210 發表于 2021-10-24 19:06 你的問題不是 定時器賦值,而是那個TMR_XX_OT 的判斷,必須放到定時器里判斷是否為0,不能放在外面判斷。 |
npn 發表于 2021-10-24 18:15 不太可能 我試過停止定時器再賦值再運行定時器都會出現這種情況 TR0 = 0;TMR_XX_OT=270;TR0 = 1; 反而在while(1)底加上4個_nop_()就可以正常運作 是因為1T單片機太快了嗎? |
TMOD = 0x00; //16位自動重載模式 bit T_OT_Flag; //增加一個標志位 void timer0_int (void) interrupt 1 { // TL0 = 0x18; //設置定時初始值 // TH0 = 0xFC; //設置定時初始值 自動重載不需要設置 if(--TMR_XX_OT==0) { TMR_XX_OT = 270; T_OT_Flag = 1; } } while 里面這么寫: while(1) { if(T_OT_Flag) { T_OT_Flag = 0; LED = !LED; } } 然后試試看。 |
8位單片機向16位變量賦值,是分成兩條指令完成的。 先賦值高8位,此時中斷觸發改變了該變量的值。 中斷返回后再去賦值低8位,導致程序出錯。 |