# 第十一節:一個在單片機上練習 C 語言的模板程序
## 【11.1 一套完整的模板源代碼。】
先給大家附上一套完整的模板源代碼,后面章節練習 C 語言的模板程序就直接復制此完整的源代碼,此源代碼適合的單片機型號是 STC89C52RC,晶振是 11.0592MHz,串口波特率是 9600,初學者只需修改代碼里從 “C 語言學習區域的開始” 到 “C 語言學習區域的結束” 的區域,其它部分不要更改。可復制的源代碼請到網上論壇原貼處直接下載本教程的文件壓縮包,解壓文件壓縮包后,直接用 WPS 辦公軟件打開 “可編輯的 WPS 文檔教程” 這個文檔,就可以復制里面相關章節的源代碼。在網上搜索 “從單片機基礎到程序框架” 就可以找到論壇原貼的出處,也可以直接到我的個人網站那里下載(www.dumenmen.com)。一套完整的模板源代碼如下:
```c
#include "REG52.H"
void View (unsigned long u32ViewData);
void to_BufferData (unsigned long u32Data, unsigned char *pu8Buffer,
unsigned char u8Type);
void SendString (unsigned char *pu8String);
/*---C 語言學習區域的開始。-----------------------------------------------*/
void main () // 主函數
{
unsigned char a; // 定義一個變量 a。
unsigned int b; // 定義一個變量 b。
unsigned long c; // 定義一個變量 c。
a = 100; // 給變量 a 賦值。
b = 10000; // 給變量 b 賦值。
c = 1000000000; // 給變量 c 賦值。
View (a); // 把第 1 個數 a 發送到電腦端的串口助手軟件上觀察。
View (b); // 把第 2 個數 b 發送到電腦端的串口助手軟件上觀察。
View (c); // 把第 3 個數 c 發送到電腦端的串口助手軟件上觀察。
while (1) {
}
}
/*---C 語言學習區域的結束。-----------------------------------------------*/
void View (unsigned long u32ViewData) {
static unsigned char Su8ViewBuffer [43];
code unsigned char Cu8_0D_0A [] = {0x0d, 0x0a, 0x00};
code unsigned char Cu8Start [] = {"開始..."};
static unsigned char Su8FirstFlag = 0;
static unsigned int Su16FirstDelay;
if (0 == Su8FirstFlag) {
Su8FirstFlag = 1;
for (Su16FirstDelay = 0; Su16FirstDelay < 10000; Su16FirstDelay++)
;
SendString (Cu8Start);
SendString (Cu8_0D_0A);
SendString (Cu8_0D_0A);
}
to_BufferData (u32ViewData, Su8ViewBuffer, 1);
SendString (Su8ViewBuffer);
to_BufferData (u32ViewData, Su8ViewBuffer, 2);
SendString (Su8ViewBuffer);
to_BufferData (u32ViewData, Su8ViewBuffer, 3);
SendString (Su8ViewBuffer);
to_BufferData (u32ViewData, Su8ViewBuffer, 4);
SendString (Su8ViewBuffer);
SendString (Cu8_0D_0A);
}
void to_BufferData (unsigned long u32Data, unsigned char *pu8Buffer, unsigned char u8Type) {
code unsigned char Cu8Array1 [] = {0xB5, 0xDA, 0x4E, 0xB8,
0xF6, 0xCA, 0xFD, 0x00};
code unsigned char Cu8Array2 [] = "十進制:";
code unsigned char Cu8Array3 [] = "十六進制:";
code unsigned char Cu8Array4 [] = "二進制:";
static unsigned char Su8SerialNumber = 1;
static unsigned int Su16BufferCnt;
static unsigned int Su16TempCnt;
static unsigned int Su16TempSet;
static unsigned long Su32Temp1;
static unsigned long Su32Temp2;
static unsigned long Su32Temp3;
static unsigned char Su8ViewFlag;
if (1 == u8Type) {
for (Su16BufferCnt = 0; Su16BufferCnt < 7; Su16BufferCnt++) {
pu8Buffer [Su16BufferCnt] = Cu8Array1 [Su16BufferCnt];
}
pu8Buffer [2] = Su8SerialNumber + '0';
pu8Buffer [Su16BufferCnt] = 0x0d;
pu8Buffer [Su16BufferCnt + 1] = 0x0a;
pu8Buffer [Su16BufferCnt + 2] = 0;
Su8SerialNumber++;
return;
} else if (2 == u8Type) {
for (Su16BufferCnt = 0; Su16BufferCnt < 7; Su16BufferCnt++) {
pu8Buffer [Su16BufferCnt] = Cu8Array2 [Su16BufferCnt];
}
Su32Temp1 = 1000000000;
Su32Temp2 = 10;
Su16TempSet = 10;
} else if (3 == u8Type) {
for (Su16BufferCnt = 0; Su16BufferCnt < 9; Su16BufferCnt++) {
pu8Buffer [Su16BufferCnt] = Cu8Array3 [Su16BufferCnt];
}
Su32Temp1 = 0x10000000;
Su32Temp2 = 0x00000010;
Su16TempSet = 8;
} else {
for (Su16BufferCnt = 0; Su16BufferCnt < 7; Su16BufferCnt++) {
pu8Buffer [Su16BufferCnt] = Cu8Array4 [Su16BufferCnt];
}
Su32Temp1 = 0x80000000;
Su32Temp2 = 0x00000002;
Su16TempSet = 32;
}
Su8ViewFlag = 0;
for (Su16TempCnt = 0; Su16TempCnt < Su16TempSet; Su16TempCnt++) {
Su32Temp3 = u32Data / Su32Temp1 % Su32Temp2;
if (Su32Temp3 < 10) {
pu8Buffer [Su16BufferCnt] = Su32Temp3 + '0';
} else {
pu8Buffer [Su16BufferCnt] = Su32Temp3 - 10 + 'A';
}
if (0 == u32Data) {
Su16BufferCnt++;
break;
} else if (0 == Su8ViewFlag) {
if ('0' != pu8Buffer [Su16BufferCnt]) {
Su8ViewFlag = 1;
Su16BufferCnt++;
}
} else {
Su16BufferCnt++;
}
Su32Temp1 = Su32Temp1 / Su32Temp2;
}
pu8Buffer [Su16BufferCnt] = 0x0d;
pu8Buffer [Su16BufferCnt + 1] = 0x0a;
pu8Buffer [Su16BufferCnt + 2] = 0;
}
void SendString (unsigned char *pu8String) {
static unsigned int Su16SendCnt;
static unsigned int Su16Delay;
SCON = 0x50;
TMOD = 0X21;
TH1 = TL1 = 256 - (11059200 / 12 / 32 / 9600);
TR1 = 1;
ES = 0;
TI = 0;
for (Su16SendCnt = 0; Su16SendCnt < 43; Su16SendCnt++) {
if (0 == pu8String [Su16SendCnt]) {
break;
} else {
SBUF = pu8String [Su16SendCnt];
for (Su16Delay = 0; Su16Delay < 800; Su16Delay++)
;
TI = 0;
}
}
}
```
【11.2 模板程序的使用說明。】

