liyaofeng2000 发表于 2005-11-24 11:35:11

功能强大的串口发送接收程序模块---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();                                                        //开全局中断

    }

Ben_Jimmy 发表于 2005-11-24 12:36:08

感谢!

学习中

andre 发表于 2005-11-24 17:23:26

代码写的水平好高啊

hotpower 发表于 2005-11-24 23:25:51

好...现在确实看明白了C++函数重载的好处了.

zh1112ou 发表于 2005-11-25 00:46:35

顶!!!

Gorgon_Meducer 发表于 2005-11-25 07:36:40

代码写的8错哈。顶下下。

不过应该利用缓冲区顺势加上多字节命令的发送和判断。

用队列,不难得哈。

acr8 发表于 2005-11-30 10:23:24

楼主、各位大虾



我不太明白这个函数

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))) 判断却相反,为什么????,请指教

acr8 发表于 2005-11-30 10:27:18

还有为什么putlast和outlast字节位置相差2就要等待?这样发送缓冲区不就变成2字节了



所定义的20字节缓冲区是不是没用了??



我新手,八成是我理解上的误区,请大虾指点一下!!!

Qhjh 发表于 2005-11-30 15:06:05

1.防止数据覆盖!

2.移植了丁丁的程序.

yjbin 发表于 2005-12-4 01:09:18

楼主能不能举一个利用你这模块的例子啊?谢谢了先!

andre 发表于 2005-12-5 11:16:07

能否完善该程序,让printf函数从串口输出啊

changjianfeng 发表于 2006-2-27 21:20:16

不错 高

Louis_Bright 发表于 2006-2-27 22:07:33

我来泼几瓢冷水:



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()

hncsxzj 发表于 2006-3-28 18:20:37

我上传一个好用一点的串行模块

以供学习



点击此处下载armok01111188.rar



下面是个应用实例

点击此处下载armok01111189.rar


-----此内容被hncsxzj于2006-03-28,18:24:02编辑过

cltemail 发表于 2006-3-31 11:57:52

幽游梦蝶真是好牛B啊,原来水平的高低可以差这么多的,在汗颜的同时又实在佩服!

wuyuntaotc 发表于 2006-4-3 16:59:23

高手挺指教,

AVR的说明上说:“UBRRH 与寄存器UCSRC 共用I/O 地址。因此访问该地址时需注意以下问题。写访问当在该地址执行写访问时, USART 寄存器选择位(URSEL) 控制被写入的寄存器。若URSEL 为0,对UBRRH 值更新; 若 URSEL 为1,对UCSRC 设置更新。”

我按照说明上说的做了,但是我用AVR Studio仿真时,UBRRH与UCSRC都是改了一个,另一个也跟着改的。顺序换了也不管用。请问这是为什么?

wentao 发表于 2006-12-14 12:29:43

幽游梦蝶的分析很精辟,也消除了自己的一些疑惑,佩服!!

joson 发表于 2006-12-14 13:18:17

真佩服

HuangTing 发表于 2007-8-24 16:38:00

幽游梦蝶,NewRenA.

肯定是以前做过比较多的串口了,才会这么快就发现其中的问题。

一语中的!

欠缺的是,没把自己的好解决办法给出来。

呵呵。谢!

wxfhw 发表于 2007-8-24 17:20:48

记号

glen_cao 发表于 2007-8-24 17:50:11

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个字节???

jiangjx 发表于 2007-9-3 09:06:54

直接采用CVAVR的串口自动代码生成功能不是很好吗? 缓冲+中断 方式

jszhouchao 发表于 2008-6-2 09:45:58

学习

whuctx 发表于 2008-6-2 09:56:02

好像也没有加接收超时判断!当出现错误数据的时候最好还是加一个接收超时判断!

cgbabc 发表于 2008-6-2 19:35:15

留个记号

huamao 发表于 2008-6-3 12:26:03

很精彩啊,受教育了,呵呵

xuepeng2000 发表于 2008-6-3 19:30:38

记号
楼主功力不错哈
挺一个

yangyi 发表于 2008-6-3 19:45:36

不错。赞一个。

penguin 发表于 2008-6-3 20:30:03

留下记号

hyljx1219 发表于 2008-6-7 14:51:21

非常不错!!

zjr0411 发表于 2008-6-8 22:49:52

ji hao

asun 发表于 2008-6-25 13:49:28

看一下,学习学习

flyan.oo 发表于 2008-8-1 12:47:15

学习+ing...

junson 发表于 2008-8-1 15:50:38

不错,做记号!

ch2003_23 发表于 2008-8-1 16:50:55

mark

fu2008 发表于 2008-8-2 21:09:09

mark.

TYMCU 发表于 2008-8-2 22:17:28

跟着溜

525133174 发表于 2008-8-3 12:45:04

串口,学问还真大啊

thomasdu 发表于 2008-8-6 17:00:20

回头我也把我的M64的串口程序整理一个简化版出来,供大家排砖

hjy3000 发表于 2009-5-11 23:57:06

学习!

dklcmc 发表于 2009-5-12 20:57:19

