博主在使用1.2版本的HAL库开发STM32H743的串口7设备的时候,遇到了如下问题:
数据发送使用HAL_UART_Transmit
进行发送,单独测试发送的时候,发送正常。
接收则是HAL_UART_Receive_IT
,逐字节进行接收并存放至数组,配合定时器进行不定长数据接收,单独测试接收的时候,接收也正常。
然后博主这里就把TX和RX短接,按理说在发送完成后的50ms以内就会打印接收到的数据(定时器设置的50ms溢出,即不定长数据间隔为50ms),但是这里并没有看到输出。
研究了下源码,可以发现,发送的时候,会把串口状态huart->gState
标记为发送忙HAL_UART_STATE_BUSY_TX
并上锁。
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;
while(huart->TxXferCount > 0U)
{
huart->TxXferCount--;
if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
tmp = (uint16_t*) pData;
huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
pData += 2U;
}
else
{
huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
}
}
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;
/* Process Unlocked */
__HAL_UNLOCK(huart);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
而接收中断触发后,中断向量入口UART7_IRQHandler
会直接调用HAL_UART_IRQHandler(&huart7)
分析中断请求,根据接收数据完成请求函数调用UART_Receive_IT(huart)
,注意这里会把huart->RxState
设置为准备接收HAL_UART_STATE_READY
并上锁。
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* 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->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
/* Computation of UART mask to apply to RDR register */
UART_MASK_COMPUTATION(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Enable the UART Parity Error interupt and RX FIFO Threshold interrupt
(if FIFO mode is enabled) or Data Register Not Empty interrupt
(if FIFO mode is disabled).
*/
if (READ_BIT(huart->Instance->CR1, USART_CR1_FIFOEN) != RESET)
{
SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
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;
}
}
查看一下定义,可以发现这里的一个设计缺陷,gState
代表发送状态,RxState
代表接收状态,通过这个状态机来保证接收和发送过程的完整性,但是,只设计了一把锁Lock
,而锁是用来保护发送或者接收单个字节的完整性的,这样就导致了在连续发送的过程中,处理接收来的数据发现串口设备被上锁了,返回busy,从而丢失了发送的数据。
typedef struct
{
USART_TypeDef *Instance; /*!< UART registers base address */
UART_InitTypeDef Init; /*!< UART communication parameters */
UART_AdvFeatureInitTypeDef AdvancedInit; /*!< UART Advanced Features initialization 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 */
uint16_t Mask; /*!< UART Rx RDR register mask */
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; /*!< UART state information related to global Handle management
and also related to Tx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO uint32_t ErrorCode; /*!< UART Error code */
}UART_HandleTypeDef;
因为串口本身是全双工,一个比较合理的设计方法就是再设计一把锁进行保护,而网上的大部分解决方案是注释掉调用锁这个过程,个人是不想改动HAL库的,于是自己写发送函数和接收函数。
首先是串口初始化,没有使用HAL_UART_Receive_IT
设置接收buffer,而是直接使能接收中断。
void MX_UART7_Init(void)
{
huart7.Instance = UART7;
huart7.Init.BaudRate = 115200;
huart7.Init.WordLength = UART_WORDLENGTH_8B;
huart7.Init.StopBits = UART_STOPBITS_1;
huart7.Init.Parity = UART_PARITY_NONE;
huart7.Init.Mode = UART_MODE_TX_RX;
huart7.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart7.Init.OverSampling = UART_OVERSAMPLING_16;
huart7.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart7.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart7) != HAL_OK)
{
Error_Handler();
}
__HAL_UART_ENABLE_IT(&huart7, UART_IT_RXNE); //使能接收中断
中断服务函数这里,因为我只使能了接收中断,所以只对接收中断进行处理,其他中断源直接清除。
void UART7_IRQHandler(void)
{
char ch;
if ((__HAL_UART_GET_FLAG(&huart7, UART_FLAG_RXNE) != RESET) &&
(__HAL_UART_GET_IT_SOURCE(&huart7, UART_IT_RXNE) != RESET))
{
ch = huart7.Instance->RDR;
__HAL_TIM_SET_COUNTER(&htim6, 0);
if (USART7_RX_STA & 0x8000) return; //上次接收完成的未处理,直接退出
if (USART7_RX_STA == 0) //长度为0,接收到的是第一个字节,启动定时器
{
__HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE);
HAL_TIM_Base_Start_IT(&htim6);
}
USART7_RX_BUF[USART7_RX_STA & 0x3FFF] = ch;
USART7_RX_STA++;
if (USART7_RX_STA > (USART_REC_LEN - 1))USART7_RX_STA = 0;
}
else
{
if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_ORE) != RESET)
{
__HAL_UART_CLEAR_OREFLAG(&huart7);
}
if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_NE) != RESET)
{
__HAL_UART_CLEAR_NEFLAG(&huart7);
}
if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_FE) != RESET)
{
__HAL_UART_CLEAR_FEFLAG(&huart7);
}
if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_PE) != RESET)
{
__HAL_UART_CLEAR_PEFLAG(&huart7);
}
if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_CTS) != RESET)
{
__HAL_UART_CLEAR_IT(&huart7, UART_FLAG_CTS);
}
if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TXE) != RESET)
{
__HAL_UART_CLEAR_IT(&huart7, UART_FLAG_TXE);
}
if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TC) != RESET)
{
__HAL_UART_CLEAR_IT(&huart7, UART_FLAG_TC);
}
if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_RXNE) != RESET)
{
__HAL_UART_CLEAR_IT(&huart7, UART_FLAG_RXNE);
}
}
}
发送过程就比较简单了,等待发送完成继续发送就行了。
void u7_printf(char* s)
{
int i=0;
while(s[i])
{
huart7.Instance->TDR = s[i];
while (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TC) == RESET);
i++;
}
}