简要:
最近一直在研究hal库的驱动函数,之前工作,学习都是使用的标准库,来学习、使用STM32的代码,所以借助电子产品世界的平台,记录一下学习STM32的hal库的学习旅程,同时也希望自己的学习经历可以帮助正在学习的32的人们。
几种不同的通讯基础知识:
单工:两种不同的通讯方,只能是单相通讯,即通讯只能从A到B,而不能从B到A,
半双工:两种不同的通讯方,通讯A和通讯B可以相互通讯,但是在同一时刻,只能存在单方向的通讯。例如:RS485的通讯方式。
全双工:两种不同的通讯方,通讯A和通讯B可以相互通讯,但是在同一时刻,可以存在双方方向的通讯。例如:RS232的通讯方式。
一:STM32U083RC-串口中断的介绍:
STM32的串口中断是指当串口收到数据时,cpu会产生串口中断信号信息,通知单片机内部的中断服务函数去处理数据。此时,在串口中断服务程序中,我们可以将数据先放到串口的接收串口缓冲区内,当把所有的数据接收完整时,再去调用串口处理函数。
通俗的说:串口中断服务函数需要使用中断接收功能实现。当有新的数据到达串口时,单片机触发中断请求,从而程序运行到中断服务程序里面。中断服务程序在处理完接收到的数据后,可以根据当时的要求进行处理。采用中断的方式优点是:提高单片机的运行效率,减少了资源浪费。
二:STM32cube软件配置过程:
之前提到的基本配置这里不再重复介绍,只是介绍有关串口的配置过程和注意事项;
在上图中主要配置选中的哪种串口,串口的工作模式、波特率、数据位、停止位 和使用的引脚信息。
配置串口接收中断
配置一下引脚的工作模式是否正常。
最后点击生成代码即可。
三:代码编写。
编写串口发送、接收中断函数:
串口1初始化部分,注意在初始化完成后,需要手动开启串口1的接收中断功能,否则程序进入不了串口的接收中断。
/** * @brief LPUART1 Initialization Function * @param None * @retval None */ static void MX_LPUART1_UART_Init(void) { /* USER CODE BEGIN LPUART1_Init 0 */ /* USER CODE END LPUART1_Init 0 */ /* USER CODE BEGIN LPUART1_Init 1 */ /* USER CODE END LPUART1_Init 1 */ hlpuart1.Instance = LPUART1; hlpuart1.Init.BaudRate = 115200; hlpuart1.Init.WordLength = UART_WORDLENGTH_8B; hlpuart1.Init.StopBits = UART_STOPBITS_1; hlpuart1.Init.Parity = UART_PARITY_NONE; hlpuart1.Init.Mode = UART_MODE_TX_RX; hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; hlpuart1.FifoMode = UART_FIFOMODE_DISABLE; if (HAL_UART_Init(&hlpuart1) != HAL_OK) { Error_Handler(); } if (HAL_UARTEx_SetTxFifoThreshold(&hlpuart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK) { Error_Handler(); } if (HAL_UARTEx_SetRxFifoThreshold(&hlpuart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK) { Error_Handler(); } if (HAL_UARTEx_DisableFifoMode(&hlpuart1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN LPUART1_Init 2 */ HAL_UART_Receive_IT(&hlpuart1,&Rxbuffer[0],1); /* USER CODE END LPUART1_Init 2 */ }
3.1 STM32复用 printf输出函数
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" #include "string.h" #include "stdint.h" const uint8_t OUTPUT_str[] = "Hello STM32U083RC! Hello NUCLEO! autor by congconggege \r\n"; UART_HandleTypeDef hlpuart1; /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /*串口1重定义*/ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&hlpuart1, (uint8_t *)&ch, 1, 0xFFFF); return ch; }
注意一下:复用printf时候,可以切换不同的串口号,根据硬件具体的要求,进行串口的复用。
然后我将代码下载到程序里面,发现串口输出功能不正常,然后进入仿真看一下,发现程序运行延时函数里面,而串口输出、指示灯功能并不正常,如下图所示:
后来想到可能是没有打开,micolib库,导致程序运行不正常,然后打开下图的按键,重新编译下载。
串口输出结果如下:
利用printf输出调试信息功能基本上是调试好了。
3.2串口接收中断调试
需要在软件代码中,找到串口接收回调函数
/** * @brief Rx Transfer completed callback. * @param huart UART handle. * @retval None */ __weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ UNUSED(huart); /* NOTE : This function should not be modified, when the callback is needed, the HAL_UART_RxCpltCallback can be implemented in the user file. */ }
由于是软件生成底层驱动代码,弱定义模式,我们使用的时候只需要将其复制到主函数中进行处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数 { uint8_t temp =0; if (huart ->Instance ==LPUART1) { temp = LPUART1->RDR; Rxbuffer[RecPoint] = temp ; RecPoint ++ ; if( RecPoint == 7) { RecPoint = 0 ; // bPack = 1 ; } /*串口发送正常函数*/ HAL_UART_Transmit(&hlpuart1, (uint8_t *)&temp, 1, 2); ///*利用串口发送中断,导致少发送字节,图片中进行测试*/ // HAL_UART_Transmit_IT(&hlpuart1,&temp,1); HAL_UART_Receive_IT(&hlpuart1, &Rxbuffer[0], 1); //开启接收中断 } } void Sendbuffer(unsigned char * buffer,int length) { int i=0 ; for(i=0 ; i<length ; i++ ) { HAL_UART_Transmit(&hlpuart1, (uint8_t *)&buffer[i], 1, 2); } }
HAL_UART_Transmit函数:是指HAL库中(hardware abstraction layer)中的一个串口发送函数,用来串口的数据发送功能 HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout) 四个参数的含义: UART_HandleTypeDef *huart:指针指向的是UART_HandleTypeDef 的结构体指针,包括串口结构体中的配置信息 const uint8_t *pData :指针指向的是数据发送缓冲区内的数据。 uint16_t Size :本次数据传输的多少个字节数 uint32_t Timeout :超时判断,单位为毫秒,如果在传输操作过程中,发送函数超过此时间值时,该函数会返回错误值。 下面我使用该函数对串口接收过来的数据进行返回。
给大家分享两个不同发送数据的图片
然后我发现使用不同的发送函数会导致接收数据不正常的情况,看了下两种不同的发送函数还是有区别的:
HAL_UART_Transmit为阻塞式发送函数,意思就是说发送数据时候会一直等待数据发送完成后才会返回。
而HAL_UART_Transmit_IT 是非阻塞式的发送函数,即发送数据时候,不会等待数据发送完成,而是立即返回,数据发送完成后会触发中断函数。
因此,如果使用HAL_UART_Transmit发送数据时候,程序会一直阻塞在该函数处,直到数据发送完成后才会继续执行下一条指令,而如果使用HAL_UART_Transmit_IT发送数据,则程序会立即返回,可以指继续执行下一条质量,数据发送完成后会触发中断函数,在中断函数中进行数据发送完成后的处理。
所以在我们使用的串口发送数据,需要根据当前函数操作进行判断使用哪种发送函数,如果数据量不大的情况下,可以使用HAL_UART_Transmit,如果数据量较大的情况下,可以使用串口接收的空闲中断方式,待接收完成所有的数据包后,在进行数据的发送,或者是使用CPU资源,使用DMA的方式进行发送数据。
调试代码:TEST03_USART_rec.zip