|
/************************************************************************ ;* 公 司: xx ;* 模 塊: serial.c ;* 功 能: 串口中斷服務程序,僅需做簡單調用即可完成串口輸入輸出的處理; ;* 出入均設有緩沖區,大小可任意設置 ;* 芯 片: AMEGA16 ;* 說 明: 未利用串口發送硬件BUFF ;* 設 計: 李耀峰 ;* 時 間: 2005-11-24 ;* 版 本: V1.0 ;* 記 錄: ;************************************************************************/
/************************************************************************ 可供使用的函數名: extern void PutByte(byte c); //放入一個字節到發送緩沖區 extern void PutString(byte *puts); //發送一個定義在程序存儲區的字符串到串口 extern void PutBytes(byte *outplace,byte j); //發送一串數據 extern void PutHEX(byte c); //發送一個字節的hex碼,分成兩個字節發 extern byte GetByte (void); //從接收緩沖區取一個byte extern void SerialInit (word baud); //串口初始化
extern byte inbufsign; //接收緩沖區數據,有數據=1。
#define CR PutString("\r\n") //發送一個回車換行 #define NUL putstring("\0") //發送一個空格 *************************************************************************/
#include <iom16V.h> #include <macros.h>
#define byte unsigned char #define word unsigned int
#define OLEN 20 //串口發送緩沖大小 #define ILEN 20 //串口接收緩沖大小
byte outbuf[OLEN]; //發送緩沖 byte inbuf[ILEN]; //接收數據緩沖 byte *outlast=outbuf; //最后由中斷傳輸出去的字節位置 byte *putlast=outbuf; //最后放入發送緩沖區的字節位置 byte *inlast=inbuf; //最后接收到接收緩沖區的字節位置 byte *getlast=inbuf; //最后從發送緩沖區取走的字節位置
struct data //位定義 { unsigned bit0:1; unsigned bit1:1; unsigned bit2:1; unsigned bit3:1; unsigned bit4:1; unsigned bit5:1; unsigned bit6:1; unsigned bit7:1; }bit_flag; #define outbufsign0 bit_flag.bit0 //緩沖區數據發完標志 發完=0 #define outbufsign bit_flag.bit1 //發送緩沖區非空標志 有=1 #define inbufful bit_flag.bit2 //接收緩沖區滿標志 滿=1
//#define inbufsign bit_flag.bit3 //接收緩沖區非空標志 有=1 //byte outbufsign0; //緩沖區數據發完標志 發完=0 //byte outbufsign; //發送緩沖區非空標志 有=1 //byte inbufful; //接收緩沖區滿標志 滿=1
byte inbufsign; //接收緩沖區非空標志 有=1
#define CR PutString("\r\n") //CR=回車換行 #define SPACE PutByte(0x20) //發送一個空格。
#pragma interrupt_handler SerialIncept_handler:12 //串口接收中斷函數 #pragma interrupt_handler SerialSend_handler:14 //串口發送中斷函數 //********************************************************************** //函 數 名: void PutByte(byte c) //功 能: 放入一個字節到發送緩沖區 //說 明: //參 數: //返 回 值: //示 范: PutByte(0x00); //*********************************************************************** void PutByte(byte c) { CLI(); //暫停串行中斷,以免數據比較時出錯 while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < putlast)&&(OLEN-(putlast-outlast)==2))) { SEI(); c++;c--; CLI(); } *putlast=c; //放字節進入緩沖區 putlast++; //發送緩沖區指針加1 if (putlast==outbuf+OLEN) putlast=outbuf; //指針到了頂部換到底部 outbufsign=1; if (!outbufsign0) //緩沖區無數據 { outbufsign0=1;
UDR=*outlast; //未發送完繼續發送 outlast++; //最后傳出去的字節位置加1 if (outlast==outbuf+OLEN) outlast=outbuf;//地址到頂部回到底部 if (putlast==outlast) outbufsign=0; //數據發送完置發送緩沖區空標志 } //緩沖區開始為空置為有,啟動發送 SEI(); }
//********************************************************************** //函 數 名: void PutString(byte *puts) //功 能: 發送字符串到串口 //說 明: //參 數: 發送的字符串 //返 回 值: //示 范: putstring("\r\n") //*********************************************************************** void PutString(byte *puts) { for(;*puts!=0;puts++) //遇到停止符0結束 PutByte(*puts); }
//********************************************************************** //函 數 名: void PutBytes(byte *outplace,byte j) //功 能: 放一串數據到發送緩沖區,需要定義發送的字節數 //說 明: //參 數: *outplace:發送的字節數據首地址指針 j:發送的字節數量 //返 回 值: //*********************************************************************** void PutBytes(byte *outplace,byte j) { int i; for(i=0;i<j;i++) { PutByte(*outplace); outplace++; } }
//********************************************************************** //函 數 名: PutHEX(unsigned char c) //功 能: 發送一個字節的hex碼,分成兩個字節發。 //說 明: 發送ASSIC碼 //參 數: 發送的數據 //返 回 值: 無 //示 范: PutHEX(i); //*********************************************************************** const byte hex_[]={"0123456789ABCDEF"}; void PutHEX(byte c) { word ch; ch=(c>>4)&0x0f; PutByte(hex_[ch]); ch=c&0x0f; PutByte(hex_[ch]); SPACE; }
//********************************************************************** //函 數 名: byte GetByte (void) //功 能: 從接收緩沖區取一個byte //說 明: 如不想等待則在調用前檢測inbufsign是否為1 //參 數: 無 //返 回 值: 接收到的數據 //示 范: i=GetByte(); //*********************************************************************** byte GetByte (void) { char c ; while (!inbufsign); //緩沖區空等待 CLI(); c=*getlast; //取數據 getlast++; //最后取走的數據位置加1 inbufful=0; //輸入緩沖區的滿標志清零 if (getlast==inbuf+ILEN) getlast=inbuf; //地址到頂部回到底部 if (getlast==inlast) inbufsign=0; //地址相等置接收緩沖區空空標志,再取數前要檢該標志 SEI(); return (c); //取回數據 }
//********************************************************************** //函 數 名: void SerialSend_handler (void) //功 能: 串口發送中斷處理 //說 明: //參 數: //返 回 值: //*********************************************************************** void SerialSend_handler (void) { UCSRA|=(1<<TXC); //清發送中斷標志 if (outbufsign) { UDR=*outlast; //未發送完繼續發送 outlast++; //最后傳出去的字節位置加1 if (outlast==outbuf+OLEN) outlast=outbuf;//地址到頂部回到底部 if (putlast==outlast) outbufsign=0; //數據發送完置發送緩沖區空標志 } else { outbufsign0=0; } }
//********************************************************************** //函 數 名: void SerialIncept_handler (void) //功 能: 串口接收中斷處理 //說 明: //參 數: //返 回 值: //*********************************************************************** void SerialIncept_handler (void) { if(!inbufful) //接收緩沖區未滿 { *inlast= UDR; //放入數據 inlast++; //最后放入的位置加1 inbufsign=1; if (inlast==inbuf+ILEN) inlast=inbuf; //地址到頂部回到底部 if (inlast==getlast) inbufful=1; //接收緩沖區滿置滿標志 } }
/********************************************************************** 函 數 名: void SerialInit (unsigned long) 功 能: 串口初始化 說 明: 串口初始化成指定波特率,開接收,發送并開相應中斷 參 數: 需要初始化的波特率 返 回 值: 無 示 范: SerialInit (38400); ***********************************************************************/ void SerialInit (word baud) { CLI(); UCSRC&=(~(1<<URSEL)); UBRRH=(byte)(baud>>8); UBRRL=(byte)baud;
UCSRB=(1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN); //接收中斷使能,發送中斷使能,接收器與發送器使能 UCSRC=(1<<URSEL)|(3<<UCSZ0); //設置幀格式: 8 個數據位, 1 個停止位*/ SEI(); //開全局中斷 }
我來潑幾瓢冷水:
1>用兩個指針和一個位域來記錄隊列的位置和狀態極其不合理,因為這需要9個字節,而且最后的目標代碼會因為指針和位域的原因產生相當長的代碼。我個人只用了兩個字節表示讀寫指針的位置加一個隊列有效數據的計數器,一共3個字節,簡單明了;
2>上述幾個讀寫指針沒有加volatile,有可能在執行讀寫時和中斷沖突;
3>既然使用了隊列緩沖,就不應該在程序中使用while語句來死等,而應該讓程序體面的失敗退出;這一點尤其體現在這個函數: byte GetByte (void) 程序有可能在這個地方死掉! while (!inbufsign); //緩沖區空等待
4>發送中斷函數: void SerialSend_handler (void) 這簡直是失敗中的失敗!當發送緩沖區為空的時候還產生多余的中斷。
5>接收中斷函數: void SerialIncept_handler (void) 這個函數也很失敗!但數據接收緩沖區滿的時候,如果再來一個數據,由于你沒有把硬件中的數據取走,同樣會再次產生中斷!也會死掉!
6>初始化函數 void SerialInit (word baud) 你在程序中添加了CLI() / SEI() 我知道你是為了在初始化的時候關掉中斷,初始化完畢后再打開,看似合理,但真的合理嗎?至少在我看來是很不合理! 一個系統,要初始化的模塊肯定不止一個,當你初始化這個模塊后就帶開了中斷,由于相應中斷的原因,也許過了n久,你其它模塊還沒有初始化! 我個人的做法是:系統開始初始化前關掉中斷:CLI(),等所有模塊初始化完畢再打開中斷SEL() |