完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
一、利用STM32CubeMX新建工程
1、配置时钟 时钟源选择外部晶振。 配置时钟树。 2·、配置定时器 暂时先勾选internal clock就行,在modbus移植过程中还会对定时器重新初始化。 3、配置串口 随便配置就行,在modbus移植过程中还会对串口重新初始化。 4、中断配置 这里注意,串口的优先级是要比定时器优先高的。 取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序。 5、配置GPIO 如果使用了485模式,还需要一个发送接收控制端,该IO配置为推挽输出模式。也可以为该引脚设置User Label。 6、创建工程 二、下载freemodbus源码并且添加到刚才创建的工程 1、复制freemodbus源码到工程文件夹 2、在demo目录下的BARE文件夹里面新建port.c文件 3、在keil中添加freemodbus源码 将modbus和demo->BARE目录下的所有C文件添加进来。 4、编辑头文件包含路径 三、编写代码 1、物理接口文件的修改 在物理层,用户只需完成串行口及超时定时器的配置即可。具体应修改接口文件portserial.c及porttimer.c。 1)portserial.c中函数的修改 void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { if(xRxEnable) { __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE); //使能接收寄存器非空中断 HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式 } else { __HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE); //禁能接收寄存器非空中断 HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式 } if (TRUE == xTxEnable) { HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式 __HAL_UART_ENABLE_IT(&huart2, UART_IT_TC); //使能发送完成中断 } else { __HAL_UART_DISABLE_IT(&huart2, UART_IT_TC); //禁能发送完成中断 } } BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { huart2.Instance = USART2; huart2.Init.BaudRate = ulBaudRate; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式 switch(eParity) { // 奇校验 case MB_PAR_ODD: huart2.Init.Parity = UART_PARITY_ODD; huart2.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits break; // 偶校验 case MB_PAR_EVEN: huart2.Init.Parity = UART_PARITY_EVEN; huart2.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits break; // 无校验 default: huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.WordLength = UART_WORDLENGTH_8B; // 无奇偶校验数据位为8bits break; } return HAL_UART_Init(&huart2) == HAL_OK ? TRUE : FALSE; } BOOL xMBPortSerialPutByte( CHAR ucByte ) { HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式 USART2->DR = ucByte; return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式 *pucByte = (USART2->DR & (uint16_t)0x00FF); return TRUE; } static void prvvUARTTxReadyISR( void ) { pxMBFrameCBTransmitterEmpty( ); } static void prvvUARTRxISR( void ) { pxMBFrameCBByteReceived( ); } void USART2_IRQHandler(void) { if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE)) // 接收非空中断标记被置位 { __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE); // 清除中断标记 prvvUARTRxISR(); } if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC)) // 发送完成中断标记被置位 { __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_TC); // 清除中断标记 prvvUARTTxReadyISR(); } } 2)porttimer.c中函数的修改 BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim4.Instance = TIM4; htim4.Init.Prescaler = 3599; // 50us记一次数 htim4.Init.CounterMode = TIM_COUNTERMODE_UP; htim4.Init.Period = usTim1Timerout50us - 1;//usTim1Timerout50us*50即为定时器溢出时间 htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim4) != HAL_OK) { return FALSE; } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK) { return FALSE; } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK) { return FALSE; } __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE); // 使能定时器更新中断 return TRUE; } inline void vMBPortTimersEnable( ) { __HAL_TIM_SET_COUNTER(&htim4, 0); // 清空计数器 __HAL_TIM_ENABLE(&htim4); // 使能定时器 } inline void vMBPortTimersDisable( ) { __HAL_TIM_DISABLE(&htim4); // 禁能定时器 } static void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } // 定时器4中断服务程序 void TIM4_IRQHandler(void) { if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE)) // 更新中断标记被置位 { __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);// 清除中断标记 prvvTIMERExpiredISR(); // 通知modbus3.5个字符等待时间到 } } 2、应用层回函数的修改 在port中,定义所需要使用的寄存器,并修改对应的回函数。以下是操作保持寄存器的示例代码。 #include "port.h" #include "mb.h" //保持寄存器起始地址 #define REG_HOLDING_START 0x0001 //保持寄存器数量 #define REG_HOLDING_NREGS 10 //保持寄存器内容 uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e,0x1111,0x1111}; /** * @brief 保持寄存器处理函数,保持寄存器可读,可读可写 * @param pucRegBuffer 读操作时--返回数据指针,写操作时--输入数据指针 * usAddress 寄存器起始地址 * usNRegs 寄存器长度 * eMode 操作方式,读或者写 * @retval eStatus 寄存器状态 */ eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) { //错误状态 eMBErrorCode eStatus = MB_ENOERR; //偏移量 int16_t iRegIndex; //判断寄存器是不是在范围内 test = usAddress; if ((usAddress >= (USHORT)REG_HOLDING_START) && (usAddress + usNRegs <= (USHORT)(REG_HOLDING_START + REG_HOLDING_NREGS)) ) { //计算偏移量 iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START);switch ( eMode ){ //读处理函数 case MB_REG_READ: while( usNRegs > 0 ) { *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } break; //写处理函数 case MB_REG_WRITE: while( usNRegs > 0 ) { usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8; usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++; iRegIndex++; usNRegs--; } break; } } else { //返回错误状态 eStatus = MB_ENOREG; } return eStatus; } /// 未使用 eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) { return MB_ENOREG; } /// 未使用 eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { return MB_ENOREG; } /// 未使用 eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { return MB_ENOREG; } 3、主函数的编写 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); //初始化 RTU模式 从机地址为1 USART1 9600 无校验 eMBInit(MB_RTU, 0x01, 0x02, 9600, MB_PAR_NONE); //启动FreeModbus eMBEnable(); while (1) { //FreeMODBUS不断查询 eMBPoll(); HAL_Delay(50); } } 四、特别注意 这里使能了串口的“发送完成中断”,而不是官方代码建议的“发送为空中断”。由于单片机复位后,发送完成标志位TC置0,或者由于上一次发送最后一个字节时,发送中断中清除了TC标志位,所以无法进入USART2_IRQHandler()函数中启动发送。因此,在eMBPoll()函数中插入如下代码,发送一帧数据中的第一个字节,触发发送完成中断,随后就会自动完成一帧数据的发送。 eMBErrorCode eMBPoll( void ) { static UCHAR *ucMBFrame; static UCHAR ucRcvAddress; static UCHAR ucFunctionCode; static USHORT usLength; static eMBException eException; int i; eMBErrorCode eStatus = MB_ENOERR; eMBEventType eEvent; /* Check if the protocol stack is ready. */ if( eMBState != STATE_ENABLED ) { return MB_EILLSTATE; } /* Check if there is a event available. If not return control to caller. * Otherwise we will handle the event. */ if( xMBPortEventGet( &eEvent ) == TRUE ) { switch ( eEvent ) { case EV_READY: break; case EV_FRAME_RECEIVED: eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); if( eStatus == MB_ENOERR ) { /* Check if the frame is for us. If not ignore the frame. */ if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) { ( void )xMBPortEventPost( EV_EXECUTE ); } } break; case EV_EXECUTE: ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; eException = MB_EX_ILLEGAL_FUNCTION; for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) { /* No more function handlers registered. Abort. */ if( xFuncHandlers.ucFunctionCode == 0 ) { break; } else if( xFuncHandlers.ucFunctionCode == ucFunctionCode ) { eException = xFuncHandlers.pxHandler( ucMBFrame, &usLength ); break; } } /* If the request was not sent to the broadcast address we * return a reply. */ if( ucRcvAddress != MB_ADDRESS_BROADCAST ) { if( eException != MB_EX_NONE ) { /* An exception occured. Build an error frame. */ usLength = 0; ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); ucMBFrame[usLength++] = eException; } if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ) { vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ); } eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); //插入代码 begin if( eStatus == MB_ENOERR ) //no error { xMBRTUTransmitFSM(); //发送一帧数据中的第一个字节,触发发送完成中断 } //插入代码 end } break; case EV_FRAME_SENT: break; } } return MB_ENOERR; } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1771 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1070 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1935浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
728浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
568浏览 3评论
593浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
551浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 15:24 , Processed in 0.655545 second(s), Total 47, Slave 40 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号