談一談單片機開發的幾種調試方案


原文地址:http://nicekwell.net/blog/20170411/tan-%5B%3F%5D-tan-dan-pian-ji-kai-fa-de-ji-chong-diao-shi-fang-an.html


單片機開發過程中,有一個好的調試系統可以極大地提高開發效率。
舉個例子,做平衡系統時調節PID參數,你會選擇 修改參數–>編譯–>燒錄–>運行–>修改…,還是做一個功能可以一邊運行一邊修改參數呢?
調試的方法有多種,在這就來分別談一下我在開發過程中使用過的幾種調試方案。
這裏的調試方案也是一種交互方案,但此方案不是爲了交互而設計,重在快速地搭建、方便地使用、高效地調試,換句話說長得醜無所謂。


我們做調試工具追求的是什麼

做調試系統無非是爲了兩點:

  1. 實時顯示一些必要信息。
  2. 實時修改,其實也就是可以實時接收指令。

爲了提高開發效率,我們進一步希望:

  1. 調試系統搭建起來方便快捷,最好可以統一標準,方便移植。
    我們不希望在調試系統上花費太多工作量,不要太複雜,儘量輕量級。
  2. 直觀顯示,看得清楚。
  3. 方便輸入,操作快捷。

幾種調試方案

1、加入屏和按鍵



最直接的方法就是在嵌入式系統中加入屏和按鍵,做出一個界面,顯示信息和接收按鍵指令。
【優點】跟隨系統,不需要依賴其他設備,可以隨時隨地調試。

【缺點】

  1. 添加了硬件。硬件的設計和焊接還是需要花費一些精力的。
  2. 構建界面同樣也要花費不少精力。
  3. 顯示信息有限。
    加入的屏通常顯示內容比較少,會受到一些限制。
  4. 按鍵功能有限。
    加入的按鍵通常也不會太多,並且如果我們考慮到長按、組合鍵等操作的話構建程序也是比較複雜的。

【總結】
肯定不能算輕量級,功能有限,可移植性差,花費精力多,屏需要額外費用。
唯一的優點就是跟隨系統,沒有其他依賴。

所以如果不是非要隨時調試的話不建議用這種方法,單純爲了調試來說代價還是比較大的。

2、串口+上位機


單片機自己不負責顯示,它把數據發送出去,由上位機顯示;也不負責按鍵檢測,由上位機負責並接收上位機指令。
單片機和上位機之間需規定好通信協議。

通信方式可以是串口也可以是其他,上位機可以是圖形也可以是命令行。

【優點】

  1. 對單片機來說做到了一定的輕量級。只需要考慮發送數據和接收指令的協議。
  2. 對於比較複雜的功能,好的上位機,尤其是圖形化的上位機可以很方便地調試。

【缺點】

  1. 需要設計通信協議,雙方都要位通信協議寫代碼。這需要花費一定的精力。
  2. 製作上位機需要花費較多的精力,並且上位機不通用。

【總結】
由於上位機在電腦上,有豐富的顯示和控制資源,做出一個好的上位機可以極其方便地提高效率。
但製作上位機要花費大量精力。

所以,如果上位機是作爲產品發佈,是值得花精力去做的;但如果僅僅是爲了調試時用一下,不值得這樣做。

到這裏有沒有注意到,其實我們是想找到一個通用的”顯示器”,能夠方便地把信息顯示出來。 顯示器是已經做好的,不需要我們再做什麼,通信協議儘可能簡單,最好能直接輸出。 後面我們會找到這麼一個好東西。

3、用樹莓派


用樹莓派進行關鍵計算,我們關心的數據都在樹莓派的程序裏。

由於樹莓派裏運行linux系統,在其連接網絡後,我們可以用局域網裏電腦的終端登陸樹莓派,在樹莓派的程序裏直接打印接口顯示出來。

注:
在命令行下打印數據並不一定是一行一行地輸出,也可以控制光標在指定位置輸出,構建出一個簡單的界面。這裏不做介紹,具體可搜索”控制檯編程”。
命令行下接收鍵盤操作也不一定非要輸入字符再回車,程序裏可以直接監測鍵盤。
總之在命令行中是可以構建出一個類似於 顯示屏+按鍵 組合的”設備”的。

【優點】

  1. 終端就相當於是一個現成的、通用的顯示屏,任意可登陸ssh的終端都可使用。
  2. 程序裏直接打印輸出!這真是太方便的,c語言中一個printf即可,相當於是一個極其簡單的通信協議。

【缺點】

  1. 貴!一個小小的智能車都要塞個樹莓派,樹莓派價格都夠好多個智能車了。
  2. 樹莓派本身是重量級。雖然程序本身是方便了,但使用之前需要配置樹莓派,雖然一個樹莓派只要配置一次。
  3. 樹莓派本身硬件功能有限,有些功能樹莓派不能直接完成,還是需要藉助單片機,並和單片機通信。
    比如記錄車輪旋轉的正交編碼器,stm32有專門的硬件模塊完成,樹莓派沒有,如果樹莓派想要記錄車輪旋轉的話還是需要藉助stm32,並且設計如何獲取stm32記錄的數據。

