当前位置: 首页 > news >正文

dedecms手机网站更新seo自学网免费

dedecms手机网站更新,seo自学网免费,长沙智能建站模板,知名的产品设计网站STM32-Modbus主机实现-正点原子精英板 实现方案最终实现效果完整工程下载 移植过程 实现方案 使用网上大神的开源Modbus主机框架: 链接: 点击跳转. 链接: 源码地址. 融合参考了另一位大神在openedv论坛开源的Modbus主机代码: 链接: 点击跳转. 得到本次的…

STM32-Modbus主机实现-正点原子精英板

  • 实现方案
    • 最终实现效果
    • 完整工程下载
  • 移植过程

实现方案

使用网上大神的开源Modbus主机框架:
链接: 点击跳转.
链接: 源码地址.
融合参考了另一位大神在openedv论坛开源的Modbus主机代码:
链接: 点击跳转.
得到本次的Modbus主机代码。
移植到正点原子STM32F1精英板,可结合我发表的
链接: 正点原子精英板移植freemodbus-v1.6.
可实现Modbus主从机,当然,得使用两个485串口。

最终实现效果

正常通讯:
完全正常通讯通讯出错重复发送命令:
完成错误通讯

完整工程下载

链接: 点击下载.

移植过程

  1. 首先下载解压Modbus主机框架
    文件目录
    解压后得到以上文件
  2. 在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();}
}
  1. 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;
}

我再连续出错里没有做什么处理,只是将状态消除好继续下一个发送命令,这里可以加入自己想要的处理。

  1. 在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;}
}
  1. 在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;}}}
}
http://www.zhongyajixie.com/news/56897.html

相关文章:

  • 做网站的人找不到了株洲seo排名
  • 做充币提现的网站搜索引擎营销的分类
  • html完整网页实例滨州网站seo
  • 开发一个定制的网站百度在线搜索
  • 织梦网站名称谷歌推广新手教程
  • 建一个类似亨物说网站建设费用电脑清理优化大师
  • 邢台seo排名seo培训班
  • 怎么把做的网站传软件开发网站
  • 如何做网站卖画seo应用领域有哪些
  • 南京专业网站设计哪个品牌微信seo排名优化软件
  • 网站上的动态图怎么做长沙关键词优化平台
  • tq网站漂浮代码google下载安装
  • 建立香港网站空间网站步骤百度热搜广告位
  • 数据库网站制作如何做网站网页
  • 哪些网站可以做seo广州番禺发布网
  • 网页设计模板的结构seo优化网站网页教学
  • 北京市怀柔区建设委员会网站淘宝关键词优化技巧教程
  • iis7 发布asp网站404十大接单平台
  • 机器人软件开发和网站开发网络营销和传统营销的区别
  • 电商网站开发需要掌握哪些知识技能杭州百度整站优化服务
  • app网站如何做推广可以推广的软件有哪些
  • 广州电商网站建设百度排名优化专家
  • 建晨网站建设百度客服人工电话24
  • 上海网站建设seodian重庆seo网站建设
  • 企业做定制网站的好处百度搜索服务
  • 给网站可以怎么做外链电商运营多少钱一个月
  • 做电脑网站手机能显示账户竞价托管费用
  • 关于做网站的笑话index百度指数
  • 网站前置审批怎么做指数函数
  • 网站报错404江苏网页定制