1. 程式人生 > >串列埠除錯所遇到問題,總結與借鑑

串列埠除錯所遇到問題,總結與借鑑

問題確定串列埠接收資料丟失,用串列埠工具傳送一串字元資料時,有時會出現會丟失一兩個資料。
可能原因1:cpu處理速度過慢,在下一次中斷來臨時,此次中斷還未處理完。
解決方法:使用ringbuffer

void USART0_IRQHandler( void ) interrupt 4 using 2
{       
    uint8_t count;      
    if(SFR_RI)
        {
//          char temp_space = ' ';
            buffer_push(SFR_SBUF0);
              SFR_RI = 0
; rev_flag = 0; // RxTimeout = 50; } if(SFR_TI) { SFR_TI = 0; gucUart0BusyFlg = 0; } } #define BUFFER_MAX 64 typedef struct _circle_buffer{ unsigned char head_pos; unsigned char tail_pos; unsigned char circle_buffer[BUFFER_MAX]; }circle_buffer; circle_buffer buffer; void
buffer_init(void) { buffer.head_pos = buffer.tail_pos = 0; } uint8_t buffer_pop(unsigned char *_buf) { if(buffer.head_pos == buffer.tail_pos) { *_buf = 0xFF; return 0; } else { *_buf = buffer.circle_buffer[buffer.head_pos]; if(++buffer.head_pos>=BUFFER_MAX) buffer.head_pos = 0
; } return 1; } void buffer_push(unsigned char _buf) { buffer.circle_buffer[buffer.tail_pos] = _buf; if(++buffer.tail_pos >= BUFFER_MAX) buffer.tail_pos = 0; if(buffer.tail_pos == buffer.head_pos) if(++buffer.head_pos >= BUFFER_MAX) buffer.head_pos = 0; } 主程式負責將資料拿出來進行處理 while(1) { while(!rev_flag) { rev_flag = 1; 這是在中斷中設定了一個標誌位,當在20ms內有資料傳送過來,則繼續接收資料。當20ms沒資料進來,則對資料進行處理 delay(20); } if(buffer_pop(&tmp)) { putchar(tmp); } }

可能原因2:優先順序過低,同時幾個中斷到來時,處理別的中斷
SFR_IP = 0x10; 本次使用串列埠0,將串列埠0對應優先順序位設定為1.

可能原因3:一次中斷同時傳進來幾個數,而只進行了一次接收工作,這個應該是在有fifo時才會有的情況。
如同:串列埠用的方式不對,你每接收一個數據需要進一次中斷,實際上應該是進一次中斷把所有資料接收完畢。通過一個超時判斷判斷是否後續還有資料,沒有資料了才退出中斷函式。
解決辦法3:可以適當增大fifo閾值,迴圈判斷fifo中是否還有資料待取出。直到空為止。
用一個while(timeout<=TIMEOUT)的迴圈做超時判斷。接到資料則清零timeout,具體的TIMEOUT時間需要檢視時序表,應該在1、2ms左右。

知識點四:串列埠有緩衝區的,你只要傳送之前判斷一下發送緩衝區是否空,如果未空則將資料傳送即可。(注意不是傳送暫存器的狀態位)。
硬體上會幫你做掉後面的事情:它會將緩衝區的值先交給傳送暫存器,然後才發出去。
而你在查詢的標誌位如果是傳送暫存器的標誌位,你的時間就浪費在要等它硬體“把緩衝區的值放到傳送暫存器+傳送出去”這兩步上面。
1)0.2ms傳送一個數據就是1S傳送5000,如果你的波特率是9600,那你的0.2ms根本起不到任何測試作用。應該用一連串的資料通過串列埠工具進行傳送測試。
2)資料需要加校驗,“接收資料+判斷資料正確性+處理資料”應該在下一次資料來之前處理完畢。
3)中斷函式裡一般只做接收,並不做資料處理。你把資料反著傳送回去就是把處理放在了中斷函式裡。

