HAL库学习笔记:三、串口通讯之USART(轮询、中断、DMA)

一、USART功能框图

USART功能框图

  • USART简介
    USART数据寄存器(USART_DR)由两个寄存器组成,一个只用于发送数据的可写寄存器TDR,一个只用于接受数据的可读寄存器RDR。当进行发送操作时,写入USART_DR的数据会自动存储到TDR中;当进行读取操作时,USART_DR 会自动获取RDR中的数据。
  • 几个发送数据比较重要的标志位
名称 描述
TE 发送使能
TXE 发送寄存器为空,发送单字节的时候使用
TC 发送完成,发送多个字节数据的时候使用
TXIE 发送完成中断使能
  • 几个接收数据比较重要的标志位
名称 描述
RE 接收使能
RXNE 读数据寄存器非空
RXNEIE 发送完成中断使能
  • USART的三种传输数据方式
    轮询、中断、DMA

二、编程要求

  • 将串口指向USART1,重定义printf函数;
  • 串口配置设置波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1;
  • 数据发送到串口助手,传输间隔为一秒;

三、使用STM32CubeMX生成初始化函数体

  • 打开软件
    打开软件
  • 选择芯片
    选择芯片
  • 配置Debug方式
    配置Debug方式
  • 配置RCC的高速时钟HSE为陶瓷晶振
    RCC时钟选择陶瓷晶振
  • 配置如图所示时钟树
    时钟树配置
  • 打开串口(USART1)
    配置USART1
  • 配置参数
    设置参数
  • 生成代码
    生成代码

四、使用Keil5编写实现函数

  • 系统时钟初始化函数(由STM32CubeMX生成
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}
  • USART1初始化函数(由STM32CubeMX生成
#include "usart.h"

UART_HandleTypeDef huart1;

/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();
  
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    GPIO_InitStruct.Pin = TXD1_Pin|RXD1_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();
  
    /**USART1 GPIO Configuration    
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX 
    */
    HAL_GPIO_DeInit(GPIOA, TXD1_Pin|RXD1_Pin);

  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
} 
  • 重定向串口(添加一个int fputc(int ch, FILE *f)函数
    记得添加标准输入输出头文件 #include < stdio.h >,否则会报错 identifier “FILE” is undefined
//重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
	/* 发送一个字节数据到串口DEBUG_USART */
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);	
	
	return (ch);
}
  • 在主函数中添加实现函数(用户添加
 while (1)
  {
    /* USER CODE END WHILE */
    printf("Ich studiere Deutsch...\r\n");
	HAL_Delay(1000);
    /* USER CODE BEGIN 3 */
  }

五、实验现象

串口调试助手
上面用到的轮询的模式。CPU不断查询IO设备,如设备有请求则加以处理。例如CPU不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低。

六、改进用中断方式收发数据

  • 双击.ioc文件,打开工程,修改配置参数
    双击
    勾选
  • 生成代码
    生成
  • 在主文件中定义接收中断缓存区
/* USER CODE BEGIN Includes */
uint8_t aRxBuffer[20];
/* USER CODE END Includes */
  • 在中断文件中引用接收缓冲数组(extern
/* USER CODE BEGIN TD */
extern uint8_t aRxBuffer[20];
/* USER CODE END TD */
  • mainwhile之间添加中断接收使能函数
 /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1,aRxBuffer,10);
  /* USER CODE END 2 */
  • 定义接受完成回调函数
/* USER CODE BEGIN 4 */
/**
  * @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
  */
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
   */
	HAL_UART_Transmit(&huart1,aRxBuffer,10,1000);
}

/* USER CODE END 4 */
  • 中断函数中添加中断接受使能函数,重新使能接受中断
/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
  HAL_UART_Receive_IT(&huart1,aRxBuffer,10);
  /* USER CODE END USART1_IRQn 1 */
}
  • 实验现象
    现象
    说明:由 HAL_UART_Receive_IT(&huart1,aRxBuffer,10) 可以知道,当接收10个数据后,触发接收中断,当接收的数据不足十个时,下次接收的数据自动补足上次空缺的数据,满十个触发中断,调用接收完成回调函数,现象如下(发送两次数据才触发中断),第二次接收自动补足第一次数据,满十个再发送。
    现象

七、改进用DMA方式收发数据

  • 双击.ioc文件,打开工程,修改配置参数
    双击
  • 配置DMA2,模式为单次传输,数据宽度为字节,外设地址不递增,存储器地址递增
    配置
  • 生成代码
    生成
  • 在主文件中定义接收中断缓存区
/* USER CODE BEGIN Includes */
uint8_t aRxBuffer[20];
/* USER CODE END Includes */
  • 在中断文件中引用接收缓冲数组(extern
/* USER CODE BEGIN TD */
extern uint8_t aRxBuffer[20];
/* USER CODE END TD */
  • mainwhile之间添加DMA接收使能函数
 /* USER CODE BEGIN 2 */
  HAL_UART_Receive_DMA(&huart1,aRxBuffer,10);
  /* USER CODE END 2 */
  • 定义接受完成回调函数
/* USER CODE BEGIN 4 */
/**
  * @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
  */
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
   */
	HAL_UART_Transmit_DMA(&huart1,aRxBuffer,10);
}

/* USER CODE END 4 */
  • 中断函数中添加中断接受使能函数,重新使能接受中断
/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
  HAL_UART_Receive_DMA(&huart1,aRxBuffer,10);
  /* USER CODE END USART1_IRQn 1 */
}
  • 实验现象
    现象
    说明:由 HAL_UART_Receive_DMA(&huart1,aRxBuffer,10) 可以知道,当接收10个数据后,触发接收中断,当接收的数据不足十个时,下次接收的数据自动补足上次空缺的数据,满十个触发中断,调用接收完成回调函数,现象如下(发送两次数据才触发中断),第二次接收自动补足第一次数据,满十个再发送。
    现象
    蓝色为发送,红色为接收。
    –END–
发布了4 篇原创文章 · 获赞 27 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42810361/article/details/94763089