本帖最后由 旮旯旭 于 2020-11-20 21:23 编辑
/* IO operation functions *******************************************************/ HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); /* Transfer Abort functions */ HAL_StatusTypeDef HAL_UART_AbortTransmit(UART_HandleTypeDef *huart); HAL_StatusTypeDef HAL_UART_AbortReceive(UART_HandleTypeDef *huart);
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart); void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
UART支持DMA 用户可使用的发送和接收API分阻塞,中断IT和DMA 阻塞:HAL_UART_Transmit、HAL_UART_Receive 中断:HAL_UART_Transmit_IT、HAL_UART_Receive_IT DMA: HAL_UART_Transmit_DMA、HAL_UART_Receive_DMA 中断处理程序HAL_UART_IRQHandler在stm32f4xx_it.c中断中被调用。如果使用了DMA来发送接收还有HAL_DMA_IRQHandler callback回调函数是weak定义的: 发送完成:HAL_UART_TxCpltCallback 接收完成:HAL_UART_RxCpltCallback 错误:HAL_UART_ErrorCallback 用户是可以重写这个3个函数来满足对业务的处理。
HAL库的串口发送接收由用户开启传输。HAL库对串口发送接收的处理在HAL_UART_IRQHandler进行处理,主要是发送、接收和错误中断的逻辑实现。 IT模式的发送和接收完成是通过计数器XferCount来实现的, 发送时:首先开启的是TXE中断,当UART的DR寄存器为空时触发TXE中断,将发送缓冲区里面的内容放入DR寄存器,同时计数器XferCount递减,当数据传递完成,计数器递减到0时关闭UART_IT_TXE,开启UART_IT_TC发送完成中断,在XferCount不为0时执行的是UART_Transmit_IT。当最后一个数据发送完成,由于在UART_Transmit_IT最后开启了UART_IT_TC,将触发中断进入UART_EndTransmit_IT,这个函数里面关闭了UART_IT_TC,同时有一个用户回调函数HAL_UART_TxCpltCallback。 接收时:开启UART_IT_RXNE,当有数据接收到时,触发中断,执行UART_Receive_IT,在这个函数里面有对不同数据位和校验位处理,用户节省了不少工作量,同时接收到一个XferCount计数器递减,当计数器归零时,提供给用户回调函数HAL_UART_RxCpltCallback。 暂不分析错误中断。 DMA模式的发送接收时通过DMA协处理器完成的 发生时将发送完成,半传输完成,错误的回调函数传递给DMA函数指针。开启相关中断,在DMA传输完成时如果回调函数非空,会执行一开始传入的回调函数。执行完DMA回调函数会关闭和开启相应的中断来实现发送和接收功能,最后的函数入口也还是HAL_UART_TxCpltCallback、HAL_UART_RxCpltCallback。 传输的具体过程见图了解详情:
HAL库提供的API由5.3的图例可以知道只有接收到Size个数据才会执行callback函数,如果要实现多字节接收有几种方式选择,如果协议数据包是定长的那么直接定义缓冲区大小,开启传输的时候填入要接收的Size。 例如:只有接收完16个字节才会进入callback,可以自行修改代码验证 HAL_UART_Receive_IT(&huart1, aRxBuffer, 16); HAL_UART_Receive_DMA(&huart1, aRxBuffer, 16); 如果是不定长的串口接收有2种方式字节接收超时和空闲中断来完成。空闲中断超时时间是1个数据的传输时间,如果数据之间的间隔比较久不能使用空闲中断来操作。具体实现看接收超时和空闲中断代码实现部分。 关于发送启动函数:HAL_UART_Transmit_IT、HAL_UART_Transmit_DMA 传输的缓冲区必须为全局或者静态缓冲区,防止在退出函数执行后栈空间被系统释放导致数据错乱。同时对于IT和DMA完成的数据传输由于是有DMA控制器完成的,如果想要再次发起传输必须等传输完成以后才可以,即使再次调用,IT和DMA发送函数做了优化虽然不会引起数据错位还是数据是发送不成功的 HAL_Status是BUSY状态,所以再次发送需要等到发送完成建立标志位,查询该标志位来或者串口发送所处的状态HAL库提供了 huart->gState 作为状态指示。if (huart->gState == HAL_UART_STATE_READY) 时才可以发送。 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; } /* Process Locked */ __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; /* Process Unlocked */ __HAL_UNLOCK(huart); /* Enable the UART Transmit data register empty Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_TXE); return HAL_OK; } else { return HAL_BUSY; } }
|