知識點5:序列口中有兩個緩衝暫存器SBUF,一個是傳送暫存器,一個是接收暫存器,在物理結構上是完全獨立的。它們都是位元組定址的暫存器,位元組地址均為99H。這個重疊的地址靠
讀/寫指令區分:序列傳送時,CPU向SBUF寫入資料,此時99H表示傳送SBUF;序列接收時,CPU從SBUF讀出資料,此時99H表示接收SBUF。51微控制器的緩衝區只有八位。

說最基本的,老的51微控制器(80C51系列)有5箇中斷源,2個優先順序,可以實現二級中斷服務巢狀。現在很多擴充套件的51微控制器已經有4個優先順序(或更多)和更多的中斷源了。

在說到中斷之前,我先來定義一下優先順序,明白了什麼是優先順序,後面的闡述就容易明白了。實際上很多人都是混淆了優先順序的含義,所以才覺得糊里糊塗。

中斷的優先順序有兩個:查詢優先順序和執行優先順序。

什麼是查詢優級呢?我們從datasheet或書上看到的預設(IP暫存器不做設定,上電覆位後為00H)的優先順序:

外部中斷0 > 定時/計數器0 > 外部中斷1 > 定時/計數器1 > 序列中斷

或 int0,timer0,int1,timer1,serial port 或 INT0、T0、INT1、T1、UART

或 PX0>PT0>PX1>PT1>PS>……

其實都是查詢優級。首先查詢優先順序是不可以更改和設定的。這是一箇中斷優先權排隊的問題。是指多箇中斷源同時產生中斷訊號時,中斷仲裁器選擇對哪個中斷源優先處理的順序。而這與是否發生中斷服務程式的巢狀毫不相干。當CPU查詢各個中斷標誌位的時候,會依照上述5個查詢優先順序順序依次查詢,當數箇中斷同時請求的時候,會優先查詢到高優查詢先級的中斷標誌位,但並不代表高查詢優先順序的中斷可以打斷已經並且正在執行的低查詢優先順序的中斷服務。

例如:當計數器0中斷和外部中斷1(按查詢優先順序,計數器0中斷>外部中斷1)同時到達時,會進入計時器0的中斷服務函式;但是在外部中斷1的中斷服務函式正在服務的情況下,這時候任何中斷都是打斷不了它的,包括邏輯優先順序比它高的外部中斷0計數器0中斷。

而中斷的執行優先順序就是你對IP暫存器的設定了。在2個優先順序的情況下,某位為1,則相應的中斷源為高優先順序;為0,則為低優先順序。

關於中斷的優先順序有三條原則:

1、CPU同時接收到幾個中斷時,首先響應優先順序最高的中斷請求;

2、正在進行的中斷過程不能被新的同級或低行優優先順序的中斷請求所中斷;

3、正在進行的低行優優先順序中斷服務,能被高行優優先順序中斷請求中斷;

若:同一執行優先順序中的中斷申請不止一個時,則有一箇中斷優先權排隊問題。同一執行優先順序的中斷優先權排隊,由中斷系統硬體確定的自然優先順序形成,優先權自高到低的順序即: 外部中斷0>定時/計數0>外部中斷1>定時/計數1>序列介面

例如:設定IP = 0x10,即設定串列埠中斷為最高優先順序,則串列埠中斷可以打斷任何其他的中斷服務函式實現巢狀,且只有串列埠中斷能打斷其他中斷的服務函式。若串列埠中斷沒有觸發,則其他幾個中斷之間還是保持邏輯優先順序,相互之間無法巢狀。

關於中斷巢狀。可以這樣說,當一箇中斷正在執行的時候,如果事先設定了中斷優先順序暫存器IP,那麼當一個更高優先順序的中斷到來的時候會發生中斷巢狀,如果沒有設定則不會發生任何巢狀;如果有同一個優先順序的中斷觸發,它並不是在“不斷的申請”,而是將它相應的中斷標誌位置即IE暫存器的某位置位,當CPU執行完當前中斷之後,按照查詢優先順序重新去查詢各個中斷標誌位,進入相應中斷。

