近日学习RT-Thread,发现在添加控制台与FinSH时遇到了问题,并没有像示例教程那样打印出信息,通过keil的simulation发现一直死在串口HAL驱动中的。
为什么会出现该现象呢?我经过仔细排查,后发现并解决了该问题。首先,将该问题复现如下:
1.准备一个MDK可以正常运行的LED程序。可以自己用keil搭建,也可以用cubeMx生成。为了快速生成工程,我采用cubeMx生成的。
2.将生成的LED工程用keil打开,然后按照 《基于 Keil MDK 移植 RT-Thread Nano》
添加RT-Thread OS
增加相关代码后,LED闪烁。
我的主函数代码为:
while (1)
{
/* USER CODE END WHILE */
Mytime++;
if(Mytime==20)
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
else if(Mytime==40)
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED2_Pin);
else if(Mytime==60)
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED3_Pin);
else if(Mytime==80)
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED4_Pin);
if(Mytime>90)
{
Mytime=0;
rt_kprintf("RT_VERSION\n");
}
rt_thread_mdelay(50);
//delay_ms(10);
/* USER CODE BEGIN 3 */
}
在protues8.9 中搭建硬件,直接导入程序后验证程序OK
3.参考《在 RT-Thread Nano 上添加控制台与 FinSH》添加链接描述
在main.c中添加相关代码:
/* USER CODE BEGIN 4 */
void rt_hw_console_output(const char *str)
{
rt_size_t i=0,size=0;
char a='\r';
__HAL_UNLOCK(&huart1);
size=rt_strlen(str);
for(i=0;i<size;i++)
{
if(*(str+i)== '\n')
{
HAL_UART_Transmit(&huart1,(uint8_t *)&a,1,1);
}
HAL_UART_Transmit(&huart1,(uint8_t *)(str+i),1,1);
}
}
char rt_hw_console_getchar(void)
{
int ch=-1;
// if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) !=RESET)
// {
// ch=huart1.Instance->DR &0xff;
// }
// else
// {
// if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE) !=RESET)
// {
// __HAL_UART_CLEAR_OREFLAG(&huart1);
// }
// rt_thread_mdelay (10);
// }
return ch;
}
/* USER CODE END 4 */
编译没有问题,然后采用自带simulation 进行仿真,发现程序一直死循环,没有运行到main函数。
4.首先,为了能编译过去,我自己定义了一个 timeout变量,让其跳出死循环。代码如下:
static HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, uint32_t Flag, FlagStatus Status, uint32_t Tickstart, uint32_t Timeout)
{
static uint32_t timeout=0;
/* Wait until flag is set */
while (((__HAL_UART_GET_FLAG(huart, Flag) ? SET : RESET) == Status) )
{
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) > Timeout))
{
/* Disable TXE, RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts for the interrupt process */
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR1_TXEIE));
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;
}
}
if((timeout++>20000))
return HAL_TIMEOUT;
}
return HAL_OK;
}在这里插入代码片
编译后通过,运行也OK。
但是运行久了就不打印了。其实这是有问题,通过单步运行,找到问题点。
原来mdk增加RTT系统后,从stratup.s中启动,先运行的是RTT中的初始化,而非Main 中初始化程序。这样其实会造成一个问题,STM32F4xx的系统时钟和外设都没初始化好,就给RTT使用,必定会造成初始中的函数运行有问题。即使按照官方给的文档,增加INIT_BOARD_EXPORT(MX_USART1_UART_Init); 这样的初始化在rt_hw_board_init()中,也是不可以的。虽然用INIT_BOARD_EXPORT 可以在 下图的第98行调用实现。
文档中说要在rt_hw_board_init 中调用对应的函数,我在V5.29中 发现不能使用。
按照上述调用,编译会出错,该函数其实在rt_components_board_init 中会自动执行。
这样,归根到底还是初始化没有按照正确的顺序执行导致的问题。
正确的执行顺序应该是在rt_hw_board_init 中将main函数中的初始函数先调用。
这个在后面看到的Keil 模拟器 STM32F103 上手指南
这个模板工程中得到验证。
/**
* This function will initial STM32 board.
*/
void rt_hw_board_init(void)
{
HAL_Init();
SystemClock_Config();
#ifdef RT_USING_HEAP
rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
#endif
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#ifdef RT_USING_CONSOLE
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
}
还有,SysTick_Handler 函数调用中,必须增加HAL_IncTick()函数调用。
/**
* This is the timer interrupt service routine.
*
*/
void SysTick_Handler(void)
{
/* enter interrupt */
rt_interrupt_enter();
HAL_IncTick();
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
}