鍍金池/ 教程/ 數(shù)據(jù)庫(kù)/ 單片機(jī)通信實(shí)例與 ASCII 碼
8.3 C 語(yǔ)言函數(shù)的形參和實(shí)參
12.2 C 語(yǔ)言指針變量的聲明
12.5 ?C 語(yǔ)言字符數(shù)組和字符指針
7.3 單片機(jī) LED 點(diǎn)陣的介紹
11.5 UART 串口通信的基本應(yīng)用
9.9 單片機(jī)蜂鳴器控制程序和驅(qū)動(dòng)電路
10. 單片機(jī)實(shí)例練習(xí)與經(jīng)驗(yàn)積累
10.3 單片機(jī)交通燈控制程序和設(shè)計(jì)原理
9.8 實(shí)用的 28BYJ-48 步進(jìn)電機(jī)控制程序
8.2 C 語(yǔ)言函數(shù)的調(diào)用
12.4 C 語(yǔ)言指向數(shù)組元素的指針
7.1 C 語(yǔ)言變量的作用域
11.2 RS232 通信接口
12.7 1602 液晶的讀寫(xiě)時(shí)序介紹
7.2 C 語(yǔ)言變量的存儲(chǔ)類(lèi)別
8. C 語(yǔ)言函數(shù)進(jìn)階與單片機(jī)按鍵
10.4 51單片機(jī) RAM 區(qū)域的劃分
12.1 C 語(yǔ)言變量的地址
11. UART 串口通信
7. 變量進(jìn)階與點(diǎn)陣 LED
8.4 單片機(jī)按鍵介紹
9.3 電機(jī)的分類(lèi)
9.1 單片機(jī) IO 口的結(jié)構(gòu)
單片機(jī)通信實(shí)例與 ASCII 碼
8.1 單片機(jī)最小系統(tǒng)解析(電源、晶振和復(fù)位電路)
9.2 單片機(jī)上下拉電阻
11.4 單片機(jī) IO 口模擬 UART 串口通信
9.5 讓 28BYJ-48 步進(jìn)電機(jī)轉(zhuǎn)起來(lái)
9.7 28BYJ-48 步進(jìn)電機(jī)控制程序基礎(chǔ)
12.8 1602 液晶指令介紹
12.3 C 語(yǔ)言指針的簡(jiǎn)單示例
8.7 單片機(jī)矩陣按鍵的掃描
7.4 單片機(jī) LED 點(diǎn)陣的圖形顯示
8.6 單片機(jī)按鍵消抖程序
10.2 單片機(jī)中 PWM 的原理與控制程序
7.6 單片機(jī) LED 點(diǎn)陣的橫向移動(dòng)(動(dòng)態(tài)顯示)
11.3 USB 轉(zhuǎn)串口通信
12.9 1602 液晶簡(jiǎn)單顯示程序
9.4 28BYJ-48 步進(jìn)電機(jī)原理
8.5 ?單片機(jī)獨(dú)立按鍵掃描程序
12. C 語(yǔ)言指針基礎(chǔ)與1602液晶的初步認(rèn)識(shí)
9. 單片機(jī)中的步進(jìn)電機(jī)與蜂鳴器
10.1 單片機(jī)數(shù)字秒表程序
7.5 單片機(jī) LED 點(diǎn)陣的縱向移動(dòng)(動(dòng)態(tài)顯示)
8.8 單片機(jī)簡(jiǎn)易加法計(jì)算器程序
11.1 單片機(jī)串行通信介紹
10.5 單片機(jī)長(zhǎng)短按鍵的應(yīng)用
12.6 1602 液晶介紹(電路和引腳圖)
9.6 28BYJ-48 步進(jìn)電機(jī)轉(zhuǎn)動(dòng)精度與深入分析

單片機(jī)通信實(shí)例與 ASCII 碼

我們學(xué)習(xí)串口通信主要是要實(shí)現(xiàn)單片機(jī)和電腦之間的信息交互,可以用電腦控制單片機(jī)的一些信息,可以把單片機(jī)的一些信息狀況發(fā)給電腦上的軟件。下面我們就做一個(gè)簡(jiǎn)單的例程,實(shí)現(xiàn)單片機(jī)串口調(diào)試助手發(fā)送的數(shù)據(jù),在我們開(kāi)發(fā)板上的數(shù)碼管上顯示出來(lái)。

#include <reg52.h>

sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