圖 11.2.1 帶串口的單片機最小系統
大多數初學者在學習 C 語言的時候,往往是在電腦端安裝上 VC 平臺軟件來練習 C 語言,這種方法只要在代碼里調用 printf 語句,編譯后就可以看到被 printf 語句調用的變量,挺方便的。本教程沒有用這種方法,既然本教程的 C 語言主要針對單片機,所以我想出了另外一種方法,這種方法就是直接在單片機上練習 C 語言,這樣會讓初學者體驗更深刻。這種方法對硬件平臺要求不高,只要 51 學習板上有一個 9 針的串口就可以,這個串口既可以用來燒錄程序,也可以用來觀察代碼里的某個變量,只要在代碼里調用 View 函數就可以達到類似 VC 平臺軟件下 printf 語句的效果,View 函數可以向串口輸出某個變量的十進制,十六進制和二進制,大家只要在電腦端的串口助手軟件就可以看到某個變量的這些信息,View 函數能查看的變量最大數值范圍是 4 個字節的 unsigned long 變量,十進制的范圍是從 0 到 4294967295,也可以查看 unsigned int 和 unsigned char 的類型變量(數據的進制以及 long,int,char 等知識點大家目前還沒接觸到,因此不懂也沒關系,當前只要有個大概的認識就可以,暫時不用深入理解,后面章節還會詳細介紹)。View 函數是我整個模板程序的其中一部分,所以要用這種方法就必須先復制我整個模板程序,初學者練習代碼的活動范圍僅僅局限于模板程序里的 “C 語言學習區域”,在此區域里有一個 main 主函數,main 主函數內有一個初始化區域,初學者往往在這個初始化區域里練習 C 語言就夠了,初學者最大的活動范圍不能超過從 “C 語言學習區域的開始” 到 “C 語言學習區域的結束” 這個范圍,
這個范圍之外其它部分的代碼主要用來實現數據處理和串口發送的功能,大家暫時不用讀懂它,直接復制過來就可以了。比如:
```c
/*---C 語言學習區域的開始。-----------------------------------------------*/
void main () // 主函數
{
//... 初始化區域,也就是主要用來給初學者學習 C 語言的區域。
while (1) {
}
}
/*---C 語言學習區域的結束。-----------------------------------------------*/
```
上述例子中,初學者練習代碼只能在從 “C 語言學習區域的開始” 到 “C 語言學習區域的結束” 這個范圍,此范圍外的代碼直接復制過來不要更改。我們再來分析分析下面節選的 main 函數源代碼:
```c
/*---C 語言學習區域的開始。-----------------------------------------------*/
void main () // 主函數
{
unsigned char a; // 定義一個變量 a。
unsigned int b; // 定義一個變量 b。
unsigned long c; // 定義一個變量 c。
a = 100; // 給變量 a 賦值。
b = 10000; // 給變量 b 賦值。
c = 1000000000; // 給變量 c 賦值。
View (a); // 在電腦串口端查看第 1 個數 a。
View (b); // 在電腦串口端查看第 2 個數 b。
View (c); // 在電腦串口端查看第 3 個數 c。
while (1) {
}
}
/*---C 語言學習區域的結束。-----------------------------------------------*/
```
上述節選的 main 函數代碼里,比如 “a=100; // 給變量 a 賦值。” 這行代碼,所謂的 “賦值” 就是 “=” 這個語句,它表面上像我們平時用的等于號,實際上不是等于號,而是代表 “給” 的意思,把 “=” 符號右邊的數復制一份給左邊的變量,比如 “a=100;” 就是代表把 100 這個數值復制一份給變量 a,執行這條指令后,a 就等于 100 了。這里的分號 “;” 代表一條程序指令的結束。 而雙斜線 “//” 是注釋語句,雙斜線 “//” 這行后面的文字或字符都是用來注釋用的,編譯器會忽略雙斜線 “//” 這一行后面的文字或字符,編譯器不把注釋文字或字符列入源代碼,也就是 “//” 這一行中后面的文字或字符是不占單片機內存的。當然 “//” 僅僅局限于當前一行代碼。上面除了 “//” 是注釋語句外,上面的 “/*” 和 “*/” 之間也是注釋語句,跟”//” 的作用一樣,只不過 “/*” 是注釋開始,“*/” 是注釋結束,它們的范圍不局限于一行,而是從 “/*” 到 “*/” 的范圍,因此可以用于注釋連著的多行文字或者字符。
接著在分析上述代碼中最重要的函數,也是本節最核心最重要的函數 View (某個變量)。比如 “ View (a); ” 這行代碼,View (a) 就是要把變量 a 的十進制,十六進制和二進制的數值都發送到串口,我們通過 USB 轉串口線讓學習板連接上電腦,在電腦串口助手軟件上就能看到被 View 函數調用的變量 a 的信息。
【11.3 如何在電腦上使用串口助手軟件查看被 View 函數調用的變量?】
前面章節在講燒錄程序時提到一個叫 “stc-isp-15xx-v6.85I” 的上位機軟件,這個軟件除了用來燒錄程序,還集成了串口助手軟件的功能。所以本節直接共用燒錄程序時的 USB 轉串口線和 “stc-isp-15xx-v6.85I” 軟件就可以了,無需額外再購買新的 USB 轉串口線和下載其它串口助手軟件,但是如何設置這個 “stc-isp-15xx-v6.85I” 上位機軟件,還是有一些需要特別注意的地方的,現在把這個詳細的步驟介紹給大家。
第一步:設置燒錄軟件的選項。
按前面章節介紹燒錄程序時所需的步驟,用 USB 轉串口線連接 51 學習板和電腦,記錄 COM 號,打開 “stc-isp-15xx-v6.85I” 軟件,選擇單片機型號,選擇對應的串口號(COM 號),設置最低波特率和最高波特率,這部分的內容跟燒錄程序時的配置步驟是一樣的,唯一必須要特別注意的是最高波特率必須選擇 9600!最低波特率建議選擇 2400。否則在燒錄完程序后,當上位機集成軟件自動切換到串口助手軟件窗口時,接收區域顯示的一些漢字信息可能會出現亂碼。
\---------------------------------- 步驟之間的分割線 ----------------------------------------

