小记stm32实现串口接收的四种方法(hal库)

开发环境

  1. STM32CUBMX
  2. 正点原子STM32F407ZGT6探索者开发板
  3. MDK-ARM 5.31

第一种方式:直接接收

  1. 配置外部时钟源小记stm32实现串口接收的四种方法(hal库)_第1张图片
    2.配置时钟树
    小记stm32实现串口接收的四种方法(hal库)_第2张图片
    3.配置串口一
    小记stm32实现串口接收的四种方法(hal库)_第3张图片
    生产代码后进入工程
    重定向printf到串口1(建议在usart.c里重定向)
    重定向代码块
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_USART1_UART_Init();
  printf("hello wrold\r\n");
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      HAL_UART_Receive(&huart1,receive_buff,10,0xff);// 接收10个字符
      if(receive_buff[0] != 0)
      {
          printf("recive buff is %s \r\n",receive_buff);
          memset(receive_buff,0,20); // 清除接收内容
      }    
  }
  /* USER CODE END 3 */
}

实验现象
小记stm32实现串口接收的四种方法(hal库)_第4张图片

第二种方式:中断方式

时钟准备和串口初始化如上一种方式所示,实例从设置中断开始
设置中断
小记stm32实现串口接收的四种方法(hal库)_第5张图片
生成代码

定义相关变量

uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_RxBuff[256] = {0};		//接收缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t	cAlmStr[] = "数据溢出(大于256)\r\n";

在usart.h 中编写回调函数

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_TxCpltCallback could be implemented in the user file
   */
 
	if(Uart1_Rx_Cnt >= 255)  //溢出判断
	{
		Uart1_Rx_Cnt = 0;
		memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
		HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);	
	}
	else
	{
		Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
	
		if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
		{
			Uart1_RxFlag = 1;
		}
	}
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
}

main函数内内容

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_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    if(Uart1_RxFlag != 0)
    {
        printf("recive buff is %s\r\n",Uart1_RxBuff);
        Uart1_RxFlag = 0;
        Uart1_Rx_Cnt = 0;
        memset(Uart1_RxBuff,0x00,256);
    }
    HAL_Delay(10);
  }
  /* USER CODE END 3 */
}

实验现象
小记stm32实现串口接收的四种方法(hal库)_第6张图片

第三种方法:定时器中断判断串口是否传输完成

此方法可以看做是看作中断接收的一种拓展用法,不同于上一种方法介绍的需要特定的格式的数据帧来判定,此方法结合了定时器,以5ms为界限,如果5ms后没有接收新的数据则认为一包数据已经发送完成。下面是配置的具体方法
1.配置时钟和串口中断的同上,然后配置定时器中断
定时器基础配置
小记stm32实现串口接收的四种方法(hal库)_第7张图片
打开定时器中断
小记stm32实现串口接收的四种方法(hal库)_第8张图片
我们设置的5ms检测串口是否发送完成
我们使用的定时器的主频是84MHZ
那么定时器频率可用下列方式计算
F = 84 M H Z ( P r e s c a l e r + 1 ) × ( C o u n t e r P e r i o d + 1 ) ; F = \frac{84MHZ}{(Prescaler+1)\times (CounterPeriod+1)}; F=(Prescaler+1)×(CounterPeriod+1)84MHZ;
配置完成后生成代码
程序部分
定义相关变量

uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_RxBuff[256] = {0};		//接收缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t	cAlmStr[] = "数据溢出(大于256)\r\n";

编写串口中断服务函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart -> Instance == USART1)
    {
        if(Uart1_Rx_Cnt == 0)
        {
            __HAL_TIM_CLEAR_FLAG(&htim6,TIM_FLAG_UPDATE);
            HAL_TIM_Base_Start_IT(&htim6);
            Uart1_RxBuff[Uart1_Rx_Cnt] = aRxBuffer;
            Uart1_Rx_Cnt ++;
            
        }
        else
        {
            Uart1_RxBuff[Uart1_Rx_Cnt ++] = aRxBuffer;
        }
        if(Uart1_Rx_Cnt >= 255)
        {
            Uart1_Rx_Cnt = 0;
            Uart1_RxFlag = 0;
            memset(Uart1_RxBuff,0x00,256);
        }
        HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
    }  
}

编写定时器中断回调函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim ->Instance == TIM6)
    {
        HAL_TIM_Base_Stop(&htim6);
        __HAL_TIM_SetCounter(&htim6,0);
        Uart1_RxFlag = 1;       
    }
}