unsigned char code LedChar[] = { //數(shù)碼管顯示字符轉(zhuǎn)換表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[7] = { //數(shù)碼管+獨(dú)立 LED 顯示緩沖區(qū)
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
unsigned char T0RH = 0; //T0 重載值的高字節(jié)
unsigned char T0RL = 0; //T0 重載值的低字節(jié)
unsigned char RxdByte = 0; //串口接收到的字節(jié)
void ConfigTimer0(unsigned int ms);
void ConfigUART(unsigned int baud);
void main(){
    EA = 1; //使能總中斷
    ENLED = 0; //選擇數(shù)碼管和獨(dú)立 LED
    ADDR3 = 1;
    ConfigTimer0(1); //配置 T0 定時(shí) 1 ms
    ConfigUART(9600); //配置波特率為9600
    while (1){ //將接收字節(jié)在數(shù)碼管上以十六進(jìn)制形式顯示出來(lái)
        LedBuff[0] = LedChar[RxdByte & 0x0F];
        LedBuff[1] = LedChar[RxdByte >> 4];
    }
}
/* 配置并啟動(dòng) T0,ms-T0 定時(shí)時(shí)間 */
void ConfigTimer0(unsigned int ms){
    unsigned long tmp; //臨時(shí)變量
    tmp = 11059200 / 12; //定時(shí)器計(jì)數(shù)頻率
    tmp = (tmp * ms) / 1000; //計(jì)算所需的計(jì)數(shù)值
    tmp = 65536 - tmp; //計(jì)算定時(shí)器重載值
    tmp = tmp + 13; //補(bǔ)償中斷響應(yīng)延時(shí)造成的誤差
    T0RH = (unsigned char)(tmp>>8); //定時(shí)器重載值拆分為高低字節(jié)
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0; //清零 T0 的控制位
    TMOD |= 0x01; //配置 T0 為模式1
    TH0 = T0RH; //加載 T0 重載值
    TL0 = T0RL;
    ET0 = 1; //使能 T0 中斷
    TR0 = 1; //啟動(dòng) T0
}
/* 串口配置函數(shù),baud-通信波特率 */
void ConfigUART(unsigned int baud){
    SCON = 0x50; //配置串口為模式1
    TMOD &= 0x0F; //清零 T1 的控制位
    TMOD |= 0x20; //配置 T1 為模式2
    TH1 = 256 - (11059200/12/32)/baud; //計(jì)算 T1 重載值
    TL1 = TH1; //初值等于重載值
    ET1 = 0; //禁止 T1 中斷
    ES = 1; //使能串口中斷
    TR1 = 1; //啟動(dòng) T1
}
/* LED 動(dòng)態(tài)掃描刷新函數(shù),需在定時(shí)中斷中調(diào)用 */
void LedScan(){
    static unsigned char i = 0; //動(dòng)態(tài)掃描索引
    P0 = 0xFF; //關(guān)閉所有段選位,顯示消隱
    P1 = (P1 & 0xF8) | i; //位選索引值賦值到 P1 口低3位
    P0 = LedBuff[i]; //緩沖區(qū)中索引位置的數(shù)據(jù)送到 P0 口
    if (i < 6){ //索引遞增循環(huán),遍歷整個(gè)緩沖區(qū)
        i++;
    }else{
        i = 0;
    }
}
/* T0 中斷服務(wù)函數(shù),完成 LED 掃描 */
void InterruptTimer0() interrupt 1{
    TH0 = T0RH; //重新加載重載值
    TL0 = T0RL;
    LedScan(); //LED 掃描顯示
}
/* UART 中斷服務(wù)函數(shù) */
void InterruptUART() interrupt 4{
    if (RI){ //接收到字節(jié)
        RI = 0; //手動(dòng)清零接收中斷標(biāo)志位
        RxdByte = SBUF; //接收到的數(shù)據(jù)保存到接收字節(jié)變量中
        //接收到的數(shù)據(jù)又直接發(fā)回,叫作-"echo",
        //用以提示用戶(hù)輸入的信息是否已正確接收
        SBUF = RxdByte;
    }
    if (TI){ //字節(jié)發(fā)送完畢
        TI = 0; //手動(dòng)清零發(fā)送中斷標(biāo)志位
    }
}

大家在做這個(gè)實(shí)驗(yàn)的時(shí)候,有個(gè)小問(wèn)題要注意一下。因?yàn)?STC89C52 下載程序是使用了 UART 串口下載,下載完程序后,程序運(yùn)行起來(lái)了,可是下載軟件最后還會(huì)通過(guò)串口發(fā)送一些額外的數(shù)據(jù),所以程序剛下載進(jìn)去不是顯示00,而可能是其他數(shù)據(jù)。大家只要把電源開(kāi)關(guān)關(guān)閉,重新打開(kāi)一次就好了。

