MDK 在 RT-Thread Nano 上添加控制檯與 FinSH 問題總結
近日學習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();
}