完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
实现方案
使用网上大神的开源Modbus主机框架: 融合参考了Modbus主机代码: 得到本次的Modbus主机代码。 移植到正点原子STM32F1精英板,可结合我发表的 可实现Modbus主从机,当然,得使用两个485串口。 最终实现效果 正常通讯: 通讯出错重复发送命令: 完整工程下载 移植过程
/** * @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); |
|
|
|
这里参考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; } } } } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1621 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1546 浏览 1 评论
980 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
686 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1599 浏览 2 评论
1867浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
650浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
518浏览 3评论
536浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
506浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 08:10 , Processed in 0.962405 second(s), Total 83, Slave 66 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号