細(xì)心的同學(xué)可能會(huì)發(fā)現(xiàn),在串口調(diào)試助手發(fā)送選項(xiàng)和接收選項(xiàng)處,還有個(gè)“字符格式發(fā)送”和“字符格式顯示”,這是什么意思呢?

先拋開(kāi)我們使用的漢字不談,那么我們常用的字符就包含了0~9的數(shù)字、A~Z/a~z 的字母、還有各種標(biāo)點(diǎn)符號(hào)等。那么在單片機(jī)系統(tǒng)里面我們?cè)趺磥?lái)表示它們呢? ASCII 碼(American Standard Code for Information Interchange,即美國(guó)信息互換標(biāo)準(zhǔn)代碼)可以完成這個(gè)使命:我們知道,在單片機(jī)中一個(gè)字節(jié)的數(shù)據(jù)可以有0~255共256個(gè)值,我們?nèi)∑渲械?~127共128個(gè)值賦予了它另外一層涵義,即讓它們分別來(lái)代表一個(gè)常用字符,其具體的對(duì)應(yīng)關(guān)系如表11-3所示。

表11-3 ASCII 碼字符表

ASCII 控制 ASCII 字符 ASCII 字符 ASCII 字符
000 NUL字符 032 (space) 064值 @ 096 ?
001 SOH 033 ! 065 A 097 a
002 STX 034 " 066 B 098 b
003 ETX 035 # 067 C 099 c
004 EOT 036 $ 068 D 100 d
005 END 037 % 069 E 101 e
006 ACK 038 & 070 F 102 f
007 BEL 039 ' 071 G 103 g
008 BS 040 ( 072 H 104 h
009 HT 041 ) 073 I 105 i
010 LF 042 * 074 J 106 j
011 VT 043 + 075 K 107 k
012 FF 044 , 076 L 108 l
013 CR 045 - 077 M 109 m
014 SO 046 078 N 110 n
015 SI 047 / 079 O 111 o
016 DLE 048 0 080 P 112 p
017 DC1 049 1 081 Q 113 q
018 DC2 050 2 082 R 114 r
019 DC3 051 3 083 S 115 s
020 DC4 052 4 084 T 116 t
021 NAK 053 5 085 U 117 u
022 SYN 054 6 086 V 118 v
023 ETB 055 7 087 W 119 w
024 CAN 056 8 088 X 120 x
025 EM 057 9 089 Y 121 y
026 SUB 058 : 090 Z 122 z
027 ESC 059 ; 091 [ 123 {
028 FS 060 < 092 |124 |
029 GS 061 = 093 ] 125 }
030 RS 062 > 094 ^ 126 ~
031 US 063 ? 095 _ 127 DEL

這樣我們就在常用字符和字節(jié)數(shù)據(jù)之間建立了一一對(duì)應(yīng)的關(guān)系,那么現(xiàn)在一個(gè)字節(jié)就既可以代表一個(gè)整數(shù)又可以代表一個(gè)字符了,但它本質(zhì)上只是一個(gè)字節(jié)的數(shù)據(jù),而我們賦予了它不同的涵義,什么時(shí)候賦予它哪種涵義就看編程者的意圖了。ASCII 碼在單片機(jī)系統(tǒng)中應(yīng)用非常廣泛,我們后續(xù)的課程也會(huì)經(jīng)常使用到它,下面我們來(lái)對(duì)它做一個(gè)直觀的認(rèn)識(shí),同學(xué)們一定要深刻理解其本質(zhì)。

對(duì)照上述表格,我們就可以實(shí)現(xiàn)字符和數(shù)字之間的轉(zhuǎn)換了,比如還是這個(gè)程序,我們發(fā)送的時(shí)候改成字符格式發(fā)送,接收還是用十六進(jìn)制接收,這樣接收和數(shù)碼管好做一下對(duì)比。

我們用字符格式發(fā)送一個(gè)小寫(xiě)的 a,返回一個(gè)十六進(jìn)制的 0x61,數(shù)碼管上顯示的也是61,ASCII 碼表里字符 a 對(duì)應(yīng)十進(jìn)制是97,等于十六進(jìn)制的 0x61;我們?cè)儆米址袷桨l(fā)送一個(gè)數(shù)字1,返回一個(gè)十六進(jìn)制的 0x31,數(shù)碼管上顯示的也是31,ASCII 表里字符1對(duì)應(yīng)的十進(jìn)制是49,等于十六進(jìn)制的 0x31。這下大家就該清楚了:所謂的十六進(jìn)制發(fā)送和十六進(jìn)制接收,都是按字節(jié)數(shù)據(jù)的真實(shí)值進(jìn)行的;而字符格式發(fā)送和字符格式接收,是按 ASCII 碼表中字符形式進(jìn)行的,但它實(shí)際上最終傳輸?shù)倪€是一個(gè)字節(jié)數(shù)據(jù)。這個(gè)表格,當(dāng)然不需要大家去記住,理解它,用的時(shí)候過(guò)來(lái)查就行了。

