手把手教你,通过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从机程序编写与调试(二)-串口及定时函数的完善的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南