【APM32E030R Micro-EVB开发板评测】modbus总线读取风速仪
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; // Modbus发送缓冲区
uint8_t rxBuffer;// 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;
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 = addr; // 从机地址
txBuffer = func; // 功能码
txBuffer = (regAddr >> 8) & 0xFF;// 寄存器地址高8位
txBuffer = regAddr & 0xFF; // 寄存器地址低8位
txBuffer = (regCnt >> 8) & 0xFF; // 寄存器数量高8位
txBuffer = regCnt & 0xFF; // 寄存器数量低8位
// 计算CRC
crc = Modbus_CRC16(txBuffer, 6);
txBuffer = crc & 0xFF; // CRC低8位
txBuffer = (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);
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 = UART_RxData(UART1);
// 对于读取保持寄存器的响应,长度是固定的:地址(1)+功能码(1)+数据长度(1)+数据(n)+CRC(2)
if (len >= 3 && len == (buf + 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 << 8) | rxBuffer;
if (crc != receivedCrc)
{
return MODBUS_CRC_ERROR;
}
// 检查从机地址和功能码
if (rxBuffer != WIND_SENSOR_ADDR || rxBuffer != MODBUS_READ_HOLDING_REG)
{
// 检查是否是错误响应
if (rxBuffer == (MODBUS_READ_HOLDING_REG | 0x80))
{
return MODBUS_RESP_ERROR;// 从机返回错误
}
return MODBUS_RESP_ERROR;
}
// 检查数据长度是否正确
if (rxBuffer != 2 * WIND_SPEED_REG_CNT)
{
return MODBUS_RESP_ERROR;
}
// 解析风速值(假设是16位无符号整数,单位0.01m/s)
uint16_t windRaw = (rxBuffer << 8) | rxBuffer;
*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);
}
}
楼主这是干什么工作的?居然还有风速仪
页:
[1]