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