两个51单片机485串口通信接收数据出错原因分析。大概发送三、五次会有一次全部接收正确。程序如下:
#include <REGx52.H>
bit flagTxd = 0; //单字节发送完成标志,用来替代 TXD 中断标志位
sbit RS485_DIR = P2^7; //485收发控制端
unsigned char txdBuf[4]={0xfc,0xf3,0xcf,0x3f}; //发送数据缓存
sbit keyOk=P3^2; //发送按键
/* 串口配置函数,baud-通信波特率 */
void ConfigUART(unsigned int baud)
{
RS485_DIR = 0; //RS485 设置为接收方向
SCON = 0x50; //配置串口为模式 1
TMOD &= 0x0F; //清零 T1 的控制位
TMOD |= 0x20; //配置 T1 为模式 2
TH1 = 256 - (11059200/12/32)/baud; //计算 T1 重载值
TL1 = TH1; //初值等于重载值
ET1 = 0; //禁止 T1 中断
ES = 1; //使能串口中断
TR1 = 1; //启动 T1
}
/* 延时程序 */
void Delay(unsigned int n)
{
while(n)n--;
}
/* 串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度 */
void UartWrite(unsigned char *buf, unsigned char len)
{ unsigned char i;
RS485_DIR = 1; //RS485 设置为发送
for(i=0;i<len;i++)
{ //循环发送所有字节
flagTxd = 0; //清零发送标志
SBUF = *buf; //发送一个字节数据
buf++;
while (!flagTxd); //等待该字节发送完成
}
Delay(50); //等待最后的停止位完成,延时时间由波特率决定
RS485_DIR = 0; //RS485 设置为接收
}
/* 发送按键扫描 */
void Okkey(void)
{
if(keyOk==0)
{
Delay(600); //延时防抖
if(keyOk==0)
{
UartWrite(txdBuf,4); //发送数据
while(!keyOk);
}
}
}
/* 主函数 */
void main(void)
{
EA = 1; //开总中断
ConfigUART(9600); //配置波特率为 9600
while(1)
{
Okkey(); //发送按键扫描
}
}
/* UART 中断服务函数 */
void InterruptUART() interrupt 4
{
if (TI)
{ //字节发送完毕
TI = 0; //清零发送中断标志位
flagTxd = 1; //设置字节发送完成标志
}
}
接收机程序:
#include <REGx52.H>
sbit RS485_DIR = P2^7;
unsigned char cntRxd = 0; //串口接收计数器
//unsigned char *ptrRxd; //串口发送指针
unsigned char RxdBuf[4]={0x00,0xff,0x00,0xff}; //接收缓存
/* 延时程序 */
void delay(void)
{
unsigned char m,n,s;
for(m=100;m>0;m--)
for(n=20;n>0;n--)
for(s=248;s>0;s--);
}
/* 串口配置函数,baud-通信波特率 */
void ConfigUART(unsigned int baud)
{ RS485_DIR=0;
SCON = 0x50; //配置串口为模式 1
TMOD &= 0x0F; //清零 T1 的控制位
TMOD |= 0x20; //配置 T1 为模式 2
TH1 = 256 - (11059200/12/32)/baud; //计算 T1 重载值
TL1 = TH1; //初值等于重载值
ET1 = 0; //禁止 T1 中断
ES = 1; //使能串口中断
TR1 = 1; //启动 T1
}
/* 把接收到的数据在P1口显示出来 */
void display(void)
{
unsigned char i,j;
unsigned char dispBuf[4];
for(i=0;i<4;i++) dispBuf[i]=RxdBuf[i];
for(j=0;j<4;j++)
{
P1=dispBuf[j];
delay();
}
}
/* 主函数 */
void main(void)
{
EA = 1; //开总中断
ConfigUART(9600); //配置波特率为 9600
cntRxd = 0;
while(1)
{
display();
}
}
/* UART 中断服务函数 */
void InterruptUART() interrupt 4
{
if (RI)
{ //接收到字节
RI = 0; //清零接收中断标志位
if (cntRxd < 4)
{ //放入缓存
RxdBuf[cntRxd] = SBUF;
cntRxd++;
}
else cntRxd=0;
}
}
RxdBuf[cntRxd]定义大一点,比如100。
/* UART 中断服务函数 */
void InterruptUART() interrupt 4
{
if (RI)
{ //接收到字节
RI = 0; //清零接收中断标志位
// if (cntRxd < 4)
// { //放入缓存
RxdBuf[cntRxd] = SBUF;
cntRxd++;
if(cntRxd >99)
cntRxd = 0;
// }
// else cntRxd=0; 这些注释掉
}
}
试试把接收的BUF变量弄大一点!
建议发送程序里在发送过程不要使用指针,采用直接从数组取值的方法,另外如果可能的话,发送部分也加显示,把发送的内容显示出来。
接收程序建议在接收中断里做一个接收完成的标志,你现在接收部分采用的方法其实是不停的显示接收缓存,即使没有接收到任何数据,也在不停的显示,这样你是否能判断出正在显示的内容是发送的内容,还是其他的内容。
这个办法尝试过,但问题还是没有得到解决。谢谢你的提议。
我之前也试过,是串口中断没初始化对。
这是没有办法的,项目要求就是这样。设备按照设定的参数运行并显示对应参数;主机在远端通过串口通讯修改设备的参数。
在测试的时候可以做一个接收结束标志,而且对于实际应用来说,更是会有一个通讯协议,只有正确接收一个数据包时,接收到的数据才算是有效的,可以被执行的,所以一个正确的接收过程是确保设备正确工作的基本
谢谢你的建议。非常感谢!
void UartWrite(unsigned char c)
{
RS485_DIR = 1; //RS485 设置为发送
flagTxd = 0; //清零发送标志
SBUF = c; //发送一个字节数据
while (!flagTxd); //等待该字节发送完成
}
Delay(50); //等待最后的停止位完成,延时时间由波特率决定
RS485_DIR = 0; //RS485 设置为接收
}
按键函数里面发送用
for(i=0;i<4;i++)
UartWrite(txdBuf[i]);
while(!keyok);
这是个不错的想法,下班回去就试试。