久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2582|回復: 24
收起左側

51單片機串口通信,表現奇怪的TI狀態位,真心求教(抱拳了老哥)

[復制鏈接]
ID:1043747 發表于 2022-9-3 19:35 | 顯示全部樓層 |閱讀模式
TI的狀態的為什么會受到一個沒有調用的函數(UARTSendString)的影響呢?真百思不得其解!
直接運行,在main函數中測試TI的狀態為1,取消注釋UARTSendString函數后,TI的狀態為0(但該函數并沒有被調用)

知道原因的老哥勞煩告訴下(手動抱拳了)

測試設備信息
開發板:普中A2
單片機:stc89c52rc
晶振:11.0592MHz
IDE:keil 5

下面是精簡后可以表現該問題的代碼

#include <REGX52.H>

//void UARTSendString(char *p) {  // **********一個沒有調用的函數竟然會影響TI位
//        
//        while(*p) { // 字符串以0結尾
//                SBUF = *p++;
//                while (TI != 1);
//                TI = 0;
//        }
//}

void UARTInit(void)                //9600bps@11.0592MHz
{
    PCON &= 0x7F;                //波特率不倍速
    SCON = 0x50;                //8位數據,可變波特率
    TI = 1;                        // 初始化傳輸發送標志位  // **********該處TI已置為1
    ES = 1;                        // 打開串口中斷
    EA = 1;                        // 允許中斷

    TMOD &= 0x0F;                //清除定時器1模式位
    TMOD |= 0x20;                //設定定時器1為8位自動重裝方式
    TL1 = 0xFD;                //設定定時初值
    TH1 = 0xFD;                //設定定時器重裝值
    ET1 = 0;                //禁止定時器1中斷
    TR1 = 1;                //啟動定時器1


}

void main(void) {

    UARTInit();
    if (TI == 1) P2_1 = 0; // **********直接運行TI的值為1,燈會亮。如果取消注釋UARTSendString函數,p2_1(led)會熄滅,即TI的狀態為0。
    while (1) {

    }
}
回復

使用道具 舉報

ID:59202 發表于 2022-9-3 23:35 | 顯示全部樓層
TI是由硬件自動置1,軟件清0,你就不該去寫1。“取消注釋UARTSendString函數,p2_1(led)會熄滅,即TI的狀態為0”,估計編譯器看你沒用到TI就沒去管它,用到UARTSendString函數了就把TI=1優化掉了
回復

使用道具 舉報

ID:121859 發表于 2022-9-4 08:05 | 顯示全部樓層
如果僅僅是這些代碼的話,估計應該是程序跑飛了,因為你中斷打開了,但是并沒有寫中斷服務程序,所以很可能會飛掉,而注釋掉的部分程序在前面,可能就被執行了,造成TI=0了。你可以將注釋部分程序挪移到后面試試效果。
回復

使用道具 舉報

ID:213173 發表于 2022-9-4 09:02 | 顯示全部樓層
void UARTSendString(char *p)在沒有注釋掉的情況下,雖然沒有被你調用,但不代表后臺不能運用。當你強制TI = 1;后,只要開了中斷,CPU必須響應,跳轉到while (TI!=1);TI=0;。后面的這句if(TI == 1) P2_1 = 0;已經沒有意義了。TI是由硬件自動置1,軟件清0。不是不可以人為置1,是在某些特殊運用方式時才采用。在不了解其內在因果關系的情況下盲目使用當然達不到目的。樓主可以在編輯器里走單步就一目了然了。
回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 09:13 | 顯示全部樓層
zhxiufan 發表于 2022-9-4 08:05
如果僅僅是這些代碼的話,估計應該是程序跑飛了,因為你中斷打開了,但是并沒有寫中斷服務程序,所以很可能 ...

嗯嗯,感謝老鐵。我又測試了一番,確認程序跑飛了使得UARTSendString函數被執行造成的,并和UARTSendString函數所處的位置無關,跑飛的情況下總會被執行。但當我把該函數替換成如下代碼void UARTSendByte(char byte) {
    SBUF = byte;
    while (TI != 1);
    TI = 0;
}


