dedecms手机网站更新seo自学网免费
STM32-Modbus主机实现-正点原子精英板
- 实现方案
- 最终实现效果
- 完整工程下载
- 移植过程
实现方案
使用网上大神的开源Modbus主机框架:
链接: 点击跳转.
链接: 源码地址.
融合参考了另一位大神在openedv论坛开源的Modbus主机代码:
链接: 点击跳转.
得到本次的Modbus主机代码。
移植到正点原子STM32F1精英板,可结合我发表的
链接: 正点原子精英板移植freemodbus-v1.6.
可实现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,GPIOAMD_MASTER_TRX_GPIO_CLK_FUN(MD_MASTER_TRX_GPIO_CLK , ENABLE);MD_MASTER_USART_CLK_FUN(MD_MASTER_USART_CLK , ENABLE);//GPIOA9 USART1_TxGPIO_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_RxGPIO_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);//使能USARTUSART_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.9GPIO_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;//20KHZTIM_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); //关闭定时器4TIM_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 statembHost.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 isrreturn 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 rxmbHost.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 timerbreak;}
}
- 在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); //将数据清0mbh_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); //将数据清0mbh_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;}}}
}