要記住,沒有設定IP時,微控制器會按照查詢優先順序(或都說邏輯優先順序)來排隊進入服務。如果要想讓某個中斷優先響應, 則要設定IP,更改執行優先順序(或者說物理優先順序)。要注意的是,當設定了IP後,當低執行優先順序中斷在執行時,如果有高執行優先順序的中斷產生,則會巢狀呼叫進入高執行優先順序的中斷。如果你是用C語言寫的程式,並在中斷服務時 using 了寄存組,要注意,兩個不同執行優先順序的中斷服務程式不要 using 同一組暫存器。

看兩個問題,如下:

1 在各個中斷都是低優先順序的時候,如果定時器0的溢位進入中斷。在這個中斷處理的過程中,外部中斷0也被觸發了,那麼是不是要發生中斷巢狀?

2 如果定時器0發生中斷的時候,進入中斷處理程式,這個時候外部中斷1條件觸發條件滿足了。因為定時器0自然優先順序比外部中斷1高,那麼定時器0的中斷處理程式繼續執行。假設定時器中斷處理程式執行的過程中,外部中斷1的觸發。條件消失了,那麼等定時器0的中斷處理完後,程式還是會進入外部中斷1處理程式嗎?

答案1:在IP事先設定了外部中斷0的優先順序的情況下,CUP會中止定時器0的中斷服務,進入外部中斷0服務程式,執行完以後再回到定時器0中斷服務程式。否則不會。

答案2:肯定會進入中斷的;外部中斷1的觸發條件滿足後會置位外部1的中斷標誌,即使後來外部中斷1的觸發條件消失了,也不會清除已置位的中斷標誌,所以等定時器0的中斷處理完後,程式判斷外部中斷的中斷標誌為1後依然會進入外部中斷1處理程式的,只有在外部中斷1處理程式中執行reti指令才會硬體清除外部中斷1的中斷標誌(這也正是為什麼中斷返回使用reti指令而不可以用ret替換的原因)…

知識點6:待學習硬體除錯

知識點7:自定義協議進行串列埠通訊傳輸資料


DWORD wr_addr =  0x1F0000;
void read_user_data_from_flash(void)
{
    uint8_t rd_data[32] = {0};
    SpiFlashDmaRead2XMem(rd_data, wr_addr,32);
    memcpy((BYTE *)user_data,rd_data,sizeof(rd_data));
    report_rsp(rd_data,32);
}

void write_user_data_to_flash(void)
{
    BYTE wr_data[32] = {0};
    memcpy(wr_data,(BYTE *)user_data,sizeof(wr_data));
    report_rsp(wr_data,32);
    SpiFlashDmaWrite4XMem(wr_data, wr_addr, sizeof(wr_data));
}

void send_telegram(void)
{
    report_rsp((BYTE *)&tx_telegram, sizeof(t_telegram));
}

extern uint8_t flag_rev;
#define MEM_REQ_MSK  0xd8
#define MEM_REQ 0x80
#define MEM_RSP 0x90
#define MEM_RD 0x20
#define MEM_ADD 0x07


void configure_input(void)
{
        uint8_t i, ret, tmp;
        static uint8_t my[10] = {0};

        ret = get_command();                                             //??è??üá?

        if(ret)
        {
            rx_telegram.CTRL = cmd[0];
            tmp = rx_telegram.CTRL & MEM_REQ_MSK;
            if(MEM_REQ == tmp)    //it's a memory access request telegram to the display
            {

                 if (rx_telegram.CTRL & MEM_RD) // read access
                 {
            //       report_rsp(cmd,5);
                    tx_telegram.point = user_data[rx_telegram.CTRL & MEM_ADD];
                    tx_telegram.CTRL  = (MEM_RSP | MEM_RD) + (rx_telegram.CTRL & MEM_ADD);
                    send_telegram();
                 }
                else
                {
                    memcpy((BYTE *)&rx_telegram,cmd,sizeof(t_telegram));
                    tx_telegram.point = user_data[rx_telegram.CTRL & MEM_ADD] = rx_telegram.point;
                    tx_telegram.CTRL  = MEM_RSP + (rx_telegram.CTRL & MEM_ADD);
                    if((rx_telegram.CTRL & MEM_ADD) == 7)
                        write_user_data_to_flash();
                    send_telegram();
                }
            }
        }


}