基于HAL库的USART串口通信配置
串口通信能够实现两块电路之间不同的通信,在开发中作为打印调试也是一门利器(printf重定向)。
串口初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| typedef struct { USART_TypeDef *Instance; /* 所使用的串口,值可以是 USART2 、USART3 、UART4、UART5、 UART7、UART8、USART1、USART6 */ UART_InitTypeDef Init; /* 串口通信参数结构体(附下表) */ ...
__IO HAL_UART_StateTypeDef State; /* 串口当前状态(仅用于条件判断) HAL_UART_STATE_RESET // 未初始化完毕 HAL_UART_STATE_READY // 就绪 HAL_UART_STATE_BUSY // 忙,处理中 HAL_UART_STATE_BUSY_TX // 忙于发送 HAL_UART_STATE_BUSY_RX // 忙于接收 HAL_UART_STATE_BUSY_TX_RX// 忙于全双工通信 HAL_UART_STATE_TIMEOUT // 超时 HAL_UART_STATE_ERROR // 错误 */ __IO uint32_t ErrorCode; /* 错误时返回的编号 */
}UART_HandleTypeDef;
typedef struct { uint32_t BaudRate; /* 波特率 */
uint32_t WordLength; /* 数据位长度,可以是: UART_WORDLENGTH_8B UART_WORDLENGTH_9B */
uint32_t StopBits; /* 停止位 UART_STOPBITS_1 UART_STOPBITS_2 */
uint32_t Parity; /* 校验位 采用何种校验是事先规定好的。通常专门设置一个奇偶校验位,用它使这组代码中“1”的个数为奇数或偶数。 若用奇校验,则当接收端收到这组代码时,校验“1”的个数是否为奇数,从而确定传输代码的正确性。 UART_PARITY_NONE // 无 UART_PARITY_EVEN // 偶校验 UART_PARITY_ODD // 奇校验 */ uint32_t Mode; /* 收发模式 UART_MODE_RX URAT_MODE_TX UART_MODE_TX_RX */
uint32_t HwFlowCtl; /* 硬件流控 硬件流:RTS/CTS (Request To Send/Clear To Send)即请求发送/清除发送协议,用于半双工时的收发切换 UART_HWCONTROL_NONE UART_HWCONTROL_RTS UART_HWCONTROL_CTS UART_HWCONTROL_RTS_CTS */ uint32_t OverSampling; /* 过采样 可配置的16倍过采样或8倍过采样,因此为速度容差与时钟容差的灵活配置提供了可能。 UART_OVERSAMPLING_16 UART_OVERSAMPLING_8 */ }UART_InitTypeDef;
|
- 调用 HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart); 初始化
- 调用 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size) ;开启接收中断
- 重写 void HAL_UART_MspInit(UART_HandleTypeDef *huart); 对IO口进行初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| //UART底层初始化,时钟使能,引脚配置,中断配置 //此函数会被HAL_UART_Init()调用 void HAL_UART_MspInit(UART_HandleTypeDef *huart) { //GPIO端口设置 GPIO_InitTypeDef GPIO_Initure; if(huart->Instance==USART1) //如果是串口1,进行串口1 MSP初始化 { __HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟 __HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟 GPIO_Initure.Pin=GPIO_PIN_9; //PA9 GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出 GPIO_Initure.Pull=GPIO_PULLUP; //上拉 GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速 GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1 HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9
GPIO_Initure.Pin = GPIO_PIN_10; //PA10 HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10 #if EN_USART1_RX_IT HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道 HAL_NVIC_SetPriority(USART1_IRQn,3,3); //抢占优先级3,子优先级3 #endif } }
|
串口读写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| // 读取串口状态 HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart);
// 从串口中接收字符(阻塞,具有毫秒级的超时管理机制) // 如果超时没接收完成,则不再接收数据到指定缓冲区,返回超时标志(HAL_TIMEOUT) HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// 开启串口接收中断 // 把 接收缓冲区指针 指向 要存放接收数据的数组,设置 接收长度,接收计数器初值,然后使能串口接收中断。接收到数据时,会触发串口中断。 // 再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,不再触发接收中断,调用串口接收完成回调函数 HAL_UART_RxCpltCallback() 。 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); // 进入DMA中断,接收串口数据(非阻塞) HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
/* 根据网上资料显示,HAL_UART_Receive_IT() 这个函数只能对串口中断接收进行一次接收,而且接收的字节大小是固定的uint16_t Size, 但是在实际使用中,不可能完全满足每次接收到的字节数都是一样的,而且是确定的。 所以大家采用的方法都是令 uint16_t Size = 1;这样的话,每接收到一个字节就中断一次。 那么中断处理函数处理的规则应该是 1、关闭此接收中断 2、将接收到的数据转移至缓存器 3、再次打开中断 */
|
1 2 3 4 5 6 7 8 9 10 11 12
| // 串口发送数据(阻塞,具有毫秒级的超时管理机制) // 如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT) HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// 进入发送中断 // 把 发送缓冲区指针 指向 要发送的数据,设置 发送长度,发送计数器初值,然后使能串口发送中断,触发串口中断。 // 再然后,串口中断函数处理,直到数据发送完成,而后关闭中断,不再发送数据,串口发送完成触发回调函数:HAL_UART_TxCpltCallback()。 HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
// 进入DMA发送中断 HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
|
中断处理
1 2 3 4 5
| HAL_UART_TxHalfCpltCallback(); 一半数据(half transfer)发送完成后,通过中断处理函数调用。 HAL_UART_TxCpltCallback(); 发送完成后,通过中断处理函数调用。 HAL_UART_RxHalfCpltCallback(); 一半数据(half transfer)接收完成后,通过中断处理函数调用。 HAL_UART_RxCpltCallback(); 接收完成后,通过中断处理函数调用。 HAL_UART_ErrorCallback(); 传输过程中出现错误时,通过中断处理函数调用。
|
STM32串口中断入口函数:
1 2
| USARTx_IRQHandler // x 可以是1到6 HAL_UART_IRQHandler
|
可看到串口发送和就是有三种通信模式:
第一种是上面用到的轮询的模式。CPU不断查询IO设备,如设备有请求则加以处理。例如CPU不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低。
第二种就是中断控制方式。当I/O操作完成时,输入输出设备控制器通过中断请求线向处理器发出中断信号,处理器收到中断信号之后,转到中断处理程序,对数据传送工作进行相应的处理。
第三种就是直接内存存取技术(DMA)方式。所谓直接传送,即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。
在 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 中再次调用 HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
,这样的话,就可以实现连续中断接收USART数据。
1 2 3 4 5 6 7 8
| void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { UART1RxBuff[UART1RxBuffCount++] = aRxBuffer; } HAL_UART_Receive_IT(huart, (uint8_t *)&aRxBuffer, 1) ; }
|
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com