编写主程序

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_TIM6_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
     if(Uart1_RxFlag == 1)
     {
         printf("rec buff is %s\r\n",Uart1_RxBuff);
         memset(Uart1_RxBuff,0x00,256);
         Uart1_Rx_Cnt = 0;
         Uart1_RxFlag = 0;
     }
     HAL_Delay(10);
  }
  /* USER CODE END 3 */
}

实验现象
小记stm32实现串口接收的四种方法(hal库)_第9张图片

第四种方法:串口空闲中断加DMA

使用DMA可以减少CPU负荷,当接收大批量数据的时候,可以防止频繁进入中断。这样有助于提高效率,下面我们介绍STM32带有的串口空闲中断来配合DMA接收数据的例子。
配置部分
1 串口配置
基本部分
小记stm32实现串口接收的四种方法(hal库)_第10张图片

配置DMA
小记stm32实现串口接收的四种方法(hal库)_第11张图片
中断配置
小记stm32实现串口接收的四种方法(hal库)_第12张图片
生产代码
定义宏定义

#define USART1_DMA_REC_SIZE 600
#define USART1_REC_SIZE 1200

编写结构体

typedef struct
{
        uint8_t UsartRecFlag; // 标志位
        uint16_t UsartRecLen; // 接收数据长度
        uint16_t UsartDMARecLEN; // DMA 接收长度
        uint8_t  Usart1DMARecBuffer[USART1_DMA_REC_SIZE]; // DMA 接收数组
        uint8_t  Usart1RecBuffer[USART1_REC_SIZE]; // 接收组
}teUsart1type;

编写打开中断函数

// 打开相关中断
void EnableUsart_It(void)
{
        __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
        __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);
        HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIZE);
}

改写中断服务函数(在stm32f4xx_it.c中)

void USART1_IRQHandler(void)
{
 
  /* USER CODE BEGIN USART1_IRQn 0 */
	uint16_t temp = 0;
	__HAL_UART_CLEAR_IDLEFLAG(&huart1);
	HAL_UART_DMAStop(&huart1); // 关闭DMA
	temp = huart1.Instance -> SR; // 清除SR状态寄存器
	temp = huart1.Instance -> DR; // 清除DR数据寄存器,用来清除中断
	temp = hdma_usart1_rx.Instance -> NDTR; // 获取未传输的数据个数
	//temp = hdma_usart2_rx.Instance -> NDTR; // F4
	Usart1type.UsartDMARecLEN = USART1_DMA_REC_SIZE - temp;
	HAL_UART_RxCpltCallback(&huart1);
  /* USER CODE END USART1_IRQn 0 */
    HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	HAL_UART_Receive_DMA(&huart1,Usart1type.Usart1DMARecBuffer,USART1_DMA_REC_SIZE);
  /* USER CODE END USART1_IRQn 1 */
}

编写中断回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
        if(huart -> Instance == USART1)
        {
            if(Usart1type.UsartRecLen > 0)
            {
                memcpy(&Usart1type.Usart1RecBuffer[Usart1type.UsartRecLen],Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLEN);
                Usart1type.UsartRecLen += Usart1type.UsartDMARecLEN;
            }
            else
            {
                memcpy(&Usart1type.Usart1RecBuffer,Usart1type.Usart1DMARecBuffer,Usart1type.UsartDMARecLEN);
                Usart1type.UsartRecLen += Usart1type.UsartDMARecLEN;
            }
            memset(Usart1type.Usart1DMARecBuffer,0x00,600);
            Usart1type.UsartRecFlag = 1;
        }
}

编写主程序

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_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  EnableUsart_It();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      if(Usart1type.UsartRecFlag == 1)
      {
          printf("rec buff is %s\r\n",Usart1type.Usart1RecBuffer);
          memset(Usart1type.Usart1RecBuffer,0x00,USART1_REC_SIZE);
          Usart1type.UsartRecLen = 0;
          Usart1type.UsartRecFlag = 0;         
      }
      HAL_Delay(20);
  }
  /* USER CODE END 3 */
}

实验现象
小记stm32实现串口接收的四种方法(hal库)_第13张图片
END
以上工程已上传Github,需要的自取,链接在下面
https://github.com/zzq0516/usart

你可能感兴趣的:(STM32小记,嵌入式,stm32,串口通信,单片机)