PIC单片机通过max485与pc通信的问题
实验室用的是dsPIC30f4011,刚接手没多久,现在要实现单片机通过max485和电脑通信,有一个问题是,当电脑发送5个字节以下时,通信是正常的,一旦超过5个字节单片机返回的数据就有问题,如上图所示!
求大神解答!
本帖最后由 往事如烟 于 2016-12-7 16:16 编辑
我把代码贴出来,欢迎大神指正!谢谢了!
#include<p30f4011.h>
_FOSC(CSW_FSCM_OFF&XT_PLL4);//4倍频晶振,Failsafe时钟关闭
_FWDT(WDT_OFF); // 关闭看门狗定时器
_FBORPOR(PBOR_OFF&MCLR_EN); //掉电复位禁止,MCLR复位时能
_FGS(CODE_PROT_OFF); //代码保护禁止
#define FCY7372800
unsigned char receive;
void delay(unsigned int z)//延时函数
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=100;y>0;y--);
}
void main(void)
{
U1BRG=51;//波特率9600
U1MODE=0x8420;//模式设置8位数据位,一位停止位,无奇偶校验
U1STA=0x8400;
TRISBbits.TRISB1=0;//设置RB1为输出状态,控制max485发送还是接收
while(1)
{
while(U1STAbits.URXDA)//接收缓冲器有数据
{
LATBbits.LATB1=0;//控制max485状态,此时为接收状态
receive=U1RXREG;//max485缓存接收的数
delay(100);
LATBbits.LATB1=1;//控制max485状态,此时为接收状态
U1TXREG=receive;//max485发送数据
delay(100);
while(U1STAbits.TRMT==0); //等待发送完毕
LATBbits.LATB1=0;
}
}
}
而且一旦第一次发送超过5个字节,往后在发送5个字节以下时,还是出现错误,就和发送超过5个字节的显示一样! 如下图!!! 提醒一下:485是半双工的,不能这样玩儿。接收控制脚的含义也没理解 估计是缓存满了没来得及读出来吧,用中断实现 砂山老妖 发表于 2016-12-7 16:46
提醒一下:485是半双工的,不能这样玩儿。接收控制脚的含义也没理解
我用一个引脚控制max485收发了呀! ssaiwo 发表于 2016-12-7 16:53
估计是缓存满了没来得及读出来吧,用中断实现
本来是用中断写的!但是不知道为什么总是显示不了!后来想用这个方法,先把功能实现了!应该不是缓存满了,我定义了一个40个字节的数组缓存数据,但是发了5个字节以上的数据就出错了! 应该是数据没有接收完整就开始发送,delay函数我没有认真算,把这个参数加大试试看。这程序编的{:dizzy:} 本帖最后由 往事如烟 于 2016-12-7 21:59 编辑
zhuxm 发表于 2016-12-7 21:43
应该是数据没有接收完整就开始发送,delay函数我没有认真算,把这个参数加大试试看。这程序编的 ...
和延时好像没关系,上图是增大延时的结果除了慢一点,还是错!我
正在尝试在中断中接收和发送!但是不知道为什么只能发送一个字节了!源码如下!
#include<p30f4011.h>
#include<uart.h>
_FOSC(CSW_FSCM_OFF&XT_PLL4);//4倍频晶振,Failsafe时钟关闭
_FWDT(WDT_OFF); // 关闭看门狗定时器
_FBORPOR(PBOR_OFF&MCLR_EN); //掉电复位禁止,MCLR复位时能
_FGS(CODE_PROT_OFF); //代码保护禁止
//#define FCY7987200
#define FCY7372800
unsigned char buf,buf1;
unsigned char flag_R=0;
unsigned char flag_T=0;
unsigned char len=0;
void __attribute__ ((__interrupt__)) _U1RXInterrupt(void);
void delay(unsigned int z)//延时函数
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=100;y>0;y--);
}
void main(void)
{
U1BRG=51;
U1MODE=0x8420;//注意,改为U1MODE=0x8020不行,为什么?
U1STA=0x8400;
//IFS0bits.U1TXIF=0;
IEC0bits.U1TXIE=0;
IFS0bits.U1RXIF=0;
IEC0bits.U1RXIE=1;
TRISBbits.TRISB1=0;
while(1)
{
}
}
void __attribute__ ((__interrupt__)) _U1RXInterrupt(void)
{
IFS0bits.U1RXIF = 0;
LATBbits.LATB1=0;//控制max485状态,此时为接收发送状态
while(U1STAbits.URXDA)//读取接收到的所有数据
{
buf=U1RXREG;
LATBbits.LATB1=1;
U1TXREG=buf;//max485发送数据
delay(1000);
while(U1STAbits.TRMT==0);
LATBbits.LATB1=0;
}
flag_R=flag_R+1;
}
楼主是刚毕业的大学生吧! 那个问题是什么原因不知道为什么,但是我用另外一种方法解决了这个问题,就是建立一个40字节缓存数组,在中断中接收数据,同时记录接收到的字节长!同时开启一个定时器中断,监控是否在发送数据,正在调试,下午发程序! 亲测可用,程序如下!
#include<p30f4011.h>
_FOSC(CSW_FSCM_OFF&XT_PLL4);//4倍频晶振,Failsafe时钟关闭
_FWDT(WDT_OFF); // 关闭看门狗定时器
_FBORPOR(PBOR_OFF&MCLR_EN); //掉电复位禁止,MCLR复位时能
_FGS(CODE_PROT_OFF); //代码保护禁止
#define FCY8000000
unsigned char buf; //定义接收数据缓存变量
unsigned char b;
unsigned char flag_R=0;//接收标志位
unsigned char len=0;//接收数据的字节长度
//函数声明
void delay(unsigned int z);
void send();
void Init();
/*******************************************************
函数名:void delay(unsigned int)
功能:延时
*******************************************************/
void delay(unsigned int z)
{
unsigned int x,y;
for(x=z;x>0;x--)
for(y=100;y>0;y--);
}
/*******************************************************
函数名:void send()
功能:max485发送数据给pc
注释:b和len一样,都是接收数据的字节长,而且b在这里被清零
*******************************************************/
void send()
{
int i;
for(i=0;i<b;i++)
{
U1TXREG=buf;//max485发送数据
while(U1STAbits.TRMT==0);//等待发送完毕
}
b=0;
while(U1STAbits.TRMT==0);//等待发送完毕
}
/*******************************************************
函数名:void Init()
功能:初始化以及配置
*******************************************************/
void Init()
{
U1BRG=51;//波特率设置为9600
U1MODE=0x8420;//模式设置,8个数据位,1个停止位,无奇偶校验
U1STA=0x8400;//设置中断模式
//IFS0bits.U1TXIF=0;
IEC0bits.U1TXIE=0;
IFS0bits.U1RXIF=0;
IEC0bits.U1RXIE=1;
IPC2bits.U1RXIP=6;
TRISE=0;//这两句是点亮8个灯作为是否进定时器中断的标志
LATE=0x3f;
TMR1 = 0; //计数寄存器TMR1=0,从0开始计数
T1CON = 0x0030; //关闭定时器,使用内部时钟,预分频比1:256
PR1 =0x1c20; //周期寄存器赋值,使定时时间为1s
IFS0bits.T1IF = 0; //清除TMR1的中断标志
IPC0bits.T1IP = 7; //中断优先级为7
IEC0bits.T1IE = 1; //使能定时中断
}
/***********************主函数******************************************/
void main(void)
{
Init();
T1CONbits.TON=1;//开启定时器中断
while(1)
{
delay(1000);//对于485电路这个延时不能去,232电路可以去掉,不去也不影响
send();//发送数据
}
}
/*********************************主函数*******************************************/
/***********************************************************************
函数名: void __attribute__ ((__interrupt__)) _U1RXInterrupt(void)
功能:串口接收中断服务函数,主要完成数据的接收以及记录接收数据的字节长
************************************************************************/
void __attribute__ ((__interrupt__)) _U1RXInterrupt(void)
{
IFS0bits.U1RXIF = 0;//中断标志位清零
while(U1STAbits.URXDA)//接收缓冲器有数据
{
buf =U1RXREG;
flag_R++;
b=flag_R;
}
len=b;
}
/**********************************************************************
函数名:void __attribute__((__interrupt__)) _T1Interrupt(void)
功能:1秒进一次中断,把flag_R清零,方便下次单片机接收
**********************************************************************/
void __attribute__((__interrupt__)) _T1Interrupt(void)
{
TMR1=0;
IFS0bits.T1IF = 0; //清定时器中断标志状态位
LATE=~LATE;
flag_R=0;
}
我后来换了一个自动485电路电路图如下! 往事如烟 发表于 2016-12-9 16:22
我后来换了一个自动485电路电路图如下!
你用这个自动转换电路,如果是波特率很高时可能会有问题,最好是自己控制,一般是需要1.5T的时间延时,根据你的串口波特率来计算1.5的时间,在发送和程序的结尾加上1.5T时间的延时就不会发生错误了,我之前做modbus协议栈时都是这样处理的。 zenghouyun 发表于 2016-12-9 22:06
你用这个自动转换电路,如果是波特率很高时可能会有问题,最好是自己控制,一般是需要1.5T的时间延时,根 ...
我试了你讲的那个延时,貌似太短了,那样我只能一次发一个字节,加长了延时还是只能收发5个字节以下的数据,一旦超过5个字节,就会出错!电路问题我还没考虑。我现在也在用modbus协议读写传感器,还请大神多指教呀!!! 你应该是计算错了,具体的计算方法可以网上查下,另外你要适当留点余量。 1.5T是当前波特率的1.5个字节时间,如果你延时了再切换方向肯定没有问题的。 zenghouyun 发表于 2016-12-11 17:50
1.5T是当前波特率的1.5个字节时间,如果你延时了再切换方向肯定没有问题的。 ...
我用的波特率是9600,传感器只接受9600或者115200,我还是用全自动收发的电路!我现在在每次接收数据时加了一个延时,就好了!和你讲的差不多!谢了!{:victory:}
页:
[1]