【總結】

對於調試這一方面來說,樹莓派是非常方便的,輸出、輸入都是直接完成。
所以如果你的項目值得用樹莓派,那調試是非常方便的;如果不需要用樹莓派,光是爲了調試方便而使用是不建議的,請考慮上面3個缺點。

雖然樹莓派本身不一定最合適,但我們找到了方向——終端。

4、串口+通用終端工具

單片機能不能用終端呢?答案是肯定的。
有多種終端工具可以通過串口使用,比如windows自帶的超級終端,linux和osx下命令行裏的minicom,以及誇三個平臺的圖形化終端secureCRT。
這些終端工具的功能簡單來說就是:1、當某個按鍵按下立刻發送該按鍵的鍵碼出去,比如按下p鍵發送字符’p’。2、接收串口數據並顯示出來。

說明:

  1. 這些終端工具的協議是相同的,可以認爲是通用的顯示器和鍵盤。
  2. 單片機可以發送一些特殊字符串完成一些特殊操作,比如清屏、控制光標位置。利用這些功能可以構建出簡單的界面。
    這些操作功能已經封裝成了函數,會在本文後面給出。
  3. 一般會先在單片機中實現printf功能,實現之後在程序中用printf即可直接在終端工具中顯示信息。
    實現printf功能的方法在這裏不介紹,具體可上網查找,工作量並不大。其實就是重定義一個函數,使printf函數通過指定串口輸出字符串。
  4. 串口通過藍牙模塊可快速實現無線傳輸。

【優點】

  1. 單片機端輕量級,上位機端無工作量。
    具體來說單片機端要做的事情有:初始化串口,重定向printf,通過特殊字符串控制光標構建界面。
  2. 上位機端通用,單片機端顯示相關的代碼方便移植。
    本文後文會給出顯示相關的庫函數。
  3. 硬件擴展幾乎沒有。

【缺點】

對比第1種方案,需要一臺電腦。其他方面都具有很大優勢。

【總結】

此方案是單片機開發調試的理想選擇。

下面會介紹如何在終端工具裏構建出界面。

如何構建終端工具裏的控制檯界面

1、構建界面用到的特殊字符串

字符(串) 功能 備註
\r 光標移到行首  
\b 退格  
“\033[2J” 清屏 清屏後光標還在原來位置
“\033[H” 光標復位,回到左上角(1行1列)  
“\033[%d;%dH”, y, x 設置光標到y行x列 終端中的行和列都是從1開始數的
“\033[%dA”, y 光標上移y行  
“\033[%dB”, y 光標下移y行  
“\033[%dD”, x 光標左移x列  
“\033[%dC”, x 光標右移x列  
“\033[?25l” 隱藏光標 在secureCRT中測試無效
“\033[?25h” 顯示光標 在secureCRT中測試無效

2、封裝好的庫函數

使用這個庫函數的前提是:
1、主函數完成了串口初始化,串口功能正常使用。
2、重定向了printf函數,printf函數可通過串口輸出字符串。

disp.h文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef __DISP_H__
#define __DISP_H__
/* 在主函數已經完成串口初始化,並且重定向printf函數之後,
   這裏提供一些列函數用於操作終端工具顯示,可在終端工具裏構建界面。 */
#include <stm32f10x_lib.h>

void disp_clean(void);
void disp_cursor_reset(void);        //復位光標位置,回到左上角(1行1列)
void disp_gotoxy(int x, int y);  //跳轉到y行x列,x和y都是從1開始數。
void disp_cursor_up(int y);      //上移x行
void disp_cursor_down(int y);    //下移y行
void disp_cursor_left(int x);    //左移x列
void disp_cursor_right(int x);   //右移x列
void disp_cursor_hide(void);     //隱藏光標,在secureCRT中測試無效。
void disp_cursor_show(void);     //顯示光標,在secureCRT中測試無效。

#endif

disp.c文件:

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
#include <disp.h>
#include <uart.h>
#include <stdio.h>

void disp_clean(void)
{
    printf("\033[2J");
}
void disp_cursor_reset(void)        //復位光標位置,回到左上角
{
    printf("\033[H");
}
void disp_gotoxy(int x, int y)  //跳轉到y行x列
{
    printf("\033[%d;%dH", y, x);
}
void disp_cursor_up(int y)      //上移x行
{
    printf("\033[%dA", y);
}
void disp_cursor_down(int y)    //下移y行
{
    printf("\033[%dB", y);
}
void disp_cursor_left(int x)    //左移x列
{
    printf("\033[%dD", x);
}
void disp_cursor_right(int x)   //右移x列
{
    printf("\033[%dC", x);
}
void disp_cursor_hide(void)     //隱藏光標
{
    printf("\033[?25l");
}
void disp_cursor_show(void)     //顯示光標
{
    printf("\033[?25h");
}


更多內容請訪問:http://nicekwell.net/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章