手把手教你,通过HAL库实现MODBUS从机程序编写与调试(二)-串口及定时函数的完善

2021/11/4 11:09:49

本文主要是介绍手把手教你,通过HAL库实现MODBUS从机程序编写与调试(二)-串口及定时函数的完善,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

1.STM32CubeMX操作

 2、程序完善

2.1完善usart.h

 2.2完善usart.c

2.2.1头文件完善

2.2.2增加回调函数

2.2.3串口初始化函数

2.3完善tim.h

2.4完善tim.c

2.4.1完善头文件

 2.4.2完善回调函数和初始化函数

2.5完善main.c

2.6调试

2.6.1KEIL的设置

 2.6.2程序下载调试


 1.STM32CubeMX操作

此部分在上一个文章,链接如下:

手把手教你,通过HAL库实现MODBUS从机程序编写与调试(一)-----STM32CubeMX操作篇_tangxianyu的博客-CSDN博客

 2、程序完善

2.1完善usart.h

为了确保在更新STM32CubeMX的时候,我们自己增加的程序不会被删除掉。我们在完善程序的时候需要将程序增加到制定的位置。例如在usart.h内的,我们将需要增加的程序到:

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

首先定义一个UART_BUF结构体,里面包含接收的缓冲数据和大小,以及发送的数据集大小。

然后定义一个串口的初始化函数E_USART_INIT来对定义的UART_BUF进行初始化。定义一个串口中断回调函数HAL_UART_RxCpltCallback.

/* USER CODE BEGIN Private defines */
typedef struct
{
	uint8_t *rx_buf;		//接收缓冲数组
	uint16_t rx_buf_cnt;	//接收缓冲计数值
	uint16_t rx_size;		//接收数据大小
	uint8_t rx_flag;		//接收完成标志位
	
	uint8_t *tx_buf;		//发送缓冲数组
	uint16_t tx_buf_cnt;	//发送缓冲计数值
	uint16_t tx_size;		//实际发送数据大小
}UART_BUF;					//串口结构体

extern UART_BUF uart4;		//串口结构体实体
/* USER CODE END Private defines */

void MX_UART4_Init(void);

/* USER CODE BEGIN Prototypes */
void E_USART_INIT(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

/* USER CODE END Prototypes */

 2.2完善usart.c

2.2.1头文件完善

在/*USER CODE BEGIN 0*/和/*USER CODE END 0*/中间添加完善程序

/* USER CODE BEGIN 0 */
#include "string.h"
#include "tim.h"
#define UART4_RXSIZE	1024	//一帧接收数据的最大值
#define UART4_TXSIZE	1024	//一帧发送数据的最大值

uint8_t uart4_rx_buf[UART4_RXSIZE];	//发送数据缓冲数组
uint8_t uart4_tx_buf[UART4_TXSIZE];	//接收数据缓冲数据

UART_BUF uart4;			//串口结构体实体
uint8_t RxBuffer;		//接收数据中间变量
/* USER CODE END 0 */

2.2.2增加回调函数

在/* USER CODE BEGIN 1 */和/* USER CODE END 1 */增加回调函数HAL_UART_RxCpltCallback。

基本原理为接收到一个数据调用回调函数(RxBuffer),然后将数据赋值给rx_buf这个数组,然后清除定时器的计数,重新开始计数,防止定时器产生中断。

/*****************************重写回调函数,实现串口数据接收**********************/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == UART4)
	{
		if(uart4.rx_buf_cnt >= UART4_RXSIZE-1)	//接收数据量超限,错误
		{
			uart4.rx_buf_cnt = 0;
			memset(uart4.rx_buf, 0x00, sizeof(uart4.rx_buf));
			HAL_UART_Transmit(huart, (uint8_t *)"数据溢出", 10, 0xFFFF);		
		}
		else									//接收正常
		{
			uart4.rx_buf[uart4.rx_buf_cnt++] = RxBuffer;	//接收数据存储到rx_buf
			HAL_TIM_Base_Stop_IT(&htim7);
			__HAL_TIM_SET_COUNTER(&htim7, 0);
			HAL_TIM_Base_Start_IT(&htim7);		//将定时器7的计数值清零后重新计数
		}
		HAL_UART_Receive_IT(huart, (uint8_t *)&RxBuffer, 1);
	}
}

2.2.3串口初始化函数

在回调函数下方增加串口参数初始化函数E_USART_INIT

