stm32 hal库分析之uart
hal库 uart
基本概念:
同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
同步是阻塞模式,异步是非阻塞模式。
UART是异步全双工。
实际看代码是会发现,UART的收发寄存器是同一个名字DR,这样看UART似乎是半双工的,但实际上收发是使用不同的寄存器,但是我们使用的是同一个名字。所以它是异步全双工的。
UART是如何初始化的
515 typedef struct 516 { 517 __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */ 518 __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */ 519 __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */ 520 __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */ 521 __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */ 522 __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */ 523 __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */ 524 } USART_TypeDef;
可以看到的是hal库根据寄存器手册的UART的寄存器,将UART定义成了,相应的结构体。然后将对应的地址指针,按照结构体类型进行强制转换,这样就可以使用这个结构体来操作对应的寄存器了。
988 #define USART2 ((USART_TypeDef *) USART2_BASE) 989 #define USART3 ((USART_TypeDef *) USART3_BASE)
可以看到,对应的寄存器的地址
870 #define USART2_BASE (APB1PERIPH_BASE + 0x4400U) 871 #define USART3_BASE (APB1PERIPH_BASE + 0x4800U)
最后将这个结构体,赋值实例化,就可以了。
158 typedef struct
159 {
160 USART_TypeDef *Instance; /*!< UART registers base address */
161
162 UART_InitTypeDef Init; /*!< UART communication parameters */
163
164 uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
165
166 uint16_t TxXferSize; /*!< UART Tx Transfer size */
167
168 __IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
169
170 uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
171
172 uint16_t RxXferSize; /*!< UART Rx Transfer size */
173
174 __IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
175
176 DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
177
178 DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
179
180 HAL_LockTypeDef Lock; /*!< Locking object */
181
182 __IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management
183 and also related to Tx operations.
184 This parameter can be a value of @ref HAL_UART_StateTypeDef */
185
186 __IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.
187 This parameter can be a value of @ref HAL_UART_StateTypeDef */
188
189 __IO uint32_t ErrorCode; /*!< UART Error code */
190
191 }UART_HandleTypeDef;
简单看一下这个重要结构体的成员:
instance:这个是最重要的,拿到的是,UART外设的寄存器的地址。
init:这个记录的是用户初始化串口的配置信息,初始化就是通过将init里面的配置信息,来配置instance里面的寄存器
pTxBuffPtr:这类似的几个结构体就是用作临时存放用户传进来的信息的。
Lock:上锁的标志位,这里根本没有上锁的这个寄存器,只是使用一个全局变量,来进行互斥使用的。
阻塞发送(非中断方式)
607 /** 608 * @brief Sends an amount of data in blocking mode. 609 * @param huart pointer to a UART_HandleTypeDef structure that contains 610 * the configuration information for the specified UART module. 611 * @param pData Pointer to data buffer 612 * @param Size Amount of data to be sent 613 * @param Timeout Timeout duration 614 * @retval HAL status 615 */ 616 HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)617 { 618 uint16_t* tmp; 619 uint32_t tickstart = 0U; 620 621 /* Check that a Tx process is not already ongoing */ 622 if(huart->gState == HAL_UART_STATE_READY) 623 { 624 if((pData == NULL ) || (Size == 0)) 625 { 626 return HAL_ERROR; 627 } 628 629 /* Process Locked */ 630 __HAL_LOCK(huart); 631 632 huart->ErrorCode = HAL_UART_ERROR_NONE; 633 huart->gState = HAL_UART_STATE_BUSY_TX; 634 635 /* Init tickstart for timeout managment */ 636 tickstart = HAL_GetTick(); 637 638 huart->TxXferSize = Size; 639 huart->TxXferCount = Size; 640 while(huart->TxXferCount > 0U) 641 { 642 huart->TxXferCount--; 643 if(huart->Init.WordLength == UART_WORDLENGTH_9B) 644 { 645 if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) 646 { 647 return HAL_TIMEOUT; 648 } 649 tmp = (uint16_t*) pData; 650 huart->Instance->DR = (*tmp & (uint16_t)0x01FF); 651 if(huart->Init.Parity == UART_PARITY_NONE) 652 { 653 pData +=2U; 654 } 655 else 656 { 657 pData +=1U; 658 } 659 } 660 else 661 { 662 if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) 663 { 664 return HAL_TIMEOUT; 665 } 666 huart->Instance->DR = (*pData++ & (uint8_t)0xFF); 667 } 668 } 669 670 if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK) 671 { 672 return HAL_TIMEOUT; 673 } 674 675 /* At end of Tx process, restore huart->gState to Ready */ 676 huart->gState = HAL_UART_STATE_READY; 677 678 /* Process Unlocked */ 679 __HAL_UNLOCK(huart); 680 681 return HAL_OK; 682 } 683 else 684 { 685 return HAL_BUSY; 686 } 687 }
分析以上的代码:
- 确认uart的状态
- uart加锁,更新初始化的时间,更改初始化的状态
- 根据配置数据位是8为还是9位,进行逐一发送
- 因为DR数据寄存器最大为9位,因此必须逐一发送
- 注意函数UART_WaitOnFlagUntilTimeout,每发完一个字节,就会去检查状态寄存器UART_FLAG_TXE,判断是否发完并且检查等待发送的过程中是否超时
所有的数据都发完了,解锁,返回状态。
无阻塞的发送(中断方式)
778 /** 779 * @brief Sends an amount of data in non blocking mode. 780 * @param huart pointer to a UART_HandleTypeDef structure that contains 781 * the configuration information for the specified UART module. 782 * @param pData Pointer to data buffer 783 * @param Size Amount of data to be sent 784 * @retval HAL status 785 */ 786 HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 787 { 788 /* Check that a Tx process is not already ongoing */ 789 if(huart->gState == HAL_UART_STATE_READY) 790 { 791 if((pData == NULL ) || (Size == 0)) 792 { 793 return HAL_ERROR; 794 } 795 796 /* Process Locked */ 797 __HAL_LOCK(huart); 798 799 huart->pTxBuffPtr = pData; 800 huart->TxXferSize = Size; 801 huart->TxXferCount = Size; 802 803 huart->ErrorCode = HAL_UART_ERROR_NONE; 804 huart->gState = HAL_UART_STATE_BUSY_TX; 805 806 /* Process Unlocked */ 807 __HAL_UNLOCK(huart); 808 809 /* Enable the UART Transmit data register empty Interrupt */ 810 SET_BIT(huart->Instance->CR1, USART_CR1_TXEIE); 811 812 return HAL_OK; 813 } 814 else 815 { 816 return HAL_BUSY; 817 } 818 }
代码分析:
无阻塞发送的代码看起来是简单了很多,但是分析起来看实际上,也是差不多的,整体过程比无阻塞发送还要复杂一点。
- 锁定串口,更新状态
- 结构体赋值,数据段的地址,长度。
- 锁定串口,开启串口中断USART_CR1_TXEIE。
当sr寄存器中的,TXE=1时,生成uart中断。
TXE = 0,数据未传输到移位寄存器 ,TXE = 1,数据传输到移位寄存器 - 结束返回
接下来时中断程序:
1629 /* UART in mode Transmitter ------------------------------------------------*/
1630 if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
1631 {
1632 UART_Transmit_IT(huart);
1633 return;
1634 }
1635
1636 /* UART in mode Transmitter end --------------------------------------------*/
1637 if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
1638 {
1639 UART_EndTransmit_IT(huart);
1640 return;
1641 }
进入第一个中断,判断 USART_SR_TXE和 USART_CR1_TXEIE被置位,其中TXEIE代表开启UART的中断,TXE就表示数据发送完成, 当TXE被硬件置位时,就会进入中断,之后我们将新的的数据放到DR寄存器重视这个位就会被再次清零,等待下一次发送数据完成之后,再次进入中断。
2304 static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart)
2305 {
2306 uint16_t* tmp;
2307
2308 /* Check that a Tx process is ongoing */
2309 if(huart->gState == HAL_UART_STATE_BUSY_TX)
2310 {
2311 if(huart->Init.WordLength == UART_WORDLENGTH_9B)
2312 {
2313 tmp = (uint16_t*) huart->pTxBuffPtr;
2314 huart->Instance->DR = (uint16_t)(*tmp & (uint16_t)0x01FF);
2315 if(huart->Init.Parity == UART_PARITY_NONE)
2316 {
2317 huart->pTxBuffPtr += 2U;
2318 }
2319 else
2320 {
2321 huart->pTxBuffPtr += 1U;
2322 }
2323 }
2324 else
2325 {
2326 huart->Instance->DR = (uint8_t)(*huart->pTxBuffPtr++ & (uint8_t)0x00FF);
2327 }
2328
2329 if(--huart->TxXferCount == 0U)
2330 {
2331 /* Disable the UART Transmit Complete Interrupt */
2332 CLEAR_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
2333
2334 /* Enable the UART Transmit Complete Interrupt */
2335 SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);
2336 }
2337 return HAL_OK;
2338 }
2339 else
2340 {
2341 return HAL_BUSY;
2342 }
2343 }
分析代码:
1. 根据数据段是8位还是9位,对DR寄存器进行置位
2. 判断最后一个字节是否发送完成,如果最后一位也已经将数据放到移位寄存器中去了,就disable中断USART_CR1_TXEIE,初始化中断USART_CR1_TCIE
USART_CR1_TCIE: 传输完成中断,有软件置位和清零,置位是会触发中断。
2351 static HAL_StatusTypeDef UART_EndTransmit_IT(UART_HandleTypeDef *huart)
2352 {
2353 /* Disable the UART Transmit Complete Interrupt */
2354 CLEAR_BIT(huart->Instance->CR1, USART_CR1_TCIE);
2355
2356 /* Tx process is ended, restore huart->gState to Ready */
2357 huart->gState = HAL_UART_STATE_READY;
2358
2359 HAL_UART_TxCpltCallback(huart);
2360
2361 return HAL_OK;
2362 }
代码分析:
到这里中断方式发送数据就完成了,接着就是调用用户的回调函数。
注意这里,所有的数据,都是在中断里面发送的
我们发现只有在发送的时候才会将TCIE,TXEIE这两个位置位,开启中断。平常的时候中断都是关闭的。
DMA方式发送(无阻塞方式发送)
873 HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 874 { 875 uint32_t *tmp; 876 877 /* Check that a Tx process is not already ongoing */ 878 if(huart->gState == HAL_UART_STATE_READY) 879 { 880 if((pData == NULL ) || (Size == 0)) 881 { 882 return HAL_ERROR; 883 } 884 885 /* Process Locked */ 886 __HAL_LOCK(huart); 887 888 huart->pTxBuffPtr = pData; 889 huart->TxXferSize = Size; 890 huart->TxXferCount = Size; 891 892 huart->ErrorCode = HAL_UART_ERROR_NONE; 893 huart->gState = HAL_UART_STATE_BUSY_TX; 894 895 /* Set the UART DMA transfer complete callback */ 896 huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt; 897 898 /* Set the UART DMA Half transfer complete callback */ 899 huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt; 900 901 /* Set the DMA error callback */ 902 huart->hdmatx->XferErrorCallback = UART_DMAError; 903 904 /* Set the DMA abort callback */ 905 huart->hdmatx->XferAbortCallback = NULL; 906 907 /* Enable the UART transmit DMA Stream */ 908 tmp = (uint32_t*)&pData; 909 HAL_DMA_Start_IT(huart->hdmatx, *(uint32_t*)tmp, (uint32_t)&huart->Instance->DR, Size); 910 911 /* Clear the TC flag in the SR register by writing 0 to it */ 912 __HAL_UART_CLEAR_FLAG(huart, UART_FLAG_TC); 913 914 /* Process Unlocked */ 915 __HAL_UNLOCK(huart); 916 917 /* Enable the DMA transfer for transmit request by setting the DMAT bit 918 in the UART CR3 register */ 919 SET_BIT(huart->Instance->CR3, USART_CR3_DMAT); 920 921 return HAL_OK; 922 } 923 else 924 { 925 return HAL_BUSY; 926 } 927 }
代码分析:
1. 锁定串口,初始化结构体变量
2. 初始化所有的回调函数
3. 开启DMA中断传输,这种传输方式是从内存拷贝数据到外设寄存器。
451 /** 452 * @brief Start the DMA Transfer with interrupt enabled. 453 * @param hdma pointer to a DMA_HandleTypeDef structure that contains 454 * the configuration information for the specified DMA Stream. 455 * @param SrcAddress The source memory Buffer address 456 * @param DstAddress The destination memory Buffer address 457 * @param DataLength The length of data to be transferred from source to destination 458 * @retval HAL status 459 */ 460 HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
在这里DMA内存和片外设的地址都是设置成自增加的
114 hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
115 hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;
- 清除传输完成中断
- 打开串口,初始化DMA
- 返回值
接下来就是等待传输中断完成。
需要注意的是:DMA的工作是不需要cpu来参与的,连续使用DMA来进行发送或接受数据时,我们需要判断上次的数据发送是否发送完成。
138 /*##-5- Send the received Buffer ###########################################*/
139 if(HAL_UART_Transmit_DMA(&UartHandle, (uint8_t*)aRxBuffer, RXBUFFERSIZE)!= HAL_OK)
140 {
141 /* Transfer error in transmission process */
142 Error_Handler();
143 }
144
145 /*##-6- Wait for the end of the transfer ###################################*/
146 while (HAL_UART_GetState(&UartHandle) != HAL_UART_STATE_READY)
147 {
148 }
149
150 /*##-7- Send the End Message ###############################################*/
151 if(HAL_UART_Transmit_DMA(&UartHandle, (uint8_t*)aTxEndMessage, TXENDMESSAGESIZE)!= HAL_OK)
152 {
153 /* Turn LED3 on: Transfer error in transmission process */
154 BSP_LED_On(LED3);
155 while(1)
156 {
157 }
158 }
因为第一句发送数据完成时,数据实际上可能没有发送完成,因为这是无阻塞的。因此发新的数据之前我们要确保上次的数据是否已经发送完成。否则的话会造成数据的丢失。
无阻塞方式接收,阻塞方式接受和发送的逻辑大同小异,这里就不再一一列举了。
需要注意的是,我们这里使用HAL_UART_Receive,还是使用HAL_UART_Receive_IT都可以接受多个字节这个都是由hal库已经做好了的。但是这样做的话接收到指定字节的数据之后,就没有办法继续接受数据。在实际的工程里,我们可能需要一直接收数据,通常的做法是,接受一定量的数据之后,在中断里面开启下一次的就收数据。
以下是简单的实例
190 void USART1_IRQHandler()
191 {
192 OSIntEnter();
193 HAL_UART_IRQHandler(&UART_Handler[UART_DEV1]);
194 HAL_UART_Receive_IT(&UART_Handler[UART_DEV1], (u8 *)&s_byUartRxdBuf[UART_DEV1][0], USART_RECLEN_TRIG_HOOK);
195 OSIntExit();
196 }
stm32 hal库分析之uart相关推荐
- stm32 HAL库分析之CAN
stm32 HAL库分析之CAN 阻塞发送 HAL_StatusTypeDef HAL_CAN_Transmit(CAN_HandleTypeDef* hcan, uint32_t Timeout) ...
- STM32的HAL库分析及使用
STM32的三种开发方式 通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的.一般大多数都会选用标准库和HAL库,而极少部分人会通过直接配 ...
- STM32 HAL库 CubeMX教程(五)串口通信基础
STM32 HAL库 CubeMX教程(五)串口通信基础 串口通信简介 CubeMX配置 初始化程序分析 程序编写 参考文献 STM32 HAL库 CubeMX系列教程 目录 串口通信简介 UART: ...
- STM32 HAL库 串口DMA(收发)和STM32串口中断接收(接收时间管理机制)+ESP8266 wifi模组通信问题
一.HAL库 串口 DMA+ESP8266模组通信问题 用STM32 HAL库串口的DMA发送和空闲中断接收处理数据,单片机发送AT指令给ESP8266 wifi模组问题:单片机连续几次给wifi模组 ...
- STM32 HAL库学习笔记1-HAL库简介
STM32 HAL库学习笔记1-HAL库简介 HAL库 SPL 库 和 HAL 库两者相互独立,互不兼容.几种库的比较如下 目前几种库对不同芯片的支持情况如下 ST 中文官网上有一篇<关于ST库 ...
- STM32 HAL库详解
STM32 HAL库整体总结 STM32 之二 HAL库详解 及 手动移植 本篇博客是对以上参考资源的一个二次总结与整理. 1. HAL库文件结构 对于开发人员而言,首先要清楚 HAL 库的文件结构. ...
- STM32 HAL库学习笔记4-SPI
STM32 HAL库学习笔记4-SPI 前言 一.SPI协议简介 SPI物理层 SPI协议层 1.基本通讯过程 2. 通讯的起始和停止信号 3. 数据有效性 4. CPOL/CPHA 及通讯模式 二. ...
- STM32 HAL库组成概述
STM32 HAL库概述 ## (一)HAL库设计思想 什么是HAL(Hardware Abstraction Layer)? from 百度百科: 硬件抽象层是位于操作系统内核与硬件电路之间的接口层 ...
- stm32+HAL库制作转速仪
stm32+HAL库制作转速仪 前言 电机在运行过程中,需要实时检测其转速的稳定性,有效反映电机的运行情况. 本文介绍了基于stm32的转速仪的设计,可以用光电门传感器和红外对管传感器测量,可以设置选 ...
最新文章
- linux服务之rsync
- 数据结构树的基本操作_《数据结构》树的基本操作.doc
- java集群_JAVA架构师学习:实践ZooKeeper 应用场景与集群管理,辛勤总结
- python字符串中find函数_Python之字符串常用花哨玩法
- C++ 11 深度学习(五)类型转换:static_cast dynamic_cast const_cast reinterpret_cast
- docker安装并运行ElasticSearch-Head插件
- 【数据泵】EXPDP导出表结构
- Mac目录映射到docker容器ubuntu目录
- 程序员的终极幻想(一):像操作数据库那样操作大脑的记忆
- par函数的bg参数-控制图片的背景色
- Java Hamcrest学习
- kindle paperwhite3 拆机越狱
- 朋友在B站魔力赏抽到的动漫周边,把我看馋了
- 计算机连接网络需要什么,宽带怎么安装需要什么_安装宽带步骤-系统城
- 通过“单键锁配置法”实现访问控制
- 《青春有你2》选手信息爬取
- 全排列【46. 全排列】
- Unity3d实现Projector(喷码效果)
- 【BW系列】SAP 讲讲BW/4 HANA和BW on HANA的区别
- pytorch 实现Faster R-cnn从头开始(一)