本文主要是介绍【STM32】BLDC驱动控制开发笔记 | 01_基于STM32F407的UART发送与接收中断实验,含重定向printf,USART+LED,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章概览
- 😶🌫️ 0 说在最前面 + 实现功能
- 👀 1 CubeMX中的配置
- 🕶 1.1 RCC & Clock Configuration时钟配置
- 🕶 1.2 SYS Debug设置
- 🕶 1.3 USART3通讯设置
- 🕶 1.4 LED小灯引脚设置(GPIO设置)
- 🕶 1.5 Project Manager 设置工程(自动生成C语言工程文件)
- 👀 2 Keil 中的配置
- 🕶 2.0 先来简单看看自动生成了什么
- 🕶 2.1 下载程序前,记得改一下的设置
- 🕶 2.2 Test 1:发送出收到的字符/字符串
- 🥽2.2.1 在【main.c】插入这些
- 🥽2.2.2 在【usart.h】插入这些
- 🥽2.2.3 在【usart.c】插入这些
- 🥽2.2.4 实验测试
- 🕶 2.3 Test 2:串口3接收中断(1位),根据收到不同内容进行相应处理
- 🥽2.3.1 在【main.c】插入这些
- 🥽2.3.2 在【usart.c】插入这些
- 🥽2.3.3 在【usart.h】插入这些
- 🥽2.3.4 实验测试(还存在一点点小问题?)
- 👀 3 调试过程中遇到的问题
- 🕶 3.1 程序如果收到的字符串长度>1可能会漏字
- 🕶 3.2 收发中文时出现乱码
- 🕶 3.3 一些零零碎碎的易错点
😶🌫️ 0 说在最前面 + 实现功能
本文实验目的:使用USART3进行串口通信,打开中断,返回接收到的内容。
硬件上,我使用的是STM32F407VGT6芯片核心板,以及一个TTL转USB的转接模块。
这块核心板在使用上有点要注意的,每次下载好程序后需要重新上电或者按一次Reset键,新烧录的程序才能开始运行。
🫥参考/学习资料:
【STM32官方参考手册】《STM32F4xx参考手册_中文_RM0090》 (文档ID 018909 第4版)
🧀英文版-STM32F4xx参考手册-RM0090(官方下载)
🧀中文版-STM32F4xx参考手册-RM0090(非官方,论坛下载)
博主使用的即是第二个链接中的版本。
一些具体实战操作教学帖:
🧀STM32CubeMX 学习(2)USART 串口实验_CSDN博客@小辉_Super —— 简单易懂,可尝试复现!
🧀STM32串口发送接收数据_CSDN博客@一只小阿大:) —— 理论层面概述+从易到难几种实验测试。
🧀【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解_CSDN博客@Z小旋 —— HAL库UART函数库介绍+详细实现步骤介绍,强烈推荐!👍🏽✨
🧀【STM32CubeMX学习】串口通信USART基础教程_CSDN博客@TRTSOYE —— 详细实现步骤介绍,和上一篇很相似但又有一定区别,强烈推荐!👍🏽✨
🧀STM32CubeMX学习笔记(6)——USART串口使用_CSDN博客@Leung_ManWah —— 讲解细致,但仅实现逐位收发,buffer大小为1。
🧀STM32cubeMX+HAL库+串口中断收发程序_CSDN博客@小马_666 —— 重写了事件回调函数的实现,和别的帖有一定区别。
偏基础理论的一些学习博客帖子:
🧀STM32 USART通信协议详细讲解—小白入门_CSDN博客@阿乔不想编程
基于源代码的分析理解:
🧀STM32中HAL库使用-串口接收(一)_CSDN博客@nice-wyh
🧀STM32中HAL库使用-串口发送(二)_CSDN博客@nice-wyh
🧀STM32 非阻塞 HAL_UART_Receive_IT 解析与实际应用_知乎博客@Yume
👀 1 CubeMX中的配置
🕶 1.1 RCC & Clock Configuration时钟配置
🥽 Pinout & Configuration – System Core – RCC
🌠>> RCC Mode and Configuration
🚦High Speed Clock (HSE) – ‘Crystal/Ceramic Resonator’
高速时钟源 – 外部晶振 (晶体或陶瓷谐振器)
🚦Low Speed Clock (LSE) – Disable
软件会自动配置两个引脚:
PH0 – RCC_OSC_IN
PH1 – RCC_OSC_OUT
🌠>> Configuration – Parameter Settings
暂时保持默认参数设置。
🥽 Clock Configuration
系统定时器配置Cortex System timer – 168MHz
🕶 1.2 SYS Debug设置
🌠>> SYS Mode and Configuration
🚦Debug – Serial Wire
🚦Timebase Source – SysTick
(PA13+PA14 = No.72, 76口)
🕶 1.3 USART3通讯设置
PD8 - USART3_TX
PD9 - USART3_RX
🌠>> USART3 – Mode & Parameter Settings
🚦Mode – Asynchronous异步模式
异步通信,需要保证通信两边设备波特率一致;
如果设为同步模式,则会通过多的CLK线保证时钟一致。
🚦Hardware Flow Control (RS232) – Disable
🌠>> USART3 – Parameter Settings
▷▶ Parameter Settings – Basic Parameters
Baud Rate – 115200 Bits/s(每秒传输115200位)
Word Length – 8 Bits (including Parity)
Parity – None
Stop Bits – 1
▷▶ Parameter Settings – Advanced Parameters
Data Direction – Receive and Transmit
Over Sampling – 16 Samples(连续16次检测到高电平,才认为是高,抗干扰作用)
🌠>> USART3 – NVIC Settings
在NVIC Setting选项栏里勾选上。如果要改优先级,需要跳转到 System Core – NVIC 来更改设置。这里暂时就用 (0, 0) 最高的优先级了。
🌠>> USART3 – GPIO Settings
这里调整GPIO设置也需要到 System Core - GPIO 里面改。
🚦GPIO mode – Alternate Function Push Pull(复用推挽)
🚦GPIO Pull-up/Pull-down – Pull-up(❗特别注意)
❗建议将UART_Rx上拉,避免在悬空时受电路中某些信号的影响而误触发。
(有关UART端口上拉与否,详情参考:🧀USART RX 不上拉的后果_CSDN博文@小康师兄、🧀# STM32系列-串口-uart-软件引脚内部上拉 或者 外部电阻上拉-原因问题的搜寻_CSDN博文@好奇龙猫)
🕶 1.4 LED小灯引脚设置(GPIO设置)
PA8 - LED0(核心板自带小灯,程序进程中闪烁表示一直在正常运行,闪烁暂停表示进入中断)
PB10 - LED1
PB11 - LED2
PE15 - LED3(三个自己配置的端口,外接面包板搭的简单电路进行测试)
🌠>> System Core – GPIO
🚦GPIO Pull-up/Pull-down – Pull-up
在默认设置的基础上,把 GPIO Pull-up/Pull-down 改成 Pull-up 模式。
结合外接电路,接有3.3V高电平,当引脚输出低电平时灯亮。因此默认上拉时,灯灭。
🕶 1.5 Project Manager 设置工程(自动生成C语言工程文件)
🌠>> Project Manager – Project
🌠>> Project Manager – Code Generator
🌠>> Project Manager – Advanced Settings
最后从CubeMX界面右上角,点 GENERATE CODE 按钮,即可。
👀 2 Keil 中的配置
(打开Project的文件在所在目录的MDK-ARM文件夹中。)
🕶 2.0 先来简单看看自动生成了什么
🚂>> 【usart.c】
void MX_USART3_UART_Init(void) 中是前面用CubeMX配置的那些UART参数。
其中用到的UART中断需要自己手动添加相应语句 HAL_UART_Receive_IT(&huart3, (uint8_t *)&RXDATA, 1); 来开启接收中断。由于 MX_USART3_UART_Init( ) 函数会在main函数的开头调用,所以可根据个人喜好把开启中断语句加在函数体中的末尾,或是直接在main函数里加。
void HAL_UART_MspInit(UART_HandleTypeDef * uartHandle) 中是前面在CubeMX配置的那些和UART相关的GPIO设定、以及NVIC设置。
这个函数已经在上一个函数的第49行调用了。
void HAL_UART_MspDeInit(UART_HandleTypeDef * uartHandle) 是和 MspInit 相反的一些操作。
🚂>> 【gpio.c】
void MX_GPIO_Init(void) 是一些别的普通GPIO端口初始化设置。
🕶 2.1 下载程序前,记得改一下的设置
找到这个小魔术棒。
>>Target – Use MicroLIB
>>Debug – Settings
OK,关掉。
🕶 2.2 Test 1:发送出收到的字符/字符串
🫡 有见到两种打开中断的函数写法,HAL_UART_Receive_IT 和 __HAL_UART_ENABLE_IT( ) 的区别?
跳转这篇小博客👉HAL_UART_Receive_IT 和 __HAL_UART_ENABLE_IT( ) 的区别?_CSDN博客@F菌的进阶之路。简单来说就是,两种函数都可以使用,这里只需要接收中断,所以用最常用的HAL库函数(第一种写法)即可。
🥽2.2.1 在【main.c】插入这些
首先是开头部分,一些相关头文件的include和存储数据的变量声明。
接下来一部分是和 UART 串口功能有关的
#include <stdio.h>
#include <string.h>
#define MAXSIZE 256 // 最大接收字节数
char RxBuffer[MAXSIZE]; // 存储接收数据的数组
uint8_t RxData; // 接收中断缓冲
uint8_t RxCount = 0; // 接收缓冲计数
uint8_t RxFlag=0; // 结束位判断正确标志
然后,在while循环体里加入发送信号语句。
/* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */if(RxFlag == 1){HAL_UART_Transmit(&huart3, (uint8_t *)&RxBuffer, RxCount,0xFFFF); //将收到的信息发送出去RxCount = 0;memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组RxFlag = 0;}}/* USER CODE END 3 */
}
最后在末尾/* USER CODE BEGIN 4 */里添加 接收中断回调函数 的重定义。
// 弱声明函数的重定义,接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{UNUSED(huart); // Prevent unused argument(s) compilation warningif(RxCount >= 255) //溢出判断{RxCount = 0;memset(RxBuffer, 0x00, sizeof(RxBuffer));HAL_UART_Transmit(&huart3, (uint8_t *)"数据溢出", 10, 0xFFFF); }else{RxBuffer[RxCount++] = RxData; //接收数据转存 if((RxBuffer[RxCount-1] == 0x0A)&&(RxBuffer[RxCount-2] == 0x0D)) //判断\r\n结束位{RxFlag = 1;
// HAL_UART_Transmit(&huart3, (uint8_t *)&RxBuffer, RxCount,0xFFFF); //将收到的信息发送出去
// while(HAL_UART_GetState(&huart3) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
// RxCount = 0;
// memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组}} HAL_UART_Receive_IT(&huart3, (uint8_t *)&RxData, 1); //再开启接收中断
}
🥽2.2.2 在【usart.h】插入这些
注意,后面几个是用extern修饰,所以不要重复给初始值!
接下来一部分是和 UART 串口功能有关的
#include <stdio.h>
#include <string.h>
#define MAXSIZE 256 //最大接收字节数
extern char RxBuffer[]; //存储接收数据的数组
extern uint8_t RxData; //接收中断缓冲
extern uint8_t RxCount; //接收缓冲计数
extern uint8_t RxFlag; //结束位判断正确标志
🥽2.2.3 在【usart.c】插入这些
在 /* USER CODE BEGIN USART3_Init 2 / 和 / USER CODE END USART3_Init 2 */ 之间插入 开启接收中断 的语句。
void MX_USART3_UART_Init(void)
{/* USER CODE BEGIN USART3_Init 0 *//* USER CODE END USART3_Init 0 *//* USER CODE BEGIN USART3_Init 1 *//* USER CODE END USART3_Init 1 */huart3.Instance = USART3;huart3.Init.BaudRate = 115200;huart3.Init.WordLength = UART_WORDLENGTH_8B;huart3.Init.StopBits = UART_STOPBITS_1;huart3.Init.Parity = UART_PARITY_NONE;huart3.Init.Mode = UART_MODE_TX_RX;huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart3.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart3) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART3_Init 2 */HAL_UART_Receive_IT(&huart3, (uint8_t *)&RxData, 1);//开启接收中断,将接收数据存储到RXDATA/* USER CODE END USART3_Init 2 */}
然后在 /* USER CODE BEGIN 1 / 和 / USER CODE END 1 */ 之间插入重定向printf的两个函数。
/* USER CODE BEGIN 1 */
//函数功能: 重定向c库函数printf到USART3
int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);return ch;
}
//函数功能: 重定向c库函数getchar,scanf到USART3
int fgetc(FILE *f)
{uint8_t ch = 0;HAL_UART_Receive(&huart3, &ch, 1, 0xffff);return ch;
}
/* USER CODE END 1 */
🥽2.2.4 实验测试
🕶 2.3 Test 2:串口3接收中断(1位),根据收到不同内容进行相应处理
先将上一段成功的代码备份,在2.2的基础上进行如下操作。
🥽2.3.1 在【main.c】插入这些
对于UART而言,现在相关的处理是通过中断方式在中断回调函数中实现,因此2.2.1中开头的头文件包含、while循环中的检测数组接收数据动作判断和传输都可以删掉了。
仅需对末尾对于接收中断回调函数的重定义进行调整。
开头声明extern变量,免得因为声明在usart.c中,这里用不了。
/* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
extern uint8_t uart3Rx;
/* USER CODE END PV */
末尾针对自己对功能的设想,修改中断回调函数的重定向。
这里实现的功能:输入0 - LED1+2+3灯全灭;输入1 - LED1亮;输入2 - LED2亮;输入3 - LED3亮;输入4 - LED1灭;输入5 - LED2灭;输入6 - LED3灭;输入7、8、9报错。
注意switch中的判断需要加单引号,如果不用引号括起来,会出错。
/* USER CODE BEGIN 4 */// 弱声明函数的重定义,接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART3) // 如果是串口3{switch (uart3Rx){case '0':printf("Command 0 recieved, ALL LED off.\r\n");HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);break;case '1':printf("Command 1 recieved, LED1 on.\r\n");HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);break;case '2':printf("Command 2 recieved, LED2 on.\r\n");HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);break;case '3':printf("Command 3 recieved, LED3 on.\r\n");HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);break;case '4':printf("Command 4 recieved, LED1 off.\r\n");HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);break;case '5':printf("Command 5 recieved, LED2 off.\r\n");HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);break;case '6':printf("Command 6 recieved, LED3 off.\r\n");HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);break;default://printf("Error! Please input again.\r\n");break;}printf("...\r\n");HAL_UART_Receive_IT(&huart3, &uart3Rx, 1); //再开启接收中断}
}/* USER CODE END 4 */
可以用LED0的持续闪烁指示程序在正常运行,而没有卡在某个中断里。
在主函数的while循环里加上LED0的电平翻转,延时500ms。
/* Infinite loop *//* USER CODE BEGIN WHILE */while (1){HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);HAL_Delay(500);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
🥽2.3.2 在【usart.c】插入这些
开头声明存储接收量的变量uart3Rx,这里因为简化情形,就用一个变量来判断而非一整句话了。
/* USER CODE BEGIN 0 */
uint8_t uart3Rx;
/* USER CODE END 0 */
相对应的,UART3的初始化函数 void MX_USART3_UART_Init(void) 中,开启接收中断的语句也作相应调整,改成这个变量。
void MX_USART3_UART_Init(void)
{/* USER CODE BEGIN USART3_Init 0 *//* USER CODE END USART3_Init 0 *//* USER CODE BEGIN USART3_Init 1 *//* USER CODE END USART3_Init 1 */huart3.Instance = USART3;huart3.Init.BaudRate = 115200;huart3.Init.WordLength = UART_WORDLENGTH_8B;huart3.Init.StopBits = UART_STOPBITS_1;huart3.Init.Parity = UART_PARITY_NONE;huart3.Init.Mode = UART_MODE_TX_RX;huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart3.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart3) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART3_Init 2 *///HAL_UART_Receive_IT(&huart3, (uint8_t *)&RxData, 1);//开启接收中断,将接收数据存储到RxDataHAL_UART_Receive_IT(&huart3, &uart3Rx, 1); // !删掉了上一行,加上了这行!/* USER CODE END USART3_Init 2 */}
末尾关于printf的重定向不变。
/* USER CODE BEGIN 1 */
//函数功能: 重定向c库函数printf到USART3
int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);return ch;
}
//函数功能: 重定向c库函数getchar,scanf到USART3
int fgetc(FILE *f)
{uint8_t ch = 0;HAL_UART_Receive(&huart3, &ch, 1, 0xffff);return ch;
}/* USER CODE END 1 */
🥽2.3.3 在【usart.h】插入这些
因为情况简化了,之前关于存储接收数据的数组的一系列东西都可以省略了。因此在usart.h开头include和声明变量的地方,针对CubeMX生成的版本仅用加上这两个头文件。别的那堆extern都可以删掉啦。
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
🥽2.3.4 实验测试(还存在一点点小问题?)
相对还是比较成功的,初始3个灯全亮,输入0全灭。后续也按照设想完成。
❓来回收发的语句如图,遇到点小问题暂时没能解决,也想求助各位网友:
发出一个数字/字符,会连续进2次接收中断,第一次是相对应的case,第二次是default,不知该怎么改?
👀 3 调试过程中遇到的问题
🕶 3.1 程序如果收到的字符串长度>1可能会漏字
如接收“123456”,但发送出的只有“135”…疑似漏掉了偶数位?
检查后发现,应该是因为在while(1)循环中,遗漏了if(RxFlag == 1)的判断直接加了语句,所以出现这个情况。
🕶 3.2 收发中文时出现乱码
详见:STM32使用keil串口输出中文乱码问题_CSDN博客@水能zai舟。
🕶 3.3 一些零零碎碎的易错点
中断回调函数中别用延时;
switch中的判断需要加单引号,如果不用引号括起来会出错;
新下载程序后LED0不闪,试试按Reset按键或者重新上电,是否程序就正常启动运行了;
…
以上就暂时是本文的全部内容。
这篇关于【STM32】BLDC驱动控制开发笔记 | 01_基于STM32F407的UART发送与接收中断实验,含重定向printf,USART+LED的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!