39. RS485通信实验

学习本章时,配合本书前面的“SCI UART——串口通信”及“CAN-FD通信实验”章节进行对比学习,效果更佳。

关于本开发板中使用的MAX3485收发器资料可以查阅《MAX3485规格书》了解。

39.1. RS-485通讯协议简介

与CAN类似,RS-485是一种工业控制环境中常用的通讯协议,它具有抗干扰能力强、传输距离远的特点。 RS-485通讯协议由RS-232协议改进而来,协议层不变,只是改进了物理层,因而保留了串口通讯协议应用简单的特点。

39.2. RS-485的物理层

从《CAN—FD通讯实验》章节中了解到,差分信号线具有很强的抗干扰能力,特别适合应用于电磁环境复杂的工业控制环境中, RS-485协议主要是把RS-232的信号改进成差分信号,从而大大提高了抗干扰特性,它的通讯网络如下图所示:

../../_images/physical_layer.png

对比CAN通讯网络,可发现它们的网络结构组成是类似的,每个节点都是由一个通讯控制器和一个收发器组成, 在RS-485通讯网络中,节点中的串口控制器使用RX与TX信号线连接到收发器上,而收发器通过差分线连接到网络总线, 串口控制器与收发器之间一般使用TTL信号传输,收发器与总线则使用差分信号来传输。 发送数据时,串口控制器的TX信号经过收发器转换成差分信号传输到总线上,而接收数据时,收发器把总线上的差分信号转化成TTL信号通过RX引脚传输到串口控制器中。

RS-485通讯网络的最大传输距离可达1200米,总线上可挂载128个通讯节点,而由于RS-485网络只有一对差分信号线,它使用差分信号来表达逻辑, 当AB两线间的电压差为-6V~-2V时表示逻辑1,当电压差为+2V~+6V表示逻辑0,在同一时刻只能表达一个信号,所以它的通讯是半双工形式的。 它与RS-232通讯协议的特性对比如下。

通讯标准

信号线

通讯方向

电平标准(发送端)

通讯距离

通讯节点数

RS232

单端TXD、RXD、GND

全双工

逻辑1:-15V~-3V

逻辑0:+3V~+15V

100米以内

只有两个节点

RS485

差分线AB

半双工

逻辑1:+2V~+6V

逻辑0:-6V~-2V

1200米

支持多个节点,支持多个主设备,任意节点间可互相通讯

RS-485与RS-232的差异只体现在物理层上,它们的协议层是相同的,也是使用串口数据包的形式传输数据。而由于RS-485具有强大的组网功能, 人们在基础协议之上还制定了MODBUS协议,被广泛应用在工业控制网络中。 此处说的基础协议是指前面串口章节中讲解的,仅封装了基本数据包格式的协议(基于数据位),而MODBUS协议是使用基本数据包组合成通讯帧格式的高层应用协议(基于数据包或字节)。 感兴趣的读者可查找MODBUS协议的相关资料了解。

由于RS-485与RS-232的协议层没有区别,进行通讯时,我们同样是使用RA6M5的SCI UART外设作为通讯节点中的串口控制器。

39.3. 实验:RS-485—双机通讯实验

本小节演示如何使用启明6M5的UART控制器与MAX3485收发器,在两个设备之间使用RS-485协议进行通讯,在本实验中使用了两个实验板, 本教程主要以“UART—485通讯”工程进行讲解。

39.3.1. 硬件设计

../../_images/two_485_connect.png

由于485只能以半双工的形式工作,在同一时间只能选择接收和发送,所以我们需要切换状态,MAX485芯片中有“RE”和“DE”两个引脚, 用于控制485芯片的收发工作状态,当RE引脚为低电平时,485芯片处于接收状态,当DE引脚为高电平时,芯片处于发送状态。 实验板中使用的MCU通过P503引脚以及P612引脚直接连接到RS485_1和RS485_2的“DE”以及“RE”这两个引脚上,所以我们只需要通过控制P503以及P612的输出电平即可控制RS485的收发状态。

所以在这节实验中,我们需要两块开发板进行通讯实验,并需要用导线把两块实验板中RS485的A、B两条总线连接起来,才能构成完整的网络。 在本实验中使用启明6M5开发板上的RS485_1进行实验,我们只需要把两块实验板之间的RS485_1的A与A连接起来,B与B连接起来即可。

39.3.2. 软件设计

39.3.2.1. 新建工程

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程模板 “05_Template”, 然后将工程文件夹重命名为 “37_RS485_Receive_Send”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程模板 “06_Template”, 然后将工程文件夹重命名为 “37_RS485_Receive_Send”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “rs485” 文件夹, 再进入 “rs485” 文件夹里面新建源文件和头文件:“rs485.c” 和 “rs485.h”。 工程文件结构如下。

文件结构
37_RS485_Receive_Send
├─ ......
└─ src
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ rs485
   │  ├─ rs485.c
   │  └─ rs485.h
   ├─ key
   │  ├─ key.c
   │  └─ key.h
   └─ hal_entry.c

39.3.2.2. 代码分析

在前面我们讲到RS485的收发模式控制方式,所以我们需要通过配置P503引脚的高低电平,就可以切换RS485的发送与接收状态。 在本实验中,我们会通过按键来切换RS485的收发状态,并发送数据给另一个单片机。

../../_images/DIR_pin.png

