2007-07-02 11:16:12 cuichuang2045 阅读数 5
  • 单片机有很多种-1.3.第1季第3部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第3个课程,主要讲了单片机的发展史,各种主流单片机的各自特点,STC51单片机的各系列的特点以及项目中如何选型主控单片机。

    2473 人正在学习 去看看 朱有鹏

char rx_char() // 由串行端口接收字符进来

{

while(1)

{

if ( SCON & 0x01 ) == 0x01 ) // 判断数据接收是否准备好

{

break;

}

}

clrbit ( SCON.0 ); //清除RI标志

retrun SBUF; // 由串行端口接收数据进来

}

[@more@]

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/801031/viewspace-923102/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/801031/viewspace-923102/

2019-11-23 10:17:48 ShenZhen_zixian 阅读数 595
  • 单片机有很多种-1.3.第1季第3部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第3个课程,主要讲了单片机的发展史,各种主流单片机的各自特点,STC51单片机的各系列的特点以及项目中如何选型主控单片机。

    2473 人正在学习 去看看 朱有鹏

单片机串口接收的几种常用的数据处理方法

一、为什么串口接收的数据需要处理
我们在做项目的时候经常会用到串口,当我们用串口和别的设备通讯的时候就需要严格遵循通讯协议,然而,仅仅是遵循通讯协议是不够的,因为单片机串口受到别的信号干扰的时候,容易出现数据错误,特别是串口发送的第一个字节和最后一个字节。一旦出现这种情况,设备之间的通讯可能会受到影响,甚至会导致系统瘫痪。另外,串口收到数据的时候,我们也需要判断一帧数据的长度,特别是指令发送比较频繁的时候。因此,串口在接收到数据之后应该先进行数据处理,再执行命令,这样能够增强产品的稳定性。

二、串口接收重点关注的几个标志
为了保证通讯的稳定性,一般的通讯协议会加入帧头、帧尾、数据长度、校验这四个标志中的一个或多个。它们的作用如下:
1、帧头:串口发送数据的第一个字节是最容易出错的,如果你把重要的指令放在第一个字节,一旦出现错误,可能会使从机执行错误的操作。而帧头能够有效规避这个问题。
2、帧尾:和帧头类似,帧尾也能避免最后一个字节出错,同时,它也可以作为接收端接收完成的标志。
3、数据长度:它可以作为接收端接收完成的标志。有时也能作为判断数据是否正确的标志。
4、校验:能够有效避免校验以外的所有数据的错误,但是校验正确不代表数据一定没有出错,每种校验方式都有一定的缺陷。
帧头、帧尾、数据长度和校验,这四种标志加起来之后能够大大的增强数据传输的稳定性,但不是每个通讯协议都会包含以上四个标志,可能只会用到其中的一两个。因为如果要发送的主要数据本身就比较长,加上这个几个标志之后会更长,这对于那种传输速度慢、传输数据时间长、传输指令频繁处理速度慢的设备来说,较长的指令可能会影响工作效率。具体我就不多说了,我今天主要讲的是接收数据的处理方法,大家根据自己的协议选择合适的处理方法就行了。

三、常用的几种数据处理方法
1、判断帧头:串口接收到第一个数据之后先判断是否是帧头,如果帧头正确就存起来继续收,反之则丢掉继续等帧头。示例代码片段如下:

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
{
	Res = USART_ReceiveData(USART1);//读取接收到的数据,同时也清除了中断标志位
		   
	USART_RX_BUF[USART_RX_STA ++] = Res;
	if(USART_RX_BUF[0] != 0xA5 && USART_RX_STA == 1)
	{//帧头错误
	     USART_RX_STA = 0;//重新接收
	}
	if(USART_RX_STA >= USART_RX_Len)
	{//接收完成
	    USART_RX_STA = 0;
	    USART_RXHANDLE_FLAG = 1;
	}
}

这种处理方法能够有效避免第一个字节出错的问题,我就试过一次主设备那边传过来的数据帧头前面多了一个字节(可能是刚开始传输的时候电压不稳定产生的纹波),用这种方法就能够之间把第一个错误的数据丢掉,从正确的帧头开始接收。但是这种方法不能够检查帧头后面的数据是否正确。
2、判断帧尾:可以把帧尾作为一帧数据接收完成的标志。另外,当接收缓存存了多个指令的时候,帧尾能够帮助我们在一堆数据中区分出哪些数据是同一个指令的。当然,如果仅仅是区分数据用帧头也可以。不过这种办法必须保证帧尾和其他数据不一样,不然就会出现错误的判断。所以有些人为了避免这个问题会用两个字节作为帧尾,不过这样一来,数据长度就更大了,影响通讯效率。
3、根据数据长度判断是否完成接收:可以通过数据长度判断接收是否完成。如果协议里面的指令长度不是统一的,我们就不能根据固定的长度来接收数据。这个时候在一帧数据里面加入数据长度这个标志,就能够给单片机一个判断的准则,单片机接收到数据长度这个标志之后,根据这个长度来接收剩下的数据。示例代码片段如下:

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
{
	Res = USART_ReceiveData(USART1);//读取接收到的数据,同时也清除了中断标志位
		   
	USART_RX_BUF[USART_RX_STA ++] = Res;
	if(USART_RX_BUF[0] != 0xA5 && USART_RX_STA == 1)
	{//帧头错误
	     USART_RX_STA = 0;//重新接收
	}
	If(USART_RX_STA == 4)
	{
	USART_RX_Len = USART_RX_BUF[3];//数据长度
	}
	if(USART_RX_STA >= (USART_RX_Len + 4))
	{//接收完成
	    USART_RX_STA = 0;
	    USART_RXHANDLE_FLAG = 1;
	}
}

