完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
概述
上一篇写了 STM32CubeMX 的串口的使用,而这篇来扒一扒,它是怎么进行封装的。其实在标准库中也类似如下过程。 一.串口实例 我们都知道,其实单片机最后其实都是对 串口相关的寄存器 进行操作,那么我们想扒一扒它的流程,必然要先知道串口相关的寄存器是哪些,因此查阅 STM32F4xx中文参考手册 ,我们可以在 第711页 找到以下相关寄存器:
这些就是操作串口所需要的寄存器,那么我们应该怎么和 HAL库 的东西对应起来,去HAL库中找找,我们就会在 stm32f407xx.h 的头文件中找到以下结构体 /** * @brief Universal Synchronous Asynchronous Receiver Transmitter */ typedef struct { __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */ __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */ __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */ __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */ __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */ __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */ __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */ } USART_TypeDef; 你会发现,寄存器和结构体中的变量名一一对应,那就是它了,我们再深入研究以下,为什么每个声明都是用uint32_t 的类型?我们还可以在 STM32F4xx中文参考手册 找到以下这张图,更直观的解释原因 从上图可以看出,每个寄存器都是之间的地址偏移都是 4个字节,也就是 32 bit ,所以是用 uint32_t ,使每个寄存器占用 4 个字节,符合芯片文档。 可是不知道你发现了没有,这边都没有说串口在内存的实际地址,有些小伙伴可能也不知道,就算有地址要怎么操作,这里我们稍微复习一下C 语言中的 常量指针 的知识,下面给出一小段代码 *(uint32_t *)0x40011000 = 0xFF; 这段代码的含义,就是将 0x40011000 这个值转为指向一个32 bit 的内存空间的指针,* 取地址符号就是使得0xFF存入该 32 bit的空间内。 因此我们应该要找到我们所使用的 串口1在内存的地址 ,说了这么多遍内存,我们就来看看 STM32 的内存到底长什么样子,我们可以在 对应的芯片手册上找到其内存映射图,当前芯片是 STM32F407,因此我们在芯片手册中会看到 从上图可以看到 STM32F407 的内存映射图,内存被分成了很多块,而串口是属于外设,因此现在只要关心从下往上数第三块内存块就行了,但是目前我们也只能从中看到有 AHB1,AHB2,APB1,APB2,用标准库的小伙伴,应该对这些词很熟悉了,就是经常要对其进行时钟初始化的外设总线。 但是我们还是没有看到我们想要的 地址,因此我们还是要借助一张时钟树的图,来判断串口1 在哪一条总线之下。 结合时钟树和内存映射图,我们可以看到,串口1的地址在0x40010000 到 0x4001 4BFF ,但是还不是真正我们想要的 最终,你会在 STM32F4xx中文参考手册 中发现一张存储的映射表,串口1的地址范围就写在那里 这个地址范围就是我们要对其进行串口操作的实际地址,但是我们的操作实际就用到了7个寄存器 为了更形象的说明,我们用以下代码,将前面提到的寄存器的实际的地址打印出来: 打印结果如下: huart1.Instance->SR 40011000 huart1.Instance->DR 40011004 huart1.Instance->BRR 40011008 huart1.Instance->CR1 4001100c huart1.Instance->CR2 40011010 huart1.Instance->CR3 40011014 huart1.Instance->CTPR 40011018 现在我们就很明白了,串口1的基地址 (40011000)+ 上面所说的地址偏移量 = 每个寄存器的实际地址, 接下来我们再看看,这个基地址是怎么在 HAL 库中用上的。我们从上面的分析中知道,串口1其实是在外设总线上的,并且是 APB2 外设总线上,所以还是在stm32f407.h 的头文件中找,我们可以找到以下宏定义:
我们可以看到它用在了串口1初始化的函数(MX_USART1_UART_Init )里面了,也终于看到了我们这个标题要说的实例( huart1 )了 接下来我们看看这个实例的结构体( UART_HandleTypeDef )长什么样子 /** * @brief UART handle Structure definition */ typedef struct __UART_HandleTypeDef { USART_TypeDef *Instance; /*!< UART registers base address */ UART_InitTypeDef Init; /*!< UART communication parameters */ uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */ uint16_t TxXferSize; /*!< UART Tx Transfer size */ __IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */ uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */ uint16_t RxXferSize; /*!< UART Rx Transfer size */ __IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */ DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */ DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */ HAL_LockTypeDef Lock; /*!< Locking object */ __IO HAL_UART_StateTypeDef gState; __IO HAL_UART_StateTypeDef RxState; __IO uint32_t ErrorCode; /*!< UART Error code */ #if (USE_HAL_UART_REGISTER_CALLBACKS == 1) void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Half Complete Callback */ void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Complete Callback */ void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Half Complete Callback */ void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Complete Callback */ void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Error Callback */ void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort CompleCallback */ void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */ void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Receive Complete Callback */ void (* WakeupCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Wakeup Callback */ void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp Init callback*/ void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp DeInitcallback*/ #endif /* USE_HAL_UART_REGISTER_CALLBACKS */ } UART_HandleTypeDef; 我们可以看到在这个结构体里看到,它需要串口基地址,串口初始化所需要的值,以及数据接收和发送所需要的指针,和当前状态标志位。 二. 串口实例初始化 我们可以在串口初始化的函数(MX_USART1_UART_Init )中,找到我们自己在STM32CubeMX 的图形界面中为串口配置的值,如下 huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; 这不是真正的初始化(因为还没操作到我们之前所说的寄存器),它只是用来记录需要初始化的值 而真正的初始化这些参数,是在 HAL_UART_Init 中的 UART_SetConfig 函数中,如下代码所示 /** * @brief Configures the UART peripheral. * @param huart Pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @retval None */ static void UART_SetConfig(UART_HandleTypeDef *huart) { uint32_t tmpreg; uint32_t pclk; /* Check the parameters */ assert_param(IS_UART_BAUDRATE(huart->Init.BaudRate)); assert_param(IS_UART_STOPBITS(huart->Init.StopBits)); assert_param(IS_UART_PARITY(huart->Init.Parity)); assert_param(IS_UART_MODE(huart->Init.Mode)); /*-------------------------- USART CR2 Configuration -----------------------*/ /* Configure the UART Stop Bits: Set STOP[13:12] bits according to huart->Init.StopBits value */ MODIFY_REG(huart->Instance->CR2, USART_CR2_STOP, huart->Init.StopBits); /*-------------------------- USART CR1 Configuration -----------------------*/ /* Configure the UART Word Length, Parity and mode: Set the M bits according to huart->Init.WordLength value Set PCE and PS bits according to huart->Init.Parity value Set TE and RE bits according to huart->Init.Mode value Set OVER8 bit according to huart->Init.OverSampling value */ tmpreg = (uint32_t)huart->Init.WordLength | huart->Init.Parity | huart->Init.Mode | huart->Init.OverSampling; MODIFY_REG(huart->Instance->CR1, (uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE | USART_CR1_OVER8), tmpreg); /*-------------------------- USART CR3 Configuration -----------------------*/ /* Configure the UART HFC: Set CTSE and RTSE bits according to huart->Init.HwFlowCtl value */ MODIFY_REG(huart->Instance->CR3, (USART_CR3_RTSE | USART_CR3_CTSE), huart->Init.HwFlowCtl); /* Check the Over Sampling */ if (huart->Init.OverSampling == UART_OVERSAMPLING_8) { /*-------------------------- USART BRR Configuration ---------------------*/ #if defined(USART6) && defined(UART9) && defined(UART10) if ((huart->Instance == USART1) || (huart->Instance == USART6) || (huart->Instance == UART9) || (huart->Instance == UART10)) { pclk = HAL_RCC_GetPCLK2Freq(); huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate); } #elif defined(USART6) if ((huart->Instance == USART1) || (huart->Instance == USART6)) { pclk = HAL_RCC_GetPCLK2Freq(); huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate); } #else if (huart->Instance == USART1) { pclk = HAL_RCC_GetPCLK2Freq(); huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate); } #endif /* USART6 */ else { pclk = HAL_RCC_GetPCLK1Freq(); huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate); } } else { /*-------------------------- USART BRR Configuration ---------------------*/ #if defined(USART6) && defined(UART9) && defined(UART10) if ((huart->Instance == USART1) || (huart->Instance == USART6) || (huart->Instance == UART9) || (huart->Instance == UART10)) { pclk = HAL_RCC_GetPCLK2Freq(); huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate); } #elif defined(USART6) if ((huart->Instance == USART1) || (huart->Instance == USART6)) { pclk = HAL_RCC_GetPCLK2Freq(); huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate); } #else if (huart->Instance == USART1) { pclk = HAL_RCC_GetPCLK2Freq(); huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate); } #endif /* USART6 */ else { pclk = HAL_RCC_GetPCLK1Freq(); huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate); } } } /** * @} */ 它真正操作到我们所说那些串口相关的寄存器(CR1, CR2, CR3, BRR)的配置,这里没看到 (SR,DR),因为它在数据的发送和接收时用到了。 三. 串口数据的发送(阻塞模式) 接下来就是对数据发送进行说明,先放出函数 /** * @brief Sends an amount of data in blocking mode. * @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01), * the sent data is handled as a set of u16. In this case, Size must indicate the number * of u16 provided through pData. * @param huart Pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @param pData Pointer to data buffer (u8 or u16 data elements). * @param Size Amount of data elements (u8 or u16) to be sent * @param Timeout Timeout duration * @retval HAL status */ HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { uint16_t *tmp; uint32_t tickstart = 0U; /* Check that a Tx process is not already ongoing */ if (huart->gState == HAL_UART_STATE_READY) { if ((pData == NULL) || (Size == 0U)) { return HAL_ERROR; } /* Process Locked */ __HAL_LOCK(huart); huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX; /* Init tickstart for timeout managment */ tickstart = HAL_GetTick(); huart->TxXferSize = Size; huart->TxXferCount = Size; /* Process Unlocked */ __HAL_UNLOCK(huart); while (huart->TxXferCount > 0U) { huart->TxXferCount--; if (huart->Init.WordLength == UART_WORDLENGTH_9B) { if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } tmp = (uint16_t *) pData; huart->Instance->DR = (*tmp & (uint16_t)0x01FF); if (huart->Init.Parity == UART_PARITY_NONE) { pData += 2U; } else { pData += 1U; } } else { if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } huart->Instance->DR = (*pData++ & (uint8_t)0xFF); } } if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } /* At end of Tx process, restore huart->gState to Ready */ huart->gState = HAL_UART_STATE_READY; return HAL_OK; } else { return HAL_BUSY; } }
接下来就是对数据接收进行说明,再放出函数 /** * @brief Receives an amount of data in blocking mode. * @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01), * the received data is handled as a set of u16. In this case, Size must indicate the number * of u16 available through pData. * @param huart Pointer to a UART_HandleTypeDef structure that contains * the configuration information for the specified UART module. * @param pData Pointer to data buffer (u8 or u16 data elements). * @param Size Amount of data elements (u8 or u16) to be received. * @param Timeout Timeout duration * @retval HAL status */ HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { uint16_t *tmp; uint32_t tickstart = 0U; /* Check that a Rx process is not already ongoing */ if (huart->RxState == HAL_UART_STATE_READY) { if ((pData == NULL) || (Size == 0U)) { return HAL_ERROR; } /* Process Locked */ __HAL_LOCK(huart); huart->ErrorCode = HAL_UART_ERROR_NONE; huart->RxState = HAL_UART_STATE_BUSY_RX; /* Init tickstart for timeout managment */ tickstart = HAL_GetTick(); huart->RxXferSize = Size; huart->RxXferCount = Size; /* Process Unlocked */ __HAL_UNLOCK(huart); /* Check the remain data to be received */ while (huart->RxXferCount > 0U) { huart->RxXferCount--; if (huart->Init.WordLength == UART_WORDLENGTH_9B) { if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } tmp = (uint16_t *) pData; if (huart->Init.Parity == UART_PARITY_NONE) { *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF); pData += 2U; } else { *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF); pData += 1U; } } else { if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } if (huart->Init.Parity == UART_PARITY_NONE) { *pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF); } else { *pData++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F); } } } /* At end of Rx process, restore huart->RxState to Ready */ huart->RxState = HAL_UART_STATE_READY; return HAL_OK; } else { return HAL_BUSY; } }
|
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1649 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1566 浏览 1 评论
994 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
693 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1613 浏览 2 评论
1871浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
656浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
526浏览 3评论
543浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
515浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-27 16:06 , Processed in 0.865442 second(s), Total 77, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号