精简的MCU裸机Modbus-RTU实现
基于GD32F305实现和测试,核心层已经初步抽象出来了,方便移植,从机部分已测试通过,主机部分暂未测试。记录备份一下,以后完善。
用于配置的头文件:
#ifndef __MODBUS_CONFIG_H__
#define __MODBUS_CONFIG_H__
#include "modbus_port.h"
// Modbus通道(串口)的总数量
#define MB_CHANNEL_COUNT 1
// 每个通道的读写缓冲区大小 (字节) 应至少为64。
#define MB_BUFFER_SIZE 128
// 主站模式下,等待从站响应的超时时间 (单位: 毫秒)
#define MB_MASTER_TIMEOUT_MS 200
// 可选的定时器源类型 (用于判断帧间隔)
#define MB_TIMER_DWT 1 // 使用DWT计数器
#define MB_TIMER_HW_TIM 2 // 使用MCU的硬件外设定时器
#define MB_TIMER_SYSTICK 3 // 使用SysTick计数 (如果其他任务占用则不推荐使用)
#define MB_TIMER_IDLE 4 // 使用串口IDLE中断检测帧尾 (帧间隔为1T,而标准modbus-rtu规定为3.5T)
// 选择定时器源类型
#define MB_TIMER_SOURCE MB_TIMER_DWT
//#define MB_TIMER_SOURCE MB_TIMER_IDLE
/* 各通道硬件与地址配置 */
// 物理串口外设
#define MB_CHANNEL_0_USART_PERIPH UART3
// 波特率
#define MB_CHANNEL_0_BAUD 9600
// 作为从站时的地址
#define MB_CHANNEL_0_ADDR 0x01
#endif
核心层:
#ifndef __MODBUS_CORE_H__
#define __MODBUS_CORE_H__
#include "modbus_port.h"
#include "modbus_config.h"
// Modbus API 函数返回状态码
typedef enum
{
MB_OK = 0, // 操作成功
MB_ERR_BUSY, // 主站正忙,无法发起新请求
MB_ERR_TIMEOUT, // 主站等待响应超时
MB_ERR_INVALID_RESPONSE, // 响应的地址/功能码/CRC等无效
MB_SUCCESS // 主站事务成功完成 (用于回调函数)
} eMBStatus;
// Modbus 通道工作模式
typedef enum
{
MODE_SLAVE = 0,
MODE_MASTER // @TODO: 主站模式暂未测试
} eMBMode;
// 内部状态机定义 (仅供内部使用)
typedef enum
{
STATE_RX_INIT,
STATE_RX_ING,
STATE_RX_DONE
} eMBRxState;
typedef enum
{
STATE_M_IDLE,
STATE_M_WAIT_RX,
STATE_M_DONE
} eMBMasterState;
// 主站事务完成回调函数指针类型
typedef void (*ModbusMasterCallback)(eMBStatus status, uint8_t* p_data, uint16_t len);
// Modbus 通道上下文结构体
typedef struct
{
uint32_t usart_periph;
uint8_t slave_address;
uint32_t baud_rate;
eMBMode mode;
volatile eMBMasterState master_state;
volatile eMBRxState slave_state;
volatile uint8_t rx_buffer[MB_BUFFER_SIZE];
volatile uint16_t rx_counter;
volatile uint8_t tx_buffer[MB_BUFFER_SIZE];
volatile uint16_t tx_len_total; // 要发送的总长度
volatile uint16_t tx_len_sent; // 已发送的字节计数
volatile uint32_t last_rx_timestamp;
volatile uint32_t master_tx_timestamp;
uint32_t t35_cycles;
uint32_t master_timeout_cycles;
ModbusMasterCallback master_callback;
} ModbusChannel;
// 初始化所有Modbus通道
void Modbus_Init(void);
// Modbus轮询函数 在主循环(while(1))中持续调用
void Modbus_Poll(void);
// 获取指定通道的上下文结构体指针
ModbusChannel *Modbus_GetChannel(uint8_t ch_idx);
// 设置指定通道的工作模式 (主站或从站)
void Modbus_SetChannelMode(uint8_t ch_idx, eMBMode mode);
// 主站主动发起异步读寄存器
eMBStatus Modbus_Master_ReadRegisters(uint8_t ch_idx, uint8_t target_slave_addr, uint16_t reg_addr, uint16_t num_of_regs, ModbusMasterCallback callback);
// 主站主动发起异步写寄存器
eMBStatus Modbus_Master_WriteRegisters(uint8_t ch_idx, uint8_t target_slave_addr, uint16_t reg_addr, uint16_t num_of_regs, uint16_t *data, ModbusMasterCallback callback);
// 供移植层ISR调用的函数
void Modbus_OnSerialReceive(uint8_t ch_idx, uint8_t data);
void Modbus_OnSerialTransmit(uint8_t ch_idx);
void Modbus_OnIdleDetected(uint8_t ch_idx);
#endif
#include "modbus_core.h"
#include "modbus_port.h"
#include "modbus_config.h"
static ModbusChannel channels[MB_CHANNEL_COUNT];
static uint32_t timer_freq;
static void Modbus_Slave_Parse(ModbusChannel* ch);
static void Modbus_Master_Parse(ModbusChannel* ch);
static uint16_t CRC16(const volatile uint8_t *pucFrame, uint16_t usLen);
ModbusChannel *Modbus_GetChannel(uint8_t ch_idx)
{
if (ch_idx < MB_CHANNEL_COUNT)
{
return &channels[ch_idx];
}
return NULL;
}
void Modbus_Init(void)
{
prvMBPortTimersInit(&timer_freq);
for (uint8_t i = 0; i < MB_CHANNEL_COUNT; i++)
{
ModbusChannel* ch = &channels[i];
const MBPortChConfig* port_cfg = prvMBPortGetChConfig(i);
if (!port_cfg)
{
while (1); // 获取配置失败
}
ch->usart_periph = port_cfg->usart_periph;
ch->baud_rate = port_cfg->baud_rate;
ch->slave_address = port_cfg->slave_addr;
ch->mode = MODE_SLAVE;
ch->slave_state = STATE_RX_INIT;
ch->master_state = STATE_M_IDLE;
ch->rx_counter = 0;
ch->master_callback = NULL;
// 标准modbus-rtu协议规定帧间隔为至少3.5个完整字符帧的时间,当波特率大于19200时,至少为1.75ms
if (ch->baud_rate > 19200)
{
ch->t35_cycles = (uint32_t)(((uint64_t)timer_freq * 1750) / 1000000UL);
}
else
{
ch->t35_cycles = (uint32_t)(((uint64_t)timer_freq * 11 * 7) / (ch->baud_rate * 2));
}
ch->master_timeout_cycles = (uint64_t)timer_freq * MB_MASTER_TIMEOUT_MS / 1000;
prvMBPortSerialInit(i);
prvMBPortSerialEnable(i, TRUE, TRUE);
}
}
void Modbus_SetChannelMode(uint8_t ch_idx, eMBMode mode)
{
if (ch_idx < MB_CHANNEL_COUNT)
{
prvMBPortEnterCritical();
channels[ch_idx].mode = mode;
channels[ch_idx].slave_state = STATE_RX_INIT;
channels[ch_idx].master_state = STATE_M_IDLE;
prvMBPortExitCritical();
}
}
void Modbus_Poll(void)
{
for (uint8_t i = 0; i < MB_CHANNEL_COUNT; i++)
{
ModbusChannel* ch = &channels[i];
#if (MB_TIMER_SOURCE == MB_TIMER_IDLE)
prvMBPortEnterCritical();
uint8_t is_done = (ch->slave_state == STATE_RX_DONE);
prvMBPortExitCritical();
if (is_done)
{
if (ch->mode == MODE_SLAVE)
{
Modbus_Slave_Parse(ch);
}
else // MODE_MASTER
{
ch->master_state = STATE_M_DONE;
Modbus_Master_Parse(ch);
}
// 复位状态机,并重新使能接收
ch->rx_counter = 0;
ch->slave_state = STATE_RX_INIT;
ch->master_state = STATE_M_IDLE;
prvMBPortSerialEnable(i, TRUE, TRUE);
}
else if (ch->mode == MODE_MASTER && ch->master_state == STATE_M_WAIT_RX)
{
// IDLE模式下仍然需要一个软件定时器来处理主站的响应超时 这里复用之前的DWT/SysTick逻辑
if ((prvMBPortTimersGetTick() - ch->master_tx_timestamp) > ch->master_timeout_cycles)
{
if (ch->master_callback)
{
ch->master_callback(MB_ERR_TIMEOUT, NULL, 0);
}
ch->master_state = STATE_M_IDLE;
prvMBPortSerialEnable(i, TRUE, TRUE);
}
}
#else
if (ch->mode == MODE_SLAVE)
{
if (ch->slave_state == STATE_RX_ING)
{
prvMBPortEnterCritical();
if ((prvMBPortTimersGetTick() - ch->last_rx_timestamp) > ch->t35_cycles) // 帧间隔超时,即一帧数据接收完毕
{
ch->slave_state = STATE_RX_DONE;
prvMBPortSerialEnable(i, TRUE, FALSE);
}
prvMBPortExitCritical();
}
if (ch->slave_state == STATE_RX_DONE) // 一帧数据接收完毕后解析并复位状态机,重新使能接收
{
Modbus_Slave_Parse(ch);
ch->rx_counter = 0;
ch->slave_state = STATE_RX_INIT;
prvMBPortSerialEnable(i, TRUE, TRUE);
}
}
else // MODE_MASTER (主站模式未测试)
{
if (ch->master_state == STATE_M_WAIT_RX)
{
if (ch->slave_state == STATE_RX_ING)
{
prvMBPortEnterCritical();
if ((prvMBPortTimersGetTick() - ch->last_rx_timestamp) > ch->t35_cycles)
{
ch->slave_state = STATE_RX_DONE;
prvMBPortSerialEnable(i, TRUE, FALSE);
}
prvMBPortExitCritical();
}
if (ch->slave_state == STATE_RX_DONE)
{
ch->master_state = STATE_M_DONE;
}
else if ((prvMBPortTimersGetTick() - ch->master_tx_timestamp) > ch->master_timeout_cycles)
{
if (ch->master_callback)
{
ch->master_callback(MB_ERR_TIMEOUT, NULL, 0);
}
ch->master_state = STATE_M_IDLE;
prvMBPortSerialEnable(i, TRUE, TRUE);
}
}
if (ch->master_state == STATE_M_DONE)
{
Modbus_Master_Parse(ch);
ch->rx_counter = 0;
ch->slave_state = STATE_RX_INIT;
ch->master_state = STATE_M_IDLE;
prvMBPortSerialEnable(i, TRUE, TRUE);
}
}
#endif
}
}
void Modbus_OnSerialReceive(uint8_t ch_idx, uint8_t data)
{
if (ch_idx >= MB_CHANNEL_COUNT)
{
return;
}
ModbusChannel* ch = &channels[ch_idx];
ch->last_rx_timestamp = prvMBPortTimersGetTick();
if (ch->slave_state == STATE_RX_INIT)
{
ch->rx_counter = 0;
ch->slave_state = STATE_RX_ING;
}
if (ch->rx_counter < MB_BUFFER_SIZE)
{
ch->rx_buffer[ch->rx_counter++] = data;
}
}
void Modbus_OnSerialTransmit(uint8_t ch_idx)
{
if (ch_idx >= MB_CHANNEL_COUNT)
{
return;
}
ModbusChannel* ch = &channels[ch_idx];
if (ch->tx_len_sent >= ch->tx_len_total)
{
// 发送完毕,关闭发送中断
prvMBPortSerialEnable(ch_idx, FALSE, FALSE);
}
}
void Modbus_OnIdleDetected(uint8_t ch_idx)
{
if (ch_idx >= MB_CHANNEL_COUNT)
{
return;
}
ModbusChannel* ch = &channels[ch_idx];
// 只有在正在接收数据时,IDLE事件才有意义
if (ch->slave_state == STATE_RX_ING)
{
ch->slave_state = STATE_RX_DONE;
// 立即禁止接收中断,防止在处理期间新数据进来
prvMBPortSerialEnable(ch_idx, TRUE, FALSE);
}
}
// 主站主动发起读保持寄存器请求
eMBStatus Modbus_Master_ReadRegisters(
uint8_t ch_idx, uint8_t target_slave_addr,
uint16_t reg_addr, uint16_t num_of_regs,
ModbusMasterCallback callback)
{
if (ch_idx >= MB_CHANNEL_COUNT)
{
return MB_ERR_BUSY;
}
ModbusChannel* ch = &channels[ch_idx];
prvMBPortEnterCritical();
if (ch->mode != MODE_MASTER || ch->master_state != STATE_M_IDLE)
{
prvMBPortExitCritical();
return MB_ERR_BUSY;
}
// 构建请求帧
ch->tx_buffer[0] = target_slave_addr;
ch->tx_buffer[1] = 0x03; // 功能码
ch->tx_buffer[2] = (uint8_t)(reg_addr >> 8);
ch->tx_buffer[3] = (uint8_t)(reg_addr & 0xFF);
ch->tx_buffer[4] = (uint8_t)(num_of_regs >> 8);
ch->tx_buffer[5] = (uint8_t)(num_of_regs & 0xFF);
ch->tx_len_total = 6;
// 计算并附加CRC
uint16_t crc = CRC16(ch->tx_buffer, ch->tx_len_total);
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(crc & 0xFF);
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(crc >> 8);
// 准备接收和状态转换
ch->master_callback = callback;
ch->slave_state = STATE_RX_INIT;
ch->rx_counter = 0;
ch->tx_len_sent = 0;
ch->master_state = STATE_M_WAIT_RX;
ch->master_tx_timestamp = prvMBPortTimersGetTick();
prvMBPortExitCritical();
// 启动非阻塞发送
prvMBPortSerialTransmit(ch_idx);
return MB_OK;
}
// 主站主动发起写保持寄存器请求
eMBStatus Modbus_Master_WriteRegisters(
uint8_t ch_idx, uint8_t target_slave_addr,
uint16_t reg_addr, uint16_t num_of_regs, uint16_t *data,
ModbusMasterCallback callback)
{
if (ch_idx >= MB_CHANNEL_COUNT)
{
return MB_ERR_BUSY;
}
ModbusChannel* ch = &channels[ch_idx];
prvMBPortEnterCritical();
if (ch->mode != MODE_MASTER || ch->master_state != STATE_M_IDLE)
{
prvMBPortExitCritical();
return MB_ERR_BUSY;
}
// 构建请求帧
ch->tx_buffer[0] = target_slave_addr;
if (num_of_regs == 1)
{
ch->tx_buffer[1] = 0x06; // 功能码
}
else
{
ch->tx_buffer[1] = 0x10; // 功能码
}
ch->tx_buffer[2] = (uint8_t)(reg_addr >> 8);
ch->tx_buffer[3] = (uint8_t)(reg_addr & 0xFF);
ch->tx_buffer[4] = (uint8_t)(num_of_regs >> 8);
ch->tx_buffer[5] = (uint8_t)(num_of_regs & 0xFF);
ch->tx_len_total = 6;
if (num_of_regs == 1)
{
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(data[0] & 0xFF);
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(data[0] >> 8);
}
else
{
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)num_of_regs;
for (uint8_t i = 0; i < num_of_regs; ++i)
{
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(data[i] & 0xFF);
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(data[i] >> 8);
}
}
// 计算并附加CRC
uint16_t crc = CRC16(ch->tx_buffer, ch->tx_len_total);
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(crc & 0xFF);
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(crc >> 8);
// 准备接收和状态转换
ch->master_callback = callback;
ch->slave_state = STATE_RX_INIT;
ch->rx_counter = 0;
ch->tx_len_sent = 0;
ch->master_state = STATE_M_WAIT_RX;
ch->master_tx_timestamp = prvMBPortTimersGetTick();
prvMBPortExitCritical();
// 启动非阻塞发送
prvMBPortSerialTransmit(ch_idx);
return MB_OK;
}
// 解析从站接收到的请求帧
static void Modbus_Slave_Parse(ModbusChannel* ch)
{
// 基本校验: 长度、地址
if (ch->rx_counter < 4 || ch->rx_buffer[0] != ch->slave_address)
{
return;
}
// CRC校验
uint16_t crc_calc = CRC16(ch->rx_buffer, ch->rx_counter - 2);
uint16_t crc_recv = (ch->rx_buffer[ch->rx_counter - 1] << 8) | ch->rx_buffer[ch->rx_counter - 2];
if (crc_calc != crc_recv)
{
return;
}
ch->tx_len_total = 0;
switch (ch->rx_buffer[1])
{
case 0x03: // 读寄存器
{
// 检查请求帧长度是否正确
if (ch->rx_counter != 8)
{
break;
}
uint16_t start_addr = (ch->rx_buffer[2] << 8) | ch->rx_buffer[3];
uint16_t num_of_regs = (ch->rx_buffer[4] << 8) | ch->rx_buffer[5];
// 数据校验: 数量在1-125之间
if (num_of_regs >= 1 && num_of_regs <= 125)
{
// 构建正常响应
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = 0x03;
ch->tx_buffer[2] = num_of_regs * 2; // 字节数
uint8_t flag_break = 0;
for (uint16_t i = 0; i < num_of_regs; i++)
{
const uint16_t *p_value = prvMBPortGetReg(start_addr + i, MB_RO);
if (!p_value) // 寄存器地址非法
{
// 构建异常响应 (0x02: Illegal Data Address)
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = ch->rx_buffer[1] | 0x80;
ch->tx_buffer[2] = 0x02;
ch->tx_len_total = 3;
flag_break = 1;
break;
}
ch->tx_buffer[3 + i * 2] = (uint8_t)(*p_value >> 8);
ch->tx_buffer[4 + i * 2] = (uint8_t)(*p_value & 0xFF);
}
if (flag_break)
{
break;
}
ch->tx_len_total = 3 + num_of_regs * 2;
}
else
{
// 构建异常响应 (0x02: Illegal Data Address)
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = ch->rx_buffer[1] | 0x80;
ch->tx_buffer[2] = 0x02;
ch->tx_len_total = 3;
}
break;
}
case 0x06: // 写单个寄存器
{
// 检查请求帧长度是否正确
if (ch->rx_counter != 8)
{
break;
}
uint16_t start_addr = (ch->rx_buffer[2] << 8) | ch->rx_buffer[3];
uint16_t write_value = (ch->rx_buffer[4] << 8) | ch->rx_buffer[5];
// 构建正常响应
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = 0x06;
ch->tx_buffer[2] = ch->rx_buffer[2];
ch->tx_buffer[3] = ch->rx_buffer[3];
ch->tx_buffer[4] = ch->rx_buffer[4];
ch->tx_buffer[5] = ch->rx_buffer[5];
uint16_t *p_value = prvMBPortGetReg(start_addr, MB_RW);
if (!p_value) // 寄存器地址非法
{
// 构建异常响应 (0x02: Illegal Data Address)
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = ch->rx_buffer[1] | 0x80;
ch->tx_buffer[2] = 0x02;
ch->tx_len_total = 3;
break;
}
*p_value = write_value;
ch->tx_len_total = 6;
break;
}
case 0x10: // 写多个寄存器
{
// 检查请求帧长度是否正确
if (ch->rx_counter < 11)
{
break;
}
uint16_t start_addr = (ch->rx_buffer[2] << 8) | ch->rx_buffer[3];
uint16_t num_of_regs = (ch->rx_buffer[4] << 8) | ch->rx_buffer[5];
uint8_t num_of_bytes = ch->rx_buffer[6];
if (num_of_bytes != 2 * num_of_regs)
{
// 构建异常响应 (0x02: Illegal Data Address)
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = ch->rx_buffer[1] | 0x80;
ch->tx_buffer[2] = 0x02;
ch->tx_len_total = 3;
break;
}
// 数据校验: 数量在1-125之间
if (num_of_regs >= 1 && num_of_regs <= 125)
{
// 构建正常响应
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = 0x06;
ch->tx_buffer[2] = ch->rx_buffer[2];
ch->tx_buffer[3] = ch->rx_buffer[3];
ch->tx_buffer[4] = ch->rx_buffer[4];
ch->tx_buffer[5] = ch->rx_buffer[5];
uint8_t flag_break = 0;
for (uint16_t i = 0; i < num_of_regs; i++)
{
uint16_t *p_value = prvMBPortGetReg(start_addr + i, MB_RW);
if (!p_value) // 寄存器地址非法
{
// 构建异常响应 (0x02: Illegal Data Address)
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = ch->rx_buffer[1] | 0x80;
ch->tx_buffer[2] = 0x02;
ch->tx_len_total = 3;
flag_break = 1;
break;
}
*p_value = (ch->rx_buffer[7 + i * 2] << 8) | ch->rx_buffer[8 + i * 2];
}
if (flag_break)
{
break;
}
ch->tx_len_total = 6;
}
else
{
// 构建异常响应 (0x02: Illegal Data Address)
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = ch->rx_buffer[1] | 0x80;
ch->tx_buffer[2] = 0x02;
ch->tx_len_total = 3;
}
break;
}
default:
{
// 非法功能码或操作,构建异常响应 (0x01: Illegal Function)
ch->tx_buffer[0] = ch->slave_address;
ch->tx_buffer[1] = ch->rx_buffer[1] | 0x80;
ch->tx_buffer[2] = 0x01;
ch->tx_len_total = 3;
break;
}
}
// 如果有响应数据,则计算CRC并非阻塞发送
if (ch->tx_len_total > 0)
{
uint16_t crc_send = CRC16(ch->tx_buffer, ch->tx_len_total);
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(crc_send & 0xFF);
ch->tx_buffer[ch->tx_len_total++] = (uint8_t)(crc_send >> 8);
ch->tx_len_sent = 0;
uint8_t ch_idx = ch - &channels[0];
prvMBPortSerialTransmit(ch_idx);
}
}
static void Modbus_Master_Parse(ModbusChannel* ch)
{
if (ch->master_callback == NULL)
{
return;
}
uint16_t crc_calc = CRC16(ch->rx_buffer, ch->rx_counter - 2);
uint16_t crc_recv = (ch->rx_buffer[ch->rx_counter - 1] << 8) | ch->rx_buffer[ch->rx_counter - 2];
if (crc_calc != crc_recv)
{
if (ch->master_callback)
{
ch->master_callback(MB_ERR_INVALID_RESPONSE, (uint8_t*)ch->rx_buffer, ch->rx_counter);
}
}
else
{
if (ch->master_callback)
{
ch->master_callback(MB_SUCCESS, (uint8_t*)ch->rx_buffer, ch->rx_counter);
}
}
}
static uint16_t CRC16(const volatile uint8_t *pucFrame, uint16_t usLen)
{
uint16_t uiCRC16 = 0xFFFF;
while (usLen--)
{
uiCRC16 ^= *pucFrame++;
for (uint8_t j = 0; j < 8; ++j)
{
if (uiCRC16 & 0x01)
{
uiCRC16 = (uiCRC16 >> 1) ^ 0xA001;
}
else
{
uiCRC16 = uiCRC16 >> 1;
}
}
}
return uiCRC16;
}
移植层:
#ifndef __MODBUS_PORT_H__
#define __MODBUS_PORT_H__
#include "gd32f30x.h"
#include <stdlib.h>
// 通道硬件配置结构体
typedef struct
{
uint32_t usart_periph; // 特定于MCU的串口外设ID/地址
uint32_t baud_rate; // 波特率
uint8_t slave_addr; // 作为从站时的地址
} MBPortChConfig;
// 从机寄存器地址
typedef enum
{
// 写
P2000 = 0x2000,
P2001,
P2002,
// 写
P2100 = 0x2100,
P2101,
P2102,
P2103,
P2104,
// 读
P2200 = 0x2200,
P2201,
P2202,
P2203,
P2204,
P2205,
P2206,
P2207,
P2208,
P2209,
P220A,
P220B,
P220C,
P220D,
} MBSlaveRegAddr;
// 寄存器读写权限
typedef enum
{
MB_RO = 0,
MB_RW
} MBSlaveRegRW;
// 从机寄存器结构体
typedef struct
{
uint16_t address;
MBSlaveRegRW rw;
uint16_t *value;
} MBSlaveReg;
// 移植层需要实现的硬件接口
uint16_t *prvMBPortGetReg(uint16_t reg_addr, MBSlaveRegRW reg_rw);
// 获取指定通道的硬件配置
const MBPortChConfig *prvMBPortGetChConfig(uint8_t ch_idx);
// 定时器配置
bool prvMBPortTimersInit(uint32_t *p_timer_freq);
// 获取定时器计数值
uint32_t prvMBPortTimersGetTick(void);
// 串口初始化
bool prvMBPortSerialInit(uint8_t ch_idx);
// 串口使能
void prvMBPortSerialEnable(uint8_t ch_idx, bool is_rx, bool is_enable);
// 串口传输
void prvMBPortSerialTransmit(uint8_t ch_idx);
// 进入临界区
void prvMBPortEnterCritical(void);
// 退出临界区
void prvMBPortExitCritical(void);
#endif
#include "modbus_port.h"
#include "modbus_config.h"
#include "modbus_core.h"
uint16_t p2000 = P2000;
uint16_t p2001 = P2001;
uint16_t p2002 = P2002;
uint16_t p2100 = P2100;
uint16_t p2101 = P2101;
uint16_t p2102 = P2102;
uint16_t p2103 = P2103;
uint16_t p2104 = P2104;
uint16_t p2200 = P2200;
uint16_t p2201 = P2201;
uint16_t p2202 = P2202;
uint16_t p2203 = P2203;
uint16_t p2204 = P2204;
uint16_t p2205 = P2205;
uint16_t p2206 = P2206;
uint16_t p2207 = P2207;
uint16_t p2208 = P2208;
uint16_t p2209 = P2209;
uint16_t p220A = P220A;
uint16_t p220B = P220B;
uint16_t p220C = P220C;
uint16_t p220D = P220D;
// 寄存器数组
static const MBSlaveReg slave_registers[] =
{
// 写
{P2000, MB_RW, &p2000},
{P2001, MB_RW, &p2001},
{P2002, MB_RW, &p2002},
// 写
{P2100, MB_RW, &p2100},
{P2101, MB_RW, &p2101},
{P2102, MB_RW, &p2102},
{P2103, MB_RW, &p2103},
{P2104, MB_RW, &p2104},
// 读
{P2200, MB_RO, &p2200},
{P2201, MB_RO, &p2201},
{P2202, MB_RO, &p2202},
{P2203, MB_RO, &p2203},
{P2204, MB_RO, &p2204},
{P2205, MB_RO, &p2205},
{P2206, MB_RO, &p2206},
{P2207, MB_RO, &p2207},
{P2208, MB_RO, &p2208},
{P2209, MB_RO, &p2209},
{P220A, MB_RO, &p220A},
{P220B, MB_RO, &p220B},
{P220C, MB_RO, &p220C},
{P220D, MB_RO, &p220D},
{NULL, MB_RO, NULL},
};
// 硬件配置数组
static const MBPortChConfig port_ch_configs[MB_CHANNEL_COUNT] =
{
// 通道 0 的配置
{
.usart_periph = MB_CHANNEL_0_USART_PERIPH,
.baud_rate = MB_CHANNEL_0_BAUD,
.slave_addr = MB_CHANNEL_0_ADDR
}
};
uint16_t *prvMBPortGetReg(uint16_t reg_addr, MBSlaveRegRW reg_rw)
{
for (int i = 0;; ++i)
{
if (!slave_registers[i].address || (reg_rw == MB_RW && slave_registers[i].rw == MB_RO))
{
break;
}
if (slave_registers[i].address == reg_addr)
{
return slave_registers[i].value;
}
}
return NULL;
}
const MBPortChConfig *prvMBPortGetChConfig(uint8_t ch_idx)
{
if (ch_idx < MB_CHANNEL_COUNT)
{
return &port_ch_configs[ch_idx];
}
return NULL;
}
bool prvMBPortTimersInit(uint32_t *p_timer_freq)
{
#if (MB_TIMER_SOURCE == MB_TIMER_IDLE)
// 在IDLE模式下,仍然需要一个时间源来处理主站超时,这里使用DWT
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
*p_timer_freq = rcu_clock_freq_get(CK_SYS);
return TRUE;
#elif (MB_TIMER_SOURCE == MB_TIMER_DWT)
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
*p_timer_freq = rcu_clock_freq_get(CK_SYS);
return TRUE;
#else
// @TODO: 实现其他定时器源 (TIM, SysTick) 初始化
#error "Selected timer source is not implemented in this port."
return FALSE;
#endif
}
uint32_t prvMBPortTimersGetTick(void)
{
#if (MB_TIMER_SOURCE == MB_TIMER_IDLE)
// 返回备用时间源(DWT)的计数值
return DWT->CYCCNT;
#elif (MB_TIMER_SOURCE == MB_TIMER_DWT)
return DWT->CYCCNT;
#else
// @TODO: 实现其他定时器源返回计数值
return 0;
#endif
}
bool prvMBPortSerialInit(uint8_t ch_idx)
{
const MBPortChConfig* port_cfg = prvMBPortGetChConfig(ch_idx);
if (!port_cfg)
{
return FALSE;
}
rcu_periph_enum rcu_gpio, rcu_usart;
uint32_t tx_port, tx_pin, rx_port, rx_pin;
uint8_t nvic_irqn;
switch (port_cfg->usart_periph)
{
case UART3:
rcu_gpio = RCU_GPIOC;
rcu_usart = RCU_UART3;
tx_port = GPIOC;
tx_pin = GPIO_PIN_10;
rx_port = GPIOC;
rx_pin = GPIO_PIN_11;
nvic_irqn = UART3_IRQn;
break;
default:
return FALSE;
}
rcu_periph_clock_enable(rcu_gpio);
rcu_periph_clock_enable(rcu_usart);
gpio_init(tx_port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, tx_pin);
gpio_init(rx_port, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, rx_pin);
usart_deinit(port_cfg->usart_periph);
usart_baudrate_set(port_cfg->usart_periph, port_cfg->baud_rate);
usart_word_length_set(port_cfg->usart_periph, USART_WL_8BIT);
usart_stop_bit_set(port_cfg->usart_periph, USART_STB_1BIT);
usart_parity_config(port_cfg->usart_periph, USART_PM_NONE);
usart_hardware_flow_rts_config(port_cfg->usart_periph, USART_RTS_DISABLE);
usart_hardware_flow_cts_config(port_cfg->usart_periph, USART_CTS_DISABLE);
usart_receive_config(port_cfg->usart_periph, USART_RECEIVE_ENABLE);
usart_transmit_config(port_cfg->usart_periph, USART_TRANSMIT_ENABLE);
usart_enable(port_cfg->usart_periph);
nvic_irq_enable(nvic_irqn, 0, 0);
#if (MB_TIMER_SOURCE == MB_TIMER_IDLE)
usart_data_receive(port_cfg->usart_periph);
usart_interrupt_flag_clear(port_cfg->usart_periph, USART_INT_FLAG_IDLE);
usart_interrupt_enable(port_cfg->usart_periph, USART_INT_IDLE);
#endif
return TRUE;
}
void prvMBPortSerialEnable(uint8_t ch_idx, bool is_rx, bool is_enable)
{
const MBPortChConfig* port_cfg = prvMBPortGetChConfig(ch_idx);
if (!port_cfg)
{
return;
}
if (is_rx)
{
if (is_enable)
{
usart_interrupt_enable(port_cfg->usart_periph, USART_INT_RBNE);
}
else
{
usart_interrupt_disable(port_cfg->usart_periph, USART_INT_RBNE);
}
}
else
{
if (is_enable)
{
usart_interrupt_enable(port_cfg->usart_periph, USART_INT_TBE);
}
else
{
usart_interrupt_disable(port_cfg->usart_periph, USART_INT_TBE);
}
}
}
void prvMBPortSerialTransmit(uint8_t ch_idx)
{
ModbusChannel* ch = Modbus_GetChannel(ch_idx);
if (!ch || ch->tx_len_total == 0)
{
return;
}
// 发送第一个字节,并使能发送中断让ISR接管
usart_data_transmit(ch->usart_periph, ch->tx_buffer[0]);
ch->tx_len_sent = 1;
prvMBPortSerialEnable(ch_idx, FALSE, TRUE); // 使能TBE中断
}
void prvMBPortEnterCritical(void)
{
__disable_irq();
}
void prvMBPortExitCritical(void)
{
__enable_irq();
}
static void prvMBUsartIrqHandler(uint8_t ch_idx)
{
ModbusChannel* ch = Modbus_GetChannel(ch_idx);
if (!ch)
{
return;
}
uint32_t usart_periph = ch->usart_periph;
if (RESET != usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_RBNE))
{
Modbus_OnSerialReceive(ch_idx, (uint8_t)usart_data_receive(usart_periph));
}
#if (MB_TIMER_SOURCE == MB_TIMER_IDLE)
if (RESET != usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_IDLE))
{
usart_data_receive(usart_periph);
Modbus_OnIdleDetected(ch_idx);
}
#endif
if (RESET != usart_interrupt_flag_get(usart_periph, USART_INT_FLAG_TBE))
{
if (ch->tx_len_sent < ch->tx_len_total)
{
usart_data_transmit(usart_periph, ch->tx_buffer[ch->tx_len_sent++]);
}
else
{
Modbus_OnSerialTransmit(ch_idx); // 通知核心层发送完毕
}
}
}
void UART3_IRQHandler(void)
{
prvMBUsartIrqHandler(0);
}
本文链接:
/archives/9r4f5ZX0
版权声明:
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
Liccsu's blog!
喜欢就支持一下吧
打赏
微信