STM32-Modbus主机实现-正点原子精英板
- 实现方案
- 最终实现效果
- 完整工程下载
- 移植过程
实现方案
可实现Modbus主从机,当然,得使用两个485串口。
最终实现效果
正常通讯:
通讯出错重复发送命令:
完整工程下载
移植过程
- 首先下载解压Modbus主机框架
解压后得到以上文件 - 在mb_port.c 内添加各个函数的具体实现
/**
* @brief MODBUS串口初始化接口
* @param baud:串口波特率
* @param parity:奇偶校验位设置
* @return NONE
* @note 需要根据使用MCU进行移植
*/
void mb_port_uartInit(uint32_t baud,uint8_t parity);
/**
* @brief 串口TX\RX使能接口
* @param txen:0-关闭tx中断 1-打开tx中断
* @param rxen:0-关闭rx中断 1-打开rx中断
* @return NONE
* @note 需要根据使用MCU进行移植
*/
void mb_port_uartEnable(uint8_t txen,uint8_t rxen);
/**
* @brief 串口发送一个byte
* @param ch:要发送的byte
* @return NONE
* @note 需要根据使用MCU进行移植
*/
void mb_port_putchar(uint8_t ch);
/**
* @brief 串口读取一个byte
* @param ch:存放读取一个byte的指针
* @return NONE
* @note 需要根据使用MCU进行移植
*/
void mb_port_getchar(uint8_t *ch);
/**
* @brief 定时器初始化接口
* @param baud:串口波特率,根据波特率生成3.5T的定时
* @return NONE
* @note 需要根据使用MCU进行移植
*/
void mb_port_timerInit(uint32_t baud);
/**
* @brief 定时器使能
* @return NONE
* @note 定时器要清0重新计数
*/
void mb_port_timerEnable(void);
/**
* @brief 定时器关闭
* @return NONE
* @note 定时器要清0重新计数
*/
void mb_port_timerDisable(void);
/**
* @brief 定时器计数清0
* @return NONE
* @note 定时器计数清0重新计数
*/
void mb_port_timerReset(void);
代码较为简单,不做过多介绍,这里我添加了一个mb_port_timerReset 计数清零函数,在mb_host.c 内用到,贴一下mb_port.c总的代码:
为了方便移植修改,使用了一些宏定义。
#include "mb_include.h"
//主机485发送/接收控制端定义
#define MD_MASTER_TX_EN_CLK_FUN RCC_APB2PeriphClockCmd
#define MD_MASTER_TX_EN_CLK RCC_APB2Periph_GPIOD
#define MD_MASTER_TX_EN_PORT GPIOD
#define MD_MASTER_TX_EN_PIN GPIO_Pin_7
//主机485串口定义
#define MD_MASTER_USART USART2
#define MD_MASTER_USART_CLK_FUN RCC_APB1PeriphClockCmd
#define MD_MASTER_USART_CLK RCC_APB1Periph_USART2
#define MD_MASTER_USART_IRQn USART2_IRQn
#define MD_MASTER_USART_IRQHandler USART2_IRQHandler
//主机485串口TX RX引脚定义
#define MD_MASTER_TRX_GPIO_CLK RCC_APB2Periph_GPIOA
#define MD_MASTER_TRX_GPIO_CLK_FUN RCC_APB2PeriphClockCmd
#define MD_MASTER_TRX_GPIO_PORT GPIOA
#define MD_MASTER_RX_PIN GPIO_Pin_3
//#define MD_MASTER_RX_SOURCE GPIO_PinSource7
#define MD_MASTER_TX_PIN GPIO_Pin_2
//#define MD_MASTER_TX_SOURCE GPIO_PinSource6
//主机使用的定时器定义
#define MD_MASTER_TIM TIM4
#define MD_MASTER_TIM_CLK RCC_APB1Periph_TIM4
#define MD_MASTER_TIM_CLK_FUN RCC_APB1PeriphClockCmd
#define MD_MASTER_TIM_IRQn TIM4_IRQn
#define MD_MASTER_TIM_IRQHandler TIM4_IRQHandler
#define RS485_Frame_Distance 10 //数据帧最小间隔(ms),超过此时间则认为是下一帧
void mb_port_uartInit(uint32_t baud,uint8_t parity)
{
/*串口部分初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//使能USART,GPIOA
MD_MASTER_TRX_GPIO_CLK_FUN(MD_MASTER_TRX_GPIO_CLK , ENABLE);
MD_MASTER_USART_CLK_FUN(MD_MASTER_USART_CLK , ENABLE);
//GPIOA9 USART1_Tx
GPIO_InitStructure.GPIO_Pin = MD_MASTER_TX_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽输出
GPIO_Init(MD_MASTER_TRX_GPIO_PORT, &GPIO_InitStructure);
//GPIOA.10 USART1_Rx
GPIO_InitStructure.GPIO_Pin = MD_MASTER_RX_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮动输入
GPIO_Init(MD_MASTER_TRX_GPIO_PORT, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = baud; //只修改波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
//串口初始化
USART_Init(MD_MASTER_USART, &USART_InitStructure);
//使能USART
USART_Cmd(MD_MASTER_USART, ENABLE);
//设定USART1 中断优先级
NVIC_InitStructure.NVIC_IRQChannel = MD_MASTER_USART_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//最后配置485发送和接收模式
MD_MASTER_TX_EN_CLK_FUN(MD_MASTER_TX_EN_CLK, ENABLE);
//GPIOG.9
GPIO_InitStructure.GPIO_Pin = MD_MASTER_TX_EN_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(MD_MASTER_TX_EN_PORT, &GPIO_InitStructure);
}
void mb_port_uartEnable(uint8_t txen,uint8_t rxen)
{
if(txen)
{
//使能发送完成中断
USART_ITConfig(MD_MASTER_USART, USART_IT_TC, ENABLE);
//MAX485操作 高电平为发送模式
GPIO_SetBits(MD_MASTER_TX_EN_PORT,MD_MASTER_TX_EN_PIN);
}
else
{
//禁止发送完成中断
USART_ITConfig(MD_MASTER_USART, USART_IT_TC, DISABLE);
//MAX485操作 低电平为接收模式
GPIO_ResetBits(MD_MASTER_TX_EN_PORT,MD_MASTER_TX_EN_PIN);
}
if(rxen)
{
//使能接收和接收中断
USART_ITConfig(MD_MASTER_USART, USART_IT_RXNE, ENABLE);
//MAX485操作 低电平为接收模式
GPIO_ResetBits(MD_MASTER_TX_EN_PORT,MD_MASTER_TX_EN_PIN);
}
else
{
USART_ITConfig(MD_MASTER_USART, USART_IT_RXNE, DISABLE);
//MAX485操作 高电平为发送模式
GPIO_SetBits(MD_MASTER_TX_EN_PORT,MD_MASTER_TX_EN_PIN);
}
}
void mb_port_putchar(uint8_t ch)
{
// huart1.Instance->DR = ch; //直接操作寄存器比HAL封装的更高效
//发送数据
USART_SendData(MD_MASTER_USART, ch);
while(USART_GetFlagStatus(MD_MASTER_USART,USART_FLAG_TXE) == RESET){};//等待发送完成
}
void mb_port_getchar(uint8_t *ch)
{
// *ch= (uint8_t)(huart1.Instance->DR & (uint8_t)0x00FF);
*ch = (uint8_t)(USART_ReceiveData(MD_MASTER_USART));
}
void mb_port_timerInit(uint32_t baud)
{
/*定时器部分初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// uint16_t PrescalerValue = 0;
//使能定时器4时钟
MD_MASTER_TIM_CLK_FUN(MD_MASTER_TIM_CLK, ENABLE);
//定时器时间基配置说明
//HCLK为72MHz,APB1经过2分频为36MHz
//MD_MASTER_TIM的时钟倍频后为72MHz(硬件自动倍频,达到最大)
//MD_MASTER_TIM的分频系数为3599,时间基频率为72 / (1 + Prescaler) = 20KHz,基准为50us
//TIM最大计数值为usTim1Timerout50u
// PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1;
//定时器1初始化
/* If baudrate > 19200 then we should use the fixed timer values
* t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
*/
if(baud>19200) //波特率大于19200固定使用1800作为3.5T
{
TIM_TimeBaseStructure.TIM_Period = 35;
}
else //其他波特率的需要根据计算
{
/* The timer reload value for a character is given by:
*
* ChTimeValue = Ticks_per_1s / ( baud / 11 )
* = 11 * Ticks_per_1s / baud
* = 220000 / baud
* The reload for t3.5 is 1.5 times this value and similary
* for t3.5.
*/
TIM_TimeBaseStructure.TIM_Period = (uint32_t)(( 7UL * 220000UL ) / ( 2UL * baud ));
}
// TIM_TimeBaseStructure.TIM_Period = RS485_Frame_Distance*10;
TIM_TimeBaseStructure.TIM_Prescaler =(uint16_t) (SystemCoreClock / 20000) - 1;//20KHZ
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(MD_MASTER_TIM, &TIM_TimeBaseStructure);
//预装载使能
TIM_ARRPreloadConfig(MD_MASTER_TIM, ENABLE);
//定时器4中断优先级
NVIC_InitStructure.NVIC_IRQChannel = MD_MASTER_TIM_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//清除溢出中断标志位
TIM_ClearITPendingBit(MD_MASTER_TIM,TIM_IT_Update);
//定时器4溢出中断关闭
TIM_ITConfig(MD_MASTER_TIM, TIM_IT_Update, DISABLE);
//定时器4禁能
TIM_Cmd(MD_MASTER_TIM, DISABLE);
}
void mb_port_timerEnable()
{
TIM_ClearITPendingBit(MD_MASTER_TIM, TIM_IT_Update);
TIM_ITConfig(MD_MASTER_TIM, TIM_IT_Update, ENABLE);
//设定定时器4的初始值
TIM_SetCounter(MD_MASTER_TIM,0x0000);
//定时器4启动
TIM_Cmd(MD_MASTER_TIM, ENABLE);
}
void mb_port_timerDisable()
{
TIM_ClearITPendingBit(MD_MASTER_TIM, TIM_IT_Update);
TIM_ITConfig(MD_MASTER_TIM, TIM_IT_Update, DISABLE);
TIM_SetCounter(MD_MASTER_TIM,0x0000);
//关闭定时器4
TIM_Cmd(MD_MASTER_TIM, DISABLE);
}
void mb_port_timerReset(void)
{
TIM_SetCounter(MD_MASTER_TIM,0x0000);
}
//串口中断服务函数
void MD_MASTER_USART_IRQHandler()
{
//发生接收中断
if(USART_GetITStatus(MD_MASTER_USART, USART_IT_RXNE) == SET)
{
//清除中断标志位
USART_ClearITPendingBit(MD_MASTER_USART, USART_IT_RXNE);
mbh_uartRxIsr();
}
//发生完成中断
if(USART_GetITStatus(MD_MASTER_USART, USART_IT_TC) == SET)
{
//清除中断标志
USART_ClearITPendingBit(MD_MASTER_USART, USART_IT_TC);
mbh_uartTxIsr();
}
}
//定时器中断服务函数
void MD_MASTER_TIM_IRQHandler()
{
if (TIM_GetITStatus(MD_MASTER_TIM, TIM_IT_Update) != RESET)
{
//清除定时器T4溢出中断标志位
TIM_ClearITPendingBit(MD_MASTER_TIM, TIM_IT_Update);
mbh_timer3T5Isr();
}
}
- mb_hook.c内需要添加各个功能码回调处理函数,以及错误处理
/**
* @brief MODBUS主机模式下接收到从机回复不同功能码的回调处理
* @param add:从机的地址
* @param data:接收到的从机发来的数据指针
* @param datalen:接收到的从机发来的数据长度
* @return NONE
* @note rec01\02\03……等数字代表功能码
*/
void mbh_hook_rec01(uint8_t add,uint8_t *data,uint8_t datalen);
void mbh_hook_rec02(uint8_t add,uint8_t *data,uint8_t datalen);
void mbh_hook_rec03(uint8_t add,uint8_t *data,uint8_t datalen);
void mbh_hook_rec04(uint8_t add,uint8_t *data,uint8_t datalen);
void mbh_hook_rec05(uint8_t add,uint8_t *data,uint8_t datalen);
void mbh_hook_rec06(uint8_t add,uint8_t *data,uint8_t datalen);
void mbh_hook_rec15(uint8_t add,uint8_t *data,uint8_t datalen);
void mbh_hook_rec16(uint8_t add,uint8_t *data,uint8_t datalen);
/**
* @brief MODBUS主机读写从机超过最大错误次数回调
* @param add:从机的地址
* @param cmd:功能码
* @return NONE
* @note
*/
void mbh_hook_timesErr(uint8_t add,uint8_t cmd);
这里参考openedv论坛那位大哥的代码,编写各个函数的实现逻辑,需要注意的是freemodbus的开关与线圈是用位来表示的,这位老哥的是单个数组元素表示一个位,为了与freemodbus兼容,这里也改成单个位表示,一个字节表示8位。
//线圈状态
uint8_t ucRegCoilsBuf[REG_COILS_SIZE/8 ] = {0x11,0xEF};
//开关输入状态
uint8_t ucRegDiscreteBuf[REG_DISCRETE_SIZE/8 ] = {0x00,0x00};
为了与freemodbus兼容参考freemodbus源码内对数组单个位操作的函数,并移植到这里,mb_hook.c全部代码:
#include "mb_include.h"
这里使用了宏定义来方便修改主机使用的数据,方便主从机移植。
/***************主机寄存器宏定义***************/
#define M_CoilsRegBuf ucRegCoilsBuf
#define M_DiscreteRegBuf ucRegDiscreteBuf
#define M_HoldingRegBuf usRegHoldingBuf
#define M_InputRegBuf usRegInputBuf
/*********************************************/
uint16_t SaveStartAddr = 0; //数据保存起始地址
uint16_t DataORLenth = 24; //数据长度 在写单个线圈寄存器时代表值
uint8_t MD_MASTER_ComErr = 8; //0代表通讯正常
#define BITS_UINT8_T 8U
/*
ucByteBuf:位存储的缓冲区。必须是2个字节。
usBitOffset: 位设置的起始地址,第一个位的偏移为0。
ucNBits: 需要修改的位的数量。该值必须小于8。
ucValue: 位的新值。在usBitOffset中的第一位的值是ucValues的最低有效位。
*/
/* ----------------------- Start implementation -----------------------------*/
void MBSetBits( uint8_t * ucByteBuf, uint16_t usBitOffset, uint8_t ucNBits,uint8_t ucValue )
{
uint16_t usWordBuf;
uint16_t usMask;
uint16_t usByteOffset;
uint16_t usNPreBits;
uint16_t usValue = ucValue;
// assert( ucNBits <= 8 );
// assert( ( size_t )BITS_UINT8_T == sizeof( uint8_t ) * 8 );
/* Calculate byte offset for first byte containing the bit values starting
* at usBitOffset. */
usByteOffset = ( uint16_t )( ( usBitOffset ) / BITS_UINT8_T );
/* How many bits precede our bits to set. */
usNPreBits = ( uint16_t )( usBitOffset - usByteOffset * BITS_UINT8_T );
/* Move bit field into position over bits to set */
usValue <<= usNPreBits;
/* Prepare a mask for setting the new bits. */
usMask = ( uint16_t )( ( 1 << ( uint16_t ) ucNBits ) - 1 );
usMask <<= usBitOffset - usByteOffset * BITS_UINT8_T;
/* copy bits into temporary storage. */
usWordBuf = ucByteBuf[usByteOffset];
usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UINT8_T;
/* Zero out bit field bits and then or value bits into them. */
usWordBuf = ( uint16_t )( ( usWordBuf & ( ~usMask ) ) | usValue );
/* move bits back into storage */
ucByteBuf[usByteOffset] = ( uint8_t )( usWordBuf & 0xFF );
ucByteBuf[usByteOffset + 1] = ( uint8_t )( usWordBuf >> BITS_UINT8_T );
}
/*
ucByteBuf:位存储的缓冲区。必须是2个字节。
usBitOffset: 位设置的起始地址,第一个位的偏移为0。
ucNBits: 需要修改的位的数量。该值必须小于8。
*/
uint8_t MBGetBits( uint8_t * ucByteBuf, uint16_t usBitOffset, uint8_t ucNBits )
{
uint16_t usWordBuf;
uint16_t usMask;
uint16_t usByteOffset;
uint16_t usNPreBits;
/* Calculate byte offset for first byte containing the bit values starting
* at usBitOffset. */
usByteOffset = ( uint16_t )( ( usBitOffset ) / BITS_UINT8_T );
/* How many bits precede our bits to set. */
usNPreBits = ( uint16_t )( usBitOffset - usByteOffset * BITS_UINT8_T );
/* Prepare a mask for setting the new bits. */
usMask = ( uint16_t )( ( 1 << ( uint16_t ) ucNBits ) - 1 );
/* copy bits into temporary storage. */
usWordBuf = ucByteBuf[usByteOffset];
usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UINT8_T;
/* throw away unneeded bits. */
usWordBuf >>= usNPreBits;
/* mask away bits above the requested bitfield. */
usWordBuf &= usMask;
return ( uint8_t ) usWordBuf;
}
//data[0] 返回的字节数 data[1]~[X]数据
void mbh_hook_rec01(uint8_t add, uint8_t *data, uint8_t datalen)
{
// uint16_t i;
//寄存器个数
int16_t Coils = DataORLenth;
//寄存器偏移量
uint16_t BitOffset=SaveStartAddr;
if ((SaveStartAddr + DataORLenth) <= REG_COILS_SIZE) //寄存器地址+数量在范围内
{
// for (i = 0; i < DataORLenth; i++)
// {
// M_CoilsRegBuf[SaveStartAddr + i] = data[1 + i / 8] & 0x01; //低位先发送
// data[1 + i / 8] >>= 1;
// }
data++;
while( Coils > 0 )
{
MBSetBits( M_CoilsRegBuf, BitOffset,
( uint8_t )( Coils > 8 ? 8 : Coils ),
*data++ );
Coils -= 8;
BitOffset +=8;
}
MD_MASTER_ComErr = 0;
}
else
{
MD_MASTER_ComErr = 255;
}
}
void mbh_hook_rec02(uint8_t add, uint8_t *data, uint8_t datalen)
{
// uint16_t i;
//寄存器个数
int16_t Coils = DataORLenth;
//寄存器偏移量
uint16_t BitOffset=SaveStartAddr;
if ((SaveStartAddr + DataORLenth) <= REG_DISCRETE_SIZE) //寄存器地址+数量在范围内
{
// for (i = 0; i < DataORLenth; i++)
// {
// M_DiscreteRegBuf[SaveStartAddr + i] = data[1 + i / 8] & 0x01; //低位先发送
// data[1 + i / 8] >>= 1;
// }
data++;
while( Coils > 0 )
{
MBSetBits( M_DiscreteRegBuf, BitOffset,
( uint8_t )( Coils > 8 ? 8 : Coils ),
*data++ );
Coils -= 8;
BitOffset +=8;
}
MD_MASTER_ComErr = 0;
}
else
{
MD_MASTER_ComErr = 255;
}
}
void mbh_hook_rec03(uint8_t add, uint8_t *data, uint8_t datalen)
{
uint8_t i;
uint8_t RegNum;
RegNum = data[0] / 2; //获取字节数
if ((SaveStartAddr + RegNum) < 1000) //寄存器地址+数量在范围内
{
for (i = 0; i < RegNum; i++)
{
M_HoldingRegBuf[SaveStartAddr + i] = data[1 + i * 2]; /高8位
M_HoldingRegBuf[SaveStartAddr + i] = data[2 + i * 2] + (M_HoldingRegBuf[SaveStartAddr + i] << 8); // 低8位+高8位
}
MD_MASTER_ComErr = 0;
}
else
{
MD_MASTER_ComErr = 255;
}
}
void mbh_hook_rec04(uint8_t add, uint8_t *data, uint8_t datalen)
{
uint8_t i;
uint8_t RegNum;
RegNum = data[0] / 2; //获取字节数
if ((SaveStartAddr + RegNum) < 1000) //寄存器地址+数量在范围内
{
for (i = 0; i < RegNum; i++)
{
M_InputRegBuf[SaveStartAddr + i] = data[1 + i * 2]; /高8位
M_InputRegBuf[SaveStartAddr + i] = data[2 + i * 2] + (M_InputRegBuf[SaveStartAddr + i] << 8); // 低8位+高8位
}
MD_MASTER_ComErr = 0;
}
else
{
MD_MASTER_ComErr = 255;
}
}
void mbh_hook_rec05(uint8_t add, uint8_t *data, uint8_t datalen)
{
uint16_t i;
i = DataORLenth;
if ((i > 0 && data[2] == 0XFF && data[3] == 0X00) || (i == 0 && data[2] == 0X00 && data[3] == 0X00))
{
MD_MASTER_ComErr = 0;
}
else
{
MD_MASTER_ComErr = 255;
}
}
void mbh_hook_rec06(uint8_t add, uint8_t *data, uint8_t datalen)
{
uint16_t i; //数据返回校验用
i = (((uint16_t)data[2]) << 8) | data[3]; //获取寄存器值
if (i == M_HoldingRegBuf[SaveStartAddr])
{
MD_MASTER_ComErr = 0;
}
else
{
MD_MASTER_ComErr = 255;
}
}
void mbh_hook_rec15(uint8_t add, uint8_t *data, uint8_t datalen)
{
uint16_t i;//数据返回校验用
i = (((uint16_t)data[2]) << 8) | data[3]; //获取寄存器数量
if (i == DataORLenth)
{
MD_MASTER_ComErr = 0;
}
else
{
MD_MASTER_ComErr = 255;
}
}
void mbh_hook_rec16(uint8_t add, uint8_t *data, uint8_t datalen)
{
uint16_t i;//数据返回校验用
i = (((uint16_t)data[2]) << 8) | ((data[3])); //获取寄存器数量
if (i == DataORLenth)
{
MD_MASTER_ComErr = 0;
}
else
{
MD_MASTER_ComErr = 255;
}
}
//连续错误处理
void mbh_hook_timesErr(uint8_t add, uint8_t cmd)
{
mbHost.state = MBH_STATE_IDLE;//状态切换为空闲
MD_MASTER_ComErr = 0;
}
我再连续出错里没有做什么处理,只是将状态消除好继续下一个发送命令,这里可以加入自己想要的处理。
- 在mb_host.c主要修改3个函数
/**
* @brief MODBUS主机给从机发送一条命令
* @param add:从机地址
* @param cmd:功能码
* @param start_address:数据起始地址
* @param data:要发送的数据
* @param len:发送的数据长度
* @return -1:发送失败 0:发送成功
* @note 该函数为非阻塞式,调用后立即返回
*/
int8_t mbh_send(uint8_t add,uint8_t cmd,uint16_t start_address,uint16_t *data,uint16_t data_len);
以及
void mbh_uartRxIsr(void);
/**
* @brief modbus主机串口接收中断处理
* @return none
* @note 放在mcu的tx中断中调用
*
*/
void mbh_uartTxIsr(void);
修改主机命令发送函数:
//发送一帧命令
int8_t mbh_send(uint8_t add, uint8_t cmd, uint16_t start_address, uint16_t *data, uint16_t data_len)
{
uint16_t crc;
uint16_t temp = 0;
uint16_t i;
int16_t Coils = data_len;
//偏移量
uint16_t BitOffset = start_address;
uint8_t *data8_P = (uint8_t *)data; //
if (mbHost.state != MBH_STATE_IDLE)return -1; //busy state
mbHost.txCounter = 0;
mbHost.rxCounter = 0;
mbHost.txBuf[0] = add;
mbHost.txBuf[1] = cmd;
mbHost.txBuf[2] = HI(start_address);
mbHost.txBuf[3] = LOW(start_address);
switch (cmd)
{
case READ_COIL:
case READ_DI:
case READ_HLD_REG:
case READ_AI:
mbHost.txBuf[4] = HI(data_len);
mbHost.txBuf[5] = LOW(data_len);
break;
case SET_COIL:
if (DataORLenth)temp = 0xFF00;
else temp = 0x0000;
mbHost.txBuf[4] = HI(temp);
mbHost.txBuf[5] = LOW(temp);
break;
case SET_HLD_REG:
mbHost.txBuf[4] = HI(data[start_address]);
mbHost.txBuf[5] = LOW(data[start_address]);
break;
case WRITE_COIL:
temp = 0;
while (Coils > 0)
{
mbHost.txBuf[7 + temp] = MBGetBits(data8_P, BitOffset,
(uint8_t)(Coils > 8 ? 8 : Coils));
Coils -= 8;
temp++;
BitOffset += 8;
}
mbHost.txBuf[4] = HI(data_len);
mbHost.txBuf[5] = LOW(data_len);
mbHost.txBuf[6] = temp;
break;
case WRITE_HLD_REG:
temp = 2 * data_len;
for (i = 0; i < data_len; i++)
{
mbHost.txBuf[7 + i * 2] = data[start_address + i] >> 8; //高字节在前
mbHost.txBuf[8 + i * 2] = data[start_address + i]; //低字节在后
}
mbHost.txBuf[4] = HI(data_len);
mbHost.txBuf[5] = LOW(data_len);
mbHost.txBuf[6] = temp;
break;
}
mbHost.txLen = 6; //data_len(2)+start_address(2)+add(1)+cmd(1)
if (cmd == WRITE_COIL || cmd == WRITE_HLD_REG)
{
mbHost.txLen += temp + 1; //mbHost.txLen=7
}
crc = mb_crc16(mbHost.txBuf, mbHost.txLen);
mbHost.txBuf[mbHost.txLen++] = (uint8_t)(crc & 0xff);
mbHost.txBuf[mbHost.txLen++] = (uint8_t)(crc >> 8);
mbHost.state = MBH_STATE_TX;
mb_port_uartEnable(1, 0); //enable tx,disable rx
/*先发送一个byte,触发TC中断*/
mb_port_putchar(mbHost.txBuf[mbHost.txCounter++]); //send first char,then enter tx isr
return 0;
}
另外两个发送字节处理与接收字节处理:
void mbh_uartRxIsr()
{
uint8_t ch;
mb_port_getchar(&ch);
switch (mbHost.state)
{
case MBH_STATE_TX_END:
mbHost.rxCounter = 0;
mbHost.rxBuf[mbHost.rxCounter++] = ch;
mbHost.state = MBH_STATE_RX;
mb_port_timerReset();//收到数据,重新从0计数
break;
case MBH_STATE_RX:
if (mbHost.rxCounter < MBH_RTU_MAX_SIZE)
{
mbHost.rxBuf[mbHost.rxCounter++] = ch;
}
mb_port_timerReset();//收到数据,重新从0计数
break;
default:
// mb_port_timerEnable();
break;
}
}
void mbh_uartTxIsr()
{
switch (mbHost.state)
{
case MBH_STATE_TX:
if (mbHost.txCounter == mbHost.txLen) //全部发送完
{
mbHost.state = MBH_STATE_TX_END;
mb_port_uartEnable(0, 1); //disable tx,enable rx
mbHost.rxTimeOut = 0; //清除接收超时计数
mb_port_timerEnable(); //open timer
}
else
{
mb_port_putchar(mbHost.txBuf[mbHost.txCounter++]);
}
break;
case MBH_STATE_TX_END:
mb_port_uartEnable(0, 1); //disable tx,enable rx
// mb_port_timerEnable(); //open timer
break;
}
}
- 在main函数中添加测试代码
int main(void)
{
uint8_t Tx_state = 0x00;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
mbh_init(9600, 0);
SaveStartAddr = 0;
DataORLenth = 9;
mbh_send(1, READ_AI, 0, usRegInputBuf, DataORLenth);
delay_init();
while (1)
{
mbh_poll();
if (MD_MASTER_ComErr == 0)
{
MD_MASTER_ComErr=255;
delay_ms(1000);
switch (Tx_state)
{
case 0:
mbh_send(1, WRITE_HLD_REG, 0, usRegInputBuf, DataORLenth);
Tx_state++;
break;
case 1:
mbh_send(1, READ_DI, 0, (uint16_t *)ucRegDiscreteBuf, DataORLenth);
Tx_state++;
break;
case 2:
mbh_send(1, WRITE_COIL, 0, (uint16_t *)ucRegDiscreteBuf, DataORLenth);
Tx_state++;
break;
case 3:
// memset(ucRegCoilsBuf, 0, 2); //将数据清0
mbh_send(1, READ_COIL, 0, (uint16_t *)ucRegCoilsBuf, DataORLenth);
Tx_state++;
break;
case 4:
ucRegCoilsBuf[0]=0xEF;
mbh_send(1, WRITE_COIL, 0, (uint16_t *)ucRegCoilsBuf, DataORLenth);
Tx_state++;
break;
case 5:
memset(usRegHoldingBuf, 0, 20); //将数据清0
mbh_send(1, READ_HLD_REG, 0, usRegHoldingBuf, DataORLenth);
Tx_state++;
break;
case 6:
usRegHoldingBuf[0]=12345;
mbh_send(1, WRITE_HLD_REG, 0, usRegHoldingBuf, DataORLenth);
Tx_state++;
break;
}
}
}
}