一,轮询模式
1.如何配置CubeMX
先打开connectivity,选中USART2,模式选择Asynchronous(异步通信),其他的都为默认
再把PA2,PA3分别设置为TX,RX
要注意的是波特率的选择,我们选择的是115200Bits/s
需要设置的就为这三个地方,其他的都为默认即可
2.需要用到的关键函数
/**
* @brief Sends an amount of data in blocking mode.
* @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
* the sent data is handled as a set of u16. In this case, Size must indicate the number
* of u16 provided through pData.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @param pData Pointer to data buffer (u8 or u16 data elements).
* @param Size Amount of data elements (u8 or u16) to be sent
* @param Timeout Timeout duration
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_UART_Transmit是用来发送数据的函数,第一个参数是操作串口的指针,第二个是要发送数据的数组的指针,第三个是发送数据的长度·,第四个是超时时间(就是说如果还没有发送完成,就停止发送)
/**
* @brief Receives an amount of data in blocking mode.
* @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
* the received data is handled as a set of u16. In this case, Size must indicate the number
* of u16 available through pData.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @param pData Pointer to data buffer (u8 or u16 data elements).
* @param Size Amount of data elements (u8 or u16) to be received.
* @param Timeout Timeout duration
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
HAL_UART_Receive是用来接收数据的函数,第一个参数是操作串口的指针,第二个是用来接受数据的数组的指针,第三个就是接受数据的长度,第四个接受超时时间。
二,串口中断模式
1.CubeMX的配置
在上面轮询模式的配置的基础上,继续配置NVIC,勾选USART2 glob interrupt即可(开启USART2中断功能)
2.需要用到的关键函数
这里用到的函数与轮询模式的非常相同
/**
* @brief Sends an amount of data in non blocking mode.
* @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
* the sent data is handled as a set of u16. In this case, Size must indicate the number
* of u16 provided through pData.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @param pData Pointer to data buffer (u8 or u16 data elements).
* @param Size Amount of data elements (u8 or u16) to be sent
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)
/**
* @brief Receives an amount of data in non blocking mode.
* @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
* the received data is handled as a set of u16. In this case, Size must indicate the number
* of u16 available through pData.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @param pData Pointer to data buffer (u8 or u16 data elements).
* @param Size Amount of data elements (u8 or u16) to be received.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
看注释可以知道,里面的参数跟轮询模式的一模一样,唯一的不同就是函数名后面多了一个_IT
还有一个就是USART2对应的中断处理函数了,这里是弱定义,只需要把这个函数放到主文件中就可以了
/**
* @brief Rx Transfer completed callbacks.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @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 could be implemented in the user file
*/
}
3.主文件中代码具体实现
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2024 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
uint8_t reveivedata[2];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Transmit_IT(&huart2,reveivedata,2);
GPIO_PinState state=GPIO_PIN_SET;
if(reveivedata[1]=='0')
{
state=GPIO_PIN_RESET;
}
if(reveivedata[0]=='R')
{
HAL_GPIO_WritePin(LED_R_GPIO_Port,LED_R_Pin,state);
}
else if(reveivedata[0]=='B')
{HAL_GPIO_WritePin(LED_B_GPIO_Port,LED_B_Pin,state);}
else if(reveivedata[0]=='H')
{HAL_GPIO_WritePin(LED_H_GPIO_Port,LED_H_Pin,state);}
HAL_UART_Receive_IT(&huart2,reveivedata,2);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
//char massage[]="hello world";
HAL_UART_Receive_IT(&huart2,reveivedata,2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//假设我们要在while里面写很多代码
//HAL_UART_Transmit(&huart2,(uint8_t *)massage,strlen(massage),100);
//HAL_Delay(1000);
//HAL_UART_Receive(&huart2,reveivedata,2,HAL_MAX_DELAY);
//HAL_UART_Transmit(&huart2,reveivedata,2,100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
这里可以看到我把HAL_UART_Receive_IT(&huart2,reveivedata,2);放在了while外面,这是因为这个接受函数不会堵塞cpu的运行,一旦执行,就会cpu无需一直等待数据接收,而是cpu可以继续执行while里的代码,当接收到数据之后,触发中断,直接跳到HAL_UART_RxCpltCallback里执行里面的操作。如果我们把HAL_UART_Receive_IT放在了while里边,那么执行了第一次后,还没有接收到数据就又执行第二次该函数,就会触发多次中断,因此我们的处理方法是放在while外面,并且在HAL_UART_RxCpltCallback中的最后再执行一次HAL_UART_Receive_IT,这样就可以没有bug的接收数据并且触发中断了。
三,DMA模式
1.什么是DMA
DMA全称是 direct memory access 直接内存访问,只要创建一条DMA通道,从哪里传输到哪里,DMA就会在合适的时机帮我们进行内存搬运,从源地址搬到目标地址。
2.CubeMX中的配置
在USART2界面中找到DMA settings,再点击Add,这样就可以添加通道了。
3.需要用到的函数和中断
/**
* @brief Receive an amount of data in DMA mode till either the expected number of data is received or an IDLE event occurs.
* @note Reception is initiated by this function call. Further progress of reception is achieved thanks
* to DMA services, transferring automatically received data elements in user reception buffer and
* calling registered callbacks at half/end of reception. UART IDLE events are also used to consider
* reception phase as ended. In all cases, callback execution will indicate number of received data elements.
* @note When the UART parity is enabled (PCE = 1), the received data contain
* the parity bit (MSB position).
* @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M = 01),
* the received data is handled as a set of uint16_t. In this case, Size must indicate the number
* of uint16_t available through pData.
* @param huart UART handle.
* @param pData Pointer to data buffer (uint8_t or uint16_t data elements).
* @param Size Amount of data elements (uint8_t or uint16_t) to be received.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
HAL_UARTEx_ReceiveToIdle_DMA这里是用来接受不定长数据的DMA接收函数,用到的中断不是之前的串口中断,而是串口空闲中断(ldle),于是用到的回调函数也不同,不是之前的HAL_UART_RxCpltCallback
而是现在的HAL_UARTEx_RxEventCallback
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
另外,由于发送的是不定长数据,当数据长度比较长的时候,可能会触发串口过半中断,这是我们虽然这个过半中断会在某些时候起作用,但是我们现在不想要它触发,所以我们得使用关闭中断的函数__HAL_DMA_DISABLE_IT
4.主函数中代码具体演示
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart==&huart2){
HAL_UARTEx_ReceiveToIdle_DMA(&huart2,reveivedata,Size);
__HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_DMA_Init();
/* USER CODE BEGIN 2 */
//char massage[]="hello world";
//HAL_UART_Receive_DMA(&huart2,reveivedata,2);
HAL_UARTEx_ReceiveToIdle_DMA(&huart2,reveivedata,sizeof(reveivedata));
__HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//假设我们要在while里面写很多代码
//HAL_UART_Transmit(&huart2,(uint8_t *)massage,strlen(massage),100);
//HAL_Delay(1000);
//HAL_UART_Receive(&huart2,reveivedata,2,HAL_MAX_DELAY);
//HAL_UART_Transmit(&huart2,reveivedata,2,100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
在回调函数中,有一个习惯就是先确认触发中断的串口是不是需要的那个串口
if(huart==&huart2)
跟串口中断模式类似,需要现在while外开启DMA中断接收函数同时关闭过半中断,这样可以避免多次触发中断。
以上就是串口基本常用用法了。