赞
踩
目录
1、串口初始化函数MX_USART2_UART_Init()
(1)MX_USART2_UART_Init()串口参数初始化函数
(2)HAL_UART_MspInit()串口功能模块初始化函数
5、启动串口接收函数HAL_UART_Receive_IT()
本文旨在以实例说明STM32单片机通过串口接收数据的实现过程。本文中使用ST的开发板NUCLEO-G474RE,板上MCU型号为STM32G474RET6。配套的扩展板为作者根据说明书自己设计制造,链接:
实例运行后,通过串口通讯助手发送0x10时开发板上的LD2灯亮,其它数据灯灭。
配置GPIO→配置时钟源和DEBUG→配置外部中断→配置串口→配置串口中断→配置系统时钟→build生成。
将PA4、PA5配置为输出GPIO_Output,PP,Pull up;PC13配置为中断模式GPIO_EXTI13,上升沿触发,用于检测按键B1的状态;PA2、PA3分别配置为串口USART2_TX和USART2_RX;外部时钟;Debug设置为Serial Wire;USART2的基本参数,波特率115200,数据长度8bit,无校验,停止位1;PA4别名BUZ,PA5别名LED,PC13别名KEY;串口中断抢占优先级1,外部中断抢占优先级4;
硬件配置完毕后,启动代码生成,IDE自动将配置好的硬件信息转换成代码。
自动生成的代码有些需要了解(比如初始化函数),而有些是需要修改的(比如while(1)循环里需要增加的代码,一些注释对里需要增加的代码),还有一些需要重写(比如,重写弱函数)。
MX_USART2_UART_Init()函数主要完成对USART2的模式和参数配置,如波特率、数据位、停止位等。因为串口模块要比GPIO复杂,所以配置参数也更多。
- /**
- * @brief USART2 Initialization Function
- * @param None
- * @retval None
- */
- static void MX_USART2_UART_Init(void)
- {
-
- /* USER CODE BEGIN USART2_Init 0 */
-
- /* USER CODE END USART2_Init 0 */
-
- /* USER CODE BEGIN USART2_Init 1 */
-
- /* USER CODE END USART2_Init 1 */
- huart2.Instance = USART2;
- huart2.Init.BaudRate = 115200;
- huart2.Init.WordLength = UART_WORDLENGTH_8B;
- huart2.Init.StopBits = UART_STOPBITS_1;
- huart2.Init.Parity = UART_PARITY_NONE;
- huart2.Init.Mode = UART_MODE_TX_RX;
- huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- huart2.Init.OverSampling = UART_OVERSAMPLING_16;
- huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
- huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
- huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
- if (HAL_UART_Init(&huart2) != HAL_OK)
- {
- Error_Handler();
- }
- if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
- {
- Error_Handler();
- }
- if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
- {
- Error_Handler();
- }
- if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK)
- {
- Error_Handler();
- }
MX_USART2_UART_Init()自动生成于main.c中;
该函数内部出现的HAL_UART_Init(UART_HandleTypeDef *huart)在stm32g4xx_hal_uart.c中。
对于串口来说,针对引脚等参数的配置,是在文件stm32g4xx_hal_msp.c中。msp是MCU support package的缩写,指的是MCU相关的支持包。关于串口有3个支持包:
- HAL_MspInit(void)
- HAL_UART_MspInit(UART_HandleTypeDef *huart)
- HAL_UART_MspDeInit(UART_HandleTypeDef *huart)
由函数名可见,其中都带有MspInit字样。这类函数的作用是进行MCU功能模块(譬如串口、定时器、ADC等)的初始化。在固件库中,通常是采用这种方式将MCU的模块初始化代码集中起来,以方便代码在不同型号的MCU上移植。
上述函数中,第一个是初始化全局Msp。后面两个函数的参数完全一样,函数名也很类似;区别是后一个函数名中多了两个字母“De”,是Default的缩写。
HAL_UART_MspInit()函数可以对串口硬件初始化、配置引脚模式以及设置中断优先级并使能中断,与对GPIO进行初始化的MX_GPIO_Init()函数所完成的功类似。
HAL_UART_MspDeInit()函数可以把串口复位成初始值,关闭串口并关闭串口中断。
HAL_UART_MspInit()函数是由函数HAL_UART_Init()(在stm32g4xx_hal_uart.c文件中定义)调用的。而HAL_UART_Init()是由MX_USART2_UART_Init()函数调用的(在if语句的条件表达式中调用)。
由于配置了串口的中断功能,所以当中断发生后就会调用相应的中断服务函数来完成一定的任务。
- void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
- {
- ... ...
- /*if no error occurs */
- errorflags = (isrflags & (uint32_t)(USART_ISR_PE|USART_ISR_FE|USART_ISR_ORE|USART_ISR_NE));
- if(errorflags == 0U)
- {
- /*------UART in mode Receiver-------*/
- if(((isrflags & USART_ISR_RXNE_RXFNE) != 0U) && (((crlits & USART_CR1_RXNEIE_RXFNEIE) != 0U)||((cr3its & USART_CR3_RXFTIE) != 0U)))
- {
- if(huart->RxISR != NULL)
- {
- huart->RxISR(huart);
- }
- return;
- }
- }
- ... ...
- }
当程序执行到huart →RxISR(huart)时,会调用UART_RxISR_8BIT()函数(如果配置数据字长为7位或8位,则调用此函数;如果数据字长为9位,则会调用另一函数UART_RxISR_16BIT),并且在该函数中会调用回调函数HAL_UART_RxCpltCallback()。这个回调函数是在stm32g4xx_hal_ uart.c中定义的弱函数。用户需要重写该函数,可以写在main.c中。
在使用中断之前,还要用到函数HAL_UART_Receive_IT()。该函数的格式如下:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size)
该函数是给将要接收的数据定义一个缓冲区pData,并指定接收数据的长度为Size(也就是要接收的字节数)。这个Size决定了调用回调函数的频率。如果Size大于1,则不会每次中断都调用回调函数,而是到Size次之后,才会再调用一次回调函数。此外,这个函数还有开启接收中断的功能,所以需要在main函数的初始化代码中调用一次HAL_UART_Receive_IT()函数。这样就可以确保开启接收中断。在执行一次回调函数时,接收中断会关闭,所以还需要再次开启接收中断。这个再次开启中断的动作,也可以在回调函数中通过调用HAL_UART_Receive_IT()函数来实现。
完成上面的硬件配置,并自动生成代码后,然后在main.c中的初始化部分调用HAL_UART_ Receive _IT()函数设置参数并开启接收中断,然后写回调函HAL_UART_RxCpltCallback(),以便对接收的数据进行处理。删除了一些注释对。
- #include "main.h"
- UART_HandleTypeDef huart2;
- /* USER CODE BEGIN PV */
- /* USER CODE END PV */
- void SystemClock_Config(void);
- static void MX_GPIO_Init(void);
- static void MX_USART2_UART_Init(void);
- int main(void)
- {
- HAL_Init();
- SystemClock_Config();
- MX_GPIO_Init();
- MX_USART2_UART_Init();
- /* USER CODE BEGIN 2 */
- /* USERCODE END 2 */
- while(1)
- {
- }
- }
main函数中出现了一个串口初始化函数MX_USART2_UART_Init()。此外,在main.c中,首先定义了一个全局变量huart2,类型为UART_HandleTypeDef。huart2是一个结构体变量,通常也称为串口句柄。这个结构体是关于UART的,它的成员有很多,有的成员本身也是结构体类型。这个结构体有些复杂。在串口初始化函数MX_USART2_UART_Init中,使用了huart2变量。
- static void MX_USART2_UART_Init(void)
- {
- huart2.Instance = USART2;
- huart2.Init.BaudRate = 115200;
- huart2.Init.WordLength = UART_WORDLENGTH_8B;
- huart2.Init.StopBits = UART_STOPBITS_1;
- huart2.Init.Parity = UART_PARITY_NONE;
- huart2.Init.Mode = UART_MODE_TX_RX;
- huart2.Init.HwFlowCtl =UART_HWCONTROL_NONE;
- ... ...
- }
由此可见,在MX_USART2_UART_Init函数中,第一句huart2.Instance =USART2,就将前面配置的USART2与结构体变量huart2关联了起来。
要实现串口接收中断,需要在主程序的初始化代码中调用HAL_UART_Receive_IT()函数。该函数的结构如下:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size)
该函数有三个参数,第一个参数的类型就是UART_HandleTypeDef,所以要将该参数与USART2关联起来。因此,HAL_UART_Receive_IT()函数的调用要可将该函数放到MX_USART2 _UART_Init()函数之后的注释对中。
HAL_UART_Receive_IT()函数的第二个参数是设置接收数据的缓冲区,可以定义一个长度为RXBUFFERSIZE的数组RxBuffer [RXBUFFERSIZE],当然这个数组以及RXBUFFERSIZE都需要另外定义(后面会将它们定义为全局变量)。
HAL_UART_Receive_IT()函数的第三个参数用于指定接收数据的长度,这个数据长度可以与接收缓冲区的长度相同,即RXBUFFERSIZE。
将RxBuffer[RXBUFFERSIZE]定义为全局变量(需要放到注释对中),并将对HAL_UART_ Receive_IT()函数的调用放置到MX_USART2_UART_Init()语句之后的注释对/* USER CODE BEGIN 2 */与/* USER CODE END 2 */中。
- #include "main.h"
- UART_HandleTypeDef huart2;
- /* USER CODE BEGIN PV */
- /* 直接使用了变量RXBUFFERSIZE */
- uint8_t RxBuffer[RXBUFFERSIZE];
- /* USER CODE END PV */
- /*Private function prototypes */
- void SystemClock_Config(void);
- static void MX_GPIO_Init(void);
- static void MX_USART2_UART_Init(void);
- int main(void)
- {
- HAL_Init();
- SystemClock_Config();
- MX_GPIO_Init();
- MX_USART2_UART_Init();
- /* USER CODE BEGIN2 */
- HAL_UART_Receive_IT(&huart2,(uint8_t*)RxBuffer,RXBUFFERSIZE);
- /* USER CODE END2 */
- while(1)
- {
- }
- }
上面直接使用了变量RXBUFFERSIZE。对该变量的定义可以放到main.h头文件中,可以用define宏(也需放置到注释对中):
- /* USER CODE BEGIN Private defines */
- #define RXBUFFERSIZE 1 //接收缓冲区的长度
- /* USER CODE END Private defines */
将RXBUFFERSIZE定义为1,也就是1字节。
重定义串口中断接收的回调函数HAL_UART_RxCpltCallback()。这个函数已经在stm32g4xx_ hal_uart.c中有定义,只不过被定义为弱函数,实际就是一个空函数。需要重写它。与写EXTI的回调函数类似,也将该函数写在main.c中。
串口有数据送来,会执行中断服务函数USART2_IRQHandler(),然后该函数又会调用函数HAL_UART_IRQHandler()。调用一定次数的HAL_UART_IRQHandler()函数后,就会自动执行回调函数HAL_UART_RxCpltCallback()。这里的“一定次数”是由HAL_UART_Receive_IT()函数的第三个参数决定的,也就是前面在主程序中用到的常量RXBUFFERSIZE。由于把RXBUFFERSIZE定义为1,所以串口收到1字节的数据后,会调用一次回调函数HAL_UART_RxCpltCallback()。当调用回调函数之时,1字节的数据已经放到了接收缓冲区中,也就是放到前面定义的数组RxBuffer中。
调用HAL_UART_Receive_IT()函数,不但实现了定义缓冲区并设置接收数据长度的功能,而且还有开启串口中断接收的功能。因此,在接收完指定长度的数据之后,需要重新开启接收中断的功能,否则后面就不会再进入中断了。可以在回调函数HAL_UART_RxCpltCallback()中调用一下HAL_UART_Receive_IT()函数,重新开启接收中断。对该函数的调用,可以连同EXTI的回调函数HAL GPIO_EXTI_Callback()一起写在main.c后面的注释对中:
- /*USER CODE BEGIN 4 */
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
- {
- HAL_UART_Receive_IT(&huart2,(uint8_t *)RxBuffer,RXBUFFERSIZE);
- }
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
- {
- HAL_GPIO_WritePin(BUZ_GPIO_Port,BUZ_Pin,GPIO_PIN_RESET);
- HAL_Delay(100); //延时
- HAL_GPIO_WritePin(BUZ_GPIO_Port,BUZ_Pin,GPIO_PIN_SET);
- }
- /*USER CODE END 4 */
在EXTI的回调函数中使用了中断的方式实现:当按键按下时,让蜂鸣器响一声。
根据串口送来的数据,控制发光二极管的亮灭。当接收到的数据为0x10(十六进制)时,点亮LD2;当接收到的数据不是0x10时,熄灭LD2。
- /*USER CODE BEGIN WHILE */
- while(1)
- {
- /*USER CODE BEGIN 3 */
- if(RxBuffer[0] == 0x10)
- HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);
- else
- HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);
- }
- /*USER CODE END 3 */
好用的 Win10 串口调试助手 + 网口调试 - 知乎 https://zhuanlan.zhihu.com/p/109941792?eqid=a328954a0002e745000000066477efb6&utm_id=0
开启串口通讯,发送0x10,开大坂上的LD2亮,发送其它内容,比如0x20,LD2灭。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。