開發環境:Window 10 64bit
開發工具:IAR Embedded Workbench
硬件:stm32f103c8t6
RT-Thread Nano 版本包含了 FinSH 組件,我們可以在reconfig.h配置使用它,使用之後我們可以在電腦上通過串口終端輸入命令調試系統。這功能用於調試或查看系統信息,在實際開發中可以帶來很多的方便,。效果如下圖:
1.基於IAR,進行RT-Thread源碼移植
1.我使用的芯片是stm32f103c8t6,基於IAR移植RT-Thread Nano,點擊下載RT-Thread Nano源碼 。如果你沒有IAR,而是使用KEIL,點擊查看參考鏈接。
2.下載源碼後,在rt-thread-3.1.3\bsp\stm32f103-msh路徑下,打開iar工作空間文件Project.eww,打開後是這樣子的:
Project->Options,General Optons->配置成自己使用的芯片、Debugger->配置調試工具:
3.配置rtconfig.h文件,在該文件里加入一下宏:
#define RT_USING_FINSH
#define FINSH_USING_HISTORY
開啓後可對 FinSH 組件相關的參數進行配置修改(在rtconfig.h文件結尾的地方):
#if defined (RT_USING_FINSH) // 開關 FinSH 組件
#define FINSH_USING_MSH // 使用 FinSH 組件 MSH 模式
#define FINSH_USING_MSH_ONLY // 僅使用 MSH 模式
#define __FINSH_THREAD_PRIORITY 5 // 設置 FinSH 組件優先級,配置該值後通過下面的公式進行計算
#define FINSH_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 8 * __FINSH_THREAD_PRIORITY + 1)
#define FINSH_THREAD_STACK_SIZE 1024 // 設置 FinSH 線程棧大小,範圍 1-4096
#define FINSH_HISTORY_LINES 5 // 設置 FinSH 組件記錄歷史命令個數,值範圍 1-32
#define FINSH_USING_SYMTAB // 使用符號表,需要打開,默認打開
#endif
4.在shell.c文件裏面找到rt_hw_console_getchar函數:
//修改點:函數返回值,增加清除RXNE標誌,去掉delay
int rt_hw_console_getchar(void)
{
int ch = -1;
if (__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_RXNE) != RESET)
{
ch = UartHandle.Instance->DR & 0xff;
__HAL_UART_CLEAR_FLAG(&UartHandle, UART_FLAG_RXNE);
}
else
{
//rt_thread_mdelay(10);
}
return ch;
}
5.在shell.c文件裏面找到finsh_thread_entry 函數,註釋掉兩個回顯:
/*.........*/
else if (ch == 0x44) /* left key */
{
if (shell->line_curpos)
{
//rt_kprintf("\b");
shell->line_curpos --;
}
continue;
}
else if (ch == 0x43) /* right key */
{
if (shell->line_curpos < shell->line_position)
{
//rt_kprintf("%c", shell->line[shell->line_curpos]);
shell->line_curpos ++;
}
continue;
}
/*.........*/
2.基於QT,開發與finsh功能對接的PC終端
1.點擊下載源碼
2.我使用的qt版本是Qt Creator 5.14,如果下載我的源碼由於版本衝突,不能使用的話可以自己新建一個工程,把代碼複製過去;或者去qt官網下載該版本。
3.效果圖:
4.關鍵代碼:
重寫鍵盤釋放事件函數,通過ev->text()可以獲取鍵盤輸入的字符(包括回退鍵,確認鍵),此外我們還需要獲取方向鍵,方向鍵的使用類似Linux終端,可通過按上鍵來翻閱之前的命令記錄,命令是在MCU裏記錄,當按了上鍵時,MCU就會返回上一次輸入的命令,命令記錄的條數最大值設置是在在rtconfig.h文件中的宏FINSH_HISTORY_LINES。
方向鍵需要發送的鍵值是不能直接通過ev->text()獲取到的,所以先通過ev->key(),不同的按鍵發送對應的控制碼,比如“上”鍵,發送的是:0x1b 0x5b 0x41即可。
void MainWindow::keyReleaseEvent(QKeyEvent *ev)
{
int code = 0xFF;
unsigned char controlkey[5]={0x1b,0x5b,0x00};
//unsigned int key = ev->key();
//qDebug("鍵值:0x%x",ev->key());
if(!ui->textEdit->hasFocus()){//判斷光標是否在textEdit控件上
return;
}
/*
* handle control key
* up key : 0x1b 0x5b 0x41
* down key: 0x1b 0x5b 0x42
* right key:0x1b 0x5b 0x43
* left key: 0x1b 0x5b 0x44
*/
controlkey[2] = 0x00;
switch (ev->key()) {
case Qt::Key_Left:
controlkey[2] = 0x44;
ui->textEdit->moveCursor(QTextCursor::Left);
break;
case Qt::Key_Right:
controlkey[2] = 0x43;
ui->textEdit->moveCursor(QTextCursor::Right);
break;
case Qt::Key_Up:
controlkey[2] = 0x41;
break;
case Qt::Key_Down:
controlkey[2] = 0x42;
break;
default:
QString text = ev->text();
if(text.size() > 0){
code = text.at(0).toLatin1();
if((code >= 0) && (code <= 0x7e)){//ASCII
if(code == '\n'||code == '\r'){//回車按鍵按下時,將光標移動到末尾
ui->textEdit->moveCursor(QTextCursor::End);
}
if(port != NULL && port->isOpen()){//發送字符到串口
port->write((const char*)&code,1);
}
}
}
break;
}
if(controlkey[2] != 0x00){
port->write((const char*)&controlkey[0],3);
}
//qDebug("0x%x %c",(char)code,(char)code);
//QWidget::keyPressEvent(ev);
}
窗口接收函數,當PC端串口接收到數據就會進入下面的函數,接受到的數據會先保存到MainSerialRecvData數組裏,然後再一個一個的追加到顯示輸入控件textEdit上,這裏我同了temp數組來過度,如果有更好的辦法可以優化一下;當CP端按下了“backspace”鍵時,MCU會往PC回發‘/b’,那麼當PC端接收到‘/b’就要刪除關標後一個字符。
void MainWindow::SerialRecvMsgEvent()
{
QByteArray MainSerialRecvData;
char temp[10]={0};
if(port->bytesAvailable()>0)
{
MainSerialRecvData = port->readAll();
//qDebug("rec data");
int count = MainSerialRecvData.count();
for(int i = 0;i<count;i++){
//qDebug("0x%x",MainSerialRecvData.at(i));
temp[0] = MainSerialRecvData[i];
if((temp[0] >= 0) && (temp[0] <= 0x7e)){//ASCII
if(temp[0] == 0x08){// '/b':backspace鍵
ui->textEdit->textCursor().deletePreviousChar();//刪除光標後面一個字符
}else{
ui->textEdit->textCursor().insertText(temp);//把字符顯示到textedit裏
}
//移動滾動條到底部
QScrollBar *scrollbar = ui->textEdit->verticalScrollBar();
if (scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
}
}
}
把qt的源碼下載解壓,就可以直接編譯運行,選擇串口,設置波特率,輸入命令是記得把輸入法切換到英文,否則輸入無效的。
3.遇到的問題
- 還沒用qt做的終端時,我在PC端用串口助手發送命令到MCU,發現MCU會丟失很多數據,因爲MCU端用輪詢的方式從UART裏獲取數據,如果處理不及時必然會丟失數據。解決這個問題,一種方法是,可以加長PC端發送兩個字符之間的時間;另一種方法是,MCU端利用中斷接收數據,先把數據發進隊列裏面(RT-Thread源碼有隊列功能),然後再while循環裏取出隊列裏的數據進行處理,這樣就不會丟失UART數據了。
- 在函數rt_hw_console_getchar裏,我註釋掉了rt_thread_mdelay(10),因爲不註釋掉,按方向鍵是沒用的,因爲MCU沒及時處理導致PC端發送的數據丟失了;但是,註釋掉也會導致比finsh線程優先級低的其他線程永遠得不到調用。
《路漫漫其修遠兮,吾將上下而求索。———屈原》