modbus命令码表:
FreeModbus文件说明
解压freemodbus文件后打开,我们需要demo目录下的BARE,该目录下的代码是空的,STM32移植工作基本就是修改:portserial.c、porttimer.c、port.h这三个文件。
mobus文件夹就是完整的源码,包含rtu、ascii、tcp:
我为了移植时在keil添加源文件和头文件方便,就把modbus所有的头文件和源文件放到了一个文件夹下,并创建了一个port.c文件,用于编写modbus所必需的回调处理函数:
STM32CUBEMX配置
时钟配置,设置主频工作在72MHz下:
配置串口1,这里随便配置就行,在modbus移植过程中还会对串口重新初始化:
配置定时器4,用于3.5个字符的定时检测,这里随便配置就行,在modbus移植过程中还会对定时器重新初始化:
中断配置,这里注意,串口的优先级是要比定时器优先高的:
取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序:
移植代码修改
生成代码后将modbus放到工程目录下:
打开keil工程添加modbus源码:
添加包含头文件:
修改modbus定时器初始化源代码porttimer.c文件
定时器的修改比较容易,将定时器设置为每50us的时长记一个数,传入的usTim1Timerout50us变量给自动装载即可,prvvTIMERExpiredISR函数需要在定时器中断服务函数中调用,它的作用是用于通知modbus协议栈3.5个字符的等待时间已经到达;由于我们在STM32CUBEMX中取消掉了定时器和串口的中断服务函数程序,所以我们在该文件中添加定时器的中断服务程序,修改后的代码如下:
- 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); // 禁能定时器
- }
- /* Create an ISR which is called whenever the timer has expired. This function
- * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
- * the timer has expired.
- */
- 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个字符等待时间到
- }
- }
复制代码
修改modbus串口初始化源代码portserial.c文件
~~~~~~~~ 在该文件中实现串口1的中断服务程序,prvvUARTTxReadyISR和prvvUARTRxISR函数需要填写进中断服务程序,前者得到作用为通知modbus协议栈串口已经空闲可以发送数据了,后者的作用为通知modbus串口1有数据到达,修改后的代码如下:
- /*
- * FreeModbus Libary: BARE Port
- * Copyright (C) 2006 Christian Walter <<a href="mailto:wolti@sil.at">wolti@sil.at</a>>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * File: $Id$
- */
- #include "port.h"
- #include "usart.h"
- /* ----------------------- Modbus includes ----------------------------------*/
- #include "mb.h"
- #include "mbport.h"
- /* ----------------------- static functions ---------------------------------*/
- static void prvvUARTTxReadyISR( void );
- static void prvvUARTRxISR( void );
- /* ----------------------- Start implementation -----------------------------*/
- void
- vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
- {
- if(xRxEnable)
- {
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // 使能接收非空中断
- }
- else
- {
- __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE); // 禁能接收非空中断
- }
- if(xTxEnable)
- {
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); // 使能发送为空中断
- }
- else
- {
- __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE); // 禁能发送为空中断
- }
- }
- BOOL
- xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
- {
- huart1.Instance = USART1;
- huart1.Init.BaudRate = ulBaudRate;
- huart1.Init.StopBits = UART_STOPBITS_1;
- huart1.Init.Mode = UART_MODE_TX_RX;
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- huart1.Init.OverSampling = UART_OVERSAMPLING_16;
- switch(eParity)
- {
- // 奇校验
- case MB_PAR_ODD:
- huart1.Init.Parity = UART_PARITY_ODD;
- huart1.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
- break;
-
- // 偶校验
- case MB_PAR_EVEN:
- huart1.Init.Parity = UART_PARITY_EVEN;
- huart1.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
- break;
-
- // 无校验
- default:
- huart1.Init.Parity = UART_PARITY_NONE;
- huart1.Init.WordLength = UART_WORDLENGTH_8B; // 无奇偶校验数据位为8bits
- break;
- }
- return HAL_UART_Init(&huart1) == HAL_OK ? TRUE : FALSE;
- }
- BOOL
- xMBPortSerialPutByte( CHAR ucByte )
- {
- USART1->DR = ucByte;
- return TRUE;
- }
- BOOL
- xMBPortSerialGetByte( CHAR * pucByte )
- {
- *pucByte = (USART1->DR & (uint16_t)0x00FF);
- return TRUE;
- }
- /* Create an interrupt handler for the transmit buffer empty interrupt
- * (or an equivalent) for your target processor. This function should then
- * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
- * a new character can be sent. The protocol stack will then call
- * xMBPortSerialPutByte( ) to send the character.
- */
- static void prvvUARTTxReadyISR( void )
- {
- pxMBFrameCBTransmitterEmpty( );
- }
- /* Create an interrupt handler for the receive interrupt for your target
- * processor. This function should then call pxMBFrameCBByteReceived( ). The
- * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
- * character.
- */
- static void prvvUARTRxISR( void )
- {
- pxMBFrameCBByteReceived( );
- }
- void USART1_IRQHandler(void)
- {
- if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) // 接收非空中断标记被置位
- {
- __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE); // 清除中断标记
- prvvUARTRxISR(); // 通知modbus有数据到达
- }
- if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE)) // 发送为空中断标记被置位
- {
- __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE); // 清除中断标记
- prvvUARTTxReadyISR(); // 通知modbus数据可以发松
- }
- }
复制代码
注意一点,一般如果使用了485芯片的话,那么同一时刻只能接收或者发送,可以将函数vMBPortSerialEnable修改成这样:
- void
- vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
- {
- if(xRxEnable)
- {
- //
- // 在此处将485芯片设置为接收模式
- //
- /* do something */
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // 使能接收非空中断
- }
- else
- {
- __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE); // 禁能接收非空中断
- }
- if(xTxEnable)
- {
- //
- // 在此处将485芯片设置为发送模式
- //
- /* do something */
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); // 使能发送为空中断
- }
- else
- {
- __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE); // 禁能发送为空中断
- }
- }
复制代码
编写modbus命令处理回调函数port.c文件
本例程只实现了读取输入寄存器和保持寄存器的功能,详细代码如下:
- #include "mb.h"
- #include "mbport.h"
- // 十路输入寄存器
- #define REG_INPUT_SIZE 10
- uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
- // 十路保持寄存器
- #define REG_HOLD_SIZE 10
- uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
- // 十路线圈
- #define REG_COILS_SIZE 10
- uint8_t REG_COILS_BUF[REG_COILS_SIZE];
- // 十路离散量
- #define REG_DISC_SIZE 10
- uint8_t REG_DISC_BUF[10];
- /// CMD4
- eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
- {
- USHORT usRegIndex = usAddress - 1;
- // 非法检测
- if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
- {
- return MB_ENOREG;
- }
- // 循环读取
- while( usNRegs > 0 )
- {
- *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
- *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
- usRegIndex++;
- usNRegs--;
- }
- // 模拟输入寄存器被改变
- for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)
- {
- REG_INPUT_BUF[usRegIndex]++;
- }
- return MB_ENOERR;
- }
- /// CMD6、3、16
- eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
- {
- USHORT usRegIndex = usAddress - 1;
- // 非法检测
- if((usRegIndex + usNRegs) > REG_HOLD_SIZE)
- {
- return MB_ENOREG;
- }
- // 写寄存器
- if(eMode == MB_REG_WRITE)
- {
- while( usNRegs > 0 )
- {
- REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
- pucRegBuffer += 2;
- usRegIndex++;
- usNRegs--;
- }
- }
-
- // 读寄存器
- else
- {
- while( usNRegs > 0 )
- {
- *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
- *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
- usRegIndex++;
- usNRegs--;
- }
- }
- return MB_ENOERR;
- }
- /// CMD1、5、15
- eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
- {
- USHORT usRegIndex = usAddress - 1;
- USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
- UCHAR ucStatus = 0;
- UCHAR ucBits = 0;
- UCHAR ucDisp = 0;
- // 非法检测
- if((usRegIndex + usNCoils) > REG_COILS_SIZE)
- {
- return MB_ENOREG;
- }
- // 写线圈
- if(eMode == MB_REG_WRITE)
- {
- while(usCoilGroups--)
- {
- ucStatus = *pucRegBuffer++;
- ucBits = 8;
- while((usNCoils--) != 0 && (ucBits--) != 0)
- {
- REG_COILS_BUF[usRegIndex++] = ucStatus & 0X01;
- ucStatus >>= 1;
- }
- }
- }
- // 读线圈
- else
- {
- while(usCoilGroups--)
- {
- ucDisp = 0;
- ucBits = 8;
- while((usNCoils--) != 0 && (ucBits--) != 0)
- {
- ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));
- }
- *pucRegBuffer++ = ucStatus;
- }
- }
- return MB_ENOERR;
- }
- /// CMD4
- eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
- {
- USHORT usRegIndex = usAddress - 1;
- USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);
- UCHAR ucStatus = 0;
- UCHAR ucBits = 0;
- UCHAR ucDisp = 0;
- // 非法检测
- if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)
- {
- return MB_ENOREG;
- }
- // 读离散输入
- while(usCoilGroups--)
- {
- ucDisp = 0;
- ucBits = 8;
- while((usNDiscrete--) != 0 && (ucBits--) != 0)
- {
- if(REG_DISC_BUF[usRegIndex])
- {
- ucStatus |= (1 << ucDisp);
- }
- ucDisp++;
- }
- *pucRegBuffer++ = ucStatus;
- }
- // 模拟改变
- for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)
- {
- REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];
- }
- return MB_ENOERR;
- }
复制代码
主函数
- int main(void)
- {
- HAL_Init();
- SystemClock_Config();
- MX_GPIO_Init();
- eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_ODD); // 初始化modbus为RTU方式,波特率9600,奇校验
- eMBEnable(); // 使能modbus协议栈
- for( ;; )
- {
- eMBPoll(); // 轮训查询
- }
- }
复制代码
移植测试
ends…
|
我的是stm32g071系列,移植上去不行怎么办