[APM32E0] 【APM32E030R Micro-EVB开发板评测】modbus总线读取风速仪

[复制链接]
48|1
abner_ma 发表于 2025-8-29 17:38 | 显示全部楼层 |阅读模式


  APM32E03X作为国产MCU,相较于同级别的ST、NXP等国际大厂产品,具有价格优势,这对于需要大规模部署的Modbus节点(如传感器、执行器、采集模块)来说至关重要。极致的成本优势。Cortex-M0+内核:48MHz的主频对于处理Modbus RTU/ASCII协议绰绰有余。MODBUS协议是一种开放式通信协议,具有传输速度快、传输距离长、传输速率高、传输可靠性高等特点。由于MODBUS协议的通信方式具有广泛的适用性,因此适用于各种工业自动化领域,如工业控制、自动化设备、数据采集等。Modbus协议本身不算复杂,主要的开销在于CRC计算和串口通信,M0+内核完全可以高效处理。
   丰富的UART资源:APM32E030提供多个UART接口,可以轻松实现,一个UART用于Modbus通信(连接RS-485芯片)。另一个UART用于打印调试信息(连接USB转TTL),非常方便开发。可以同时实现多个Modbus主站或从站。
   低功耗特性 该芯片具有多种低功耗模式(睡眠、停机、待机)。对于电池供电或需要节能的Modbus设备,可以在空闲时进入低功耗模式,当串口收到数据时再通过中断唤醒,极大地降低了平均功耗。

风速仪:
AA.png
硬件


    APM32E030      MAX485       风速仪
     PA9(TX)  →    DI     PA10(RX) →    RO     PA8      →    DE/RE        (控制收发方向)    3.3V     →    VCC    GND      →    GND              A      →     A              B      →     B


"modbus.C

  1. #include "modbus.h"
  2. #include "apm32e0xx_uart.h"
  3. #include "apm32e0xx_gpio.h"
  4. #include "apm32e0xx_rcm.h"
  5. #include "delay.h"  // 假设已实现延时函数

  6. // 控制RS485收发方向的引脚定义
  7. #define MODBUS_DIR_PORT    GPIOA
  8. #define MODBUS_DIR_PIN     GPIO_PIN_8

  9. uint8_t txBuffer[8];   // Modbus发送缓冲区
  10. uint8_t rxBuffer[16];  // Modbus接收缓冲区

  11. // 计算CRC16校验
  12. uint16_t Modbus_CRC16(uint8_t *data, uint8_t len)
  13. {
  14.     uint16_t crc = 0xFFFF;
  15.     uint8_t i, j;
  16.    
  17.     for (i = 0; i < len; i++)
  18.     {
  19.         crc ^= data[i];
  20.         for (j = 0; j < 8; j++)
  21.         {
  22.             if (crc & 0x0001)
  23.             {
  24.                 crc >>= 1;
  25.                 crc ^= 0xA001;
  26.             }
  27.             else
  28.             {
  29.                 crc >>= 1;
  30.             }
  31.         }
  32.     }
  33.     return crc;
  34. }

  35. // 初始化UART和RS485控制引脚
  36. void Modbus_Init(uint32_t baudrate)
  37. {
  38.     GPIO_Config_T gpioConfig;
  39.     UART_Config_T uartConfig;
  40.    
  41.     // 使能时钟
  42.     RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
  43.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_UART1);
  44.    
  45.     // 配置UART1引脚
  46.     // PA9 - UART1_TX
  47.     gpioConfig.mode = GPIO_MODE_AF_PP;
  48.     gpioConfig.speed = GPIO_SPEED_50MHz;
  49.     gpioConfig.pin = GPIO_PIN_9;
  50.     GPIO_Config(GPIOA, &gpioConfig);
  51.    
  52.     // PA10 - UART1_RX
  53.     gpioConfig.mode = GPIO_MODE_IN_FLOATING;
  54.     gpioConfig.pin = GPIO_PIN_10;
  55.     GPIO_Config(GPIOA, &gpioConfig);
  56.    
  57.     // 配置RS485方向控制引脚PA8
  58.     gpioConfig.mode = GPIO_MODE_OUT_PP;
  59.     gpioConfig.speed = GPIO_SPEED_50MHz;
  60.     gpioConfig.pin = GPIO_PIN_8;
  61.     GPIO_Config(MODBUS_DIR_PORT, &gpioConfig);
  62.     GPIO_ResetBit(MODBUS_DIR_PORT, MODBUS_DIR_PIN);  // 初始为接收模式
  63.    
  64.     // 配置UART1
  65.     uartConfig.baudRate = baudrate;
  66.     uartConfig.wordLength = UART_WORD_LEN_8B;
  67.     uartConfig.stopBits = UART_STOP_BIT_1;
  68.     uartConfig.parity = UART_PARITY_NONE;
  69.     uartConfig.hwFlowControl = UART_HW_FLOW_CTRL_NONE;
  70.     uartConfig.mode = UART_MODE_TX_RX;
  71.     UART_Config(UART1, &uartConfig);
  72.    
  73.     // 使能UART
  74.     UART_Enable(UART1);
  75. }

  76. // 发送Modbus请求帧
  77. void Modbus_SendRequest(uint8_t addr, uint8_t func, uint16_t regAddr, uint16_t regCnt)
  78. {
  79.     uint16_t crc;
  80.    
  81.     // 构建请求帧
  82.     txBuffer[0] = addr;                // 从机地址
  83.     txBuffer[1] = func;                // 功能码
  84.     txBuffer[2] = (regAddr >> 8) & 0xFF;  // 寄存器地址高8位
  85.     txBuffer[3] = regAddr & 0xFF;       // 寄存器地址低8位
  86.     txBuffer[4] = (regCnt >> 8) & 0xFF;   // 寄存器数量高8位
  87.     txBuffer[5] = regCnt & 0xFF;        // 寄存器数量低8位
  88.    
  89.     // 计算CRC
  90.     crc = Modbus_CRC16(txBuffer, 6);
  91.     txBuffer[6] = crc & 0xFF;          // CRC低8位
  92.     txBuffer[7] = (crc >> 8) & 0xFF;   // CRC高8位
  93.    
  94.     // 切换到发送模式
  95.     GPIO_SetBit(MODBUS_DIR_PORT, MODBUS_DIR_PIN);
  96.    
  97.     // 发送数据
  98.     for (uint8_t i = 0; i < 8; i++)
  99.     {
  100.         UART_TxData(UART1, txBuffer[i]);
  101.         while (UART_ReadStatusFlag(UART1, UART_FLAG_TXBE) == RESET);
  102.     }
  103.    
  104.     // 等待发送完成
  105.     while (UART_ReadStatusFlag(UART1, UART_FLAG_TC) == RESET);
  106.    
  107.     // 切换回接收模式
  108.     GPIO_ResetBit(MODBUS_DIR_PORT, MODBUS_DIR_PIN);
  109. }

  110. // 接收Modbus响应帧
  111. uint8_t Modbus_ReceiveResponse(uint8_t *buf, uint8_t maxLen, uint32_t timeout)
  112. {
  113.     uint8_t len = 0;
  114.     uint32_t tickStart = Delay_GetTick();  // 获取当前滴答计数
  115.    
  116.     while (1)
  117.     {
  118.         // 检查超时
  119.         if (Delay_GetTick() - tickStart > timeout)
  120.         {
  121.             return 0;  // 超时
  122.         }
  123.         
  124.         // 检查是否有数据接收
  125.         if (UART_ReadStatusFlag(UART1, UART_FLAG_RXBNE) != RESET)
  126.         {
  127.             buf[len++] = UART_RxData(UART1);
  128.             
  129.             // 对于读取保持寄存器的响应,长度是固定的:地址(1)+功能码(1)+数据长度(1)+数据(n)+CRC(2)
  130.             if (len >= 3 && len == (buf[2] + 5))
  131.             {
  132.                 break;  // 接收完成
  133.             }
  134.             
  135.             // 防止缓冲区溢出
  136.             if (len >= maxLen)
  137.             {
  138.                 break;
  139.             }
  140.         }
  141.     }
  142.    
  143.     return len;
  144. }

  145. // 读取风速值
  146. uint8_t Modbus_ReadWindSpeed(float *windSpeed)
  147. {
  148.     uint8_t len;
  149.     uint16_t crc, receivedCrc;
  150.    
  151.     // 发送读取请求
  152.     Modbus_SendRequest(WIND_SENSOR_ADDR, MODBUS_READ_HOLDING_REG,
  153.                       WIND_SPEED_REG_ADDR, WIND_SPEED_REG_CNT);
  154.    
  155.     // 等待响应(超时时间500ms)
  156.     len = Modbus_ReceiveResponse(rxBuffer, sizeof(rxBuffer), 500);
  157.    
  158.     // 检查是否超时
  159.     if (len == 0)
  160.     {
  161.         return MODBUS_TIMEOUT;
  162.     }
  163.    
  164.     // 检查CRC
  165.     crc = Modbus_CRC16(rxBuffer, len - 2);
  166.     receivedCrc = (rxBuffer[len - 1] << 8) | rxBuffer[len - 2];
  167.     if (crc != receivedCrc)
  168.     {
  169.         return MODBUS_CRC_ERROR;
  170.     }
  171.    
  172.     // 检查从机地址和功能码
  173.     if (rxBuffer[0] != WIND_SENSOR_ADDR || rxBuffer[1] != MODBUS_READ_HOLDING_REG)
  174.     {
  175.         // 检查是否是错误响应
  176.         if (rxBuffer[1] == (MODBUS_READ_HOLDING_REG | 0x80))
  177.         {
  178.             return MODBUS_RESP_ERROR;  // 从机返回错误
  179.         }
  180.         return MODBUS_RESP_ERROR;
  181.     }
  182.    
  183.     // 检查数据长度是否正确
  184.     if (rxBuffer[2] != 2 * WIND_SPEED_REG_CNT)
  185.     {
  186.         return MODBUS_RESP_ERROR;
  187.     }
  188.    
  189.     // 解析风速值(假设是16位无符号整数,单位0.01m/s)
  190.     uint16_t windRaw = (rxBuffer[3] << 8) | rxBuffer[4];
  191.     *windSpeed = windRaw * 0.01f;  // 转换为m/s
  192.    
  193.     return MODBUS_NO_ERROR;
  194. }
    main函数

  1. #include "apm32e0xx.h"
  2. #include "modbus.h"
  3. #include "delay.h"
  4. #include <stdio.h>

  5. float windSpeed = 0.0f;
  6. uint8_t modbusStatus;

  7. int main(void)
  8. {
  9.     // 初始化系统时钟
  10.     SystemInit();
  11.    
  12.     // 初始化延时函数(基于SysTick)
  13.     Delay_Init();
  14.    
  15.     // 初始化Modbus(风速仪常用波特率:9600)
  16.     Modbus_Init(9600);
  17.    
  18.     while (1)
  19.     {
  20.         // 读取风速
  21.         modbusStatus = Modbus_ReadWindSpeed(&windSpeed);
  22.         
  23.         // 处理读取结果
  24.         if (modbusStatus == MODBUS_NO_ERROR)
  25.         {
  26.             // 成功读取到风速,可在这里处理数据
  27.      printf("Wind Speed: %.2f m/s\r\n", windSpeed);
  28.         }
  29.         else
  30.         {
  31.             // 处理错误
  32.             switch (modbusStatus)
  33.             {
  34.                 case MODBUS_TIMEOUT:
  35.                     // 超时错误处理
  36.                     break;
  37.                 case MODBUS_CRC_ERROR:
  38.                     // CRC校验错误处理
  39.                     break;
  40.                 case MODBUS_RESP_ERROR:
  41.                     // 响应错误处理
  42.                     break;
  43.             }
  44.         }
  45.         
  46.         // 1秒读取一次
  47.         Delay_Ms(1000);
  48.     }
  49. }
  
DawnFervor 发表于 2025-8-30 13:47 | 显示全部楼层
楼主这是干什么工作的?居然还有风速仪
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:项目经理
简介:资深嵌入式开发工程师

97

主题

183

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部