/*****************************对UART_BUF结构体的实体uart4赋值**********************/
void E_USART_INIT(UART_HandleTypeDef *huart)
{
	if(huart->Instance == UART4)
	{
		uart4.rx_buf = uart4_rx_buf;	//接收数据变量初始化
		uart4.rx_buf_cnt = 0;
		uart4.rx_size = 0;
		uart4.rx_flag = 0;
		
		uart4.tx_buf = uart4_rx_buf;	//发送数据变量初始化
		uart4.tx_buf_cnt = UART4_TXSIZE;
		uart4.tx_size = 0;		
		
		HAL_UART_Receive_IT(huart, uart4.rx_buf, 1);	//开启接收中断
	}

}

在初始化过程中最后一句话是开启中断,让每接收一个数据进入一次回调函数。

2.3完善tim.h

增加在tim.c中的函数声明,在/* USER CODE BEGIN Prototypes */和/* USER CODE END Prototypes */中间增加:

/* USER CODE BEGIN Prototypes */
void E_TIM_INIT(TIM_HandleTypeDef *htim);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
/* USER CODE END Prototypes */

2.4完善tim.c

2.4.1完善头文件

在头文件增加一个函数引用

/* USER CODE BEGIN 0 */
#include "usart.h"
/* USER CODE END 0 */

 2.4.2完善回调函数和初始化函数

在/* USER CODE BEGIN 1 */和/* USER CODE END 1 */内部增加时间回调函数和时间初始化函数。具体含义已在注释内说明,此处不赘述。

/* USER CODE BEGIN 1 */
/*****************************重写时间回调函数**********************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim7)
	{
		__HAL_TIM_CLEAR_FLAG(&htim7, TIM_FLAG_UPDATE);	//产生中断证明超过4ms没有接收到数据了,一帧接收完成
		HAL_TIM_Base_Stop_IT(&htim7);		//中断之后停止定时器,开启在下一次接收到数据开始
		uart4.rx_size = uart4.rx_buf_cnt;	//将接收到数据数量赋值
		uart4.rx_buf_cnt = 0;				//清零
		uart4.rx_flag = 1;					//接收完成,置1
	}

}

void E_TIM_INIT(TIM_HandleTypeDef *htim)
{
	if(htim == &htim7)
	{
		__HAL_TIM_CLEAR_FLAG(htim, TIM_FLAG_UPDATE);		//手动添加
		HAL_TIM_Base_Start_IT(htim);
	}

}

/* USER CODE END 1 */

2.5完善main.c

上面的函数完善都是为了处理数据发送和接收的函数,通过以上处理可以完成数据的发送和接受。在移植MODBUS函数之前可以对数据发送和接受是否正常进行测试。此处的main.c就是完成数据的转发功能,即接收到什么数据就向外转发什么数据。

首先在  /* USER CODE BEGIN 2 */和  /* USER CODE END 2 */内部增加定时器和串口的初始化函数

  /* USER CODE BEGIN 2 */
	E_USART_INIT(&huart4);
	E_TIM_INIT(&htim7);
  /* USER CODE END 2 */

然后再在主函数的while循环内增加程序:

  while (1)
  {
    /* USER CODE END WHILE */
	  
    /* USER CODE BEGIN 3 */
	  // 此处是用来测试收啥发啥的。	  
	  if(uart4.rx_flag == 1)	//如果一帧数据接收完成,其在tim7的回调函数置位
	  {
		HAL_UART_Transmit(&huart4, (uint8_t *)(uart4.rx_buf), uart4.rx_size, 500);//向外转发收到的数据
		while(__HAL_UART_GET_FLAG(&huart4,UART_FLAG_TC)!=SET);	//死循环等待
		uart4.rx_buf_cnt = 0;									//计数值清零
		uart4.rx_flag=0;										//接收完成标志位清零
	  }
	  
	  HAL_Delay(5);		//每间隔5ms循环一次
  }
  /* USER CODE END 3 */
}

2.6调试

2.6.1KEIL的设置

为了确保每次程序下载完成后后自动复位运行,需要对KEIL进行的debug进行设置,设置步骤如下:将Reset and Run前面的√点上。

 

 2.6.2程序下载调试

编译无误后将程序下载到电路板中,然后将电路板的UART4的串口与电脑口相连。打开串口调试助手,按照程序在串口调试助手内进行相应配置。

配置完成之后,打开串口就可以进行数据发送接收了。

 

 

 



这篇关于手把手教你,通过HAL库实现MODBUS从机程序编写与调试(二)-串口及定时函数的完善的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程