×
单片机 > 单片机程序设计 > 详情

HAL库教程6:串口数据接收

发布时间:2020-06-18 发布时间:
|

STM32的串口接收机制

  与阻塞式发送函数HAL_UART_Transmit配套,有个阻塞式的接收函数,HAL_UART_Receive,但此函数不常用,串口接收通常使用中断函数HAL_UART_Receive_IT。HAL库的串口中断比较复杂,主要流程如下:

  USART1_IRQHandler:由硬件调用,不是HAL库函数,寄存器编程或固件库编程也需要调用此函数;

  HAL_UART_IRQHandler:通过中断类型(发送中断还是接收中断)来判断调用哪个函数;

  UART_Receive_IT:此函数可以指定,每收到若干个数据,调用一次回调函数;这是因为,每收到一个字节,都会把此函数的接收计数器-1,如果接收计数器为零,调用串口接收回调函数HAL_UART_RxCpltCallback(实际上HAL库一共提供了5个回调函数,只有这个函数在接收完成时调用)。

  HAL_UART_RxCpltCallback:弱函数,用户可以在此函数中编写业务逻辑。清除中断标记,是中断处理函数一定要做的事情,但是对于用户函数,把这个操作给隐藏了


使能串口接收中断

  由于串口不方便传参数,所以我通常会定义一些用于串口通信的全局变量。也可以模仿库函数,把这些变量打包成一个结构体。


//UART.c

unsigned char UART1_Rx_Buf[MAX_REC_LENGTH] = {0}; //USART1存储接收数据

unsigned char UART1_Rx_flg = 0;                   //USART1接收完成标志

unsigned int  UART1_Rx_cnt = 0;                   //USART1接受数据计数器

unsigned char UART1_temp[REC_LENGTH] = {0};       //USART1接收数据缓存


  由于这些变量也要在main.c文件中使用,跨文件使用,可以在头文件中做外部声明:


#ifndef __UART_H

#define __UART_H


#ifdef __cplusplus

extern "C" {

#endif

  

#define REC_LENGTH  1

#define MAX_REC_LENGTH  1024 

  

extern unsigned char UART1_Rx_Buf[MAX_REC_LENGTH];

extern unsigned char UART1_Rx_flg ;

extern unsigned int  UART1_Rx_cnt ;

extern unsigned char UART1_temp[REC_LENGTH];

    

#ifdef __cplusplus

}

#endif


#endif 


  要使用中断来接收串口数据,则必须开启中断。并且,每次处理完串口接收中断以后,会自动关闭中断,如果想循环接收数据,则必须在处理完中断以后,再次开启中断。

  我们希望完成初始化以后就开始接收串口数据,所以要修改串口初始化函数。


//main.c

static void MX_USART1_UART_Init(void)

{

  /* USER CODE BEGIN USART1_Init 2 */

  HAL_UART_Receive_IT(&huart1,(uint8_t *)UART1_temp,REC_LENGTH);

  /* USER CODE END USART1_Init 2 */

}


串口接收中断函数处理

  程序的逻辑:

  如果接收到了指定数量的串口数据(在本例中,指定的数量是1字节),则会执行回调函数HAL_UART_RxCpltCallback。此函数是个弱函数,用户可以根据业务逻辑来“重载”。我们要在此函数中,把串口收到的数据打包,并判断结束符判断数据结束。我们规定,只发送ASCII码,并以0x0a作为结束符。


//UART.c

/**

  * @brief 串口中断回调函数

  * @param 调用回调函数的串口

  * @note  串口每次收到数据以后都会关闭中断,如需重复使用,必须再次开启

  * @retval None

  */  

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

  if(huart->Instance==USART1)

  {

    UART1_Rx_Buf[UART1_Rx_cnt] = UART1_temp[0];

    UART1_Rx_cnt++;

    if(0x0a == UART1_temp[0])

    {

      UART1_Rx_flg = 1;

    }

    HAL_UART_Receive_IT(&huart1,(uint8_t *)UART1_temp,REC_LENGTH);

  }

}

  主函数中,实现“串口应声虫”的功能,收到什么就发送什么。如果串口数据接收完成,则发送出去然后把数组,计数器,标志都恢复初始状态。


//main() while(1)

    if(UART1_Rx_flg)

    {

      HAL_UART_Transmit(&huart1,UART1_Rx_Buf,UART1_Rx_cnt,0x10);    //发送接收到的数据

      for(int i = 0;i

        UART1_Rx_Buf[i] = 0;

      UART1_Rx_cnt = 0;

      UART1_Rx_flg = 0;

    }   



  现象:向串口发送ASCII码,单片机收到什么数据,就返回什么数据。注意,发送给串口的数据结尾要有回车键。


数据阶段的方法与ASCII码

  一组数据怎么判断是否结束?

  2种方法:

  1特定时间,特定的时间内没有收到新的数据,认为这一组数据就结束了。这种方法在定时器的章节来实现。

  2特定字符,通信双方约定,用特定的字符作为结束,比如把0xff作为结束符。收到0xff就把数据截断。就像我们演讲,最后说一句谢谢大家,下边的人就知道了你讲完了,该鼓掌了。谢谢就是结束符。

  但是这种做法有一个弊端,就是正常通信的数据不允许在使用0xff。

  对于ASCII码,正常情况下是不会发送0x0d与0x0a(回车与换行)的,所以可以用作结束符。

  ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的系统,并等同于国际标准ISO/IEC 646。

  ASCII码主要用于英文字符的显示,不包含中文。标准ASCII码只有7位(最高位是校验位),所以只能显示2^7=128个字符,其中0-31还是不能显示的字符,例如回车,作用是控制字符或通信字符。

  假如,发送的数据是0x31,它可能代表着十六进制的数字0x31,也可能表示十进制的数字49(十六进制与十进制虽然看上去不一样,但表示的数是同样的大小),还可能表示ASCII码,字符’1’。这三者,在传输线上使用示波器来观察,波形是一模一样的,接收方把它理解为0x31还是理解为字符,要看通信双方的约定。



关键字:HAL库  串口  数据接收 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
基于AT91M42800A的LED显示系统设计