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

开发环境

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

第一种方式:直接接收

  1. 配置外部时钟源配置时钟源
    2.配置时钟树
    配置时钟树
    3.配置串口一
    串口1
    生产代码后进入工程
    重定向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 */
}

实验现象
实验现象

第二种方式:中断方式

时钟准备和串口初始化如上一种方式所示,实例从设置中断开始
设置中断
打开中断
生成代码

定义相关变量

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 */
}

实验现象
在这里插入图片描述

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

此方法可以看做是看作中断接收的一种拓展用法,不同于上一种方法介绍的需要特定的格式的数据帧来判定,此方法结合了定时器,以5ms为界限,如果5ms后没有接收新的数据则认为一包数据已经发送完成。下面是配置的具体方法
1.配置时钟和串口中断的同上,然后配置定时器中断
定时器基础配置
在这里插入图片描述
打开定时器中断
在这里插入图片描述
我们设置的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 */
}

实验现象

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

使用DMA可以减少CPU负荷,当接收大批量数据的时候,可以防止频繁进入中断。这样有助于提高效率,下面我们介绍STM32带有的串口空闲中断来配合DMA接收数据的例子。
配置部分
1 串口配置
基本部分
在这里插入图片描述

配置DMA
在这里插入图片描述
中断配置
在这里插入图片描述
生产代码
定义宏定义

#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 */
}

实验现象
在这里插入图片描述
END
以上工程已上传Github,需要的自取,链接在下面
https://github.com/zzq0516/usart

  • 46
    点赞
  • 300
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
当然可以!这里是一个使用STM32HAL库进行串口通信的简单实例: 首先,需要在CubeMX中配置串口。打开CubeMX并选择你的微控制器型号,然后点击"Peripherals"选项卡,在左侧的"USART"菜单下选择一个可用的串口。根据你的需求,配置串口的参数,例如波特率、数据位、停止位等。最后点击"Pinout & Configuration"选项卡,将串口的引脚分配给正确的GPIO引脚。 生成代码后,在你的工程中打开`main.c`文件,并添加以下代码: ```c #include "stm32fxxx_hal.h" UART_HandleTypeDef huart2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART2_UART_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); uint8_t txData[] = "Hello, world!\r\n"; uint8_t rxData[20]; while (1) { HAL_UART_Transmit(&huart2, txData, sizeof(txData), HAL_MAX_DELAY); HAL_UART_Receive(&huart2, rxData, sizeof(rxData), HAL_MAX_DELAY); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { while(1); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { while(1); } HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); } static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { while(1); } } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } ``` 这段代码初始化了系统时钟、GPIO和USART2串口。在主循环中,我们通过串口发送"Hello, world!\r\n",然后通过串口接收数据。 请注意,在上述代码中,我们使用了USART2串口和GPIOA的引脚2和3。如果你选择了不同的串口和引脚,请相应地修改代码。 希望这个例子能帮助到你!如有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值