你们这些人是不是天才啊!!!

tb8246 发表于 2009-5-13 23:08:18

雁过留痕

xqingfeng 发表于 2009-5-17 17:30:59

很不错,收下了,谢谢!!

wkxu 发表于 2009-5-18 18:58:29

佩服

snail0204 发表于 2009-6-25 16:14:27

幽游梦蝶 说的很有道理

rainword 发表于 2009-7-6 12:30:48

我也正在弄串口,学习

zzmlang 发表于 2009-7-7 12:23:07

mark!
努力向高手学习!

yujiuha 发表于 2009-7-7 14:20:07

>上述几个读写指针没有加volatile,有可能在执行读写时和中断冲突

這句我不能理解,我只知道非原子操作時,為避免和中斷沖突,要關中斷。

buxiaohui 发表于 2009-8-18 23:46:10

>上述几个读写指针没有加volatile,有可能在执行读写时和中断冲突

这句话,我也不是很理解。能不能解释一下?感谢了!

gzyzgq 发表于 2010-1-2 08:32:10

顶啊! 参考使用

hongyancl 发表于 2010-1-2 09:38:21

长见识了

sunmy 发表于 2010-1-2 14:02:47

顶!

chinamanzhong 发表于 2010-1-3 21:35:28

留个足印

lengmo 发表于 2010-1-3 23:30:53

留印

sonna 发表于 2010-1-5 00:20:58

记号

ds444 发表于 2010-1-5 00:55:09

mark

Excellence 发表于 2010-1-5 08:36:15

玛瑞咖。

liumaojun_cn 发表于 2010-1-5 13:33:40

mark

whlx622 发表于 2010-1-29 18:31:26

mark

avr-arm 发表于 2010-1-29 18:58:55

代码太漂亮啦,是不是效率低了点:(

gxy508 发表于 2010-1-30 11:25:53

mark

UPING 发表于 2010-1-30 12:48:03

MARK

master0722 发表于 2010-2-3 12:55:44

记号,好东西啊啊!!!

chenloveyj 发表于 2010-2-3 12:57:38

学习。。

cgbabc 发表于 2010-2-3 14:10:35

顶一个

ju748 发表于 2010-2-3 18:56:49

记号

smtgg 发表于 2010-2-3 20:48:18

mark

xiaowei0588 发表于 2010-2-4 12:54:34

很好!

gxy508 发表于 2010-2-4 13:53:06

mark

cool33777612 发表于 2010-3-6 11:15:11

继续顶

gxy508 发表于 2010-3-6 12:23:14

mark

huohuansong 发表于 2010-3-6 14:38:27

mark

smartzou 发表于 2010-3-6 15:24:13

mark~~

hongyancl 发表于 2010-3-6 15:28:56

回复【楼主位】liyaofeng2000
-----------------------------------------------------------------------

mark

zuoyi 发表于 2010-3-6 15:46:40

顶一下

wugaohui04 发表于 2010-3-6 15:50:46

.....

crose0106 发表于 2010-3-6 17:46:26

mark

Npng 发表于 2010-3-9 10:55:09

拖回去 好好研读
特别是幽游梦蝶的那几盆冷水

renjun861214 发表于 2010-3-15 22:05:06

ding

thinki 发表于 2010-3-15 23:18:23

mark~~

sanguo 发表于 2010-3-17 19:47:05

biaoji最近这块然人

sonic5566 发表于 2010-3-17 20:48:53

mark

zhuwei310600 发表于 2010-3-25 17:11:17

mark

cnzhy88 发表于 2010-4-16 18:28:20

看了.顶下.

RF2010 发表于 2010-4-18 10:55:36

NRF24L01无线数传模块(13RMB为人民服务)
http://item.taobao.com/auction/item_detail.htm?item_num_id=5029339086

4555 发表于 2010-4-18 22:21:56

顶!!!

boy364100 发表于 2010-4-18 23:34:24

顶~~~

jackmo 发表于 2010-5-12 14:49:07

mark

ybqwf 发表于 2010-5-12 15:37:03

好好学习串口。。。

hongyancl 发表于 2010-5-12 21:31:36

回复【楼主位】liyaofeng2000
-----------------------------------------------------------------------

ddddddddddddddddddd

hongyancl 发表于 2010-5-12 21:31:51

回复【楼主位】liyaofeng2000
-----------------------------------------------------------------------

ddddddddddddddddddd

weilan2200 发表于 2010-5-26 21:13:18

mark!

QQ373466062 发表于 2010-5-27 07:55:37

Mark

waking 发表于 2010-5-27 10:24:47

mark

jeoge 发表于 2010-8-9 09:41:25

mark

htgyhgj 发表于 2010-8-9 10:56:56

mark

ringan865 发表于 2010-8-9 12:36:01

mark

andrew_dj 发表于 2010-8-13 13:08:59

mark

joni 发表于 2010-8-14 13:18:49

mark

hiferr 发表于 2010-8-31 15:31:28

做个记号,过几天 来看
页: [1] 2
查看完整版本: 功能强大的串口发送接收程序模块---2005-11-24调试完成