新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 一個URAT(RS232)低層驅動 中間層軟件示例

        一個URAT(RS232)低層驅動 中間層軟件示例

        作者: 時間:2016-11-10 來源:網絡 收藏
        一般教科書上提供的UART收發的程序往往是一段采用輪循(Polling)方式完成收發的簡單代碼。但對于高速的AVR來講,采用這種方式大大降低了MUC的效率。在使用AVR時,應根據芯片本身的特點(片內大容量數據存儲器RAM,更適合采用高級語言編寫系統程序),編寫高效可靠的UART收發接口(低層)程序。下面是一個典型的USART的接口程序。

        #include

        #define RXB8 1
        #define TXB8 0
        #define UPE 2
        #define OVR 3
        #define FE 4
        #define UDRE 5
        #define RXC 7

        #define FRAMING_ERROR (1<#define PARITY_ERROR (1<#define DATA_OVERRUN (1<#define DATA_REGISTER_EMPTY (1<#define RX_COMPLETE (1<
        // USART0 Receiver buffer
        #define RX_BUFFER_SIZE0 8
        char rx_buffer0[RX_BUFFER_SIZE0];
        unsigned char rx_wr_index0,rx_rd_index0,rx_counter0;
        // This flag is set on USART0 Receiver buffer overflow
        bit rx_buffer_overflow0;

        // USART0 Receiver interrupt service routine
        #pragma savereg-
        interrupt [USART0_RXC] void uart0_rx_isr(void)
        {
        char status,data;
        #asm
        push r26
        push r27
        push r30
        push r31
        inr26,sreg
        push r26
        #endasm
        status=UCSR0A;
        data=UDR0;
        if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
        {
        rx_buffer0[rx_wr_index0]=data;
        if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0;
        if (++rx_counter0 == RX_BUFFER_SIZE0)
        {
        rx_counter0=0;
        rx_buffer_overflow0=1;
        };
        };
        #asm
        popr26
        outsreg,r26
        popr31
        popr30
        popr27
        popr26
        #endasm
        }
        #pragma savereg+

        #ifndef _DEBUG_TERMINAL_IO_
        // Get a character from the USART0 Receiver buffer
        #define _ALTERNATE_GETCHAR_
        #pragma used+
        char getchar(void)
        {
        char data;
        while (rx_counter0==0);
        data=rx_buffer0[rx_rd_index0];
        if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0;
        #asm("cli")
        --rx_counter0;
        #asm("sei")
        return data;
        }
        #pragma used-
        #endif

        // USART0 Transmitter buffer
        #define TX_BUFFER_SIZE0 8
        char tx_buffer0[TX_BUFFER_SIZE0];
        unsigned char tx_wr_index0,tx_rd_index0,tx_counter0;

        // USART0 Transmitter interrupt service routine
        #pragma savereg-
        interrupt [USART0_TXC] void uart0_tx_isr(void)
        {
        #asm
        push r26
        push r27
        push r30
        push r31
        inr26,sreg
        push r26
        #edasm
        if (tx_counter0)
        {
        --tx_counter0;
        UDR0=tx_buffer0[tx_rd_index0];
        if (++tx_rd_index0 == TX_BUFFER_SIZE0) tx_rd_index0=0;
        };
        #asm
        popr26
        outsreg,r26
        popr31
        popr30
        popr27
        popr26
        #endasm
        }
        #pragma savereg+

        #ifndef _DEBUG_TERMINAL_IO_
        // Write a character to the USART0 Transmitter buffer
        #define _ALTERNATE_PUTCHAR_
        #pragma used+
        void putchar(char c)
        {
        while (tx_counter0 == TX_BUFFER_SIZE0);
        #asm("cli")
        if (tx_counter0 || ((UCSR0A & DATA_REGISTER_EMPTY)==0))
        {
        tx_buffer0[tx_wr_index0]=c;
        if (++tx_wr_index0 == TX_BUFFER_SIZE0) tx_wr_index0=0;
        ++tx_counter0;
        }
        else
        UDR0=c;
        #asm("sei")
        }
        #pragma used-
        #endif

        // Standard Input/Output functions
        #include

        // Declare your global variables here

        void main(void)
        {

        // USART0 initialization
        // Communication Parameters: 8 Data, 1 Stop, No Parity
        // USART0 Receiver: On
        // USART0 Transmitter: On
        // USART0 Mode: Asynchronous
        // USART0 Baud rate: 9600
        UCSR0A=0x00;
        UCSR0B=0xD8;
        UCSR0C=0x06;
        UBRR0H=0x00;
        UBRR0L=0x67;

        // Global enable interrupts
        #asm("sei")

        while (1)
        {
        // Place your code here

        };
        }

        這段由CVAVR程序生成器產生的UART接口代碼是一個非常好的、高效可靠,并且值得認真學習和體會的。其特點如下:
        l.它采用兩個8字節的接收和發送緩沖器來提高MCU的效率,如當主程序調用Putchar()發送數據時,如果UART口不空閑,就將數據放入發送緩沖器中,MCU不必等待,可以繼續執行其它的工作。而UART的硬件發送完一個數據后,產生中斷,由中斷服務程序負責將發送緩沖器中數據依次送出。
        2.數據緩沖器結構是一個線性的循環隊列,由讀、寫和隊列計數器3個指針控制,用于判斷隊列是否空、溢出,以及當前數據在隊列中的位置。
        3.用編譯控制命令#pragma savereg-和#pragma savereg+,使得由CVAVR在生成的中斷服務程序中不進行中斷保護(CVAVR生成中斷保護會將比較多的寄存器壓入堆棧中),而在中斷中嵌入匯編,只將5個在本中斷中必須要保護的寄存器壓棧。這樣提高了UART中斷處理的速度,也意味著提高了MCU的效率。
        4.由于在接口程序Putchar()、Getchar()和中斷服務程序中都要對數據緩沖器的讀、寫和隊列計數器3個指針判斷和操作,為了防止沖突,在Putchar()、Getchar()中對3個指針操作時臨時將中斷關閉,提高了程序的可靠性。
        建議讀者能逐字逐句地仔細分析該段代碼,真正理解和領會每一句語句(包括編譯控制命令的作用)的作用,從中體會和學習如何編寫效率高,可靠性好,結構優良的系統代碼。這段程序使用的方法和技巧,對編寫SPI、I2C的串行通信接口程序都是非常好的借鑒。
        作為現在的單片機嵌入式系統的工程師,不僅要深入全面的掌握芯片和各種器件的性能,具備豐富的硬件設計能力;同時也必須提高軟件的設計能力。要學習和掌握有關數據結構、操作系統、軟件工程、網絡協議等方面的知識,具有設計編寫大的復雜系統程序的能力。

        USART應用實例
        使用ATmega128實現一個工業設備的主控制板,它與由ATmega8管理的按鍵和LED顯示構成的控制面板距離在2米左右,兩者之間采用USART通信聯系。考慮到在實際應用中,倆者之間交換的數據很少,通信速度也不需要很高,重要的是保證通信的可靠和抗干擾,因此在硬件設計上采用電流環的連接方式,見圖5.4。
        在圖中通信雙方采用光隔和三極管,將USART的電平變化變成電流變化后傳送連接,如同工業上使用的20mA電流環通信一樣,大大提高了通信的抗干擾能力。
        通信協議和規程的制定:
        l.通信速率采用2400bps(速率太高時電流環的變化會跟不上)。
        2. 用戶數據包采用定長格式,每個數據包長度為6個字節,其中第1個字節是數據包起始字節0xBB,第6字節為數據包結束字節0xEE,其它為用戶命令、數據和系統狀態參數。
        3.每次通信由A端發起,下發一個數據包;B端收到一個正確的數據包后,必須返回一個數據包應答。
        4.A端下發一個數據包后,在300ms內沒有正確收到應答包時(在2400bps時傳送6個字節的時間約為30ms),將再次重發;3次重發均不能正確收到應答包則報警。
        5.在系統正常工作時,A端每隔250ms下發一個數據包,B端如果在1s內沒有正確收到一個下發的數據包,將進入安全保護程序。
        在這個應用實例中,USART接口的發送程序與前面給出的典型例程中的一樣,而對USART的接收程序進行了改動和簡化,使其更加符合在本系統中使用。

        #define UART_BEGIN_STX0xBB
        #define UART_END_STX0xEE
        #define RX_BUFFER_SIZE06

        char rx_buffer0[RX_BUFFER_SIZE0];
        unsigned char rx_counter;
        bit Uart_RecvFlag

        // USART Receiver interrupt service routine
        #pragma savereg-
        interrupt [USART_RXC] void uart_rx_isr(void)
        {
        unsigned char status,data;
        #asm
        push r26
        push r27
        push r30
        push r31
        inr26,sreg
        push r26
        #endasm

        status=UCSRA;
        data=UDR;
        if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
        {
        if (!Uart_RecvFlag)
        {
        rx_buffer[rx_counter] = data;
        switch (rx_counter)
        {
        case 0:
        if (data == UART_BEGIN_STX)rx_counter = 1;
        break;
        case 1:
        case 2:
        case 3:
        case 4:
        rx_counter++;
        break;
        case 5:
        rx_counter = 0;
        if (data == UART_END_STX)Uart_RecvFlag = 1;
        break;
        }
        }
        }
        else
        rx_counter = 0;

        #asm
        popr26
        outsreg,r26
        popr31
        popr30
        popr27
        popr26
        #endasm
        }
        #pragma savereg+
        …………
        void main(void)
        {
        while(1)
        {
        if (Uart_RecvFlag)
        {
        …………//處理收到的數據包
        Uart_RecvFlag = 0;//允許USART接受新的數據包
        }
        …………//處理其它任務
        }
        }

        在這段代碼中,接收中斷服務程序直接對數據包的起始字符和結束字符進行判斷,并完成對整個數據包的接收。當接收到正確的6個字符的數據包后,將“Uart_RecvFlag”標志置位,通知上層程序處理收到的數據。一旦“Uart_RecvFlag”標志置位后,中斷服務程序將不再接收新的數據(放棄掉收到的字節),使得數據緩沖區不會溢出。
        上層程序的設計,應保證以200ms左右的間隔對“Uart_RecvFlag”標志位進行一次判斷。一旦判斷“Uart_RecvFlag”標志置位后,馬上進行處理,回送應答數據。處理完后將“Uart_RecvFlag”標志清除,允許USART接收新的數據包。
        還可以考慮在數據包中增加“數據包編號”和“數據校驗”2個字節,以進一步提高通信的可靠性。


        評論


        技術專區

        關閉
        主站蜘蛛池模板: 大连市| 夏河县| 南靖县| 肃南| 新龙县| 稷山县| 射洪县| 晋州市| 石门县| 宣汉县| 区。| 伊吾县| 休宁县| 南昌县| 湖南省| 安丘市| 安达市| 衡水市| 富川| 伽师县| 汉阴县| 凌云县| 烟台市| 黄大仙区| 鹤峰县| 北安市| 皋兰县| 铁岭市| 石泉县| 咸丰县| 兴义市| 通化县| 吴川市| 皮山县| 绥中县| 庆元县| 罗田县| 炉霍县| 凯里市| 大邑县| 中卫市|