39.3.2.3. FSP配置

我们先打开e2 studio/RASC软件里面的FSP配置一下FSP和芯片。其中包括了RS485的配置以及按键中断的部分。 关于调试串口的配置以及按键中断部分内容可以查看之前的教学章节。 首先点开“Pins”->“Peripherals”->“Connectivity:SCI”->“SCI5”来配置SCI模块: 将“Operation Mode”配置为“Asynchronous UART”。并根据RS-485收发器连接的引脚配置“TXD”和“RXD”。

../../_images/rs485_config.png

点击配置窗口底部的“Stack”,如图步骤加入UART模块。

../../_images/rs485_config_3.png

击刚刚加入的窗口,在左下角的“属性”窗口中配置中断,波特率等属性。

../../_images/rs485_config_4.png

关于一些配置的属性如下:

表37-1:RS-485属性描述

属性

描述

RS-485 Support

启用对串口对RS-485的支持

DE Pin

启用或禁用RS-485半双工通信模式的DE引脚

DE Pin Polarity

选择DE引脚的极性,即低电平有效或是高电平有效,我们使用的ISL83485IBZ-T

芯片是高电平有效

DE Port Number

DE引脚的端口号

DE Pin Number

DE引脚的编号

在窗口底部的“Stack”中如图步骤加入我们的按键中断模块。

../../_images/irq_config.png

点击刚刚加入的窗口,在左下角的“属性”窗口中配置通道,回调函数等属性。

../../_images/irq_config2.png

39.3.2.4. RS-485初始化

代码清单37-1:RS-485初始化
/*RS485_1初始化*/
void RS485_1_Init(void)
{
    R_SCI_UART_Open(rs485_1.p_ctrl, rs485_1.p_cfg);
}

我们将RS485发送数据的函数封装起来,封装为一个 “void RS485_Send_Example( uint8_t ch )” 函数。

代码清单37-2:RS-485发送数据
/*发送数据函数*/
void RS485_Send_Example( uint8_t ch )
{
    /*串口写入函数*/
    R_SCI_UART_Write (&rs485_1_ctrl, (uint8_t*) &ch, 1);
}

在RS485初始化时,我们在头文件中同样定义好RS485切换 发送/接收 模式的函数。

代码清单37-3:收发模式定义
/*定义RS485_1的收发模式*/
#define     RS485_1_TX     R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_05_PIN_03,BSP_IO_LEVEL_HIGH)    //发送模式
#define     RS485_1_RX     R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_05_PIN_03,BSP_IO_LEVEL_LOW)     //接受模式

39.3.2.5. RS-485中断回调

代码清单37-4:RS-485中断回调
/*RS485_1中断回调函数*/
void rs485_1_callback(uart_callback_args_t * p_args)
{
    switch(p_args->event)
    {
        /*接收数据时将数据打印出来*/
        case UART_EVENT_RX_CHAR:
            R_SCI_UART_Write (&g_uart4_ctrl, (uint8_t*) &p_args->data, 1);
            break;
        default:
            break;
    }
}

39.3.2.6. 按键中断函数

首先我们要开启配置好的外部中断,还有按键中断发送数据的函数,我们将其一一封装起来。

代码清单37-5:开启外部中断
void IRQ_Init(void)
{
    fsp_err_t err = FSP_SUCCESS;
    /*开启外部中断*/
    err = g_external_irq_on_icu.open(&sw2_external_irq_ctrl, &sw2_external_irq_cfg);
    assert(FSP_SUCCESS == err);
    /*使能外部中断*/
    err = g_external_irq_on_icu.enable(&sw2_external_irq_ctrl);
    assert(FSP_SUCCESS == err);
}
代码清单37-6:按键中断回调函数
/*按键中断回调函数*/
void external_irq_callback(external_irq_callback_args_t *p_args)
{
    /*防止编译器产生没有使用形参的警告*/
    FSP_PARAMETER_NOT_USED(p_args);

    uint8_t i;

    /*配置RS485_1为发送模式*/
    RS485_1_TX;

    /*LED指示状态*/
    LED1_ON;
    LED3_OFF;

    for(i =0; i <= 10; i++){
    /*发送数据*/
    RS485_Send_Example( i );
    /*等待发送时间*/
    R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
    }
}

39.3.2.7. hal_entry函数

代码清单37-7:hal_entry函数
void hal_entry(void)
{
    /* TODO: add your own code here */

    IRQ_Init();         // 开启按键中断
    IO_Init();          // 按键中断初始化
    RS485_1_Init();     // RS485_1初始化
    Debug_UART4_Init(); // SCI4 UART调试串口初始化

    while(1)
    {
        /*没有按下按键的时候,使RS485处于接收状态接收数据*/
        RS485_1_RX;

        /*LED灯指示状态*/
        LED1_OFF;
        LED3_ON;
    }
#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

39.3.2.8. 下载验证

保证开发板相关硬件连接正确,用Type-C线连接开发板“USB TO UART”接口跟电脑。 正确连接两块开发板的RS485线路, 本次实验需要使用到串口调试助手,配置好串口参数并打开串口后, 接收状态下绿灯常亮,按下我们开发板上的Key1按键,发送设备切换到发送状态,红灯亮, 将另一块开发板的串口接收区设置为16进制接收,就可以在另一块开发板上的接收区看见我们发送的“0-10”字符。