打印
[应用相关]

STM32 RS485通信

[复制链接]
1972|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
晓伍|  楼主 | 2019-7-5 08:43 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
RS485是主流的一种多机通信方式,多用于不同设备间的数据传送。最常见的是主控板和485接口的传感器间的通信。下面介绍常用的通信方式及代码,包括常用的CRC校验。



初始化代码:(初始化控制引脚(收发)和对应串口(看硬件连接))

//bound:波特率          
void RS485_Init(u32 bound)
{  

        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);//使能GPIOE时钟
       
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;                                 //PE1端口配置
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOE, &GPIO_InitStructure);
       
        USART2_Init_JAVE( bound);
       
        RS485_TX_EN=0;                        //默认为接收模式
}

串口初始化部分代码不再介绍,其中RS485_TX_EN是控制引脚输出低,为接收模式。


使用特权

评论回复
沙发
晓伍|  楼主 | 2019-7-5 08:43 | 只看该作者
下面是发送和接收部分的代码

//RS485发送len个字节.
//buf:发送区首地址
//len:发送的字节数(为了和本代码的接收匹配,这里建议不要超过64个字节)
void RS485_Send_Data(u8 *buf,u8 len)
{
        u8 t;
        RS485_TX_EN=1;                        //设置为发送模式
          for(t=0;t<len;t++)                //循环发送数据
        {                  
                while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);          
                USART_SendData(USART2,buf[t]);
        }         

        while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);               
        RX2_Point=0;          
        RS485_TX_EN=0;                                //设置为接收模式       
}
//RS485查询接收到的数据
//buf:接收缓存首地址
//len:读到的数据长度
void RS485_Receive_Data(u8 *buf,u8 *len)
{
        u8 rxlen=RX2_Point;
        u8 i=0;
        *len=0;                                //默认为0
        delay_ms(10);//等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
        if(rxlen==RX2_Point&&rxlen)//接收到了数据,且接收完成了
        {
                for(i=0;i<rxlen;i++)
                {
                        buf[i]=RX2_Temp[i];       
                }               
                *len=RX2_Point;        //记录本次数据长度
                RX2_Point=0;                //清零
        }
}

RX2_Point是串口2的接收指针。



解释一下接收函数,首先定义一个临时变量并赋予它RX2_Point的值,然后做一定的延时。如果这时候已经接收完成了,那么RX_Point的值不会变,rxlen也依然等于它。若还没接收完成,RX2_Point 的值会继续增加rxlen就不会相等,这时候执行这个函数就不会有想得到的结果了,所以在发送和接收之间必须有足够的延时等待从机响应主机的命令。后面以一个传感器读数示例会体现这一点。

使用特权

评论回复
板凳
晓伍|  楼主 | 2019-7-5 08:44 | 只看该作者
RS485通信通常需要使用两个字节的校验码来增加信号传输的可靠性,如果传输过程无误,校验码输出0。

获取CRC代码:

参数分别为:输入数字首地址,校验长度,输出结果(16位,即两字节)

有关于CRC的计算流程可自查百度

void Get_CRC(u8 *buf,u8 len,u16 *crc_out)
{
    int i=0,j=0;
    u16 crc,out;
                crc=0xffff;
    for(i=0;i<len;i++)
    {
        out=buf[i]&0x00ff;
                                crc=crc^out;
        for(j=0;j<8;j++)
        {
                                                out=crc&0x0001;
            if(out==1){
                                                        crc >>= 1;
                                                        crc=crc^0xa001;
            }
                                                else{
                                                        crc >>= 1;
                                                }
        }
    }
                *crc_out=crc;
}


主从通信中,主机给从机发送命令,一般也要带校验码,这个校验码有些设备的说明书会带上命令对应的校验码。可以直接发送,也可以程序根据命令计算出校验码后发送。

使用特权

评论回复
地板
晓伍|  楼主 | 2019-7-5 08:44 | 只看该作者
下面以一485接口的传感器通信为例:

float Task_Read_485Senor(void)
{
                 unsigned char RS485[11]={0x01,0x04,0x00,0x00,0x00,0x03,0,0};
                 unsigned char num=6;
                 u16 CRC_RES=0,temp;         
                 RS485_Init(9600);                                        //初始化485
                 Get_CRC(RS485,num,&CRC_RES);                //获取命令CRC
                 RS485[6]=CRC_RES%256;                                //CRC低位
                 RS485[7]=CRC_RES/256;                                //CRC高位
                 RS485_Send_Data(RS485,8);                        //发送8个字节读取命令
                 delay_ms(2000);                                        //延时一定时间,确保接收完成了
                 RS485_Receive_Data(RS485,&num);        //接收结果写入数组

                 Get_CRC(RS485,num,&CRC_RES);                //接收结果CRC校验

                 if( CRC_RES == 0 ){                //有效数据               
                                 //return 读数;
                 }
                 else{                                                                        //无效
                         MyPrintf("CRC校验错误\r\n");
                         return 0xff;                                //返回失败
                 }
                 return 0xff;
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

4113

帖子

1

粉丝