4、根据接收时间判断一帧数据的长度。根据波特率计算出两个字节传输的时间间隔,接收到数据之后定时器开始计时,在定时器中断触发之前收到数据就清空,重新计时,超过两个字节的间隔时间,就认为是一帧数据接收完成。具体的程序我就不写了,这个网上能找到很多例程。这种方法适合接收长度不定的情况,在这个方法的基础上还可以加上帧头帧尾等标志,增强稳定性。
5、校验处理:校验一般是在接收完成之后进行,校验是很必要的,因为它包含一帧数据的所有字节,通过校验能够大大的减少出错的概率。

四、总结
其实串口接收数据处理主要要注意两点,第一点是单片机如何确定一帧数据接收完成,第二点是单片机如果判断接收到的数据是正确的指令。第一点可以通过帧尾,数据长度等标志确定接收完成。第二点可以先通过帧头初步判断指令的正确性,再通过校验二次处理,判断指令是否正确接收。

关于串口接收数据处理的相关内容就介绍到这里,如果还有什么问题,可以留言,如果文章有哪里写的不对,欢迎指正,谢谢!

2018-06-05 00:43:08 zZzZzZ__ 阅读数 4335
  • 单片机有很多种-1.3.第1季第3部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第3个课程,主要讲了单片机的发展史,各种主流单片机的各自特点,STC51单片机的各系列的特点以及项目中如何选型主控单片机。

    2473 人正在学习 去看看 朱有鹏

使用两个单片机开发版,串口3.0与3.1交叉互接,两个开发版分别烧写串口接收,串口发送的单片机程序观察P0端口输出值的变化

============接收串口的单片机程序===========

#include <reg52.h>

#define uchar unsigned char

#define uint  unsigned int


uchar recieve; //接收到的数据存放在该变量中


void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);

}

void UART_init()
{
TMOD = 0x20;  //T1工作模式2, 8位自动重装,作为串口方式1时钟溢出率
TH1 = 0xfd;
TL1 = 0xfd;  //比特率9600
TR1 = 1; //启动T1定时器
SM0 = 0;
SM1 = 1;  //串口工作方式1,10位异步,一位起始位,一位停止位,8位数据位
REN = 1; //串口允许接收
EA  = 1; //开总中断
ES  = 1; //串口中断打开

}

/*至此串口配置完成*/

void main()
{
UART_init(); //串口初始化,调用串口初始化程序 
while(1)  //等待串口接收
{
while(!RI);         //判断RI是否为1(接收完成)
recieve=SBUF; //将接收的数据传递给recieve
RI=0;               //清除接收中断等待下一次接收
P1=recieve;  //将收的数据显示在P1口;
}
}




==================发送串口===================
#include <reg52.h>


#define uchar unsigned char
#define uint  unsigned int


uchar num=1; //存放要发送的数据


/*void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);

} */

//延时未使用

void UART_init()
{
TMOD = 0x20;  //T1工作模式2  8位自动重装
TH1 = 0xfd;
TL1 = 0xfd;  //比特率9600(与发送单片机保持相同比特率)
TR1 = 1; //启动T1定时器
SM0 = 0;
SM1 = 1;  //串口工作方式1 10位异步
REN = 1; //串口允许接收
EA  = 1; //开总中断
ES  = 1; //串口中断打开
}
void main()
{
UART_init(); //串口初始化
while(1)
{
SBUF=num; //将要发送的数据传递给SBUF
while(!TI); //等待发送完成
TI=0;        //清除发送中断标志,准备下一次发送

num=num++10; //num加10

                if(num>=255)

                num=0;

                 delay(1000);  //延时1000毫秒=1秒(每间隔1秒发送一次数据) 

}
}

2018-06-09 17:50:38 duidaifen3896 阅读数 3408
  • 单片机有很多种-1.3.第1季第3部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第3个课程,主要讲了单片机的发展史,各种主流单片机的各自特点,STC51单片机的各系列的特点以及项目中如何选型主控单片机。

    2473 人正在学习 去看看 朱有鹏

  今天在用51单片机进行串口收发数据的时候遇到了这样一个问题,上位机给单片机的字符数据是什么类型的,单片机又是怎样存储的?

串口中断如下

/* UART中断服务函数 */
void InterruptUART() interrupt 4
{
	if (RI)  //接收到字节
	{
  	   RI = 0;  //手动清零接收中断标志位
  	   table1[j] = SBUF;  //接收到的数据保存到接收字节变量中
	   if(table1[0] == 0x0D)
  	       f = 1;
       else 
		   f =0;
       table1[j] = table1[j]&0x0f;
	   j++;
	   flag1 = 1;	
								
    }
    else  //字节发送完毕
    {
       TI = 0;  //手动清零发送中断标志位
       FS_Bit = 0;
    }
} 

