STM32+HAL+裸机+FreeModbus(从站)移植

一:平台工具资源介绍
二:使用CubeMX创建基础工程
三:添加FreeModbus软件包到工程
四:适配FreeModbus
五:测试

前言

这是基于STM32从站的FreeModbus-rtu移植,使用hal库。

一:软硬件工具资源介绍

1、CubeMX6.4.0

2、FreeeModbus源码1.5.0版本

3、STM32G474控制板

4、keil5.36

二:使用CubeMX创建基础工程

1、配置串口

在这里插入图片描述
在这里插入图片描述

2、配置定时器

在这里插入图片描述
在这里插入图片描述

三:添加FreeModbus软件包到工程

1、源码文件介绍,并提取使用到的文件

新建一个名为STM32MB的文件夹。然后打开freeModbus代码包的demo文件夹,之后将BARE文件夹内所有内容复制到STM32MB文件夹下,复制完成如图在这里插入图片描述
回到freeModbus代码包,复制整个modbus文件夹也粘贴到STM32MB文件夹内,完成效果如图
在这里插入图片描述
将STM32MB文件夹移动到stm32cubeMX生成的工程目录下,如图
在这里插入图片描述

2、在keil新建FreeModbus分支文件夹 FreeModbus_Port 、FreeModbus_Source并从源码添加文件

打开工程,引入STM32MB内的所有头文件,并新建名为MB和MB_Port的组,MB内添加STM32MB文件夹下modbus文件夹内所有c文件以及根目录的demo.c文件,MB_Port内添加STM32MB文件夹下port文件夹内所有c文件,如图所示
在这里插入图片描述

四:适配FreeModbus

源码修改三个文件,串口portserial.c;定时器porttimer.c;port.h;

1、串口适配

修改MB_Port下的portserial.c文件(串口设置)


#include "port.h"
#include "stm32g4xx_hal.h" // 增加自己的头文件
#include "usart.h"  // 增加自己的头文件
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
 void prvvUARTTxReadyISR( void );
 void prvvUARTRxISR( void );

/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
		if (xRxEnable)															//将串口收发中断和modbus联系起来,下面的串口改为自己使能的串口
			{
				HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);  // 485方向设置为输入
				__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);	//我用的是串口1,故为&huart1
			}
		else
			{
				HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);// 485方向设置为输出
				__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 )
{
    return TRUE; //  改为 TRUE 因为cubemx已经初始化了
}

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. */
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); // 485方向设置为输出
	
		if(HAL_UART_Transmit (&huart1 ,(uint8_t *)&ucByte,1,0x01) != HAL_OK )	//添加发送一位代码
		{
    	    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);		// 485方向设置为输入	
		
			return FALSE ;
		}
		else
		{
    	    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);	// 485方向设置为输入
			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;
}

/* 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.
 */
 void prvvUARTTxReadyISR( void )//删去前面的static,方便在串口中断使用
{
    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 )//删去前面的static,方便在串口中断使用
{
    pxMBFrameCBByteReceived(  );
}

修改串口全局中断服务函数

// 在/* USER CODE BEGIN PFP */后添加以下代码,用于和modbus的串口和定时器功能代码联系
extern void prvvUARTTxReadyISR(void);
extern void prvvUARTRxISR(void);
extern void prvvTIMERExpiredISR( void );

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 */
}

2、定时器适配

修改MB_Port下的porttimer.c文件(定时器设置)



/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
#include "stm32g4xx_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( void ) //使能定时器中断,我用的是定时器4,所以为&htim4
{
    /* 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_SetCounter(&htim4,0);
		__HAL_TIM_ENABLE(&htim4);	
}

inline void
vMBPortTimersDisable( void )//取消定时器中断
{
    /* Disable any pending timers. */
			__HAL_TIM_DISABLE(&htim4);
			__HAL_TIM_SetCounter(&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();
}


修改定时器全局中断服务函数

/**
  * @brief This function handles TIM4 global interrupt.
  */
void TIM4_IRQHandler(void)
{
  /* USER CODE BEGIN TIM4_IRQn 0 */

  /* USER CODE END TIM4_IRQn 0 */
//  HAL_TIM_IRQHandler(&htim4);
  /* USER CODE BEGIN TIM4_IRQn 1 */

    if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE)) // ?????????
    {
        __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);// ??????
        prvvTIMERExpiredISR();	// ??modbus3.5????????
    }

  /* USER CODE END TIM4_IRQn 1 */
}

3、port.h

修改完Modbus与stm32的接口文件之后要在port.h文件内定义总中断,位置在port.h文件的32行和33行,修改为如下所示,并在port.h前包含上stm32的hal库,如图所示


#ifndef _PORT_H
#define _PORT_H

#include <assert.h>
#include <inttypes.h>
#include "stm32g4xx_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)   //开总中断


typedef uint8_t BOOL;

typedef unsigned char UCHAR;
typedef char CHAR;

typedef uint16_t USHORT;
typedef int16_t SHORT;

typedef uint32_t ULONG;
typedef int32_t LONG;

#ifndef TRUE
#define TRUE            1
#endif

#ifndef FALSE
#define FALSE           0
#endif

#endif

五:编写modbus功能处理函数,并测试

硬件接口方面结束之后就可以开始写功能了,在MB–>demo.c中有功能示例,我们根据功能示例来修改对应的功能并使能modbus,这里只说输入寄存器功能,其它的一次类推,就不多赘述。就是自己设置一个数组,将数据放到数组内,并在被读取时根据数据位置将数据返回去。

1、

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
STM32 HAL库是针对STMicroelectronics的STM32系列微控制器提供的一套硬件抽象层(Hardware Abstraction Layer)库。它提供了一组API函数和驱动程序,用于简化STM32微控制器的配置和操作。 HAL库的目标是提供一种统一的编程接口,使得开发人员可以更方便地访问STM32微控制器的功能和外设。它屏蔽了底层硬件的差异性,使得开发人员可以更专注于应用逻辑的开发,而不用过于关注底层硬件细节。 HAL库的主要特点包括: 1. 硬件抽象:HAL库提供了一种抽象的接口,隐藏了底层硬件的细节,使得开发人员可以以相同的方式访问不同型号的STM32微控制器。 2. 配置灵活:HAL库提供了丰富的配置选项,可以通过宏定义和配置文件进行灵活配置,以满足不同应用需求。 3. 可移植性:HAL库是基于CMSIS(Cortex Microcontroller Software Interface Standard)标准开发的,因此具有较好的平台移植性,可以在不同的开发环境和编译器上使用。 4. 常用功能支持:HAL库提供了一系列常用功能的API函数,如GPIO操作、定时器控制、中断处理、串口通信等,方便开发人员快速完成常见的任务。 不过需要注意的是,HAL库虽然提供了较高层次的抽象,但在一些对性能要求较高的应用中,可能会需要更底层的编程方式来实现。因此,在选择使用HAL库时,需要根据应用需求进行权衡和选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值