字符和數(shù)據(jù)之間的轉(zhuǎn)換
我們學(xué)串口通信的應(yīng)用主要是實(shí)現(xiàn)單片機(jī)和電腦之間的信息互發(fā),可以用電腦控制單片機(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; //LED選擇地址線3
sbit ENLED = P1^4; //LED總使能引腳
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[6] = { //數(shù)碼管
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 ()
{
P0 = 0xFF; //P0口初始化
ADDR3 = 1; //選擇數(shù)碼管
ENLED = 0; //LED總使能
EA = 1; //開(kāi)總中斷
ConfigTimer0(1); //配置T0定時(shí)1ms
ConfigUART(9600); //配置波特率為9600
while(1)
{ //將接收字節(jié)在數(shù)碼管上以十六進(jìn)制形式顯示出來(lái)
LedBuff[0] = LedChar[RxdByte & 0x0F];
LedBuff[1] = LedChar[RxdByte >> 4];
}
}
void ConfigTimer0(unsigned int ms) //T0配置函數(shù)
{
unsigned long tmp;
tmp = 11059200 / 12; //定時(shí)器計(jì)數(shù)頻率
tmp = (tmp * ms) / 1000; //計(jì)算所需的計(jì)數(shù)值
tmp = 65536 - tmp; //計(jì)算定時(shí)器重載值
tmp = tmp + 31; //修正中斷響應(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
}
void ConfigUART(unsigned int baud) //串口配置函數(shù),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
}
void LedScan() //LED顯示掃描函數(shù)
{
static unsigned char index = 0;
P0 = 0xFF; //關(guān)閉所有段選位,顯示消隱
P1 = (P1 & 0xF8) | index; //位選索引值賦值到P1口低3位
P0 = LedBuff[index]; //相應(yīng)顯示緩沖區(qū)的值賦值到P0口
if (index < 5) //位選索引0-5循環(huán),因有6個(gè)數(shù)碼管
index++;
else
index = 0;
}
void InterruptTimer0() interrupt 1 //T0中斷服務(wù)函數(shù)
{
TH0 = T0RH; //定時(shí)器重新加載重載值
TL0 = T0RL;
LedScan(); //LED掃描顯示
}
void InterruptUART() interrupt 4
{
if (RI) //接收到字節(jié)
{
RI = 0; //手動(dòng)清零接收中斷標(biāo)志位
RxdByte = SBUF; //接收到的數(shù)據(jù)保存到接收字節(jié)變量中
SBUF = RxdByte; //接收到的數(shù)據(jù)又直接發(fā)回,這叫回顯-"echo",以提示用戶輸入的信息是否已正確接收
}
if (TI) //字節(jié)發(fā)送完畢
{
TI = 0; //手動(dòng)清零發(fā)送中斷標(biāo)志位
}
}
大家在做這個(gè)實(shí)驗(yàn)的時(shí)候,有個(gè)小問(wèn)題要注意一下。因?yàn)槲覀僑TC89C52RC下載程序是使用了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)系如下表。
表1 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ì)知道有示波器和邏輯分析儀這類測(cè)量?jī)x器。如果學(xué)校實(shí)驗(yàn)室或者公司里有示波器或者邏輯分析儀這類儀器,可以拿過(guò)來(lái)抓一下串口波形,直觀的了解一下。如果暫時(shí)還沒(méi)有這些儀器,先知道這么回事,有條件再說(shuō)。因?yàn)楣ぞ哳惖臇|西有的比較昂貴,有條件可以盡量使用學(xué);蛘吖镜。在這里我用一款簡(jiǎn)易的邏輯分析儀把串口通信的波形抓出來(lái)給大家看一下,大家了解一下即可,如圖1所示。
圖1 邏輯分析儀串口數(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.102ms,大概是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ù),如圖2所示。
圖2 串口調(diào)試助手?jǐn)?shù)據(jù)顯示
我們用邏輯分析儀把這個(gè)數(shù)據(jù)抓出來(lái)看一下,如圖3所示。
圖3 邏輯分析儀抓取數(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。
編輯:admin 最后修改時(shí)間:2018-05-08