问题1:

单片机将接收到的数据放到SBUF中存储,但这个SBUF只能存储8位数据(1个字节),那么就有了第一个问题:如何接收多位数据?

答:我是定义了一个table1[]数组,将每个接收到的8位数据放到数组里,然后指针自加1,在另一个中断里判断一共接收到了几个数据(j起自加和计数作用),然后延时1s后,将接收到的每个元素输出(1s足够接收到几个字节的数据了)。

问题2

那么table1[]里的数据是什么类型的呢?

答:它是一个二进制数据,是接收到字节的二进制ascii码,如果再用SBUF发送出去,也是发送的二进制数据,不过上位机接收的时候自动转换成字节了。

问题三

如何将接收到的二进制ascii码进行十进制运算呢?

答:其实二进制与十进制计算方法、过程、结果都一样。但是用字符类型传给单片机的数字的二进制与对应十进制的二进制是不一样的,比如说上位机字符格式发送过来一个数据2,单片机接收到的二进制ascii码0011 0010,而十进制的2对应的二进制是 0000 0010,所以不能直接对字符格式的数字进行运算,所以我用了

table1[j] = table1[j]&0x0f;
将高四位都清0,与十进制的2对应起来,然后就可以进行运算了。(但是上位机发送一个大写字母R与字符2是一样的,因为R的ascii码是 0000 0010)


2015-06-29 13:47:12 l7904883 阅读数 4224
  • 单片机有很多种-1.3.第1季第3部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第3个课程,主要讲了单片机的发展史,各种主流单片机的各自特点,STC51单片机的各系列的特点以及项目中如何选型主控单片机。

    2473 人正在学习 去看看 朱有鹏

1单个字符的发送与接收

在中断函数中,如果接收到数据则RI由硬件置1,这时候把SBUF缓冲区的数据赋值给Buffer,并将RI置0,等待下次接收。同时,将接收到的数据再放入缓冲区,发送给PC机。当发送完毕的时候TI会被硬件置1,这时候需要将TI置0,以待下次发送。

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
//定义接收 字符
uchar Buffer;
/********************************************************************
             功能:串口初始化,波特率9600,方式1
*********************************************************************/        
void   URATinit( )
{
TMOD=0x20;
SCON=0x50;
EA=1;
ES=1;
TR1=1;
TH1=0xfd;
TL1=0xfd;
}
/********************************************************************
             功能:中断函数
*********************************************************************/        
void receive() interrupt 4
{
 if(RI)
 { 
  Buffer=123;
  RI=0;
 }
 SBUF=Buffer;
 while(!TI);
 TI=0;
}
//主函数
void  main()
{
 URATinit( );
}

2 字符串接收

在中断函数当中用Buffer[]接收收到的数据,同时将Buffer[]再发送给上位机。这里要注意变量i的定义。 如果定义为全局变量则Buffer[0-5]都可以接收到数据,需要对i计数,防止大于5溢出。

#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
//定义接收 数组
uchar Buffer[5]={0};
uchar i=0,j=0;


//串口初始化函数
void   URATinit( )
{
 TMOD=0x20;
 SCON=0x50;
 EA=1;
 ES=1;
 TR1=1;
 TH1=0xfd;
 TL1=0xfd;
}
//中断函数
void receive() interrupt 4
{
 if(RI) //PC机向单片机发送命令是否被识别
 { 
  Buffer[i]=SBUF;
  RI=0; //清0准备下一次接收
 }
 SBUF=Buffer[i];
 while(!TI) ;
 TI=0;
 i++;
 if(i>=5){
  i=0;
 }
}
//主函数
void  main()
{
 URATinit( );
}

总结:单片机串口终端是怎么产生的


你要用软件允许中断,
即C语言中EA = 1; 允许总中断ES = 1;
//允许串口中断汇编中可用 SETB EA ;允许总中断SETB ES ;
允许串口中断当单片机接收到一帧数据后,RI会置1,向CPU申请中断,若之前有中断允许,则产生了中断,进入中断服务程序。
当然,单片机发送完一帧数据,TI也会置1,同样会产生中断!
一般我们在发送数据时要关中断,因为一般你不用在发送时不用处理数据;
接收数据时要开中断,以便你在中断服务程序中将接收到的数据进行存储并处理。 

不管你有没有允许中断,上位机(此时即给单片机发送信息的机器)只要给单片机发送数据,单片机就会自动接收数据,并把它放在数据缓冲器SBUF中,如果你之前有允许串行口中断,RI就会置1,向单片机CPU申请中断,并进入中断服务程序,做完中断函数后就会自动返回断点。如果你没有允许中断,便不会产生串行中断。其实,别的中断都是某个I/O口电平变化产生。这只是外部中断产生条件,不过,你之前也需要用软件允许外部中断。

单片机串口通信

阅读数 231

没有更多推荐了,返回首页