UART串列埠通訊淺談之(三)--字元與資料的轉換
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/solar_Lan/article/details/78093692
學串列埠通訊的應用主要是實現微控制器和電腦之間的資訊互發,可以用電腦控制微控制器的一些資訊,可以把微控制器的一些資訊狀況發給電腦上的軟體。下面就做一個簡單的例程,實現微控制器串列埠除錯助手傳送的資料,在開發板上的數碼管上顯示出來。
-
#include <reg52.h>
-
sbit ADDR3 = P1^3; //LED選擇地址線3
-
sbit ENLED = P1^4; //LED總使能引腳
-
unsigned char code LedChar[] = { //數碼管顯示字元轉換表
-
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
-
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
-
};
-
unsigned char LedBuff[6] = { //數碼管
-
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-
};
-
unsigned char T0RH = 0; //T0過載值的高位元組
-
unsigned char T0RL = 0; //T0過載值的低位元組
-
unsigned char RxdByte = 0; //串列埠接收到的位元組
-
void ConfigTimer0(unsigned int ms);
-
void ConfigUART(unsigned int baud);
-
void main ()
-
{
-
P0 = 0xFF; //P0口初始化
-
ADDR3 = 1; //選擇數碼管
-
ENLED = 0; //LED總使能
-
EA = 1; //開總中斷
-
ConfigTimer0(1); //配置T0定時1ms
-
ConfigUART(9600); //配置波特率為9600
-
while(1)
-
{ //將接收位元組在數碼管上以十六進位制形式顯示出來
-
LedBuff[0] = LedChar[RxdByte & 0x0F];
-
LedBuff[1] = LedChar[RxdByte >> 4];
-
}
-
}
-
void ConfigTimer0(unsigned int ms) //T0配置函式
-
{
-
unsigned long tmp;
-
tmp = 11059200 / 12; //定時器計數頻率
-
tmp = (tmp * ms) / 1000; //計算所需的計數值
-
tmp = 65536 - tmp; //計算定時器過載值
-
tmp = tmp + 31; //修正中斷響應延時造成的誤差
-
T0RH = (unsigned char)(tmp >> 8); //定時器過載值拆分為高低位元組
-
T0RL = (unsigned char)tmp;
-
TMOD &= 0xF0; //清零T0的控制位
-
TMOD |= 0x01; //配置T0為模式1
-
TH0 = T0RH; //載入T0過載值
-
TL0 = T0RL;
-
ET0 = 1; //使能T0中斷
-
TR0 = 1; //啟動T0
-
}
-
void ConfigUART(unsigned int baud) //串列埠配置函式,baud為波特率
-
{
-
SCON = 0x50; //配置串列埠為模式1
-
TMOD &= 0x0F; //清零T1的控制位
-
TMOD |= 0x20; //配置T1為模式2
-
TH1 = 256 - (11059200/12/32) / baud; //計算T1過載值
-
TL1 = TH1; //初值等於過載值
-
ET1 = 0; //禁止T1中斷
-
ES = 1; //使能串列埠中斷
-
TR1 = 1; //啟動T1
-
}
-
void LedScan() //LED顯示掃描函式
-
{
-
static unsigned char index = 0;
-
P0 = 0xFF; //關閉所有段選位,顯示消隱
-
P1 = (P1 & 0xF8) | index; //位選索引值賦值到P1口低3位
-
P0 = LedBuff[index]; //相應顯示緩衝區的值賦值到P0口
-
if (index < 5) //位選索引0-5迴圈,因有6個數碼管
-
index++;
-
else
-
index = 0;
-
}
-
void InterruptTimer0() interrupt 1 //T0中斷服務函式
-
{
-
TH0 = T0RH; //定時器重新載入過載值
-
TL0 = T0RL;
-
LedScan(); //LED掃描顯示
-
}
-
void InterruptUART() interrupt 4
-
{
-
if (RI) //接收到位元組
-
{
-
RI = 0; //手動清零接收中斷標誌位
-
RxdByte = SBUF; //接收到的資料儲存到接收位元組變數中
-
SBUF = RxdByte; //接收到的資料又直接發回,這叫回顯-"echo",以提示使用者輸入的資訊是否已正確接收
-
}
-
if (TI) //位元組傳送完畢
-
{
-
TI = 0; //手動清零傳送中斷標誌位
-
}
-
}
大家在做這個實驗的時候,有個小問題要注意一下。因為STC89C52RC下載程式是使用了UART串列埠下載,下載完程式後,程式執行起來了,可是下載軟體最後還會通過串列埠傳送一些額外的資料,所以程式剛下載進去不是顯示00,而可能是其他資料。重啟開啟一次就好了。
常用的字元就包含了0~9的數字、A~Z/a~z的字母、還有各種標點符號等。那麼在微控制器系統裡面我們怎麼來表示它們呢?
ASCII碼(American Standard Code for Information Interchange,即美國資訊互換標準程式碼)可以完成這個使命:在微控制器中一個位元組的資料可以有0~255共256個值,我們取其中的0~127共128個值賦予了它另外一層涵義,即讓它們分別來代表一個常用字元,其具體的對應關係如下表。
表1-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 |
這樣就在常用字元和位元組資料之間建立了一一對應的關係,那麼現在一個位元組就既可以代表一個整數又可以代表一個字元了,但它本質上只是一個位元組的資料,可以賦予了它不同的涵義,什麼時候賦予它那種涵義就看程式設計者的意圖了。ASCII碼在微控制器系統中應用非常廣泛,下面來對它做一個直觀的認識,一定要深刻理解其本質。
對照上述表格,就可以實現字元和數字之間的轉換了,比如還是這個程式,我們傳送的時候改成字元格式傳送,接收還是用十六進位制接收,這樣接收和數碼管好做一下對比。
用字元格式傳送一個小寫的a,返回一個十六進位制的0x61,數碼管上顯示的也是61,ASCII碼錶裡字元a對應十進位制是97,等於十六進位制的0x61;再用字元格式傳送一個數字1,返回一個十六進位制的0x31,數碼管上顯示的也是31,ASCII表裡字元1對應的十進位制是49,等於十六進位制的0x31。這下大家就該清楚了:所謂的十六進位制傳送和十六進位制接收,都是按位元組資料的真實值進行的;而字元格式傳送和字元格式接收,是按ASCII碼錶中字元形式進行的,但它實際上最終傳輸的還是一個位元組資料。這個表格,當然不需要大家去記住,理解它,用的時候過來查就行了。
通訊的學習,不像前邊控制部分那麼直觀了,通訊部分我們的程式只能獲得一個結果,而其過程我們卻無法直接看到,所以慢慢的可能大家就會知道有示波器和邏輯分析儀這類測量儀器。如果學校實驗室或者公司裡有示波器或者邏輯分析儀這類儀器,可以拿過來抓一下串列埠波形,直觀的瞭解一下。如果暫時還沒有這些儀器,先知道這麼回事,有條件再說。因為工具類的東西有的比較昂貴,有條件可以儘量使用學校或者公司的。在這裡我用一款簡易的邏輯分析儀把串列埠通訊的波形抓出來給大家看一下,大家瞭解一下即可,如圖1-1所示。
圖1-1 邏輯分析儀串列埠資料示意圖
分析儀和示波器的作用,就是把通訊過程的波形抓出來進行分析。先大概說一下波形的意思。波形左邊是低位,右邊是高位,上邊這個波形是電腦傳送給微控制器的,下邊這個波形是微控制器回發給電腦的。以上邊的波形為例,左邊第一位是起始位0,從低位到高位依次是10001100,順序倒一下,就是資料0x31,也就是ASCII碼錶裡的‘1’。大家可以注意到分析儀在每個資料位都給標了一個白色的點,表示是資料,起始位和無資料的時候都沒有這個白點。時間標T1和T2的差值在右邊顯示出來是0.102ms,大概是9600分之一,稍微有點偏差,在容許範圍內即可。通過圖1-1,我們可以清晰的瞭解了串列埠通訊的收發的詳細過程。
那我們這裡再來了解一下,如果我們使用串列埠除錯助手,用字元格式直接傳送一個“12”,我們在我們的數碼管上應該顯示什麼呢?串列埠除錯助手應該返回什麼呢?經過試驗發現,我們數碼管顯示的是32,而串列埠除錯助手返回十六進位制顯示的是31、32兩個資料,如圖1-2所示。
圖1-2 串列埠除錯助手資料顯示
我們用邏輯分析儀把這個資料抓出來看一下,如圖1-3所示。
圖1-3 邏輯分析儀抓取資料
對於ASCII碼錶來說,數字本身是字元而非資料,所以如果傳送“12”的話,實際上是是分別傳送了“1”和“2”兩個字元,微控制器呢,先收到第一個字元“1”,在數碼管上會顯示出31這個對應數字,但是瞬間馬上就又收到了“2”這個字元,數碼管瞬間從31變成了32,而我們視覺上呢,根本是沒有辦法發現這種快速變化的,所以我們感覺數碼管直接顯示的是32。