本次遇到的问题是USART串口发送函数卡死程序,究其原因原来是串口发送函数中的发送空寄存器没有置位,且超时时间设置的太大导致程序死循环,直到发送超时退出。
在调用CUBE的串口发送函数时一定要注意写的方式。关于传送完毕,有人用如下方法等待传送完毕虽然方案可行
while(HAL_OK !=HAL_UART_Transmit(&huart2, transmit,len, timeout));
这容易卡死,一般也不要这样写这是第一种情况,使用while有几个弊端,不仅程序写的难看 万一,HAL_UART_Transmit()返回的不是HAL_OK而是HAL_TIMEOUT,程序卡死在这里。
还有一种情况就是上边提到的超时时间太长导致程序短暂的卡死,下边简单介绍。
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(); //这里每次在发送前拿到systick中断中的计数值,作为计数的初值。
huart->TxXferSize = Size;
huart->TxXferCount = Size;
while (huart->TxXferCount > 0U)
{
//下边这个函数会去判断每一个字节是否发送 UART_FLAG_TXE,并进行超时判断。
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);
}
huart->TxXferCount--;
}
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;
}
}
HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, uint32_t Flag, FlagStatus Status, uint32_t Tickstart, uint32_t Timeout)
{
/* Wait until flag is set */
while ((__HAL_UART_GET_FLAG(huart, Flag) ? SET : RESET) == Status)//在这里对发送空寄存器进行判断,如果为0,进行超时判断。
{
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) > Timeout))//这里拿到SYSTICK的最新计数值减去传入的初值与超时时间进行比较,当大于超时时间时,退出。
{
/* Disable TXE, RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts for the interrupt process */
#if defined(USART_CR1_FIFOEN)
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE | USART_CR1_TXEIE_TXFNFIE));
#else
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR1_TXEIE));
#endif
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
huart->gState = HAL_UART_STATE_READY;
huart->RxState = HAL_UART_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(huart);
return HAL_TIMEOUT;
}
}
}
return HAL_OK;
}
USART的各个寄存器描述如下:
WAKE:唤醒的方法 (Wakeup method) 0:被空闲总线唤醒; 1:被地址标记唤醒。
PCE:检验控制使能 (Parity control enable)
PS:校验选择 (Parity selection) 0:偶校验;1:奇校验。
PEIE:PE中断使能 (PE interrupt enable)
TXEIE:发送缓冲区空中断使能 (TXE interrupt enable)
TCIE:发送完成中断使能 (Transmission complete interrupt enable)
RXNEIE:接收缓冲区非空中断使能 (RXNE interrupt enable)
IDLEIE:IDLE中断使能 (IDLE interrupt enable) 0:禁止产生中断; 1:当USART_SR中的IDLE为’1’时,产生USART中断。
TE:发送使能 (Transmitter enable)
RE:接收使能 (Receiver enable)
RWU:接收唤醒 (Receiver wakeup) 0:接收器处于正常工作模式; 1:接收器处于静默模式。
TXE:发送数据寄存器空 (Transmit data register empty)
当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。
0:数据还没有被转移到移位寄存器;
1:数据已经被转移到移位寄存器。
TC:发送完成 (Transmission complete)
当包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’。如果USART_CR1中的TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。在我遇到的问题中改发送空标志位没有置位,并且我的超时时间又设置的比较大,导致一直等待知道超时退出为止,所以合理的设置超时时间可以有效的保证硬件没有及时置位(或者芯片抽风了硬件未能自动置位)程序超时退出,不至于等太久。
HAL_UART_Transmit(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size, uint32_t Timeout)
这个Timeout是按照毫秒级的,就是ms
因为timeout = HAL_GetTick() + Timeout;HAL_GetTick()是毫秒级的....所以当传输一个字节的数据的时候超时时间可以设定为传输时间,当你波特率是9600 bit/s的时候,那么一秒能传输9600位数据,8位数据一个字节,这样1s就传输了1200字节,即1200byte,那么一个字节传输的时间就是1000ms/1200byte=0.84ms,于是当在波特率为9600传输一个字节的数据的时候超时时间可以设定为1ms,写作:HAL_UART_Transmit (&huart6 ,(uint8_t *)&ucByte,1,0x01);,那么当你波特率是115200的时候,传输一个字节的超时时间理论是大于0.0695ms也即最小1ms,写作:HAL_UART_Transmit (&huart6 ,(uint8_t *)&ucByte,1,0x01);,同理要是传输10个字节,传输时间乘以10,就是0x0A了,那么这个时间要根据发送的数据量进行调整,个人经验设置为最大发送字节时间的2倍即可。