STM32F103移植freeModebusRTU(HAL)

一、 ModbusRTU介绍及下载

1.1 介绍

  • MODBUS 通讯协议,是1979年由美国Modicon 公司提出的,就是被称为PLC 之父的迪克·莫利先生创造的品牌。 MODBUS 是世界上第一个用于工业现场的总线协议,可以说,它的出现标志着工业现场从模拟量时代向通讯时代迈进。

详细介绍

1.2下载

  • 下载建议去GitHub去下:官网

二、移植准备

  • 既然是移植嘛,就和加项目和加头文件路径分不开了,解压并打开刚刚下载的文件,点进demo里,新建一个STM32MB文件夹在这里插入图片描述
  • 将BARE文件夹里的文件全部复制到刚刚建立的STM32MB文件夹里在这里插入图片描述
  • 再把modbus文件夹全部复制到刚刚新建的STM32MB文件夹,到最后的结果如下图在这里插入图片描述

三、STMCUBEMX建立项目

  • 选择F103C8板子的步骤就不讲了,这里直接设置外部时钟在这里插入图片描述
  • 使能一个USART为异步通信,并且加上中断在这里插入图片描述
  • 进入时钟设置,将HCLK改为72,然后外部时钟频率为8(不同板子不一样,如果不一样建议上网查一下,如果不和你所用的一致,后面用ModbusPoll会报超时)在这里插入图片描述
  • 定时器设置如图所示,参数这些也要一致,同时记得把中断加上,具体解释请移步,这篇解释很清楚,我这里就记录一下步骤

基于STM32CubeMX移植freeModbusRTU(从站)

在这里插入图片描述

-在NVIC里改一下TIM4定时器中断的优先级,比串口的优先级低就行了在这里插入图片描述

  • 项目管理,这些弄好之后生成项目就行了在这里插入图片描述在这里插入图片描述

三、移植

  • 在文件夹里打开项目里的MDK-ARM文件,将开始新建的STM32MB文件复制到这里在这里插入图片描述
  • 在keil里打开项目,添加分组和文件,MB分组是modbus里的所有.c文件在这里插入图片描述
  • MB_PORT分组里添加的文件是的STM32MB除了modbus文件以外的所有.c文件在这里插入图片描述
  • 添加的头文件路径在这里插入图片描述

portserial.c文件更改

  • vMBPortSerialEnable函数改为
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    if (xRxEnable)															//将串口收发中断和modbus联系起来,下面的串口改为自己使能的串口
			{
				__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);	//我用的是串口1,故为&huart1
			}
		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);
			}	

}
  • xMBPortSerialInit函数的返回值改为true
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    return TRUE;//改为TURE,串口初始化在usart.c定义,mian函数已完成
}
  • xMBPortSerialPutByte和xMBPortSerialGetByte更改为
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
			if(HAL_UART_Transmit (&huart1 ,(uint8_t *)&ucByte,1,0x01) != HAL_OK )	//添加发送一位代码
			   return FALSE ;
			else
      return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
    if(HAL_UART_Receive (&huart1 ,(uint8_t *)pucByte,1,0x01) != HAL_OK )//添加接收一位代码
			    return FALSE ;
	   else
       return TRUE;
}
  • 将prvvUARTTxReadyISR和prvvUARTRxISR前面的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.
 */
 void prvvUARTRxISR( void )
{
    pxMBFrameCBByteReceived(  );
}

porttimer.c文件

  • 基本全部函数都有改动,这里直接代码了,里面的定时器根据自己选择改动
/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * 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$
 */

/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
#include "stm32f1xx_hal.h"
#include "tim.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
void prvvTIMERExpiredISR( void );

/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )//定时器初始化直接返回TRUE,已经在mian函数初始化过
{
    return  TRUE;
}


inline void
vMBPortTimersEnable(  )//使能定时器中断,我用的是定时器4,所以为&htim4
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
				 /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
		__HAL_TIM_CLEAR_IT(&htim4,TIM_IT_UPDATE);
		__HAL_TIM_ENABLE_IT(&htim4,TIM_IT_UPDATE);
		__HAL_TIM_SET_COUNTER(&htim4,0);
		__HAL_TIM_ENABLE(&htim4);
}

inline void
vMBPortTimersDisable(  )//取消定时器中断
{
    /* Disable any pending timers. */
	__HAL_TIM_DISABLE(&htim4);
			__HAL_TIM_SET_COUNTER(&htim4,0);
			__HAL_TIM_DISABLE_IT(&htim4,TIM_IT_UPDATE);
			__HAL_TIM_CLEAR_IT(&htim4,TIM_IT_UPDATE);

}

