0%

CUBEIDE配置STM32串口及解决串口死锁问题

生成串口代码的教程很多,也很简单,这里不做赘述
这里主要介绍一下printf重定向的方法,以及HAL_UART_Transmit和HAL_UART_Receive_IT函数导致的串口死锁问题

串口单字节发送函数

在usart.c中增加串口发送函数,方便后续开发,并能很好解决死锁问题

1
2
3
4
5
void uart4SendByte(uint8_t ch)
{
while((UART4->ISR&0X40) == 0);
UART4->TDR = (uint8_t)ch;
}

printf重定向

只需要在工程任意位置加入以下代码即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
//HAL_UART_Transmit(&huart4, (uint8_t *)&ch, 1, 0xFFFF);
uart4SendByte(ch); //与HAL_UART_Transmit取其中一个即可
return ch;
}

串口死锁

串口死锁的原因是HAL_UART_Transmit和HAL_UART_Receive_IT函数相互调用__HAL_LOCK(huart)导致串口状态被异常修改,返回HAL_BUSY 状态,无法再次进入中断接收数据

解决方法1

修改回调函数HAL_UART_RxCpltCallback,在里面做解锁操作,虽然不会再出现死锁现象,但是过多的异常检测会导致丢失部分数据,不建议使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
if(UartHandle->Instance == UART4)
{
uint16_t i = 0;
while(HAL_UART_Receive_IT(&huart4, (uint8_t *)aRxBuffer, 1) != HAL_OK )
{
if(i++ > 10000 )
{
huart4.RxState = HAL_UART_STATE_READY;
__HAL_UNLOCK(&huart4);
}
}
}
}

解决方法2

数据发送使用自定义函数,不要再调用HAL_UART_Transmit

1
2
3
4
5
void uart4SendByte(uint8_t ch)
{
while((UART4->ISR&0X40) == 0);
UART4->TDR = (uint8_t)ch;
}

解决方法3

修改HAL_UART_Transmit函数,将__HAL_LOCK(huart)和__HAL_UNLOCK(huart)注释掉,注意每次重新生成代码都要重新注释一下,并不是一个一劳永逸的方法,建议使用方法2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint8_t *pdata8bits;
uint16_t *pdata16bits;
uint32_t tickstart;

/* Check that a Tx process is not already ongoing */
if (huart->gState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}

//__HAL_LOCK(huart);

huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_BUSY_TX;

/* Init tickstart for timeout management */
tickstart = HAL_GetTick();

huart->TxXferSize = Size;
huart->TxXferCount = Size;

/* In case of 9bits/No Parity transfer, pData needs to be handled as a uint16_t pointer */
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
pdata8bits = NULL;
pdata16bits = (uint16_t *) pData;
}
else
{
pdata8bits = pData;
pdata16bits = NULL;
}

//__HAL_UNLOCK(huart);

while (huart->TxXferCount > 0U)
{
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
if (pdata8bits == NULL)
{
huart->Instance->TDR = (uint16_t)(*pdata16bits & 0x01FFU);
pdata16bits++;
}
else
{
huart->Instance->TDR = (uint8_t)(*pdata8bits & 0xFFU);
pdata8bits++;
}
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;

return HAL_OK;
}
else
{
return HAL_BUSY;
}
}