1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本章节为大家讲解LPUART(Low power universal asynchronous receiver transmitter,低功耗通用异步收发器)的基础知识和对应的HAL库API。相比第29章的通用串口,增加了低功耗特性。
65.1 初学者重要提示
LPUART的全称是Low power universal synchronous asynchronous receiver transmitter,中文意思是低功耗通用异步收发器,简称LPUART。 65.2.1 低功耗串口的硬件框图 认识一个外设,最好的方式就是看它的框图,方便我们快速的了解串口的基本功能,然后再看手册了解细节。 通过这个框图,我们可以得到如下信息:
65.2.2 低功耗串口的基本功能 STM32的串口功能很强大,支持太多的模式。我们只需关心我们最常用的特性即可。我们的串口驱动使用的串口中断+FIFO结构,没有使用DMA。因此我们只讨论和串口中断、串口常规参数有关的知识。 STM32串口的优越特性:(只列了举常用的)
其它中断不常用,包括:CTS改变、LIN断开符检测、检测到总线为空闲(在DMA不定长接收方式会用到)、溢出错误、帧错误、噪音错误、校验错误。 65.2.3 低功耗串口的高级特性 H7系列的串口支持了一些高级特性,比如:
相比第29章的通用串口,低功耗串口不支持超时接收和自适应波特率。 65.2.4 低功耗串口的数据帧格式 串口支持的帧格式如下(M和PCE都是LPUART_CR1寄存器的位,其中M位用于控制帧长度,PCE用于使能奇偶校验位): 这里特别注意奇偶校验位,用户在配置的时候可以选择奇校验和偶校验,校验位是占据的最高位。比如选择M=00,PCE=1,即7bit的数据位。
了解到帧格式后,再来看一下实际数据发送时,数据位的先后顺序: 65.2.5 低功耗串口的支持的时钟和波特率 低功耗定时器支持如下几种时钟: 这里我们重点关注PCLK3(D3PCLK1),HSI和LSE。
65.2.6 低功耗串口的支持唤醒方式 低功耗串口的唤醒主要是通过接收数据来唤醒,具体唤醒的方如下:
唤醒成功时的时序效果如下,特别注意唤醒信号位置: 唤醒失败时的时序效果: 65.2.7 低功耗串口发送时序图 这个时序图非常具有代表性,可以帮助大家很好的理解TC发送完成中断和TXE空中断。 65.2.8 单工,半双工和全双工通讯 单工:在一个单工的串行通讯系统中,一般至少有两根线(信号线和地线),数据传送只有一个方向,例如可以使用单工数据传送将数据从一个简单的数据监测系统传送到PC上。 半双工:在半双工串行通信系统中,一般同样要求至少有两根线。这里的数据传送是双向的。然而,同一个时刻只能为一个方向。在上面的数据监测的例子中做了一些变化,可以使用半双工通讯机制发送信息到嵌入式模块(来设置参数,比如采样率)。此外,在其他时候,可以使用这个种连接将嵌入式装置上的数据下载到PC中。 全双工:在一个全双工的串行通信系统中,一般要求至少有三根线(信号线A,信号线B和地线)。信号线A将传输一个方向上的数据,同时信号线B传送另一个方向上的数据。 65.3 低功耗串口的HAL库用法 串口的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置GPIO、时钟,并根据需要配置NVIC、中断和DMA。下面我们逐一展开为大家做个说明。 65.3.1 低功耗串口寄存器结构体USART_TypeDef USART相关的寄存器是通过HAL库中的结构体USART_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义: typedef struct { __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ __IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ uint16_t RESERVED2; /*!< Reserved, 0x12 */ __IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */ __IO uint16_t RQR; /*!< USART Request register, Address offset: 0x18 */ uint16_t RESERVED3; /*!< Reserved, 0x1A */ __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ __IO uint16_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ uint16_t RESERVED4; /*!< Reserved, 0x26 */ __IO uint16_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ uint16_t RESERVED5; /*!< Reserved, 0x2A */ __IO uint32_t PRESC; /*!< USART clock Prescaler register, Address offset: 0x2C */ } USART_TypeDef; 这个结构体的成员名称和排列次序和CPU的USART寄存器是一 一对应的。 __IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏: #define __O volatile /*!< Defines 'write only' permissions */ #define __IO volatile /*!< Defines 'read / write' permissions */ 下面我们看下LPUART的定义,在stm32h743xx.h文件。 #define PERIPH_BASE (0x40000000UL) #define D3_APB1PERIPH_BASE (PERIPH_BASE + 0x18000000UL) #define LPUART1_BASE (D3_APB1PERIPH_BASE + 0x0C00UL) #define LPUART1 ((USART_TypeDef *) LPUART1_BASE) <----- 展开这个宏,(USART_TypeDef *) 0x58000C00 我们访问LPUART1的CR1寄存器可以采用这种形式:LPUART1->CR1 = 0。 65.3.2 低功耗串口句柄结构体UART_HandleTypeDef HAL库在USART_TypeDef的基础上封装了一个结构体UART_HandleTypeDef,定义如下: typedef struct __UART_HandleTypeDef { USART_TypeDef *Instance; UART_InitTypeDef Init; UART_AdvFeatureInitTypeDef AdvancedInit; uint8_t *pTxBuffPtr; uint16_t TxXferSize; __IO uint16_t TxXferCount; uint8_t *pRxBuffPtr; uint16_t RxXferSize; __IO uint16_t RxXferCount; uint16_t Mask; uint32_t FifoMode; uint16_t NbRxDataToProcess; uint16_t NbTxDataToProcess; void (*RxISR)(struct __UART_HandleTypeDef *huart); void (*TxISR)(struct __UART_HandleTypeDef *huart); DMA_HandleTypeDef *hdmatx; DMA_HandleTypeDef *hdmarx; HAL_LockTypeDef Lock; / __IO HAL_UART_StateTypeDef gState; __IO HAL_UART_StateTypeDef RxState; __IO uint32_t ErrorCode; #if (USE_HAL_UART_REGISTER_CALLBACKS == 1) void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart); void (* WakeupCallback)(struct __UART_HandleTypeDef *huart); void (* RxFifoFullCallback)(struct __UART_HandleTypeDef *huart); void (* TxFifoEmptyCallback)(struct __UART_HandleTypeDef *huart); void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); #endif } UART_HandleTypeDef; 注意事项: 条件编译USE_HAL_HRTIM_REGISTER_CALLBACKS用来设置使用自定义回调还是使用默认回调,此定义一般放在stm32h7xx_hal_conf.h文件里面设置: #define USE_HAL_UAR_REGISTER_CALLBACKS 1 通过函数HAL_UART_RegisterCallback注册回调,取消注册使用函数HAL_UART_UnRegisterCallback。 这里重点介绍前三个参数,其它参数主要是HAL库内部使用和自定义回调函数。
SET_BIT(huart->Instance->CR1, USART_CR1_TXEIE)。
typedef struct { uint32_t BaudRate; /* 波特率 */ uint32_t WordLength; /* 数据位长度 */ uint32_t StopBits; /* 停止位 */ uint32_t Parity; /* 奇偶校验位 */ uint32_t Mode; /* 发送模式和接收模式使能 */ uint32_t HwFlowCtl; /* 硬件流控制 */ uint32_t OverSampling; /* 过采样,可以选择8倍和16倍过采样 */ uint32_t Prescaler; /* 串口分频 */ uint32_t FIFOMode; /* 串口FIFO使能 */ uint32_t TXFIFOThreshold; /* 发送FIFO的阀值 */ uint32_t RXFIFOThreshold; /* 接收FIFO的阀值 */ }UART_InitTypeDef;
typedef struct { uint32_t AdvFeatureInit; /* 初始化的高级特性类别 */ uint32_t TxPinLevelInvert; /* Tx引脚电平翻转 */ uint32_t RxPinLevelInvert; /* Rx引脚电平翻转 */ uint32_t DataInvert; /* 数据逻辑电平翻转 */ uint32_t Swap; /* Tx和Rx引脚交换 */ uint32_t OverrunDisable; /* 接收超时检测禁止 */ uint32_t DMADisableonRxError; /* 接收出错,禁止DMA */ uint32_t AutoBaudRateEnable; /* 自适应波特率使能 */ uint32_t AutoBaudRateMode; /* 自适应波特率的四种检测模式选择 */ uint32_t MSBFirst; /* 发送或者接收数据时,高位在前 */ } UART_AdvFeatureInitTypeDef; |
|
|
|
配置串口参数,其实就是配置结构体UART_HandleTypeDef的成员。比如下面配置为波特率115200,8个数据位,无奇偶校验,1个停止位。
UART_HandleTypeDef UartHandle; /* 配置如下: - 数据位 = 8 Bits - 停止位 = 1 bit - 奇偶校验位 = 无 - 波特率 = 115200bsp - 硬件流控制 (RTS 和 CTS 信号) */ UartHandle.Instance = LPUART1; UartHandle.Init.BaudRate = 115200; UartHandle.Init.WordLength = UART_WORDLENGTH_8B; UartHandle.Init.StopBits = UART_STOPBITS_1; UartHandle.Init.Parity = UART_PARITY_NONE; UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; UartHandle.Init.Mode = UART_MODE_TX_RX; UartHandle.Init.OverSampling = UART_OVERSAMPLING_16; UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; if(HAL_UART_Init(&UartHandle) != HAL_OK) { Error_Handler(); } 65.3.3 低功耗串口的底层配置(GPIO、时钟、中断等) 串口外设的基本参数配置完毕后还不能使用,还需要配置GPIO、时钟、中断等参数,比如下面配置使用引脚PA9和PA10。 /* LPUART1的GPIO PA9, PA10 */ #define LPUART1_CLK_ENABLE() __HAL_RCC_LPUART1_CLK_ENABLE() #define LPUART1_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define LPUART1_TX_GPIO_PORT GPIOA #define LPUART1_TX_PIN GPIO_PIN_9 #define LPUART1_TX_AF GPIO_AF3_LPUART #define LPUART1_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define LPUART1_RX_GPIO_PORT GPIOA #define LPUART1_RX_PIN GPIO_PIN_10 #define LPUART1_RX_AF GPIO_AF3_LPUART /* ********************************************************************************************************* * 函 数 名: InitHardUart * 功能说明: 配置串口的硬件参数和底层 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void InitHardUart(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit; #if LPUART1_FIFO_EN == 1 /* 使能 GPIO TX/RX 时钟 */ LPUART1_TX_GPIO_CLK_ENABLE(); LPUART1_RX_GPIO_CLK_ENABLE(); /* 使能 USARTx 时钟 */ LPUART1_CLK_ENABLE(); /* 配置TX引脚 */ GPIO_InitStruct.Pin = LPUART1_TX_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = LPUART1_TX_AF; HAL_GPIO_Init(LPUART1_TX_GPIO_PORT, &GPIO_InitStruct); /* 配置RX引脚 */ GPIO_InitStruct.Pin = LPUART1_RX_PIN; GPIO_InitStruct.Alternate = LPUART1_RX_AF; HAL_GPIO_Init(LPUART1_RX_GPIO_PORT, &GPIO_InitStruct); /* 配置NVIC the NVIC for UART */ HAL_NVIC_SetPriority(LPUART1_IRQn, 0, 1); HAL_NVIC_EnableIRQ(LPUART1_IRQn); /* 配置波特率、奇偶校验 */ bsp_SetLPUartParam(LPUART1, LPUART1_BAUD, UART_PARITY_NONE, UART_MODE_TX_RX); SET_BIT(LPUART1->ICR, USART_ICR_TCCF); /* 清除TC发送完成标志 */ SET_BIT(LPUART1->RQR, USART_RQR_RXFRQ); /* 清除RXNE接收标志 */ SET_BIT(LPUART1->CR1, USART_CR1_RXNEIE); /* 使能PE. RX接受中断 */ #endif } 总结下来就是以下几点:
关于这个底层配置有以下几点要着重说明下:
#define GPIO_AF3_LPUART ((uint8_t)0x03) /* LPUART Alternate Function mapping */ #define GPIO_AF8_LPUART ((uint8_t)0x08) /* LPUART Alternate Function mapping */ 具体使用那个,要看数据手册,比如我们这里使用引脚PA9和PA10,对应的复用如下: 那么使用GPIO_AF3_LPUART1即可。
注,早前使用F1和F4时候,经常会有网友咨询为什么串口中断服务程序里面没有做清除标志。 下面我们介绍__HAL_USART_GET_FLAG函数。这个函数用来检查LPUART/USART标志位是否被设置。 /** @brief Check whether the specified USART flag is set or not. * @param __HANDLE__: specifies the USART Handle * @param __FLAG__: specifies the flag to check. * This parameter can be one of the following values: * @arg USART_FLAG_TXFT: TXFIFO threshold flag * @arg USART_FLAG_RXFT: RXFIFO threshold flag * @arg USART_FLAG_RXFF: RXFIFO Full flag * @arg USART_FLAG_TXFE: TXFIFO Empty flag * @arg USART_FLAG_REACK: Receive enable ackowledge flag * @arg USART_FLAG_TEACK: Transmit enable ackowledge flag * @arg USART_FLAG_BUSY: Busy flag * @arg USART_FLAG_TXE: Transmit data register empty flag * @arg USART_FLAG_TC: Transmission Complete flag * @arg USART_FLAG_RXNE: Receive data register not empty flag * @arg USART_FLAG_IDLE: Idle Line detection flag * @arg USART_FLAG_ORE: OverRun Error flag * @arg USART_FLAG_UDR: UnderRun Error flag * @arg USART_FLAG_NE: Noise Error flag * @arg USART_FLAG_FE: Framing Error flag * @arg USART_FLAG_PE: Parity Error flag * @retval The new state of __FLAG__ (TRUE or FALSE). */ #define __HAL_USART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->ISR & (__FLAG__)) == (__FLAG__)) |
|
|
|
USART_FLAG有如下几种取值:
请大家重点关注上表中红字部分,LPUART/USART标志是需要软件主动清零的。清零有两种方式:一种是调用__HAL_USART_CLEAR_FLAG函数,另一种是操作相关寄存器后自动清零。 /** @brief Clear the specified USART pending flag. * @param __HANDLE__: specifies the USART Handle. * @param __FLAG__: specifies the flag to check. * This parameter can be any combination of the following values: * @arg USART_FLAG_TXFT: TXFIFO threshold flag * @arg USART_FLAG_RXFT: RXFIFO threshold flag * @arg USART_FLAG_RXFF: RXFIFO Full flag * @arg USART_FLAG_TXFE: TXFIFO Empty flag * @arg USART_FLAG_REACK: Receive enable ackowledge flag * @arg USART_FLAG_TEACK: Transmit enable ackowledge flag * @arg USART_FLAG_WUF: Wake up from stop mode flag * @arg USART_FLAG_RWU: Receiver wake up flag (is the USART in mute mode) * @arg USART_FLAG_SBKF: Send Break flag * @arg USART_FLAG_CMF: Character match flag * @arg USART_FLAG_BUSY: Busy flag * @arg USART_FLAG_ABRF: Auto Baud rate detection flag * @arg USART_FLAG_ABRE: Auto Baud rate detection error flag * @arg USART_FLAG_RTOF: Receiver timeout flag * @arg USART_FLAG_LBD: LIN Break detection flag * @arg USART_FLAG_TXE: Transmit data register empty flag * @arg USART_FLAG_TC: Transmission Complete flag * @arg USART_FLAG_RXNE: Receive data register not empty flag * @arg USART_FLAG_IDLE: Idle Line detection flag * @arg USART_FLAG_ORE: OverRun Error flag * @arg USART_FLAG_NE: Noise Error flag * @arg USART_FLAG_FE: Framing Error flag * @arg USART_FLAG_PE: Parity Error flag * @retval The new state of __FLAG__ (TRUE or FALSE). */ #define __HAL_USART_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->ICR = (__FLAG__)) 上面介绍的USART标志大部分能够设置为产生中断,也就是有对应的USART中断标志。我们只介绍几个串口驱动要用到的中断标志: USART_IT_TXE:TXE:发送数据寄存器空(此时数据可能正在发送)。 USART_IT_TC:发送完成 。 USART_IT_RXNE:接收数据寄存器非空。 中断缺省都是关闭的,通过__HAL_USART_ENABLE_IT函数可以使能相应的中断标志。函数定义如下: /** @brief Enable the specified USART interrupt. * @param __HANDLE__: specifies the USART Handle. * @param __INTERRUPT__: specifies the USART interrupt source to enable. * This parameter can be one of the following values: * @arg USART_IT_RXFF: RXFIFO Full interrupt * @arg USART_IT_TXFE: TXFIFO Empty interrupt * @arg USART_IT_RXFT: RXFIFO threshold interrupt * @arg USART_IT_TXFT: TXFIFO threshold interrupt * @arg USART_IT_TXE : Transmit Data Register empty interrupt * @arg USART_IT_TC : Transmission complete interrupt * @arg USART_IT_RXNE: Receive Data register not empty interrupt * @arg USART_IT_IDLE: Idle line detection interrupt * @arg USART_IT_PE : Parity Error interrupt * @arg USART_IT_ERR : Error interrupt(Frame error, noise error, overrun error) * @retval None */ #define __HAL_USART_ENABLE_IT(__HANDLE__, __INTERRUPT__) (((((uint8_t)(__INTERRUPT__)) >> 5U) == 1)? ((__HANDLE__)->Instance->CR1 |= (1U << ((__INTERRUPT__) & USART_IT_MASK))): ((((uint8_t)(__INTERRUPT__)) >> 5U) == 2)? ((__HANDLE__)->Instance->CR2 |= (1U << ((__INTERRUPT__) & USART_IT_MASK))): ((__HANDLE__)->Instance->CR3 |= (1U << ((__INTERRUPT__) & USART_IT_MASK)))) STM32一个串口的中断服务程序入口地址只有一个,进入中断服务程序后,我们需要判断是什么原因进入的中断,因此需要调用一个函数来检测中断标志。函数原型如下: #define __HAL_USART_GET_IT(__HANDLE__, __IT__) ((__HANDLE__)->Instance->ISR & ((uint32_t)1 << ((__IT__)>> 0x08))) 中断处理完毕后,必须软件清除中断标志,否则中断返回后,会重入中断。清中断标志位的函数为: #define __HAL_USART_CLEAR_IT(__HANDLE__, __IT_CLEAR__) ((__HANDLE__)->Instance->ICR = (uint32_t)(__IT_CLEAR__)) 正如前面介绍的,不是所有的标志都需要用这个函数清零。 注意:操作串口的寄存器不限制必须要用HAL库提供的API,比如要操作寄存器CR1,直接调用LPUART1->CR1操作即可。 65.3.5 低功耗串口初始化流程总结 使用方法由HAL库提供: 第1步:定义UART_HandleTypeDef类型串口结构体变量,比如UART_HandleTypeDef huart。 第2步:使用函数HAL_UART_MspInit初始化串口底层,不限制一定要用此函数里面初始化,用户也可以自己实现。
b、配置GPIO的复用模式。
b、使能串口中断。
b、使能DMA接口时钟。 c、配置串口的发送和接收DMA结构体变量。 d、配置DMA发送和接收通道。 e、关联DMA和串口的句柄。 f、配置发送DMA和接收DMA的传输完成中断和中断优先级。 第3步:配置串口的波特率,位长,停止位,奇偶校验位,流控制和发送接收模式。 第4步:如果需要,可以编程高级特性,比如TX/RX交换引脚,自动波特率检测。通过第1步串口结构体变量huart的结构体成员AdvancedInit来设置。 第5步:串口初始化调用的函数HAL_UART_Init初始化。 第6步:根据需要可以做动态注册回调。 首先使能宏定义USE_HAL_UART_REGISTER_CALLBACKS。 然后调用函数HAL_UART_RegisterCallback() 就可以注册如下回调函数: (+) TxHalfCpltCallback (+) TxCpltCallback (+) RxHalfCpltCallback (+) RxCpltCallback (+) ErrorCallback (+) AbortCpltCallback (+) AbortTransmitCpltCallback (+) AbortReceiveCpltCallback (+) WakeupCallback (+) RxFifoFullCallback (+) TxFifoEmptyCallback (+) MspInitCallback (+) MspDeInitCallback 函数HAL_UART_UnRegisterCallback允许取消注册的回调函数如下: (+) TxHalfCpltCallback (+) TxCpltCallback (+) RxHalfCpltCallback (+) RxCpltCallback (+) ErrorCallback (+) AbortCpltCallback (+) AbortTransmitCpltCallback (+) AbortReceiveCpltCallback (+) WakeupCallback (+) RxFifoFullCallback (+) TxFifoEmptyCallback (+) MspInitCallback (+) MspDeInitCallback 关于动态注册回调函数注意以下几点:
此文件涉及到的函数较多,这里把几个常用的函数做个说明:
其实V7开发板设计的低功耗串口FIFO驱动文件bsp_lpuart_fifo.c仅用到了函数HAL_UART_Init,其它函数都没有用到,不过这里也为大家做个说明。 65.4.1 函数HAL_UART_Init 函数原型: HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart) { /* 省略 */ if(huart->gState == HAL_UART_STATE_RESET) { huart->Lock = HAL_UNLOCKED; /* 初始化硬件: GPIO, CLOCK */ HAL_UART_MspInit(huart); } huart->gState = HAL_UART_STATE_BUSY; /* 禁止串口 */ __HAL_UART_DISABLE(huart); /* 配置串口参数 */ if (UART_SetConfig(huart) == HAL_ERROR) { return HAL_ERROR; } /* 配置串口高级特性 */ if (huart->AdvancedInit.AdvFeatureInit != UART_ADVFEATURE_NO_INIT) { UART_AdvFeatureConfig(huart); } /* 清寄存器的一些标志位 */ CLEAR_BIT(huart->Instance->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN)); CLEAR_BIT(huart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN)); /* 使能串口 */ __HAL_UART_ENABLE(huart); return (UART_CheckIdleState(huart)); } 函数描述: 此函数用于初始化串口的基础特性和高级特性。 函数参数:
|
|
|
|
注意事项:
1、函数HAL_UART_MspInit用于初始化USART的底层时钟、引脚等功能。需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。 2、如果形参huart的结构体成员gState没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量UART_HandleTypeDef UartHandle。 对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个 HAL_UART_STATE_RESET = 0x00U。 解决办法有三 方法1:用户自己初始串口和涉及到的GPIO等。 方法2:定义UART_HandleTypeDef UartHandle为全局变量。 方法3:下面的方法 if(HAL_UART_DeInit(&UartHandle) != HAL_OK){ Error_Handler();} if(HAL_UART_Init(&UartHandle) != HAL_OK){ Error_Handler();}3、注意串口的中断状态寄存器USART_ISR复位后,TC发送完成状态和RXNE接收状态都被置1,如果用户使能这两个中断前,最好优先清除中断标志。 使用举例: UART_HandleTypeDef UartHandle; /* USART3工作在UART模式 */ /* 配置如下: - 数据位 = 8 Bits - 停止位 = 1 bit - 奇偶校验位 = 无 - 波特率 = 115200bsp - 硬件流控制 (RTS 和 CTS 信号) */ UartHandle.Instance = USART3; UartHandle.Init.BaudRate = 115200; UartHandle.Init.WordLength = UART_WORDLENGTH_8B; UartHandle.Init.StopBits = UART_STOPBITS_1; UartHandle.Init.Parity = UART_PARITY_NONE; UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; UartHandle.Init.Mode = UART_MODE_TX_RX; UartHandle.Init.OverSampling = UART_OVERSAMPLING_16; UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; if(HAL_UART_Init(&UartHandle) != HAL_OK) { Error_Handler(); } 65.4.2 函数HAL_UART_Transmit 函数原型: HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { /* 省略 */ if(huart->gState == HAL_UART_STATE_READY) { /* 省略 */ while(huart->TxXferCount > 0U) { huart->TxXferCount--; /* 等待发送空中断标志 */ if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } } /* 等待发送完成中断 */ if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } /* 省略 */ return HAL_OK; } else { return HAL_BUSY; } } 函数描述: 此函数以查询的方式发送指定字节。看源码的话,程序里面最重要的就是上面代码中置红的两个标志,发送空标志和发送完成标志。发送空标志表示发送数据寄存器为空,数据还在移位寄存器里面,而发送完成标志表示数据已经从移位寄存器发送出去。 函数参数:
/* ********************************************************************************************************* * 函 数 名: fputc * 功能说明: 重定义putc函数,这样可以使用printf函数从串口1打印输出 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } 65.4.3 函数HAL_UART_Receive 函数原型: HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { /* 省略 */ if(huart->RxState == HAL_UART_STATE_READY) { /* 省略 */ while(huart->RxXferCount > 0U) { huart->RxXferCount--; if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } } /* 省略 */ return HAL_OK; } else { /* 省略 */ return HAL_BUSY; } } 函数描述: 此函数以查询的方式接收指定字节。这个函数相对比较好理解,就是等待上面程序中的RXNE标志,置位了表示接收数据寄存器已经存入数据。 函数参数: 第1个参数是UART_HandleTypeDef类型结构体指针变量。 第2个参数是要接收的数据地址。 第3个参数是要接收的数据大小,单位字节。 第4个参数是溢出时间,单位ms。 返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。 使用举例: /* ********************************************************************************************************* * 函 数 名: fgetc * 功能说明: 重定义getc函数,这样可以使用scanff函数从串口1输入数据 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ int fgetc(FILE *f) { int ret; HAL_UART_Receive(&UartHandle, (uint8_t *)&ret, 1, HAL_MAX_DELAY); return ret; } 65.4.4 函数HAL_UART_Transmit_IT 函数原型: HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { if(huart->gState == HAL_UART_STATE_READY) { if((pData == NULL ) || (Size == 0U)) { return HAL_ERROR; } __HAL_LOCK(huart); huart->pTxBuffPtr = pData; huart->TxXferSize = Size; huart->TxXferCount = Size; huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX; __HAL_UNLOCK(huart); if (READ_BIT(huart->Instance->CR1, USART_CR1_FIFOEN) != RESET) { /* 使能FIFO发送中断 */ SET_BIT(huart->Instance->CR3, USART_CR3_TXFTIE); } else { /* 使能发空中断 */ SET_BIT(huart->Instance->CR1, USART_CR1_TXEIE); } return HAL_OK; } else { return HAL_BUSY; } } 函数描述: 此函数以中断的方式发送指定字节,可以选择使能FIFO中断方式或者发送空中断方式。具体数据的发送是在中断处理函数HAL_UART_IRQHandler里面实现。 函数参数:
使用中断方式要使能串口中断,此贴有完整例子: UART_HandleTypeDef UartHandle; uint8_t s_ucBuf[5]; /* 数据发送 */ HAL_UART_Transmit_IT(&UartHandle, s_ucBuf, 1); HAL_UART_Transmit_IT(&UartHandle, (uint8_t*)"KEY_DOWN_K1rn", 13); 65.4.5 函数HAL_UART_Receive_IT 函数原型: HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { if(huart->RxState == HAL_UART_STATE_READY) { if((pData == NULL ) || (Size == 0U)) { return HAL_ERROR; } __HAL_LOCK(huart); huart->pRxBuffPtr = pData; huart->RxXferSize = Size; huart->RxXferCount = Size; UART_MASK_COMPUTATION(huart); huart->ErrorCode = HAL_UART_ERROR_NONE; huart->RxState = HAL_UART_STATE_BUSY_RX; __HAL_UNLOCK(huart); /* 使能错误中断: (Frame error, noise error, overrun error) */ SET_BIT(huart->Instance->CR3, USART_CR3_EIE); if (READ_BIT(huart->Instance->CR1, USART_CR1_FIFOEN) != RESET) { /* 使能奇偶校验失败中断 */ SET_BIT(huart->Instance->CR1, USART_CR1_PEIE); /* 使能FIFO接收中断 */ SET_BIT(huart->Instance->CR3, USART_CR3_RXFTIE); } else { /* 使能奇偶校验失败中断和接收中断 */ SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE); } return HAL_OK; } else { return HAL_BUSY; } } 函数描述: 此函数以中断的方式接收指定字节,可以选择使能FIFO中断方式或者普通中断方式,两种方式使能了奇偶校验中断失败和错误中断。具体数据的接收是在中断处理函数HAL_UART_IRQHandler里面实现。 函数参数:
使用中断方式要使能串口中断,此贴有完整例子: UART_HandleTypeDef UartHandle; uint8_t s_ucBuf[5]; /* 数据接收*/ HAL_UART_Receive_IT(&UartHandle, s_ucBuf, 1); 65.4.6 函数HAL_UART_Transmit_DMA 函数原型: HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { if(huart->gState == HAL_UART_STATE_READY) { if((pData == NULL ) || (Size == 0U)) { return HAL_ERROR; } __HAL_LOCK(huart); huart->pTxBuffPtr = pData; huart->TxXferSize = Size; huart->TxXferCount = Size; huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX; /* 注册各种DMA回调函数 */ huart->hdmatx->XferCpltCallback = UART_DMATransmitCplt; huart->hdmatx->XferHalfCpltCallback = UART_DMATxHalfCplt; huart->hdmatx->XferErrorCallback = UART_DMAError; huart->hdmatx->XferAbortCallback = NULL; /* 使能串口发送DMA通道 */ HAL_DMA_Start_IT(huart->hdmatx, (uint32_t)huart->pTxBuffPtr, (uint32_t)&huart->Instance->TDR, Size); /* 清除传输TC完成标志 */ __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_TCF); __HAL_UNLOCK(huart); /* 使能串口发送DMA传输 */ SET_BIT(huart->Instance->CR3, USART_CR3_DMAT); return HAL_OK; } else { return HAL_BUSY; } } 函数描述: 此函数以DMA的方式发送指定字节。这里是用的DMA中断方式HAL_DMA_Start_IT进行的发送。所以使用此函数的话,不要忘了写DMA中断服务程序。而且DMA的配置也是需要用户实现的,可以直接在函数HAL_UART_MspInit里面实现,也可以放在其它位置。 函数参数:
65.4.7 函数HAL_UART_Receive_DMA 函数原型: HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { if(huart->RxState == HAL_UART_STATE_READY) { if((pData == NULL ) || (Size == 0U)) { return HAL_ERROR; } __HAL_LOCK(huart); huart->pRxBuffPtr = pData; huart->RxXferSize = Size; huart->ErrorCode = HAL_UART_ERROR_NONE; huart->RxState = HAL_UART_STATE_BUSY_RX; /* 注册各种DMA回调函数 */ huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt; huart->hdmarx->XferErrorCallback = UART_DMAError; huart->hdmarx->XferAbortCallback = NULL; /* 使能串口接收DMA通道 */ HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->RDR, (uint32_t)huart->pRxBuffPtr, Size); __HAL_UNLOCK(huart); /* 使能串口校验错误中断 */ SET_BIT(huart->Instance->CR1, USART_CR1_PEIE); /* 使能串口错误中断:(Frame error, noise error, overrun error) */ SET_BIT(huart->Instance->CR3, USART_CR3_EIE); /* 使能串口接收DMA传输 */ SET_BIT(huart->Instance->CR3, USART_CR3_DMAR); return HAL_OK; } else { return HAL_BUSY; } } 函数描述: 此函数以DMA的方式接收指定字节。这里是用的DMA中断方式HAL_DMA_Start_IT进行的接收。所以使用此函数的话,不要忘了写DMA中断服务程序。而且DMA的配置也是需要用户实现的,可以直接在函数HAL_UART_MspInit里面实现,也可以放在其它位置。 函数参数:
65.5 总结 本章节就为大家讲解这么多,涉及到的知识点和API函数比较多,需要花点时间消化,后面用到的多了,就可以熟练掌握了。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1767 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1069 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1935浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
728浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
567浏览 3评论
593浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
551浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 02:07 , Processed in 1.113887 second(s), Total 52, Slave 46 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号