STM32串口通信的三种模式(HAL库)

一,轮询模式

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>&copy; 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中断接收函数同时关闭过半中断,这样可以避免多次触发中断。

以上就是串口基本常用用法了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值