此UARTSendByte函數就不會被執行。那么此時程序跑飛了嗎?還是說只是沒跑到該函數來?

這又讓我有了新的疑問,當打開串口中斷且未有處理程序時,是否一定會跑飛?跑到哪里由什么決定?
回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 09:32 | 顯示全部樓層
wulin 發表于 2022-9-4 09:02
void UARTSendString(char *p)在沒有注釋掉的情況下,雖然沒有被你調用,但不代表后臺不能運用。當你強制TI ...

void UARTSendString(char *p)在沒有注釋掉的情況下,雖然沒有被你調用,但不代表后臺不能運用。當你強制TI = 1;后,只要開了中斷,CPU必須響應,跳轉到while (TI!=1);TI=0;。后面的這句if(TI == 1) P2_1 = 0;已經沒有意義了。TI是由硬件自動置1,軟件清0。不是不可以人為置1,是在某些特殊運用方式時才采用。在不了解其內在因果關系的情況下盲目使用當然達不到目的。樓主可以在編輯器里走單步就一目了然了。


謝謝解惑,但還是有疑問,cpu為什么會跳轉到「while (TI!=1);TI=0;」處呢?
當我把UARTSendString函數換成UARTSendByte函數,TI位并不會受到影響
UARTSendByte函數同樣具有「while (TI!=1);TI=0;」,為什么cpu又不跳轉了呢?


void UARTSendByte(char byte) {
    SBUF = byte;
    while (TI != 1);
    TI = 0;
}



至于為何我需要手動置1,因為我想使用庫函數printf來調試輸出,但printf輸出需要TI位為1才執行


回復

使用道具 舉報

ID:213173 發表于 2022-9-4 10:30 | 顯示全部樓層
censv 發表于 2022-9-4 09:32
謝謝解惑,但還是有疑問,cpu為什么會跳轉到「while (TI!=1);TI=0;」處呢?
當我把UARTSendString函 ...

改函數名導致出錯的形式變化并沒有改變出錯的本質。在編輯器里走單步!走單步!走單步!重要的事情說3遍!
回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 10:57 | 顯示全部樓層
wulin 發表于 2022-9-4 10:30
改函數名導致出錯的形式變化并沒有改變出錯的本質。在編輯器里走單步!走單步!走單步!重要的事情說3遍 ...

你可能沒有仔細看我的回復,修改的并不只是函數名,而且也和函數名無關。
修改的內容包括:函數參數(由指針變成整型),函數體外層while去掉了

關于單步調試,我手頭沒有仿真器。

還有關于為什么會跳轉以及修改后不會,在源代碼層面(不涉及匯編)的單步調試真的能看出來?
即便能看到跳轉,但為什么會跳轉以及為什么會跳轉到此處仍難解惑

盼回復!
回復

使用道具 舉報

ID:59202 發表于 2022-9-4 15:07 | 顯示全部樓層
樓主提出這個問題其實還是很有意思的,但我覺得已經超出了你目前的知識范圍,這已經涉及到硬件底層操作和編譯器底層編譯邏輯了,我們還是先學會爬再去學跑吧。其實我對硬件底層操作和編譯器底層編譯邏輯也沒太多了解,在這只是說說自己的見解吧。單片機在打開全局中斷和相應中斷后,如果相應的中斷標志位置1,單片機檢測到后會保護當前現場,既把相關寄存器壓入棧中保存,然后將指令地址跳轉到中斷向量地址,通常中斷向量地址處也是一條跳轉指令,跳轉到真正的中斷函數處,這些保護現場和跳轉命令都是編譯器自動生成的,如果我們人為置位中斷標志又沒有編寫中斷函數,編譯器編譯也能通過,我們可以想象那些跳轉指令后面大概率會跟著空指令,系統也大概率會死雞。其實這些可以做個小實驗驗證一下也不難