/* 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.
 */
void prvvTIMERExpiredISR( void )//modbus定时器动作,需要在中断内使用
{
    ( void )pxMBPortCBTimerExpired(  );
}


port.h文件

  • 加上hal库的中断头文件,并且更改一下ENTER_CRITICAL_SECTION( )和EXIT_CRITICAL_SECTION( )的定义,改为
#ifndef _PORT_H
#define _PORT_H

#include <assert.h>
#include <inttypes.h>
#include "stm32f1xx_hal.h"


#define	INLINE                      inline
#define PR_BEGIN_EXTERN_C           extern "C" {
#define	PR_END_EXTERN_C             }

#define ENTER_CRITICAL_SECTION( )   __set_PRIMASK(1) 	 //关总中断
#define EXIT_CRITICAL_SECTION( )    __set_PRIMASK(0)   //开总中断
  • 更改完效果如图所示在这里插入图片描述

stm32f1xx_it.c文件

  • 引入外部函数,注意代码加的位置
/* USER CODE BEGIN PFP */
extern void prvvUARTTxReadyISR(void);
extern void prvvUARTRxISR(void);
extern void prvvTIMERExpiredISR( void );

/* USER CODE END PFP */
  • 更改串口1的中断处理函数
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE)!= RESET) 
		{
			prvvUARTRxISR();//接收中断
		}

	if(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE)!= RESET) 
		{
			prvvUARTTxReadyISR();//发送中断
		}
	
  HAL_NVIC_ClearPendingIRQ(USART1_IRQn);
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE END USART1_IRQn 1 */
}
  • 更改定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)	//定时器中断回调函数,用于连接porttimer.c文件的函数
{
  /* NOTE : This function Should not be modified, when the callback is needed,
            the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
  	prvvTIMERExpiredISR( );
}

demo.c文件

  • 下面代码替换demo.c的代码就行了,这里就是将数据放在数组内,测试读取的时候是否正常,如果正常会就会读取到这些数据
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- Defines ------------------------------------------*/
#define REG_INPUT_START 0
#define REG_INPUT_NREGS 5

/* ----------------------- Static variables ---------------------------------*/
static USHORT   usRegInputStart = REG_INPUT_START;
//static 
uint16_t   usRegInputBuf[REG_INPUT_NREGS];
uint16_t InputBuff[5];

eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;
		int             i;
		InputBuff[0] = 0x11;
		InputBuff[1] = 0x22;
		InputBuff[2] = 0x33;
		InputBuff[3] = 0x44;
	
    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegInputStart );
				for(i=0;i<usNRegs;i++)
				{
					*pucRegBuffer=InputBuff[i+usAddress-1]>>8;
					pucRegBuffer++;
					*pucRegBuffer=InputBuff[i+usAddress-1]&0xff;
					pucRegBuffer++;
				}
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}

eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
{
    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;
}

main.c

  • 启动modbus需要在main函数进行初始化、开启侦听操作,所以需要在main.c里添加代码,添加头文件
/* USER CODE BEGIN Includes */
#include "mb.h"
#include "mbport.h"
/* USER CODE END Includes */

  • mian函数
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM4_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
		eMBInit( MB_RTU, 0x01, 1, 115200, MB_PAR_NONE);//初始化modbus,走modbusRTU,从站地址为0x01,端口为1。
	 eMBEnable(  );//使能modbus
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
			( void )eMBPoll(  );//启动modbus侦听
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、测试

  • 测试工具modbuspoll,下载链接(提取码1111):https://pan.baidu.com/s/1yDcfLR_UptdxCXMwErHgPg
  • 打开modbuspoll,点击setup第一个读写定义在这里插入图片描述
  • 设置如图,点击apply应用在这里插入图片描述
  • 点击connection的connect,进入连接设置,选项如图,我使用的是usb转ttl,用的是com端口,我连接的口是com7,所以选择的是port7在这里插入图片描述
  • 连接之后,结果如下,我这是做完截图,连接时没有no connection,这里的值与demo.c里设置的值一致,移植成功。在这里插入图片描述

五、总结

  • modbus移植并不难,就是设置定时器的那几个参数比较麻烦,其他还需要注意的就是外部晶振和外部时钟,这些仔细一点弄好了基本就没有问题了。

六、参考资料

基于STM32CubeMX移植freeModbusRTU(从站)

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值