功能强大的串口发送接收程序模块---2005-11-24调试完成
/************************************************************************;*公 司: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
") //发送一个回车换行
#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; //发送缓冲
byte inbuf; //接收数据缓冲
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
") //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
")
//***********************************************************************
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=c&0x0f;
PutByte(hex_);
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(); //开全局中断
} 感谢!
学习中 代码写的水平好高啊 好...现在确实看明白了C++函数重载的好处了. 顶!!! 代码写的8错哈。顶下下。
不过应该利用缓冲区顺势加上多字节命令的发送和判断。
用队列,不难得哈。 楼主、各位大虾
我不太明白这个函数
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();
}
putlast为最后放入发送缓冲区的字节位置,而outlast是最后由中断传送的字节位置,从道理上讲,应先从把数据存放到缓冲区,才能中断发送,所以putlast的字节位置应该大于或等于outlast才对呀,而while((((outlast-putlast)==2)&&(outlast > putlast ))||((outlast < putlast)&&(OLEN-(putlast-outlast)==2))) 判断却相反,为什么????,请指教 还有为什么putlast和outlast字节位置相差2就要等待?这样发送缓冲区不就变成2字节了
所定义的20字节缓冲区是不是没用了??
我新手,八成是我理解上的误区,请大虾指点一下!!! 1.防止数据覆盖!
2.移植了丁丁的程序. 楼主能不能举一个利用你这模块的例子啊?谢谢了先! 能否完善该程序,让printf函数从串口输出啊 不错 高 我来泼几瓢冷水:
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() 我上传一个好用一点的串行模块
以供学习
点击此处下载armok01111188.rar
下面是个应用实例
点击此处下载armok01111189.rar
-----此内容被hncsxzj于2006-03-28,18:24:02编辑过 幽游梦蝶真是好牛B啊,原来水平的高低可以差这么多的,在汗颜的同时又实在佩服! 高手挺指教,
AVR的说明上说:“UBRRH 与寄存器UCSRC 共用I/O 地址。因此访问该地址时需注意以下问题。写访问当在该地址执行写访问时, USART 寄存器选择位(URSEL) 控制被写入的寄存器。若URSEL 为0,对UBRRH 值更新; 若 URSEL 为1,对UCSRC 设置更新。”
我按照说明上说的做了,但是我用AVR Studio仿真时,UBRRH与UCSRC都是改了一个,另一个也跟着改的。顺序换了也不管用。请问这是为什么? 幽游梦蝶的分析很精辟,也消除了自己的一些疑惑,佩服!! 真佩服 幽游梦蝶,NewRenA.
肯定是以前做过比较多的串口了,才会这么快就发现其中的问题。
一语中的!
欠缺的是,没把自己的好解决办法给出来。
呵呵。谢! 记号 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;
初学者:没见过这种定义方式,请问编译后bit_flag占用
一个字节还是8个字节??? 直接采用CVAVR的串口自动代码生成功能不是很好吗? 缓冲+中断 方式 学习 好像也没有加接收超时判断!当出现错误数据的时候最好还是加一个接收超时判断! 留个记号 很精彩啊,受教育了,呵呵 记号
楼主功力不错哈
挺一个 不错。赞一个。 留下记号 非常不错!! ji hao 看一下,学习学习 学习+ing... 不错,做记号! mark mark. 跟着溜 串口,学问还真大啊 回头我也把我的M64的串口程序整理一个简化版出来,供大家排砖 学习! 你们这些人是不是天才啊!!! 雁过留痕 很不错,收下了,谢谢!! 佩服 幽游梦蝶 说的很有道理 我也正在弄串口,学习 mark!
努力向高手学习! >上述几个读写指针没有加volatile,有可能在执行读写时和中断冲突
這句我不能理解,我只知道非原子操作時,為避免和中斷沖突,要關中斷。 >上述几个读写指针没有加volatile,有可能在执行读写时和中断冲突
这句话,我也不是很理解。能不能解释一下?感谢了! 顶啊! 参考使用 长见识了 顶! 留个足印 留印 记号 mark 玛瑞咖。 mark mark 代码太漂亮啦,是不是效率低了点:( mark MARK 记号,好东西啊啊!!! 学习。。 顶一个 记号 mark 很好! mark 继续顶 mark mark mark~~ 回复【楼主位】liyaofeng2000
-----------------------------------------------------------------------
mark 顶一下 ..... mark 拖回去 好好研读
特别是幽游梦蝶的那几盆冷水 ding mark~~ biaoji最近这块然人 mark mark 看了.顶下. NRF24L01无线数传模块(13RMB为人民服务)
http://item.taobao.com/auction/item_detail.htm?item_num_id=5029339086 顶!!! 顶~~~ mark 好好学习串口。。。 回复【楼主位】liyaofeng2000
-----------------------------------------------------------------------
ddddddddddddddddddd 回复【楼主位】liyaofeng2000
-----------------------------------------------------------------------
ddddddddddddddddddd mark! Mark mark mark mark mark mark mark 做个记号,过几天 来看
页:
[1]
2