評分

參與人數 1黑幣 +20 收起 理由
admin + 20 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

ID:213173 發表于 2022-9-4 16:04 | 顯示全部樓層
censv 發表于 2022-9-4 10:57
你可能沒有仔細看我的回復,修改的并不只是函數名,而且也和函數名無關。
修改的內容包括:函數參數(由 ...

無標題.jpg
回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 16:37 | 顯示全部樓層

首先感謝你幫我調試截圖!圖中看TI=0;這句代碼一定是執行了。

但為什么會跳轉到一個不相關的函數,即程序計數器pc為什么會指向該函數內部,源代碼級別的調試難以釋疑,也可能是我沒看出來

回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 16:48 | 顯示全部樓層
xxxevery 發表于 2022-9-4 15:07
樓主提出這個問題其實還是很有意思的,但我覺得已經超出了你目前的知識范圍,這已經涉及到硬件底層操作和編 ...

多謝回帖,給我提供了新的思路,你說的合理。程序跑飛到另一個函數的的原因,很可能是串口中斷服務程序的跳轉地址被編譯器錯誤的填寫導致,而且keil 5 ide也有很多bug,如果keil在此能給個err或warning就更好了
回復

使用道具 舉報

ID:624769 發表于 2022-9-4 18:31 | 顯示全部樓層
void UARTInit(void)                //9600bps@11.0592MHz
{
    PCON &= 0x7F;                //波特率不倍速
    SCON = 0x50;                //8位數據,可變波特率
    TI = 1;                        // 初始化傳輸發送標志位  // **********該處TI已置為1
   ES = 1;                        // 打開串口中斷     <==  只要有這句  如果你寫了 串口中斷函數,那么TI 一定會變0  如果沒有寫串口 中斷函數,那么程序一定跑飛!!!
    EA = 1;                        // 允許中斷

    TMOD &= 0x0F;                //清除定時器1模式位
    TMOD |= 0x20;                //設定定時器1為8位自動重裝方式
    TL1 = 0xFD;                //設定定時初值
    TH1 = 0xFD;                //設定定時器重裝值
    ET1 = 0;                //禁止定時器1中斷
    TR1 = 1;                //啟動定時器1


}

評分

參與人數 1黑幣 +50 收起 理由
admin + 50 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

ID:624769 發表于 2022-9-4 18:52 | 顯示全部樓層
censv 發表于 2022-9-4 09:32
謝謝解惑,但還是有疑問,cpu為什么會跳轉到「while (TI!=1);TI=0;」處呢?
當我把UARTSendString函 ...

誰說 printf 必須TI 為1 才可以的?

printf 調用的是 putchar
而你只要把 putchar 判斷的標志位 從TI改成其他的,比如我們常用的 TIbusy 就完全可以不考慮 TI 狀態,何必給自己找麻煩呢?
回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 18:56 | 顯示全部樓層
188610329 發表于 2022-9-4 18:31
void UARTInit(void)                //9600bps@11.0592MHz
{
    PCON &= 0x7F;                //波特 ...

多謝回復
寫了中斷處理函數,TI也不一定變0,比如空的處理函數。沒寫中斷函數,跑飛能理解。
但這種處理方式不合理。因為根本就不應該編譯通過

你能確定沒中斷函數一定跑飛嗎?
回復

使用道具 舉報

16#
無效樓層,該帖已經被刪除
ID:624769 發表于 2022-9-4 19:04 | 顯示全部樓層
censv 發表于 2022-9-4 18:56
多謝回復
寫了中斷處理函數,TI也不一定變0,比如空的處理函數。沒寫中斷函數,跑飛能理解。
但這種處 ...

沒中斷處理函數, 只要你開了中斷, 中斷請求標志位被置位,程序 100% 跑飛,至于跑飛后,是否能再跑回原程序,這就要看運氣了。 我不知道你 Debug 是怎么看的, 當你 ES = 1; EA = 1; 只要你 TI = 1; 走下一步,程序必定會 跳轉到: C: 0023  而如果你寫了中斷函數, C:0023  這里就是一個長跳轉(LJMP),到你的中斷函數,如果你沒有寫中斷函數, C:0023 這里,就什么都有可能了。這就是 “跑飛”
回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 19:13 | 顯示全部樓層
188610329 發表于 2022-9-4 19:04
沒中斷處理函數, 只要你開了中斷, 中斷請求標志位被置位,程序 100% 跑飛,至于跑飛后,是否能再跑回原 ...

理解了,多謝,沒仿真器debug不了

串口中斷觸發時程序計數器必定跳到 C:0023,這是人為規定的?
回復

使用道具 舉報

ID:624769 發表于 2022-9-4 19:26 | 顯示全部樓層
censv 發表于 2022-9-4 18:56
多謝回復
寫了中斷處理函數,TI也不一定變0,比如空的處理函數。沒寫中斷函數,跑飛能理解。
但這種處 ...

你覺得不合理,是因為你的知識儲備不夠,如果,你對單片機運作原理有足夠的了解,你就不會有這種想法了。

你打開  REGX52.H   你會看到: sbit ES         =   IE^4;
換句話說, 對KEIL 來說,你只是給 某個 BIT 位 置1了 而已, 鬼知道你是在開中斷?  
知道你是在開中斷的,是單片機,不是KEIL。 知道為什么中斷函數要用 interrupt 4 來定位么? 那是為了給 C:0023 加一句長跳轉。 你知識儲備足夠的話,你可以直接 _at_ 0x0023 直接給代碼,當然如果中斷函數足夠短的話。而開了 串口中斷 要 跳到 0023 去執行,這個也是 單片機自己知道,不是KEIL 知道。舉個最簡單的例子,T2, 在STC89 系列時是 interrupt 5,  在STC 15 系列之后,是: interrupt 16  這能去控制?? 不出事??

因此,KEIL 拿什么(或者說憑借什么來判斷)來控制你編譯不通過?
回復

使用道具 舉報

ID:624769 發表于 2022-9-4 19:36 | 顯示全部樓層
censv 發表于 2022-9-4 19:13
理解了,多謝,沒仿真器debug不了

串口中斷觸發時程序計數器必定跳到 C:0023,這是人為規定的?

這是由單片機公司規定的,比如,你的 STC89 系列:
interrupt.png

他定義在 0023, 其他單片公司,只要 51 核的為了兼容,基本都 定義在 0023 當然,如果愿意 也可以定義在0063,或者 006B 主要看廠家的喜好了。
回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 20:09 來自觸屏版 | 顯示全部樓層
188610329 發表于 2022-9-4 18:52
誰說 printf 必須TI 為1 才可以的?

printf 調用的是 putchar

直接修改lib文件夾中的putchar.c文件就可以嗎?還需要重新編譯嗎?

btw,TIbusy是什么?變量嗎?
回復

使用道具 舉報

ID:1043747 發表于 2022-9-4 20:45 來自觸屏版 | 顯示全部樓層
188610329 發表于 2022-9-4 19:26
你覺得不合理,是因為你的知識儲備不夠,如果,你對單片機運作原理有足夠的了解,你就不會有這種想法了。 ...

keil還是知道的,比如在新建工程時,會讓選擇芯片類型
回復

使用道具 舉報

ID:624769 發表于 2022-9-4 21:05 | 顯示全部樓層
censv 發表于 2022-9-4 20:45
keil還是知道的,比如在新建工程時,會讓選擇芯片類型

KEIL 連 你沒有用 STC89C5xRC.H  這個頭文件 用的是: REGX52.H 都不知道,他能知道啥?
另外,你都知道選擇芯片類型了,頭文件還在用 REGX52.H,連這點最基本的統一都做不到。KEIL 要真的管那么寬的話(極端嚴謹的查驗策略),估計,你跑馬燈的代碼一天都編譯不出來……

選擇芯片型號,只是一個簡單框架,不說要在設置里勾選項目,讓KEIL知道,去控制,
就STC的芯片型號來講,很多芯片的參數他還是錯的,這樣都不影響編譯,你覺得這個選擇芯片型號的用處到底有多大? 最后,你分析一下芯片型號的設定參數,你覺得里面能有中斷控制的記號么? 里面無非就是幾個RAM ROM 的大小, 頭文件的指定,以及MCU的速度 僅此而已……
回復

使用道具 舉報

ID:624769 發表于 2022-9-4 21:46 | 顯示全部樓層
censv 發表于 2022-9-4 20:09
直接修改lib文件夾中的putchar.c文件就可以嗎?還需要重新編譯嗎?

btw,TIbusy是什么?變量嗎?

自定的標志, 你隨便找幾個  開串口中斷的 范例,或者STC的范例就可以。就會看到 TIbusy, T1busy , Uartbusy 這類標志以及用法了了, while(!TI) 這種方式 淘汰太久了……
回復

使用道具 舉報

ID:59202 發表于 2022-9-4 23:37 | 顯示全部樓層
看來還是對keil的底層編譯邏輯不太了解啊,上個回帖我說在開了串口中斷的情況下,手工將TI置1,如果沒有寫中斷函數編譯器也會生成現場保護程序并跳到中斷向量地址然后很大概率死機,但今天用stc8H8K64U單片機做了個小實驗(雖說比stc89c5x系列高級多了,但中斷情況應該差不多),結果就是打開全局中斷和串口中斷,手工設置TI=1,如果寫了中斷函數則可以進入中斷函數并順利退出,如果沒有寫中斷函數則沒什么影響,TI一直保持不變,不會因為一個沒有執行的其他函數中對它有操作而變化,也沒死機,main主程序順利執行,看來編譯器應該在沒有中斷函數的情況下,只是把TI置1,并沒有生成壓棧,跳轉等指令,已經很智能了。所以說樓主不用再這個問題上再糾結了,你本身就是一個非常規操作,在一些低版本的編譯器中可能會產生一些奇怪的指令(當然也不排除你的程序本身就有問題,畢竟我們也沒看到所有程序),不如你升級一下keil版本再試試。

評分

參與人數 1黑幣 +50 收起 理由
admin + 50 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

ID:1043747 發表于 2022-9-6 19:59 來自觸屏版 | 顯示全部樓層
188610329 發表于 2022-9-4 21:46
自定的標志, 你隨便找幾個  開串口中斷的 范例,或者STC的范例就可以。就會看到 TIbusy, T1busy , Uartb ...

多謝,受教了
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 欧美日韩不卡在线 | 久久精品国产久精国产 | 电影在线 | 一区二区精品 | 仙人掌旅馆在线观看 | 国产日韩欧美 | 国产一区二区三区四区在线观看 | 成人久久18免费网站麻豆 | 2022国产精品 | 亚洲国产精品99久久久久久久久 | 久久综合欧美 | 午夜精品一区二区三区三上悠亚 | 午夜av在线 | 国产乱人伦精品一区二区 | 精品乱码一区二区 | 免费视频一区二区三区在线观看 | 免费看一级毛片 | 国产精品久久久久久一区二区三区 | 久久久.com| 国产精品看片 | 欧美在线视频免费 | 色久伊人 | 日韩一二区 | 国产精品精品视频一区二区三区 | 亚洲欧美中文日韩在线v日本 | 国产综合av | 337p日本欧洲亚洲大胆 | 国产精品久久二区 | 国产欧美日韩精品一区二区三区 | 日韩成人高清 | 亚洲国产成人精品久久久国产成人一区 | 久久不卡日韩美女 | 中文字幕第一页在线 | 久久亚洲欧美日韩精品专区 | 成人激情免费视频 | 国产日韩欧美一区二区 | 日韩成人一区二区 | 亚洲国产视频一区二区 | 亚洲精品九九 | 玖玖爱365 | 久久精品视频9 |