485通信协议

STM32-20-485

1. 485总线

  • 串口、UART、TTL、RS232、RS422、RS485关系:
    在这里插入图片描述

    关系与区别:

    特性串口UARTTTLRS232RS422RS485
    定义数据传输接口硬件设备电平标准通信标准通信标准通信标准
    电平--0V/5V±3V到±15V-6V到+6V-6V到+6V
    通信方式串行串行串行串行差分串行差分串行
    最大传输距离--短距离100米1200米1200米
    支持设备数---点对点多点多点
    典型应用数据传输微控制器数字电路计算机外设工业自动化工业自动化

    串口通信是一种通用的通信接口,可以通过不同的硬件和电平标准(如UART、TTL、RS232、RS422、RS485)来实现各种不同的应用需求。这些标准各有优缺点,适用于不同的应用场景,如短距离高速通信或长距离多设备通信。

  • 485总线介绍:

    RS485是串行通信标准,使用差分信号传输,抗干扰能力强,常用于工控领域。RS485具有强大的组网功能,在串口基础协议之上还制定MODBUS协议

    • 串口基础协议:仅指封装了基本数据包格式的协议(基于数据位

    • MODBUS协议:使用基本数据包组合成通讯帧格式的高层应用协议(基于数据包或字节

    • 关系:

      特性串口基础协议MODBUS协议
      通信模式点对点主从模式
      数据传输方式异步或同步异步
      数据格式起始位、数据位、校验位、停止位设备地址、功能码、数据域、校验码
      波特率可变固定在特定应用中
      应用场景广泛应用于各种串行通信场景工业自动化系统
      校验方式奇偶校验或无校验CRC校验(RTU模式)或LRC校验(ASCII模式)
      数据传输效率较低较高(特别是RTU模式)

      串口基础协议是一种底层通信协议,用于实现基本的串行数据传输,而MODBUS协议是一种应用层协议,基于串行通信实现设备间的数据交换,特别适用于工业控制系统。MODBUS协议利用串口基础协议传输数据,但增加了设备地址、功能码和校验等机制,以实现更复杂和可靠的通信。

  • 连接示意图:
    在这里插入图片描述

  • 485通信电路分析:
    在这里插入图片描述

    TP8485是一种用于实现RS-485通信的收发器芯片。它的主要作用是将微控制器或其他设备的UART信号转换为RS-485标准的差分信号,从而实现可靠的远距离通信。

  • 单片机A发送数据到单片机B示意图:
    在这里插入图片描述

    单片机A代码:

    void RS485_Send_Init(void) 
    {
        //配置UART发送
        UART_Init(baud_rate);
    
        //配置DE和RE控制的GPIO引脚
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_InitStruct.Pin = DE_PIN | RE_PIN;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        HAL_GPIO_Init(GPIO_PORT, &GPIO_InitStruct);
    
        //初始化时将DE和RE设置为低电平
        HAL_GPIO_WritePin(GPIO_PORT, DE_PIN, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIO_PORT, RE_PIN, GPIO_PIN_RESET);
    }
    
    void RS485_Send(uint8_t *data, uint16_t length) 
    {
        //设置DE和RE为高电平,使能发送模式
        HAL_GPIO_WritePin(GPIO_PORT, DE_PIN, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIO_PORT, RE_PIN, GPIO_PIN_SET);
    
        //发送数据
        HAL_UART_Transmit(&huart, data, length, HAL_MAX_DELAY);
    
        //发送完成后,将DE和RE设置为低电平,禁用发送器
        HAL_GPIO_WritePin(GPIO_PORT, DE_PIN, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIO_PORT, RE_PIN, GPIO_PIN_RESET);
    }
    

    单片机B代码:

    void RS485_Receive_Init(void) 
    {
        //配置UART接收
        UART_Init(baud_rate);
    
        //配置DE和RE控制的GPIO引脚
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_InitStruct.Pin = DE_PIN | RE_PIN;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        HAL_GPIO_Init(GPIO_PORT, &GPIO_InitStruct);
    
        //初始化时将DE设置为低电平,RE设置为低电平,使能接收器
        HAL_GPIO_WritePin(GPIO_PORT, DE_PIN, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIO_PORT, RE_PIN, GPIO_PIN_RESET);
    }
    
    uint8_t RS485_Receive(void) 
    {
        uint8_t rec_data;
        HAL_UART_Receive(&huart, &rec_data, 1, HAL_MAX_DELAY);
        return rec_data;
    }
    

    作为驱动器和接收器时,电平状态:
    在这里插入图片描述

    波形示意图:
    在这里插入图片描述

    接线示意图:
    在这里插入图片描述

2. 485相关HAL库驱动

驱动函数关联寄存器功能描述
__HAL_RCC_USARTx_CLK_ENABLE()使能串口时钟
HAL_UART_Init()USART_CR1/CR2初始化串口
__HAL_UART_ENABLE_IT()USART_CR1使能串口相关中断
HAL_UART_Receive()USART_DR串口接收数据
HAL_UART_Transmit()USART_DR串口发送数据
__HAL_UART_GET_FLAG()USART_SR查询当前串口的状态

3. 485配置步骤

  1. 配置串口工作参数

    HAL_UART_Init()
    
  2. 串口底层初始化

    HAL_UART_MspInit()
    
  3. 开启串口异步接收中断

    __HAL_UART_ENABLE_IT()
    
  4. 设置优先级,使能中断

    HAL_NVIC_SetPriority()
    HAL_NVIC_EnableIRQ()
    
  5. 编写中断服务函数

    USARTx_IRQHandler()
    HAL_UART_Receive()    
    
  6. 串口数据发送

    HAL_UART_Transmit()
    

4. 代码实现

  • 功能: 通过连接两个战舰STM32F103的RS485接口,然后由KEY0控制发送,当按下一个开发板的KEY0的时候,就发送5个数据给另外一个开发板,并在两个开发板上分别显示发送的值和接收到的值。

  • 硬件原理图:
    在这里插入图片描述

  • RS485初始化函数:

    void rs485_init(uint32_t baudrate)
    {
        // IO 及 时钟配置 
        RS485_RE_GPIO_CLK_ENABLE(); // 使能 RS485_RE 脚时钟 
        RS485_TX_GPIO_CLK_ENABLE(); // 使能 串口TX脚 时钟 
        RS485_RX_GPIO_CLK_ENABLE(); // 使能 串口RX脚 时钟 
        RS485_UX_CLK_ENABLE();      // 使能 串口 时钟 
    
        GPIO_InitTypeDef gpio_initure;
        gpio_initure.Pin = RS485_TX_GPIO_PIN;
        gpio_initure.Mode = GPIO_MODE_AF_PP;
        gpio_initure.Pull = GPIO_PULLUP;
        gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_initure); // 串口TX 脚 模式设置 
    
        gpio_initure.Pin = RS485_RX_GPIO_PIN;
        gpio_initure.Mode = GPIO_MODE_AF_INPUT;
        HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_initure); // 串口RX 脚 必须设置成输入模式 
    
        gpio_initure.Pin = RS485_RE_GPIO_PIN;
        gpio_initure.Mode = GPIO_MODE_OUTPUT_PP;
        gpio_initure.Pull = GPIO_PULLUP;
        gpio_initure.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(RS485_RE_GPIO_PORT, &gpio_initure); // RS485_RE 脚 模式设置 
    
        // USART 初始化设置 
        g_rs485_handler.Instance = RS485_UX;                  // 选择485对应的串口 
        g_rs485_handler.Init.BaudRate = baudrate;             // 波特率 
        g_rs485_handler.Init.WordLength = UART_WORDLENGTH_8B; // 字长为8位数据格式 
        g_rs485_handler.Init.StopBits = UART_STOPBITS_1;      // 一个停止位 
        g_rs485_handler.Init.Parity = UART_PARITY_NONE;       // 无奇偶校验位 
        g_rs485_handler.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控 
        g_rs485_handler.Init.Mode = UART_MODE_TX_RX;          // 收发模式 
        HAL_UART_Init(&g_rs485_handler);                      // HAL_UART_Init()会使能UART2 
    
    #if RS485_EN_RX // 如果使能了接收 
        // 使能接收中断 
        __HAL_UART_ENABLE_IT(&g_rs485_handler, UART_IT_RXNE); // 开启接收中断 
        HAL_NVIC_EnableIRQ(RS485_UX_IRQn);                    // 使能USART2中断 
        HAL_NVIC_SetPriority(RS485_UX_IRQn, 3, 3);            // 抢占优先级3,子优先级3 
    #endif
    
        RS485_RE(0); // 默认为接收模式 
    }
    
  • RS485发送len个字节函数:

    void rs485_send_data(uint8_t *buf, uint8_t len)
    {
        RS485_RE(1);                                         // 进入发送模式 
        HAL_UART_Transmit(&g_rs485_handler, buf, len, 1000); // 串口2发送数据 
        g_RS485_rx_cnt = 0;
        RS485_RE(0); // 进入接收模式 
    }
    
  • RS485查询接收到的数据函数:

    void rs485_receive_data(uint8_t *buf, uint8_t *len)
    {
        uint8_t rxlen = g_RS485_rx_cnt;
        uint8_t i = 0;
        *len = 0;     // 默认为0 
        delay_ms(10); // 等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束 
    
        if (rxlen == g_RS485_rx_cnt && rxlen) // 接收到了数据,且接收完成了 
        {
            for (i = 0; i < rxlen; i++)
            {
                buf[i] = g_RS485_rx_buf[i];
            }
    
            *len = g_RS485_rx_cnt; // 记录本次数据长度 
            g_RS485_rx_cnt = 0;    // 清零
        }
    }
    
  • 接收中断服务函数:

    uint8_t g_RS485_rx_buf[RS485_REC_LEN]; // 接收缓冲, 最大 RS485_REC_LEN 个字节. 
    uint8_t g_RS485_rx_cnt = 0;            // 接收到的数据长度 
    
    void RS485_UX_IRQHandler(void)
    {
        uint8_t res;
    
        if ((__HAL_UART_GET_FLAG(&g_rs485_handler, UART_FLAG_RXNE) != RESET)) // 接收到数据 
        {
            HAL_UART_Receive(&g_rs485_handler, &res, 1, 1000);
    
            if (g_RS485_rx_cnt < RS485_REC_LEN)         // 缓冲区未满 
            {
                g_RS485_rx_buf[g_RS485_rx_cnt] = res;   // 记录接收到的值 
                g_RS485_rx_cnt++;                       // 接收数据增加1 
            }
        }
    }
    
  • 主函数:

    int main(void)
    {
        uint8_t key;
        uint8_t i = 0, t = 0;
        uint8_t cnt = 0;
        uint8_t rs485buf[5];
    
        HAL_Init();                                 /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
        delay_init(72);                             /* 延时初始化 */
        usart_init(115200);                         /* 串口初始化为115200 */
        led_init();                                 /* 初始化LED */
        lcd_init();                                 /* 初始化LCD */
        key_init();                                 /* 初始化按键 */
        rs485_init(9600);                           /* 初始化RS485 */
    
        lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
        lcd_show_string(30,  70, 200, 16, 16, "RS485 TEST", RED);
        lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
        lcd_show_string(30, 110, 200, 16, 16, "KEY0:Send", RED);    /* 显示提示信息 */
    
        lcd_show_string(30, 130, 200, 16, 16, "Count:", RED);       /* 显示当前计数值 */
        lcd_show_string(30, 150, 200, 16, 16, "Send Data:", RED);   /* 提示发送的数据 */
        lcd_show_string(30, 190, 200, 16, 16, "Receive Data:", RED);/* 提示接收到的数据 */
    
        while (1)
        {
            key = key_scan(0);
    
            if (key == 2)   /* KEY0按下,发送一次数据 */
            {
                for (i = 0; i < 5; i++)
                {
                    rs485buf[i] = cnt + i;      /* 填充发送缓冲区 */
                    lcd_show_xnum(30 + i * 32, 170, rs485buf[i], 3, 16, 0X80, BLUE);    /* 显示数据 */
                }
    
                rs485_send_data(rs485buf, 5);   /* 发送5个字节 */
            }
    
            rs485_receive_data(rs485buf, &key);
    
            if (key)    /* 接收到有数据 */
            {
                if (key > 5) key = 5;    /* 最大是5个数据. */
    
                for (i = 0; i < key; i++)
                {
                    lcd_show_xnum(30 + i * 32, 210, rs485buf[i], 3, 16, 0X80, BLUE);    /* 显示数据 */
                }
            }
    
            t++;
            delay_ms(10);
    
            if (t == 20)
            {
                LED0_TOGGLE();  /* LED0闪烁, 提示系统正在运行 */
                t = 0;
                cnt++;
                lcd_show_xnum(30 + 48, 130, cnt, 3, 16, 0X80, BLUE);    /* 显示数据 */
            }
        }
    }
    

声明:资料来源(战舰STM32F103ZET6开发板资源包)

  1. Cortex-M3权威指南(中文).pdf
  2. STM32F10xxx参考手册_V10(中文版).pdf
  3. STM32F103 战舰开发指南V1.3.pdf
  4. STM32F103ZET6(中文版).pdf
  5. 战舰V4 硬件参考手册_V1.0.pdf
  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值