实现方案
使用网上大神的开源Modbus主机框架:
融合参考了Modbus主机代码:
得到本次的Modbus主机代码。
移植到正点原子STM32F1精英板,可结合我发表的
可实现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 串口TXRX使能接口
* @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 2 3……等数字代表功能码
*/
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);
实现方案
使用网上大神的开源Modbus主机框架:
融合参考了Modbus主机代码:
得到本次的Modbus主机代码。
移植到正点原子STM32F1精英板,可结合我发表的
可实现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 串口TXRX使能接口
* @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 2 3……等数字代表功能码
*/
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);
举报