通信的學(xué)習(xí),不像前邊控制部分那么直觀了,通信部分我們的程序只能獲得一個(gè)結(jié)果,而其過(guò)程我們卻無(wú)法直接看到,所以慢慢的可能大家就會(huì)知道有示波器和邏輯分析儀這類(lèi)測(cè)量?jī)x器。如果學(xué)校實(shí)驗(yàn)室或者公司里有示波器或者邏輯分析儀這類(lèi)儀器,可以拿過(guò)來(lái)抓一下串口波形,直觀的了解一下。如果暫時(shí)還沒(méi)有這些儀器,先知道這么回事,有條件再說(shuō)。因?yàn)楣ぞ哳?lèi)設(shè)備有的比較昂貴,有條件可以盡量使用學(xué)校或者公司的。在這里我用一款簡(jiǎn)易的邏輯分析儀把串口通信的波形抓出來(lái)給大家看一下,大家了解一下即可,如圖11-7所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-two/images/41.png" alt="" />

圖11-7 邏輯分析儀串口數(shù)據(jù)示意圖

分析儀和示波器的作用,就是把通信過(guò)程的波形抓出來(lái)進(jìn)行分析。先大概說(shuō)一下波形的意思。波形左邊是低位,右邊是高位,上邊這個(gè)波形是電腦發(fā)送給單片機(jī)的,下邊這個(gè)波形是單片機(jī)回發(fā)給電腦的。以上邊的波形為例,左邊第一位是起始位0,從低位到高位依次是10001100,順序倒一下,就是數(shù)據(jù) 0x31,也就是 ASCII 碼表里的‘1’。大家可以注意到分析儀在每個(gè)數(shù)據(jù)位都給標(biāo)了一個(gè)白色的點(diǎn),表示是數(shù)據(jù),起始位和無(wú)數(shù)據(jù)的時(shí)候都沒(méi)有這個(gè)白點(diǎn)。時(shí)間標(biāo) T1 和 T2 的差值在右邊顯示出來(lái)是 0.102 ms,大概是9600分之一,稍微有點(diǎn)偏差,在容許范圍內(nèi)即可。通過(guò)圖11-7,我們可以清晰的了解了串口通信的收發(fā)的詳細(xì)過(guò)程。

那我們這里再來(lái)了解一下,如果我們使用串口調(diào)試助手,用字符格式直接發(fā)送一個(gè)“12”,我們?cè)谖覀兊臄?shù)碼管上應(yīng)該顯示什么呢?串口調(diào)試助手應(yīng)該返回什么呢?經(jīng)過(guò)試驗(yàn)發(fā)現(xiàn),我們數(shù)碼管顯示的是32,而串口調(diào)試助手返回十六進(jìn)制顯示的是31、32兩個(gè)數(shù)據(jù),如圖11-8所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-two/images/42.png" alt="" />

圖11-8 串口調(diào)試助手?jǐn)?shù)據(jù)顯示

我們?cè)儆眠壿嫹治鰞x把這個(gè)數(shù)據(jù)抓出來(lái)看一下,如圖11-9所示。

http://wiki.jikexueyuan.com/project/mcu-tutorial-two/images/43.png" alt="" />

圖11-9 邏輯分析儀抓取數(shù)據(jù)

對(duì)于 ASCII 碼表來(lái)說(shuō),數(shù)字本身是字符而非數(shù)據(jù),所以如果發(fā)送“12”的話,實(shí)際上是是分別發(fā)送了“1”和“2”兩個(gè)字符,單片機(jī)呢,先收到第一個(gè)字符“1”,在數(shù)碼管上會(huì)顯示出31這個(gè)對(duì)應(yīng)數(shù)字,但是馬上就又收到了“2”這個(gè)字符,數(shù)碼管瞬間從31變成了32,而我們視覺(jué)上呢,是沒(méi)有辦法發(fā)現(xiàn)這種快速變化的,所以我們感覺(jué)數(shù)碼管直接顯示的是32。