圖 11.3.2 設置上位機的串口助手選項
第二步:設置串口助手軟件的選項。
先點擊右上方選中 “串口助手” 選項切換到串口助手的窗口,接收緩沖區選擇 “文本模式”,串口選擇匹配的 COM 號(跟燒錄軟件一致的 COM 號),波特率必須選擇 9600,勾選上 “編程完成后自動打開串口” 選項,最后點擊 “打開串口” 按鈕使之切換到顯示 “關閉串口” 的文字狀態,至此串口助手軟件的設置完畢。接下來就是按燒錄程序的流程,打開新的 HEX 程序文件,程序燒錄完成后上位機軟件會自動切換到串口助手的串口,就可以觀察到 View 函數從單片機上發送過來的某個變量的十進制,十六進制,二進制的信息了。接收緩沖區的窗口比較小,如果收到的信息比較多,只要在上下方向拖動窗口右邊的滑塊就可以依次看到全部的信息。如果想讓單片機重新發送數據,只要讓 51 學習板斷電重啟就可以重發一次數據,當串口助手的接收區接收的信息太多影響觀察時,大家可以點擊 “清空接收區” 的按鈕來清屏,然后斷電重啟讓它再重發一次數據。在電腦的串口助手軟件里觀察到的數據格式大概是什么樣子的呢?比如編譯完本章節上述完整的模板源代碼程序后,會在串口助手軟件里看到 a,b,c 三個變量的信息如下:
開始...
第 1 個數
十進制:100
十六進制:64
二進制:1100100
第 2 個數
十進制:10000
十六進制:2710
二進制:10011100010000
第 3 個數
十進制:1000000000
十六進制:3B9ACA00
二進制:111011100110101100101000000000
多說一句,燒錄程序后,當軟件自動切換到串口助手軟件選項的窗口時,串口助手窗口顯示單片機返回的信息,這時有可能第一行的文字 “開始...” 會丟失或者顯示不出來,但是后面其它的關鍵信息不受影響,我猜測可能是串口助手軟件本身的某個環節存在的小 bug,跟我們沒關系,我們不用深究原因,因為不會影響我們的使用,此時也有一種解決辦法,就是只要讓單片機斷電重啟重發一次數據就可以正確地看到第一行的文字 “開始...”。
【11.4 如何利用現有的工程編輯編譯新的源代碼?】
本教程后面有很多章節的源代碼,是不是每個章節都要重新建一個工程?其實不用。我們只要用一個工程就可以編譯編輯本教程所有章節的源代碼。方法很簡單,就是打開一個現有的工程,用快捷組合鍵 “Ctrl+A” 把原工程里面的 C 源代碼全部選中,再按 “Backspace” 清空原來的代碼,然后再復制本教程相關章節的代碼粘貼到工程的 C 文檔里,重新編譯一次就可以得到對應的 Hex 格式的燒錄文件。用這種方法的時候,建議大家做好每個程序代碼的備份。每完成一個項目的小進度,都要及時把源代碼存儲到電腦硬盤里,電腦硬盤里每個項目對應一個項目文件夾,每個項目文件夾里包含很多不同版本編號的源代碼文件,每個源代碼文件名都有流水編號,方便識別最新版本的程序,每天下班前都要把最新版本的源代碼文件上傳到自己的網盤里備份,在互聯網時代,把源代碼存到自己的網盤,可以隨時異地存取,即使遇到電腦故障損壞也不擔心數據永久丟失。
【11.5 編輯源代碼的 5 個常用快捷鍵。】
介紹一下常用的快捷鍵,好好利用這 5 個快捷鍵,會讓你在編輯源代碼時效率明顯提高。
(1)選中整篇所有的內容:組合鍵 Ctrl+A。
(2)把選中的內容復制到臨時剪貼板:組合鍵 Ctrl+C。
(3)把臨時剪貼板的內容粘貼到光標開始處:組合鍵 Ctrl+V。
(4)把選中的一行或者幾行內容整體往右邊移動:單鍵 Tab。每按一次就移動幾個空格,很實用。
(5)把選中的一行或者幾行內容整體往左邊移動:組合鍵 Shift+Tab。每按一次就移動幾個空格,很實用。
- 首頁
- 第一節:我的價值觀
- 第二節:初學者的疑惑
- 第三節:單片機最重要的一個特性
- 第四節:平臺軟件和編譯器軟件的簡介
- 第五節:用Keil2軟件關閉,新建,打開一個工程的操作流程
- 第六節:把.c源代碼編譯成.hex機器碼的操作流程
- 第七節:本節預留
- 第八節:把.hex機器碼程序燒錄到單片機的操作流程
- 第九節:本節預留
- 第十節:程序從哪里開始,要到哪里去?
- 第十一節:一個在單片機上練習C語言的模板程序
- 第十二節:變量的定義和賦值
- 【TODO】第十三節:賦值語句的覆蓋性
- 【TODO】第十四節:二進制與字節單位,以及常用三種變量的取值范圍
- 【TODO】第十五節:二進制與十六進制
- 【TODO】第十六節:十進制與十六進制
- 【TODO】第十七節:加法運算的5種常用組合
- 【TODO】第十八節:連加、自加、自加簡寫、自加1
- 【TODO】第十九節:加法運算的溢出
- 【TODO】第二十節:隱藏中間變量為何物?
- 【TODO】第二十一節:減法運算的5種常用組合。
- 【TODO】第二十二節:連減、自減、自減簡寫、自減1
- 【TODO】第二十三節:減法溢出與假想借位
- 【TODO】第二十四節:借用unsigned long類型的中間變量可以減少溢出現象
- 【TODO】第二十五節:乘法運算中的5種常用組合
- 【TODO】第二十六節:連乘、自乘、自乘簡寫,溢出
- 【TODO】第二十七節:整除求商
- 【TODO】第二十八節:整除求余
- 【TODO】第二十九節:“先余后商”和“先商后余”提取數據某位,哪家強?
- 【TODO】第三十節:邏輯運算符的“與”運算
- 【TODO】第三十一節:邏輯運算符的“或”運算
- 【TODO】第三十二節:邏輯運算符的“異或”運算
- 【TODO】第三十三節:邏輯運算符的“按位取反”和“非”運算
- 【TODO】第三十四節:移位運算的左移
- 【TODO】第三十五節:移位運算的右移
- 【TODO】第三十六節:括號的強制功能---改變運算優先級
- 【TODO】第三十七節:單字節變量賦值給多字節變量的疑惑
- 【TODO】第三十八節:第二種解決“運算過程中意外溢出”的便捷方法
- 【TODO】第三十九節:if判斷語句以及常量變量的真假判斷
- 【TODO】第四十節:關系符的等于“==”和不等于“!=”
- 【TODO】第四十一節:關系符的大于“>”和大于等于“>=”
- 【TODO】第四十二節:關系符的小于“<”和小于等于“<=”
- 【TODO】第四十三節:關系符中的關系符:與“&&”,或“||”
- 【TODO】第四十四節:小括號改變判斷優先級
- 【TODO】第四十五節: 組合判斷if...else if...else
- 【TODO】第四十六節: 一維數組
- 【TODO】第四十七節: 二維數組
- 【TODO】第四十八節: while循環語句
- 【TODO】第四十九節: 循環語句do while和for
- 【TODO】第五十節: 循環體內的continue和break語句
- 【TODO】第五十一節: for和while的循環嵌套
- 【TODO】第五十二節: 支撐程序框架的switch語句
- 【TODO】第五十三節: 使用函數的三要素和執行順序
- 【TODO】第五十四節: 從全局變量和局部變量中感悟“棧”為何物
- 【TODO】第五十五節: 函數的作用和四種常見書寫類型
- 【TODO】第五十六節: return在函數中的作用以及四個容易被忽略的功能
- 【TODO】第五十七節: static的重要作用
- 【TODO】第五十八節: const(./book/或code)在定義數據時的作用
- 【TODO】第五十九節: 全局“一鍵替換”功能的#define
- 【TODO】第六十節: 指針在變量(./book/或常量)中的基礎知識
- 【TODO】第六十一節: 指針的中轉站作用,地址自加法,地址偏移法
- 【TODO】第六十二節: 指針,大小端,化整為零,化零為整
- 【TODO】第六十三節: 指針“化整為零”和“化零為整”的“靈活”應用
- 【TODO】第六十四節: 指針讓函數具備了多個相當于return的輸出口
- 【TODO】第六十五節: 指針作為數組在函數中的入口作用
- 【TODO】第六十六節: 指針作為數組在函數中的出口作用
- 【TODO】第六十七節: 指針作為數組在函數中既“入口”又“出口”的作用
- 【TODO】第六十八節: 為函數接口指針“定向”的const關鍵詞
- 【TODO】第六十九節: 宏函數sizeof(./book/)
- 【TODO】第七十節: “萬能數組”的結構體
- 【TODO】第七十一節: 結構體的內存和賦值
- 【TODO】第七十二節: 結構體的指針
- 【TODO】第七十三節: 結構體數據的傳輸存儲和還原
- 【TODO】第七十四節: 結構體指針在函數接口處的頻繁應用
- 【TODO】第七十五節: 指針的名義(例:一維指針操作二維數組)
- 【TODO】第七十六節: 二維數組的指針
- 【TODO】第七十七節: 指針唯一的“單向輸出”通道return
- 【TODO】第七十八節: typedef和#define和enum
- 【TODO】第七十九節: 各種變量常量的命名規范
- 【TODO】第八十節: 單片機IO口驅動LED
- 【TODO】第八十一節: 時間和速度的起源(指令周期和晶振頻率)
- 【TODO】第八十二節: Delay“阻塞”延時控制LED閃爍
- 【TODO】第八十三節: 累計主循環的“非阻塞”延時控制LED閃爍
- 【TODO】第八十四節: 中斷與中斷函數
- 【TODO】第八十五節: 定時中斷的寄存器配置
- 【TODO】第八十六節: 定時中斷的“非阻塞”延時控制LED閃爍
- 【TODO】第八十七節: 一個定時中斷產生N個軟件定時器
- 【TODO】第八十八節: 兩大核心框架理論(四區一線,switch外加定時中斷)
- 【TODO】第八十九節: 跑馬燈的三種境界
- 【TODO】第九十節: 多任務并行處理兩路跑馬燈
- 【TODO】第九十一節: 蜂鳴器的“非阻塞”驅動
- 【TODO】第九十二節: 獨立按鍵的四大要素(自鎖,消抖,非阻塞,清零式濾波)
- 【TODO】第九十三節: 獨立按鍵鼠標式的單擊與雙擊
- 【TODO】第九十四節: 兩個獨立按鍵構成的組合按鍵
- 【TODO】第九十五節: 兩個獨立按鍵的“電腦鍵盤式”組合按鍵
- 【TODO】第九十六節: 獨立按鍵“一鍵兩用”的短按與長按
- 【TODO】第九十七節: 獨立按鍵按住不松手的連續均勻觸發
- 【TODO】第九十八節: 獨立按鍵按住不松手的“先加速后勻速”的觸發
- 【TODO】第九十九節: “行列掃描式”矩陣按鍵的單個觸發(原始版)
- 【TODO】第一百節: “行列掃描式”矩陣按鍵的單個觸發(優化版)
- 【TODO】第一百零一節: 矩陣按鍵鼠標式的單擊與雙擊
- 【TODO】第一百零二節: 兩個“任意行輸入”矩陣按鍵的“有序”組合觸發
- 【TODO】第一百零三節: 兩個“任意行輸入”矩陣按鍵的“無序”組合觸發
- 【TODO】第一百零四節: 矩陣按鍵“一鍵兩用”的短按與長按
- 【TODO】第一百零五節: 矩陣按鍵按住不松手的連續均勻觸發
- 【TODO】第一百零六節: 矩陣按鍵按住不松手的“先加速后勻速”觸發
- 【TODO】第一百零七節: 開關感應器的識別與軟件濾波
- 【TODO】第一百零八節: 按鍵控制跑馬燈的啟動和暫停和停止
- 【TODO】第一百零九節: 按鍵控制跑馬燈的方向
- 【TODO】第一百一十節: 按鍵控制跑馬燈的速度
- 第一百一十一節: 工業自動化設備的開關信號的運動控制
- 【TODO】第一百一十二節: 數碼管顯示的基礎知識
- 【TODO】第一百一十三節: 動態掃描的數碼管顯示數字
- 【TODO】第一百一十四節: 動態掃描的數碼管顯示小數點
- 【TODO】第一百一十五節: 按鍵控制數碼管的秒表
- 【TODO】第一百一十六節: 按鍵控制數碼管的倒計時
- 【TODO】第一百一十七節: 按鍵切換數碼管窗口來設置參數
- 【TODO】第一百一十八節: 按鍵讓某位數碼管閃爍跳動來設置參數
- 【TODO】第一百一十九節: 一個完整的人機界面的程序框架的脈絡
- 【TODO】第一百二十節: 按鍵切換窗口切換局部來設置參數
- 【TODO】第一百二十一節: 可調參數的數碼管倒計時
- 【TODO】第一百二十二節: 利用定時中斷做的“時分秒”數顯時鐘
- 【TODO】第一百二十三節: 一種能省去一個lock自鎖變量的按鍵驅動程序
- 【TODO】第一百二十四節: 數顯儀表盤顯示“速度、方向、計數器”的跑馬燈
- 【TODO】第一百二十五節: “雙線”的肢體接觸通信
- 【TODO】第一百二十六節: “單線”的肢體接觸通信
- 【TODO】第一百二十七節: 單片機串口接收數據的機制
- 【TODO】第一百二十八節: 接收“固定協議”的串口程序框架
- 【TODO】第一百二十九節: 接收帶“動態密匙”與“累加和”校驗數據的串口程序框架
- 【TODO】第一百三十節: 接收帶“動態密匙”與“異或”校驗數據的串口程序框架
- 【TODO】第一百三十一節: 靈活切換各種不同大小“接收內存”的串口程序框架
- 【TODO】第一百三十二節:“轉發、透傳、多種協議并存”的雙緩存串口程序框架
- 【TODO】第一百三十三節:常用的三種串口發送函數
- 【TODO】第一百三十四節:“應用層半雙工”雙機串口通訊的程序框架