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内存和片外设的地址都是设置成自增加的
1
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 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/2019/ic-news031143442.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。『本文转载自网络,版权归原作者所有,如有侵权请联系删除』