?? 單片機串口通信.txt
字號:
單片機串口通信
串口控制程序一般分為查詢和中斷兩者方式。查詢方式適用于簡單的應用,簡單可靠,但是缺點是需要占用處理器資源,在發送或者接收數據的時候不能做其它的事情,處理器利用率低。中斷方式下,在發送或者接受數據的時候處理器還可以做其它的工作,效率較高。
對于稍微復雜的系統來說,中斷方式管理串口程序將會更加有效。中斷處理方式也可分為幾種,其中采用循環緩沖區的方式比較高效。循環緩沖區為定義的一定長度的RAM區間,對于接受數據來說,中斷中收到的數據將存入RAM中,然后等待主程序來讀取。其中會涉及到數據見的協調問題,寫數據的時候不能把還沒有讀取的數據覆蓋掉,讀數據的時候應該讀取的是緩沖區中最老的數據。當緩沖區已滿的時候,寫入的新數據應該覆蓋掉最老的數據。這些問題的處理可以使用兩個指針來實現。
初始化時兩個指針均指向RAM區間的底部,如圖1所示。當中斷中接收到一個數據的時候,將這個數據寫入寫指針WPTR指向的存儲單元,然后調整寫指針指向下一個空余的RAM區間,程序上處理就是把寫指針加一,如圖2所示。同理,寫入N個數據后寫指針同步更新,如圖3所示。
當讀數據的時候,首先判斷緩沖區中是否有數據,方法是判斷讀指針和寫指針是否相等,如果相等表明沒有數據,如圖5所示。如果讀指針和寫指針不等,那么讀取緩沖區中的數據,然后調整讀指針,當寫指針和讀指針相等的時候,表明緩沖區中的有效數據已經讀取完,此時讀指針和寫指針相等。
當有數據再次寫入的時候,繼續緊接著上次寫入的地址后寫入新的數據,如果數據長度超過緩沖區的長度,寫指針重新返回緩沖區的底部重新開始(這是循環緩沖的由來),如圖6所示。此時如果有數據讀出,讀指針做同樣的更新。如果沒有數據讀出,一直有數據寫入,可能會出現緩沖區寫滿的情況,如圖7所示。此時如果仍然沒有數據讀取,繼續有數據寫入的時候,為了保留新的數據,必須丟棄老的數據,即寫指針可能超過讀指針,此時,讀指針必須和諧指針同步更新,這樣才能保證讀取的是沒有被覆蓋的最老的數據,如圖8所示。
需要注意的是,讀指針在中斷過程中也可能被更改,因此,讀數據的子程序需要對讀指針的更改進行保護,方法是在讀數據的時候關閉串行口中斷。下面是循環緩沖區接收數據的程序實例。
FT, 盡然連文本都不能上傳,代碼只好貼出來吧。
/*
* FileName: uart.h
* Description: header file for SerialPort
* Author: SangWei, HUST-CEEE-2004
* Contact: swkyer@163.com, swkyer@hotmail.com
* Date: 2005-09-22
*
* Platform: P89C6102(Philips), KeilC51(ver: 7.20)
*
* (C)All Rights Reserved.
*/
#ifndef __UART_H__
#define __UART_H__
#define UARTBUFFLEN 64 /* 串口緩沖區64個字節 */
void UartInit(void);
void SendChar(unsigned char ch);
unsigned char IsUartReceived(void);
unsigned char ReadChar(unsigned char idata *buf);
#endif /* __UART_H__ */
/*
* FileName: uart.c
* Description: Implementation of SerialPort
* Author: SangWei, HUST-CEEE-2004
* Contact: swkyer@163.com, swkyer@hotmail.com
* Date: 2005-09-22
*
* Platform: P89C6102(Philips), KeilC51(ver: 7.20)
*
* (C)All Rights Reserved.
*/
#i nclude <intrins.h>
#i nclude "hardware.h"
#i nclude "uart.h"
unsigned char data ramuse;
static unsigned char idata uartbuf;
static unsigned char idata bufwptr, bufrptr;
extern unsigned char xdata uartbuff[UARTBUFFLEN];
/*
* 初始化串口, 波特率9600
*/
void UartInit(void)
{
CLR_DOG;
status = 0;
bufwptr = 0; // 寫指針
bufrptr = 0; // 讀指針
MAX485CTL = 0; // 接收數據
PCON = 0x00; // 設置串口波特率, 時鐘頻率30MHZ
T2CON = 0x30; // 定時器2作為波特率發生器
SCON = 0x50; // 模式1
// n = 65536 - [fosc/baud*32]
// n = 65536 - 30000000/(19200*32) = 65536 - 49 = 65490 = 0xffd2
// n = 65536 - 22118400/(19200*32) = 65536 - 36 = 65500 = 0xffdc
#ifdef _DEBUG_AT_HOME
RCAP2H = 0xff;
RCAP2L = 0xb8; // baud rate 9600
TH2 = 0xff;
TL2 = 0xb8;
#else
RCAP2H = 0xff;
RCAP2L = 0x9e; // baud rate 9600
TH2 = 0xff;
TL2 = 0x9e;
#endif
IP = 0x10; // 串口中斷優先級高
REN = 1;
TI = 0;
RI = 0;
TR2 = 1; // 啟動定時器2
ES0 = 1; // 使能串口中斷
CLR_DOG;
}
/*
* 發送一個字節數據
*/
void SendChar(unsigned char ch)
{
SBUF = ch;
while(TI == 0);
TI = 0;
}
/*
* 判斷是否接收到數據,如果沒有返回0,如果有,返回數據長度
*/
unsigned char IsUartReceived(void)
{
unsigned char num;
CLR_DOG;
EA = 0; // 關中斷
if(bufwptr == bufrptr)
num = 0;
else if(bufwptr > bufrptr)
num = (bufwptr-bufrptr);
else // if(bufwptr < bufrptr)
num = (UARTBUFFLEN-bufwptr+bufrptr);
EA = 1;
return num;
}
/*
* 從緩沖區中讀一個字節數據
* 返回剩下的字節數
*/
unsigned char ReadChar(unsigned char idata *buf)
{
unsigned char ch, num;
CLR_DOG;
USE_ERAM;
EA = 0;
if(bufwptr == bufrptr)
{
*buf = 0;
num = 0;
}
else if(bufwptr > bufrptr)
{
ch = uartbuff[bufrptr];
*buf = ch;
bufrptr++;
num = (bufwptr-bufrptr);
}
else // if(bufwptr < bufrptr)
{
ch = uartbuff[bufrptr];
*buf = ch;
bufrptr++;
if(bufrptr == UARTBUFFLEN)
bufrptr = 0;
num = (UARTBUFFLEN-bufrptr+bufwptr);
}
EA = 1;
return num;
}
/*
* 串口中斷接受
*/
void UartInt(void) interrupt 4 using 1
{ // use eram
if(RI)
{
ES0 = 0;
ramuse = AUXR; // 保存進入中斷時的ram區間狀態
ramuse &= 0x03;
AUXR = 0x00;
uartbuf = SBUF;
RI = 0; // 清中斷標志
CLR_DOG;
uartbuff[bufwptr] = uartbuf; // 數據寫入緩沖區
if(bufwptr >= UARTBUFFLEN-1) // 循環緩沖指針
bufwptr = 0;
else
bufwptr++;
if(bufwptr == bufrptr) // 未讀數據被覆蓋,讀指針更新到寫指針的上一個單元
bufrptr = bufwptr + 1;
AUXR = ramuse;
ES0 = 1;
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -