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设备,可以在空闲时进入低功耗模式,当串口收到数据时再通过中断唤醒,极大地降低了平均功耗。
风速仪:
硬件
APM32E030 MAX485 风速仪
PA9(TX) → DI PA10(RX) → RO PA8 → DE/RE (控制收发方向) 3.3V → VCC GND → GND A → A B → B
"modbus.C
- #include "modbus.h"
- #include "apm32e0xx_uart.h"
- #include "apm32e0xx_gpio.h"
- #include "apm32e0xx_rcm.h"
- #include "delay.h" // 假设已实现延时函数
- // 控制RS485收发方向的引脚定义
- #define MODBUS_DIR_PORT GPIOA
- #define MODBUS_DIR_PIN GPIO_PIN_8
- uint8_t txBuffer[8]; // Modbus发送缓冲区
- uint8_t rxBuffer[16]; // Modbus接收缓冲区
- // 计算CRC16校验
- uint16_t Modbus_CRC16(uint8_t *data, uint8_t len)
- {
- uint16_t crc = 0xFFFF;
- uint8_t i, j;
-
- for (i = 0; i < len; i++)
- {
- crc ^= data[i];
- for (j = 0; j < 8; j++)
- {
- if (crc & 0x0001)
- {
- crc >>= 1;
- crc ^= 0xA001;
- }
- else
- {
- crc >>= 1;
- }
- }
- }
- return crc;
- }
- // 初始化UART和RS485控制引脚
- void Modbus_Init(uint32_t baudrate)
- {
- GPIO_Config_T gpioConfig;
- UART_Config_T uartConfig;
-
- // 使能时钟
- RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_UART1);
-
- // 配置UART1引脚
- // PA9 - UART1_TX
- gpioConfig.mode = GPIO_MODE_AF_PP;
- gpioConfig.speed = GPIO_SPEED_50MHz;
- gpioConfig.pin = GPIO_PIN_9;
- GPIO_Config(GPIOA, &gpioConfig);
-
- // PA10 - UART1_RX
- gpioConfig.mode = GPIO_MODE_IN_FLOATING;
- gpioConfig.pin = GPIO_PIN_10;
- GPIO_Config(GPIOA, &gpioConfig);
-
- // 配置RS485方向控制引脚PA8
- gpioConfig.mode = GPIO_MODE_OUT_PP;
- gpioConfig.speed = GPIO_SPEED_50MHz;
- gpioConfig.pin = GPIO_PIN_8;
- GPIO_Config(MODBUS_DIR_PORT, &gpioConfig);
- GPIO_ResetBit(MODBUS_DIR_PORT, MODBUS_DIR_PIN); // 初始为接收模式
-
- // 配置UART1
- uartConfig.baudRate = baudrate;
- uartConfig.wordLength = UART_WORD_LEN_8B;
- uartConfig.stopBits = UART_STOP_BIT_1;
- uartConfig.parity = UART_PARITY_NONE;
- uartConfig.hwFlowControl = UART_HW_FLOW_CTRL_NONE;
- uartConfig.mode = UART_MODE_TX_RX;
- UART_Config(UART1, &uartConfig);
-
- // 使能UART
- UART_Enable(UART1);
- }
- // 发送Modbus请求帧
- void Modbus_SendRequest(uint8_t addr, uint8_t func, uint16_t regAddr, uint16_t regCnt)
- {
- uint16_t crc;
-
- // 构建请求帧
- txBuffer[0] = addr; // 从机地址
- txBuffer[1] = func; // 功能码
- txBuffer[2] = (regAddr >> 8) & 0xFF; // 寄存器地址高8位
- txBuffer[3] = regAddr & 0xFF; // 寄存器地址低8位
- txBuffer[4] = (regCnt >> 8) & 0xFF; // 寄存器数量高8位
- txBuffer[5] = regCnt & 0xFF; // 寄存器数量低8位
-
- // 计算CRC
- crc = Modbus_CRC16(txBuffer, 6);
- txBuffer[6] = crc & 0xFF; // CRC低8位
- txBuffer[7] = (crc >> 8) & 0xFF; // CRC高8位
-
- // 切换到发送模式
- GPIO_SetBit(MODBUS_DIR_PORT, MODBUS_DIR_PIN);
-
- // 发送数据
- for (uint8_t i = 0; i < 8; i++)
- {
- UART_TxData(UART1, txBuffer[i]);
- while (UART_ReadStatusFlag(UART1, UART_FLAG_TXBE) == RESET);
- }
-
- // 等待发送完成
- while (UART_ReadStatusFlag(UART1, UART_FLAG_TC) == RESET);
-
- // 切换回接收模式
- GPIO_ResetBit(MODBUS_DIR_PORT, MODBUS_DIR_PIN);
- }
- // 接收Modbus响应帧
- uint8_t Modbus_ReceiveResponse(uint8_t *buf, uint8_t maxLen, uint32_t timeout)
- {
- uint8_t len = 0;
- uint32_t tickStart = Delay_GetTick(); // 获取当前滴答计数
-
- while (1)
- {
- // 检查超时
- if (Delay_GetTick() - tickStart > timeout)
- {
- return 0; // 超时
- }
-
- // 检查是否有数据接收
- if (UART_ReadStatusFlag(UART1, UART_FLAG_RXBNE) != RESET)
- {
- buf[len++] = UART_RxData(UART1);
-
- // 对于读取保持寄存器的响应,长度是固定的:地址(1)+功能码(1)+数据长度(1)+数据(n)+CRC(2)
- if (len >= 3 && len == (buf[2] + 5))
- {
- break; // 接收完成
- }
-
- // 防止缓冲区溢出
- if (len >= maxLen)
- {
- break;
- }
- }
- }
-
- return len;
- }
- // 读取风速值
- uint8_t Modbus_ReadWindSpeed(float *windSpeed)
- {
- uint8_t len;
- uint16_t crc, receivedCrc;
-
- // 发送读取请求
- Modbus_SendRequest(WIND_SENSOR_ADDR, MODBUS_READ_HOLDING_REG,
- WIND_SPEED_REG_ADDR, WIND_SPEED_REG_CNT);
-
- // 等待响应(超时时间500ms)
- len = Modbus_ReceiveResponse(rxBuffer, sizeof(rxBuffer), 500);
-
- // 检查是否超时
- if (len == 0)
- {
- return MODBUS_TIMEOUT;
- }
-
- // 检查CRC
- crc = Modbus_CRC16(rxBuffer, len - 2);
- receivedCrc = (rxBuffer[len - 1] << 8) | rxBuffer[len - 2];
- if (crc != receivedCrc)
- {
- return MODBUS_CRC_ERROR;
- }
-
- // 检查从机地址和功能码
- if (rxBuffer[0] != WIND_SENSOR_ADDR || rxBuffer[1] != MODBUS_READ_HOLDING_REG)
- {
- // 检查是否是错误响应
- if (rxBuffer[1] == (MODBUS_READ_HOLDING_REG | 0x80))
- {
- return MODBUS_RESP_ERROR; // 从机返回错误
- }
- return MODBUS_RESP_ERROR;
- }
-
- // 检查数据长度是否正确
- if (rxBuffer[2] != 2 * WIND_SPEED_REG_CNT)
- {
- return MODBUS_RESP_ERROR;
- }
-
- // 解析风速值(假设是16位无符号整数,单位0.01m/s)
- uint16_t windRaw = (rxBuffer[3] << 8) | rxBuffer[4];
- *windSpeed = windRaw * 0.01f; // 转换为m/s
-
- return MODBUS_NO_ERROR;
- }
main函数
- #include "apm32e0xx.h"
- #include "modbus.h"
- #include "delay.h"
- #include <stdio.h>
- float windSpeed = 0.0f;
- uint8_t modbusStatus;
- int main(void)
- {
- // 初始化系统时钟
- SystemInit();
-
- // 初始化延时函数(基于SysTick)
- Delay_Init();
-
- // 初始化Modbus(风速仪常用波特率:9600)
- Modbus_Init(9600);
-
- while (1)
- {
- // 读取风速
- modbusStatus = Modbus_ReadWindSpeed(&windSpeed);
-
- // 处理读取结果
- if (modbusStatus == MODBUS_NO_ERROR)
- {
- // 成功读取到风速,可在这里处理数据
- printf("Wind Speed: %.2f m/s\r\n", windSpeed);
- }
- else
- {
- // 处理错误
- switch (modbusStatus)
- {
- case MODBUS_TIMEOUT:
- // 超时错误处理
- break;
- case MODBUS_CRC_ERROR:
- // CRC校验错误处理
- break;
- case MODBUS_RESP_ERROR:
- // 响应错误处理
- break;
- }
- }
-
- // 1秒读取一次
- Delay_Ms(1000);
- }
- }
|