一. 單燈閃爍 1. 單片機及其外部引腳
單片機是將 CPU、RAM、ROM、定時器/計數器、輸入/輸出接口電路、中斷、串行通 信接口等部件集成在一塊芯片上的微型計算機,也稱為微控制器。
MCS-51 單片機共有 40 個引腳,其中電源引腳 2 根,外接晶體振蕩器引腳 2 根,控制 引腳 4 根以及 4 組 8 位 I/O(輸入/輸出)引腳 32 根。
(1)主電源引腳(2 根) VCC(40 腳):電源輸入,接+5V 電源;GND(20 腳):接地線。 (2)外接晶振引腳(2 根) XTAL1(19 腳):片內振蕩電路的輸入端;XTAL2(18 腳):片內振蕩電路的輸出端。
(3)控制引腳(4 根) RST/VPP(9 腳):復位引腳,引腳上出現 2 個機器周期的高電平將使單片機復位。 ALE/PROG(30 腳):地址鎖存允許信號。 PSEN(29 腳):外部存儲器讀選通信號。 EA /VPP(31 腳):程序存儲器的內外部選通,接低電平從外部程序存儲器讀指令,如 果接高電平則從內部程序存儲器讀指令。
(4)I/O(輸入/輸出)引腳(32 根) AT89S51 單片機有 4 組 8 位的 I/O 口,分別位 P0、P1、P2、P3 口,每個口有 8 位(8 根引腳),共 32 根;分別為 P0.0~P0.7、P1.0~P1.7、P2.0~P2.7 和 P3.0~P3.7。
2. 單片機最小系統
單片機最小系統(或者最小應用系統)是指用最少的元件組成可以工作的單片機的系統。 對 MCS-51 系列單片機來說,最小系統一般包括:單片機、時鐘電路和復位電路。
(1)時鐘電路
MCS-5l 單片機內部有一個用于構成振蕩器的高增益反相放大器,引腳 XTALl 和 XTAL2 分別是反相放大器的輸入端和輸出端,由這個放大器與作為反饋元件的片外晶體或陶瓷諧振 器一起構成了一個自激振蕩器,如圖 1-1 所示,這種方式形成的時鐘信號稱為內部時鐘方式。 電容 C1、C2 一般為 30pF;石英晶振的頻率 fosc 為 6MHz、12MHz 和 11.0592MHz 等。
(2)復位電路
MCS-51 單片機的 RST/VPD 引腳是復位輸入端,在振蕩器運行時,RST 端至少要保 持 2 個機器周期為高電平,才完成一次復位。如圖 1-2 所示,是上電按鍵復位電路。一般電 阻 R 可以為 10KΩ,電容 C 為 10uF。
特別注意:對于 31 腳( EA /Vpp),當接高電平時,單片機在復位后從內部 ROM 的 0000H 開始執行;當接低電平時,復位后直接從外部 ROM 的 0000H 開始執行。
3. 單片機與 LED 發光二極管的連接
單片機與 LED 的連接如圖 1-3 所示, LED 對應單片機的一個輸出引腳。當相應引腳輸 出為低電平時,電流從 VCC 流入單片機,LED 開始發光,發光亮度由串聯的限流電阻控制; 當相應引腳輸出為高電平時,沒有電流通過 LED,LED 熄滅。
4.代碼
#include "reg52.h"
void Delay(unsigned int t);
sbit P00=P0^0;
void main(void){
while(1){
P00=1;
Delay(1000);
P00=0;
Delay(1000);
}
}
void Delay(unsigned int t){
while(--t);
}
二. 流水彩燈 1.基礎學習
2.代碼
//LEDshift .c:LED流水燈程序
//用數組實現循環流水
#include
unsigned char code table[8] ={ 0xfe, 0xfd, 0xfb,0xf7,0xef, 0xdf, 0xbf, 0x7f };
void mDelay(unsigned int Delay) //延時程序
{
unsigned int i;
for(;Delay>0;Delay--)
{ for(i=0; i<54; i++) ;
}
}
void main()
{
unsigned char i;
while(1)
{ for ( i =0 ; i<8 ; i++ )
{
P1= table[ i ];
mDelay(500); /*延時 500 毫秒*/
}
}
}
三. 鍵控燈亮 1.基礎
無論單片機對外界進行何種控制,或接受外部的何種控制,都是通過 I/O 口進行的。 MCS-51 單片機總共有 P0、P1、P2、P3 四個 8 位 I/O 口,每個 I/O 口都有鎖存器、輸出驅 動器和輸入緩沖器。
P0 口是 8 位漏極開路型雙向 I/O 口,它的作用為:(1)通用 I/O 口:作為輸出口時需外 加上拉電阻(1K~10K),作為輸入口時先寫 1;(2)低 8 位地址/數據分時復用口(單片機系 統擴展時)。
P1 口是內部帶上拉電阻的 8 位準雙向 I/O 口,它的作用為通用 I/O 口:輸出不用外加上 拉電阻,輸入也先寫 1。
P2 口是內部帶上拉電阻的 8 位準雙向 I/O 口,它的作用為:(1)通用 I/O 接口:輸出不 用外加上拉電阻,輸入也先寫 1;(2)高 8 位地址口(單片機系統擴展時)。
P3 口是內部帶上拉電阻 8 位準雙向 I/O 口,它的作為用:(1)通用 I/O 接口:輸出不用 外加上拉電阻,輸入也先寫 1;(2)第二功能:P3.0、P3.1:RxD、TxD(串口接收、發送 端);P3.2、P3.3:INT0、INT1 (外部 0、1 中斷);P3.4、P3.5:T0、T1(定時/計數器 0、 1 外部脈沖輸入端);P3.6、P3.7:WR 、 RD (片外數據存儲器的寫、讀信號)。
2.代碼與連線
#include"reg52.h"
#include"intrins.h"
#define uchar unsigned char
#define uint unsigned int
sbit LED0=P1^0;
sbit led1=P2^0;
sbit key=P3^0;
sbit sw=P3^1;
sbit k2=P3^2;
void Delay(uint x){
uchar i;
while(x--){
for(i=0;i<120;i++);
}
}
void main(){
while(1){
if(key) LED0=0;
else LED0=1;
if(sw) P0=1;
else{
uchar i;
P0=0xfe;
for(i=0;i<7;i++){
Delay(500);
P0=_crol_(P0,1);
}
}
if(k2==0){ //按鍵按下,產生一系列抖動,延時20ms消抖
Delay(20);
while(k2==0);//判斷按鍵是否松開。松開則P3_1!=0,跳出循環
Delay(20);
// P2^0 = ~ P2^0;//將LED狀態取反
led1=~ led1;
}
}
}
四.中斷系統
1.基礎學習 1.中斷:程序在執行過程中由于外界的原因而被中間打斷的情況
中斷處理程序(服務):中斷之后執行的程序;
主程序:原來運行的程序;
斷點:主程序被斷開的位置;
中斷源:引起中斷的原因,或能發出中斷申請的來源;
中斷請求(申請):中斷源要求服務的請求。
中斷技術的優點:相對查詢方式(查詢方式 類似于在while循環中有個if,每次while都會進行if操作,而中斷是突發的),cpu效率更高;1.實時處理2.故障處理
中斷源:1.外部設備中斷源,2.故障源,3.控制對象中斷源,4.定時/計時脈沖中斷源
中斷系統功能:1.能實現中斷及返回2.能實現優先權排隊3.能實現中斷嵌套
2.中斷過程:中斷請求>中斷響應>中斷處理
中斷響應:條件:中斷標志是否開放,是否高級中斷,是否執行完一條指令等。
中斷處理:1.保護現場:根據需要把斷點處有關存儲器的內容壓入堆棧,以保護主程序運行結果。
2.開/關中斷標志:根據cpu響應中斷后開/關中斷情況,適當處理中斷標志,以確保保護/恢復現場的正確進行,確保所需中斷嵌套的實現。(當中斷來的時候,若中斷標志未開,則此次中斷失?。?/p>
3.中斷處理
4.恢復現場:在完成中斷服務之后,返回被中斷的主程序之前,恢復堆棧中保護的各有關寄存器的內容。
3.中斷返回:
中斷服務程序最后,用一條中斷返回指令(RETI),保證返回主程序繼續執行被中斷的程序。
4.進一步認識
中斷源:51系列有5個 ,52系列有6個
5個中斷源:2個外部中斷源,2個定時中斷源,1個串行口中斷源
5.中斷優先級
6.語法函數
7.注意
2.實操代碼
#include
void mDelay(unsigned int Delay)
{
unsigned int data i;
for(;Delay>0;Delay--)
{
for(i=0; i<54; i++) ;
}
}
void main()
{ IT0=1; //外部0中斷邊沿觸發
EA=1; //開總中斷
EX0=1; //開外部0中斷
while(1)
{ P1=0xff; //8個LED熄滅
}
}
void ex0_int() interrupt 0
{
unsigned char data j;
for(j=0;j<6;j++)
{
P1=0x00; //8個LED點亮
mDelay(1000); //延時1000ms
P1=0xff; //8個LED熄滅
mDelay(1000); //延時1000ms
}
}
五. 定時/計數器
作為定時器時,它是對機器周期計數,即對片內振蕩器輸出的時鐘信號經12分頻后的脈沖計數,即一個機器周期計數器的數值加1。作為計數器時,它是對引腳T0(P3^4)和T1(P3^5)上的外部脈沖信號計數。當外部輸出產生由高電平至低電平的下降沿時,計數器加1。此實驗重點在于代碼的掌握和配置。了解計數與定時原理、會計算初值并賦予、掌握TMOD和TCON。
1. 基礎認識
1.工作方式寄存器(TMOD)(不能位尋址)
GATE:門控位。當GATE=0時,為軟件啟動,當GATE=1時,為硬件啟動。(課程為軟件啟動)
:定時或計數功能選擇位。1為計數方式,0為定時方式。
工作方式1常用,最大脈沖個數65536(
)
工作方式2常用,最大脈沖個數256(
),自動重裝
2.控制寄存器(TCON)(可以位尋址)
TF0(TF1):定時器/計數器T0(T1)計數溢出標志位。 =1 計數溢出; =0 計數未滿。此標志位可用于申請中斷或供CPU查詢。中斷方式:自動清零;查詢方式:必須軟件清零。
TR0(TR1):運行控制位。=1 啟動計數; =0 停止計數。該位由軟件進行置位/清零。
IE0(IE1):外部中斷0/1請求標志。 =1 存在中斷請求; =0 無中斷請求。
IT0(IT1);外部中斷觸發方式選擇。 =1 低電平觸發; =0 下降沿觸發。
3.初值寄存器(TH1、TL1,TH0、TL0)
fosc=12MHz,1Tm=1us,
2.定時器
流程為:計算計數初值,TMOD初始化,啟動和停止控制,中斷的開放/禁止
方式1:
#include "reg51.h"
#define uchar unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//數碼表
uchar int count,t1,t2;
void main(){
TMOD=0x10; //選用T1,工作方式2
TH1=65536/256; //初值高位 理論此時應已溢出,但由于若輸入x為0,則錯誤,輸入65536卻達到效果
TL1=65536%256; //初值低位
EA=1; //開全中斷
ET1=1; //開定時器1中斷
TR1=1; //啟動定時器
while(1){;}
}
void T1_int() interrupt 3{
TH1=65536/256; //再次賦值
TL1=65536%256;
count++;
t1=count0/15; //計算出1s
t2=count/150;
P0=table[t1]; //顯示
P2=table[t2];
}
方式2:自動重裝
# include
sbit LED1=P1^0;
void main()
{
TMOD = 0x02; //定時方式2
TH0 = 0x06; //置初值
TL0 = 0x06;
EA = 1; ET0 = 1; //開T0中斷
TR0 = 1; //啟動T0
while(1) { ; }
}
void T0_int( ) interrupt 1
{
LED1 =!LED1; //LED取反
}
3.計數器 4.課外拓展
1.產生占空比可調的矩形波
#include // 包含51單片機的頭文件,定義了單片機的寄存器和位操作等功能
sbit LED0 = P1^0; // 定義P1口的第0位為LED0,用于控制一個LED燈
unsigned int i; // 定義一個無符號整型變量i,用于計數
void main() // 主函數
{
TMOD = 0x01; // 配置定時器/計數器模式寄存器TMOD,設置定時器0為模式1(16位自動重裝載)
TH0 = 65286 / 256; // 給定時器高位寄存器TH0設置初始值,這里用于預設一個較大的延時以初始化
TL0 = 65286 % 256; // 給定時器低位寄存器TL0設置初始值,配合TH0完成初始值設置
EA = 1; // 開啟全局中斷允許
ET0 = 1; // 允許定時器0中斷
LED0 = 0; // 初始化時LED燈為熄滅狀態
i = 0; // 初始化計數器i
TR0 = 1; // 啟動定時器0
while(1) { ; } // 無限循環,程序主要行為由中斷服務程序控制
}
void T0_int() interrupt 1 // 定時器0中斷服務程序
{
i++; // 每次進入中斷,計數器i加1
if(i % 2 == 0) // 如果i是偶數
{
TH0 = (65536 - 500) / 256; // 重新設置定時器初值,使得定時時間為500us(高字節)
TL0 = (65536 - 500) % 256; // (低字節),用于控制LED較慢閃爍的周期
LED0 = 1; // 切換LED狀態為亮
}
else
{
TH0 = (65536 - 250) / 256; // 重新設置定時器初值,使得定時時間為250us,用于較快閃爍的周期
TL0 = (65536 - 250) % 256;
LED0 = 0; // 切換LED狀態為熄滅
}
}
2.對P3.4引腳的輸入信號進行二分頻
#include // 包含頭文件,該文件定義了51單片機的特殊功能寄存器和位定義等。

sbit LED0 = P1^0; // 定義P1口的第0位為LED0,通常用來控制一個LED燈的亮滅。
void main() // 程序的主函數
{
TMOD = 0x60; // 配置定時器模式寄存器TMOD,設置定時器T1為模式2(自動重裝載8位計數器)。
TH1 = 255; // 設置定時器T1的高8位初值為255,與TL1一起構成256計數值,因為是模式2,所以這是最大計數值。
TL1 = 255; // 設置定時器T1的低8位初值也為255,與TH1相同原因。
EA = 1; // 開啟全局中斷允許位。
ET1 = 1; // 允許定時器T1中斷。
LED0 = 0; // 初始化LED0為熄滅狀態。
TR1 = 1; // 啟動定時器T1。
while(1) { ; } // 無限循環,程序的主要邏輯由中斷服務程序處理。
}
void T1_int() interrupt 3 // 定時器T1的中斷服務程序,中斷號為3。
{
LED0 = !LED0; // 切換LED0的狀態,如果之前是亮則變為滅,反之亦然。
}
六. 串行通信接口
51單片機內有一個通用全雙工串行口,可以四種工作模式和不同的波特率工作。串行通信分為同步通信和異步通信,同步通信按數據塊傳輸,對硬件要求高,課程采用異步通信,按字符傳輸。串行通信的重點在于代碼和波特率控制。首先根據波特率計算出T1的初值,配置TMOD、TH1、TL1、TR1,若TMOD=0x20,則表示選擇T1方式中的工作方式2,TLx具有自動重裝初值的8位定時器/計數器,THx為初值寄存器。再配置SCON、PCON、EA、ES,若SCON=0x50,則表示串口工作方式為1,且REN=1;PCON的首位是SMOD,是波特率的控制位,1表示波特率加倍,0表示波特率不加倍;ES=1且EA=1表示串行中斷打開,中斷的作用是,當接收移位寄存器接收完一幀的數據后,RI=1,申請中斷,通知CPU從SBUF中取走數據。
1.基礎配置學習 1.串行通信的兩種形式
并行通信只適合短距離通信,速度快效率高,需要數據線條數多。串行通信分為異步通信和同步通信。
同步通信:一般一次傳送一個數據塊。每個數據塊的開頭以同步字符SYN加以指示使發送方和接收方取得同步。數據塊的各字符間沒有起始位和停止位,提高了通信速度。但為了能保持同步傳送,接受與發送雙方必須用一個時鐘來協調收發器的工作,這就增加了設備的復雜性。
異步通信:發送和接受方采用獨立時鐘。一幀信息表示一個字符,一幀信息包括起始位(為0信號,占一位)、數據位(傳輸時低位在先,高位在后)、奇偶校驗位(可要可不要)、停止位(為1信號,可1位、1位半或2位)
在串行通信中,通信雙方事先應該做好一下約定:
1.字符格式。雙方要事先約定字符的編碼形式、奇偶校驗形式、起始位、停止位的規定。例如,用ASCII碼通信,有效數據為7位,加一個奇校驗位、一個起始位、一個停止位共10位。
2.波特率。波特率就是數據的傳送速率,即每秒傳送的二進制數位數,單位為位/s,即1波特=1b/s。在異步通信中,發送端和接收端的波特率必須一致。
2.串行通信模式(單工、半雙工、全雙工)
三種傳輸方式都是用同一線路傳輸一種性質的信號,為了充分利用線路資源,可通過使用多路復用器或多路集線器,采用頻分、時分或碼分復用技術,實現在同一線路上的資源共享,稱為多工傳輸方式。
3.SCON與PCON(核心控制)
在串行通信時,用串行通信控制寄存器SCON和電源控制寄存器PCON控制串行接口的工作方式和波特率。
1.認識寄存器(SBUF)
51單片機串行接口有一個發送緩沖器和一個接收緩沖器。發送緩沖器只能寫入信息,用于存儲發送信息。接收緩存器只能讀出信息,用于存儲接受到的信息。此外,接收緩沖寄存器與發送緩沖寄存器共用一個地址,但由于操作是獨立的故不會發生沖突。(老師解釋,類似于一個門牌號,兩個房間。)對接收/發送緩沖寄存器SBUF的操作,無論是否采用中斷方式工作,每接收/發送一個數據都必須用指令對串行中斷標志RI/TI清零,以備下一次接收發送能正確進行。
接收方式:串行數據通過引腳RXD(P3^0)進入。接收寄存器之間還有移位寄存器,從而構成雙緩沖結構,避免出現幀重疊錯誤,即在下一幀數據來時,前一幀數據還沒有讀走。
發送方式:串行數據通過TXD(P3^1)送出。發送數據時,由于CPU是主動的,不會發生幀重疊錯誤,發送電路不需要雙緩沖結構,可以提高發送數據發送速度。
2.控制寄存器(SCON)
1.工作方式
方式0:8位同步移位寄存器。
課程以工作方式1為主,10位UART(8位通用異步接收器/發送器),波特率T1溢出率/n。(當SMOD=1時,n=16;當SMOD=0時,n=32。)方式1在應用上,只能用于雙機通信,不能用于多機通信。
方式2:11位UART,波特率與SMOD有關。
方式3:11位UART,波特率可調。除波特率外,同方式2。
2.SCON 98H(其他位的認識)
3.波特率計算(1s內串口出去的位數)
定時器的溢出率是指1s內產生溢出的次數。T1作波特率發生器時,用定時方式2(自動重裝功能)可以避免通過程序反復裝入初值所引起的定時誤差,使波特率更加穩定。
定時器每秒所溢出的次數為 (1/溢出周期):
溢出率:
先確定波特率,再計算T1的計數初值,然后進行T1的初始化(初值):
3.電源控制寄存器(PCON)
PCON的字節地址為87H,沒有位尋址功能。主要實現對單片機電源的管理控制。
SMOD:是串行口波特率系數控制位。為1時,串行口波特率加倍。系統復位時,SMOD=0.
4.中斷允許寄存器IE
ES=0 禁止串行中斷
ES=1 允許串行中斷
EA=0 禁止中斷
EA=1 允許中斷
2.實操(連接原理圖,代碼分析,效果展示)
代碼分析
#include"reg51.h"
#include"intrins.h"
unsigned char data senddata,sendcnt,recvdata,d;
void Delay(unsigned int x){
unsigned char i;
while(x--){
for(i=0;i<120;i++);
}
}
void main() { // 主函數開始
TMOD=0x20; // 配置定時器模式寄存器TMOD。這里設置為模式2,用于自動重裝載的方式工作,常用于波特率發生器。
TL1=0xFD; // 設置定時器1的低8位初值。與TH1一起用于設定串口的波特率。
TH1=0xFD; // 設置定時器1的高8位初值。注意:實際的初值應根據系統時鐘和所需的波特率計算得出。
TR1=1; // 啟動定時器1。
SCON=0x50; // 配置串行口控制寄存器SCON。這里設置了工作于模式1(8位UART,可變波特率),并且允許接收。
// 具體來說,0x50 = 0b01010000,其中SM0=0, SM1=1選擇模式1,REN=1允許接收。
PCON=0x00; // 設置電源控制寄存器PCON。這里清零,表示不使用掉電模式或雙速模式。
senddata=06; // 定義并初始化要發送的數據為十六進制的06(十進制中的6)。
EA=1; // 開啟全局中斷允許位。
ES=1; // 開啟串行口中斷允許位,以便在發送完成或接收到數據時產生中斷。
SBUF=senddata; // 將數據送入串行口緩沖器SBUF,準備發送。
while(1){;} // 進入無限循環,等待中斷發生。主要因為主程序在此處不再執行其他操作,依賴中斷處理后續任務。
} // 主函數結束
void ser_int() interrupt 4{ //串行中斷
if(RI){ // 如果是接收中斷請求標志RI被置位(即有數據接收)
P0=SBUF; // 將接收到的數據暫存到P0端口
switch(SBUF){ // 根據接收到的數據(現在在SBUF中)執行不同的操作
case 1:{
P1=0xff;
Delay(500);
P1=0x00;
Delay(500);
P1=0xff;
Delay(500);
P1=0x00;
Delay(500);
};break;
case 2:{
P1=0x00;
};break;
case 3:{
P1=0xfe;
while(1){
for(d=0;d<7;d++){
Delay(500);
P1=_crol_(P1,1);
}
break;
}
};break;
}
RI=0; // 清除接收中斷標志位
}else if(TI){ // 如果是發送中斷請求標志TI被置位(即數據發送完畢)
TI=0; // 清除發送中斷標志位
SBUF=senddata; // 發送數據
}
}
3.胡老師文件
#include
unsigned char data Senddata,Sendcnt; //全局變量,在所有程序里有效
void main()
{
TMOD=0x20; TL1=0xfd; TH1=0xfd; TR1=1; //初始化T1,波特率9600
SCON=0x50; PCON=0x00; //初始化串口,方式1,接收數據
Senddata=1; Sendcnt=10; //發送數據為1,字節數為10
EA=1; ES=1; //開串口中斷
SBUF=Senddata; //發送數據給SBUF
while(1) ; //等待串口中斷
}
void ser_int( ) interrupt 4 //串口中斷服務子程序
{
if(RI) //是接收中斷
{
P2=SBUF; //讀取SBUF數據送P1口顯示
RI=0; //清接收中斷標志位
}
else if(TI) //是發送中斷
{
TI=0; //清發送中斷標志位
Sendcnt--; //發送字節數減1
if(Sendcnt!=0) //沒有發送完10個字節
{
Senddata++; //發送數據加1
SBUF=Senddata; //發送數據送SBUF
}
}
}
七. LED數碼管顯示器接口
數碼管分為共陰和共陽,共陰和共陽分別有自己的顯示代碼表;顯示分為動態顯示和靜態顯示,動態顯示要用數組調動好顯示代碼表,掌握對要顯示位的變換方式(動態顯示某一位),要刷新好位的顯示。
1靜態顯示(通過編碼使數碼管顯示數字) 1.要顯示字符時,相應的發光二極管恒定的導通或截至,直到顯示另一個字符。
優點:亮度較高,編程容易,管理較簡單。
缺點:占用I/O口資源較多,每位占用8根I/O線。
連接:每位的每根段選(發光二極管)連接一根I/O線。
應用:一般用于顯示位數較少的場合。在顯示位數較多時,一般采用動態顯示。
2.段選碼表
3.代碼分析
#include
unsigned char code dis_tab[16]={0xC0,0xF9,0xA4,0xB0, 0x99,0x92,0x82,0xF8
,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E} ;
//顯示代碼表,共陽
void main()
{
while(1)
{
P1 = dis_tab[1]; //P1顯示1
P2 = dis_tab[2];
P3 = dis_tab[3];
}
}
4.串行擴展LED顯示器靜態(未練習)
可進位
2. 動態顯示(利用視覺原理,使每位數字在同一時間內閃爍30多次,達到視覺常亮的目的)
以靜態顯示為基礎
1.基礎認識
基本特點:一位一位地分時輪流點亮各位顯示器,對每一位顯示器來說,每隔一段時間輪流電 亮一次。
顯示亮度:顯示器的亮度既與導通電流有關,也與點亮和熄滅時間的比例有關。
連接方式:將LED數碼管顯示器的所有段選位并聯在一起,由一個8位I/O口控制。 公共端分別由相應的I/O口控制,實現各位顯示器的分時選通。
2.代碼解析與仿真圖記錄
#include
#include
unsigned char code dis_tab[16]={0x3f,0x06,0x5b,0x4f,0x66, 0x6d, 0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71} ;
//顯示代碼表,共陰
unsigned char data dis_data,bit_data;
void mDelay(unsigned int Delay)
{ unsigned int i;
for(;Delay>0;Delay--)
{ for(i=0;i<114;i++) {;}
}
}
void display() //顯示函數
{ unsigned char i;
bit_data = 0xfe; //設置初始位選數據
dis_data = 1;
for(i=0; i<6; i++)
{
P2 = 0xff; //刷新,全滅
P1 = dis_tab[dis_data];//P1輸出 這個數
P2 = bit_data; //設置初始位
mDelay(5);
bit_data =_crol_( bit_data , 1 ); //位選碼左移1位
dis_data ++; //加1,到數組下一個數
}
}
void main()
{
while(1)
{
display();
}
}
3.補充說明
未仿真Lcd1602
八. 鍵盤接口
鍵盤分為獨立式鍵盤和行列式鍵盤,按照按鍵識別方式分為編碼鍵盤(由硬件識別鍵的閉合)和未編碼鍵盤(由軟件識別鍵的閉合)。按鍵開關會產生抖動,要消抖,消抖分為軟件消抖和硬件消抖,學習過程中常用軟件消抖。鍵盤掃描的工作方式有三種,首先分為查詢掃描方式和中斷掃描方式,查詢掃描方式又分為定時查詢和實時查詢。先講獨立式鍵盤,課程主要學習了獨立式鍵盤,未實踐矩陣式鍵盤。
1.獨立式鍵盤 1.基礎知識 1.消除抖動(課程中主要采用軟件消抖,即延時消抖)
硬件消抖:RS觸發器(詳細原理未說明)
軟件消抖:延時5~10ms左右再測
2.鍵盤掃描工作方式 1.實時查詢(單片機一直循環執行查詢程序進行查詢)
鍵盤掃描一般包括四個步驟:
1.判斷鍵盤有無鍵按下。
2.去除按鍵抖動
3.求鍵值(鍵號)。在矩陣式鍵盤中,閉合按鍵的鍵號可以用公式獲得。
4.判斷按鍵是否釋放。按鍵閉合一次僅進行一次按鍵功能操作,等按鍵釋放以后再執行按鍵指定功 能操作。
2.定時查詢(每隔固定時間間隔查詢一次或每到固定時間查詢一次)
以實時查詢為基礎,使用定時/計數器功能,在實時查詢函數外加上延時或定時函數。
3.中斷查詢(只有中斷產生時,即鍵盤按下時,才會掃描鍵盤)
實際應用中按鍵并不經常工作,此方法既能即時處理鍵盤輸入,又能提高CPU效率。
2.原理圖與代碼分析
此原理圖包括中斷方式:
# include
sbit K0=P1^0; //定義按鍵所連I/O口
sbit K1=P1^1;
sbit K2=P1^2;
//延時子程序:由Delay參數決定延時時間
void mDelay(unsigned int Delay)
{
unsigned int i;
for(;Delay>0;Delay--)
{ for(i=0;i<114;i++) ;
}
}
//按鍵掃描子程序,判斷按鍵是否按下,如果按下并處理
void keyscan(void){

if(!K0){ //K0按鍵是否按下
mDelay(5); //延時消抖
if(!K0) { //K0按鍵確實按下,進行處理
... ;
while(!K0); //等待K0按鍵釋放
}
}
if(!K1){ //K1按鍵是否按下
mDelay(5); //延時消抖
if(!K1){ //K1按鍵確實按下,進行處理
... ;
while(!K1); //等待K1按鍵釋放
}
}
if(!K2){ //K2按鍵是否按下
mDelay(5); //延時消抖
if(!K2){ //K2按鍵確實按下,進行處理
... ;
while(!K2) ; //等待K2按鍵釋放
}
}
}
void main(void)
{
while(1)
{
P1=0xff; //設置P1為輸入方式
keyscan();
}
}
2. 矩陣式鍵盤(未實操,僅原理圖)
# include
//延時子程序:由Delay參數決定延時時間
void mDelay(unsigned int Delay)
{ unsigned int i;
for(;Delay>0;Delay--)
{ for(i=0;i<114;i++) {;}
}
}
//按鍵掃描子程序,有鍵按下,返回鍵值00-15;無鍵按下,返回0xff。
unsigned char Matrix_key(void)
{
unsigned char key, temp1,temp2;
key=0xff; // 初始化鍵值
P2=0x0f; // 輸出列掃描字
if( (P2&0x0F) !=0x0f) // 讀行線狀態,判斷是否有鍵按下
{
mDelay(10); // 延時消抖
P2=0x0f; // 輸出列掃描字
temp1=P2&0x0f; // 讀行線狀態到temp1中
if( temp1!=0x0f ) // 有鍵按下
{ P2=0xf0; // 輸出行掃描字
temp2=P2&0xf0; // 讀列線狀態到temp2中
switch( temp1|temp2 )
{ case 0xee:key=0; break;
case 0xde:key=1; break;
case 0xbe:key=2; break;
case 0x7e:key=3; break;
case 0xed:key=4; break;
case 0xdd:key=5; break;
case 0xbd:key=6; break;
case 0x7d:key=7; break;
case 0xeb:key=8; break;
case 0xdb:key=9; break;
case 0xbb:key=10;break;
case 0x7b:key=11;break;
case 0xe7:key=12;break;
case 0xd7:key=13;break;
case 0xb7:key=14;break;
case 0x77:key=15;break;
default: key=0xff; break; } } }
else { ; }
return ( key ); //返回鍵值
}
void main(void)
{ unsigned char key_value;
while(1)
{
key_value = Matrix_key(); //調用按鍵識別子程序
switch(key_value) //根據按鍵分支
{
case 0: P0=0xfe; break; //0號按鍵,LED1點亮
case 1: P0=0xfd; break; //1號按鍵,LED2點亮
case 2: P0=0xfb; break; //2號按鍵,LED3點亮
case 3: P0=0xf7; break; //3號按鍵,LED4點亮
case 4: P0=0xef; break; //4號按鍵,LED5點亮
case 5: P0=0xdf; break; //5號按鍵,LED6點亮
case 6: P0=0xbf; break; //6號按鍵,LED7點亮
case 7: P0=0x7f; break; //7號按鍵,LED8點亮
default: break; //無效按鍵
}
}
}
九. 存儲器擴展
包括ROM程序存儲器(存儲)和RAM數據存儲器(內存),重點在對引腳的認識和對存儲器的認識,然后正確的連接,代碼是次要的。
1.初步認識
51單片機的內部資源(RAM、ROM、I/O、A/D、D/A等)不足時,需要擴展。
對51單片機來說,并行存儲器擴展的基本原則時:P0口提供數據線;P2、P0口提供地址線,其中低位用于片內選擇,高位用于芯片選擇;用
控制程序存儲器的讀操作,用
和
控制數據存儲器讀寫操作。
1.存儲器擴展的基本方法(總線擴展法)
數據總線(用于單片機與存儲器的數據傳送):由P0口提供。芯片數目計算(沒有寫)。
地址總線(用于尋址存儲單元):由P0口和P2口共同提供。由于P0口是分時復用傳遞地址和數據信息,所以當P0口傳送地址信息時,常用ALE信號控制地址鎖存器對P0口提供的低8位地址A0~A7進行鎖存后輸出,與P2口輸出的高8位地址組成16位總線。
控制總線(用于協調控制數據信息與地址信息的正確傳送):
ALE:地址鎖存控制。ALE的下降沿控制鎖存器鎖存P0口輸出的低8位地址,與74LS373的 使能端相連。
:程序存儲器ROM的讀控制信號。執行程序存儲器讀指令MOVC時,該信號有效, 與程序存儲器輸出使能端相連。
:程序存儲器選擇。低電平表示系統從片外程序存儲器0000H開始讀程序;高電平表示 系統從片內程序存儲器0000H開始讀程序,超出片內程序存儲器的范圍后,自動轉到 片外程序存儲器。
、
:片外數據存儲器的讀寫控制。執行片外數據存儲器讀寫指令MOVX時,信號有 效,分別與存儲器擴展芯片的輸出使能和寫使能端相連。
2. 選通方式
存儲器擴展芯片的芯片選通線通常由高位地址線直接選通或經地址譯碼器譯碼后選通。
線選法:先將擴展儲存器芯片的地址線與單片機的地址總線從低位開始順次相連后,剩余的高位地址線的一根或幾根直接連接到各擴展存儲器的片選線上?!疽话闫x端(
、
等)低電平有效,只要連接片選信號端的引線狀態位為低電平,就表示選中該芯片。高位地址線A13~A15中,同一時刻只允許有一根線為低電平,另外兩根必須為高電平,否則出錯?!?/p>
譯碼選擇法(包括部分譯碼和全譯碼):類同上,將地址總線連好后,剩余的高位地址線,連接譯碼器,從譯碼器的輸出端的選中的輸出引腳輸出一個0,進而選中芯片。如果將剩余的高位地址線全部連接到譯碼器,則為全譯碼法,若沒有,則為部分譯碼法。(部分譯碼法仍然會像線選法一樣造成重疊,若有N條高位地址線不參加譯碼,則有2的N次方個重疊,重疊的地址真正能存儲信息的只有一個,因而會造成浪費。在存儲器擴展容量不大的情況下,選擇線選發,電路會簡單些,可降低成本;當擴展較大時,選擇全譯碼法,可消除地址重疊,充分利用存儲空間。)
2.進一步認識(對芯片的認識)
對于8051單片機,只要加上振蕩電路和復位電路,該系統就可以工作了,常稱這樣的系統為最小系統。對于不帶片內ROM的單片機,如8031,8032,必須在片外擴展ROM之后才能構成最小系統
1.ROM 程序存儲器(儲存):
EPROM:紫外線可擦除電可編程的半導體只讀存儲器,掉電后信息不會丟失。(EPROM中的程序由專門的編程器寫入,由專門的擦除器擦除,擦除后全為1狀態。擦除時紫外線強度為12000mw/cm2,波長為I=2537A,時間為10~20min。)常用的EPROM有:2716、2732、2764...
:電可擦除電可編程,掉電后信息不丟失;編程簡單,不需要專門的編程器和擦除器;兼有RAM和ROM的特點,故既可做ROM也可做RAM;常用的并行E2PROM有2816、2864
2. RAM 數據存儲器(內存):
51單片機內部有256B的RAM存儲器,但對需要大容量數據緩沖器的應用系統(如數據采集系統),就須外部擴展RAM。常用的半導體靜態隨機存取存儲器SRAM,如6116,6264,62256。
也可作外部RAM(但速度慢),但因它掉電后信息不丟失,對某些要求不間斷工作、對于一些關鍵性的實時數據不允許丟失的場合較合適。
3. 編程和使用
1.RAM舉例(6264)
#include"reg51.h"
unsigned char code *point1;
unsigned char xdata *point2;
void main(){
char i;
while(1){
point1=0x20; //定義point1的位置
point2=0x40;
for(i=0;i<5;i++){
*point2=*point1; //使point2位置的數據等于point1位置的數據
point1++; //point1位置延1
point2++;
}
}
}
十. 并行I/O口擴展
與存儲器類似。由于51只有四個I/O口,會不夠用,類似于存儲器對外部的尋址和數據讀寫,進行擴展。以51和74LS373同74LS244的實驗來看,51的P0口同時與373和244連接,再373接燈,244接開關,當開關按下時,244將信號0發送給P0口,51單片機接收并輸出給373,而后燈亮。
1. 74LS373與74LS244
1. 74LS373 74LS373 是一種 3 態輸出的 8D 觸發器,它的 OE 端和 LE 端是控制端,當它的 OE 端 為低電平時只要在 LE 端產生一個正跳變,D0~D7 將被鎖存到 Q0~Q7 端輸出,在其它情 況下 Q0~Q7 端的輸出保持不變??梢岳?74LS373 這一特性擴展并行輸出口,圖 10-1 使 用一片 74LS373 擴展輸出口,如果將未使用到的地址線都置為 1,則可以得到該片 74LS377 的地址為 7FFFH。如果單片機要從該片 74LS373 輸出數據,可以執行如下指令:
char xdata * = 0x7fff;
char data Outdata;
…
* = Outdata;
2. 74LS244 74LS244 是一種三態輸出的八總線驅動器,無鎖存功能。它的 OE 端是控制端,當它的 OE 端為低電平時,則 74LS244 將 A 端數據傳送至 Y 端;在其它情況下不傳送數據,并輸 出高阻態。可以利用 74LS244 這一特性擴展并行輸入口,圖 10-2 使用一片 74LS245 擴展輸 入口,如果將未使用到的地址線都置為 1,則可以得到該片 74LS244 的地址為 BFFFH。如 23 果單片機要從該片 74LS244 讀取數據,可以執行如下指令:
char xdata * = 0xbfff;
char data Indata;
…
Indata = *;
2.實操
接線圖和代碼
3.注意
特點:電路簡單、成本低、配置靈活
輸出接口:由于大多數外設速度遠遠低于CPU速度,輸出接口電路必須有鎖存功能,鎖存CPU給外設的數據,以便外設慢慢處理。
輸入接口:由于大多數外設沒有3態緩存功能,而掛在數據總線的輸出端必須有三態緩存功能,故輸入接口電路必須有三態緩存功能。
1.74373(是一種8D鎖存器)
2.74244是一種三態輸入8總線收發器/驅動器,無鎖存功能??勺鳛槌B數據輸入設備的輸入接口
十一. A-D轉換
A-D轉換器是將模擬信號轉換成數字信號的器件,以ADC0809為例(與ADC0808區別很?。?/p>
1基礎認識
此ADC可大致分為三個部分,
1.輸入切換(模擬量進入ADC的入口)
ALE:地址鎖存允許信號輸入端。ALE信號有效時,將當前地址鎖存。
ADD C、B、A : 八路模擬信號選擇端。地址編碼000~111(二進制)表示IN0~IN7八路A-D通道。
2.A/D轉換(ADC的核心,接受輸入的模擬量,進行逐次比較,并輸出數字量給輸出接口輸出)
積分型AD
ms(低速)
精度高,抗干擾能力強,價格便宜,速度慢,應用于數字儀表(電容充放電需要時間)
逐次比較型AD
us(中速)
抗干擾能力弱,其他適中(逐個比較)
并行型AD
ns(高速)
高速電路(飛機、軍工等高科技產業運用,價格貴)
START:啟動A-D轉換信號輸入端。當START輸入一個正脈沖時,啟動ADC0809開始A-D轉換。
EOC:A-D轉換結束信號輸出端,高電平有效。EOC=0,正在進行轉換;EOC=1,轉換結束。(該 狀態信號既可作為查詢的狀態標志,又可作為中斷請求信號使用)
CLOCK:外部時鐘輸入端。時鐘頻率高,A-D轉換速度快,允許范圍為10~1290kHz。(通常將 8051單片機的ALE端直接或分頻后與ADC的CLOCK連接。)
VREF(+)、VREF(-):正負基準電壓輸入端。(接A-D轉換器的256電阻階梯)
3.輸出接口(輸出數字量給單片機,由單片機進一步處理)
OE:A-D轉換結果輸出允許控制端。OE=1,允許A-D轉換結果從D0~D7端輸出。
D0~D7:數字量輸出端。為三態緩沖輸出形式,可以和單片機的并行數據線直接相連。
2.代碼分析與原理圖連線
原理解析
通電后,首先由ADC0808的已被選中的模擬信號輸入端輸入電信號,然后經ADC0808內部處理輸出數字量,將數字量輸入到單片機中,如圖輸入P1口,對數據進行處理后,再通過單片機內部的程序,動態顯示出來。
原理圖連線(未使用74HC373和D鎖存器)
代碼分析
# include
#define uchar unsigned char //定義uchar為無符號字符數據類型
const unsigned char LED_TAB[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,
0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
//顯示代碼
sbit OE=P3^0;
sbit EOC=P3^1;
sbit ST=P3^2;
sbit ADDA=P3^4; //通道選擇
sbit ADDB=P3^5;
sbit ADDC=P3^6;
uchar ADC,datal[3]; //存放ADC轉換后的數據
void delay_lms(void){
unsigned char temp = 249;
while(--temp);
temp=249;
while(--temp);
}
void display(void){ //定義顯示函數
unsigned char i;
unsigned char dsSel =0xfd; //顯示位初值
datal[0]=ADC/100; //ADC轉化后的數據的百位
datal[1]=ADC0/10; //
datal[2]=ADC0; //
for(i=0;i<3;i++){
P2=dsSel; //將顯示位送到P2口,依次顯示百、十、個位
P0=LED_TAB[datal[i]]; //送顯示代碼到P0口
delay_lms(); //延時1ms
dsSel<<=1; //顯示位左移一位
dsSel|=0x01; //最低位置1
}
}
void main(void){
while(1){
ADDA=1; //選擇通道3
ADDB=1;
ADDC=0;
ST=0;ST=1;ST=0; //START正脈沖,啟動ADC開始轉換
while(EOC==0); //等待轉換結束
OE=1; //允許輸出
ADC=P1; //轉換結果存儲到ADC單元
OE=0; //關閉輸出
display(); //調用顯示程序
}
}
十二. D-A轉換
將數字信號轉換成模擬信號,輸出是電壓或電流信號
1.基礎認識(以DAC0832為例 由數據鎖存器與DAC寄存器構成兩級數據輸入鎖存)
單片機將數據發送給數據鎖存器,再到DAC寄存器,再到D/A轉換器
1.轉換的核心在D/A轉換器
VREF(Ur):基準電壓輸入端,可在-10V~+10V范圍內調節。
深入理解51單片機串口通信及通信實例
串口通信的原理
串口通信()的概念非常簡單,串口按位(bit)發送和接收字節。盡管比按字節(byte)的并行通信慢,但是串口可以在使用一根線發送數據的同時用另一根線接收數據。它很簡單并且能夠實現遠距離通信。比如IEEE488定義并行通行狀態時,規定設備線總長不得超過20米,并且任意兩個設備間的長度不得超過2米;而對于串口而言,長度可達1200米。典型地,串口用于ASCII碼字符的傳輸。通信使用3根線完成,分別是地線、發送、接收。由于串口通信是異步的,端口能夠在一根線上發送數據同時在另一根線上接收數據。其他線用于握手,但不是必須的。串口通信最重要的參數是波特率、數據位、停止位和奇偶校驗。對于兩個進行通信的端口,這些參數必須匹配。
a,波特率:這是一個衡量符號傳輸速率的參數。指的是信號被調制以后在單位時間內的變化,即單位時間內載波參數變化的次數,如每秒鐘傳送240個字符,而每個字符格式包含10位(1個起始位,1個停止位,8個數據位),這時的波特率為240Bd,比特率為10位*240個/秒=2400bps。一般調制速率大于波特率,比如曼徹斯特編碼)。通常電話線的波特率為14400,28800和36600。波特率可以遠遠大于這些值,但是波特率和距離成反比。高波特率常常用于放置的很近的儀器間的通信,典型的例子就是GPIB設備的通信。
b,數據位:這是衡量通信中實際數據位的參數。當計算機發送一個信息包,實際的數據往往不會是8位的,標準的值是6、7和8位。如何設置取決于你想傳送的信息。比如,標準的ASCII碼是0~127(7位)。擴展的ASCII碼是0~255(8位)。如果數據使用簡單的文本(標準ASCII碼),那么每個數據包使用7位數據。每個包是指一個字節,包括開始/停止位,數據位和奇偶校驗位。由于實際數據位取決于通信協議的選取,術語“包”指任何通信的情況。
c,停止位:用于表示單個包的最后一位。典型的值為1,1.5和2位。由于數據是在傳輸線上定時的,并且每一個設備有其自己的時鐘,很可能在通信中兩臺設備間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,并且提供計算機校正時鐘同步的機會。適用于停止位的位數越多,不同時鐘同步的容忍程度越大,但是數據傳輸率同時也越慢。
d,奇偶校驗位:在串口通信中一種簡單的檢錯方式。有四種檢錯方式:偶、奇、高和低。當然沒有校驗位也是可以的。對于偶和奇校驗的情況,串口會設置校驗位(數據位后面的一位),用一個值確保傳輸的數據有偶個或者奇個邏輯高位。例如,如果數據是011,那么對于偶校驗,校驗位為0,保證邏輯高的位數是偶數個。如果是奇校驗,校驗位為1,這樣就有3個邏輯高位。高位和低位不真正的檢查數據,簡單置位邏輯高或者邏輯低校驗。這樣使得接收設備能夠知道一個位的狀態,有機會判斷是否有噪聲干擾了通信或者是否傳輸和接收數據是否不同步。
RS232概述
在我們電腦上,一般都會有一個9針的串行接口,這個串行接口叫做RS232接口,它和UART通信有關聯,但是由于現在筆記本電腦不帶9針串口,所以和單片機通信越來越趨于使用USB虛擬串口。
九針串口分工頭和母頭
公頭上5下4,上5從左到右為1.2.3.4.5;下4從左到右為6.7.8.9;
母頭上5下4,上5從左到右為5.4.3.2.1;下4從左到右為9.8.7.6;
RS232接口一共有9個引腳,分別定義是:1、載波檢測DCD;2、接收數據RXD;3、發送數據TXD;4、數據終端準備好DTR;5、信號地線SG;6、數據準備好DSR;7、請求發送RTS;8、清除發送CTS;9、振鈴提示RI。我們要讓這個串口和我們單片機進行通信,我們只需要關心其中的2腳RXD、3腳TXD和5腳GND即可
雖然這三個引腳的名字和我們單片機上的串口名字一樣,但是卻不能直接和單片機對連通信,這是為什么呢?隨著我們了解的內容越來越多,我們得慢慢知道,不是所有的電路都是5V代表高電平而0V代表低電平的。對于RS232標準來說,它是個反邏輯,也叫做負邏輯。為何叫負邏輯?它的TXD和RXD的電壓,-3V~-15V電壓代表是1,+3~+15V電壓代表是0。低電平代表的是1,而高電平代表的是0,所以稱之為負邏輯。因此電腦的9針RS232串口是不能和單片機直接連接的,需要用一個電平轉換芯片MAX232來完成
這個芯片就可以實現把標準RS232串口電平轉換成我們單片機能夠識別和承受的UART0V/5V電平。從這里大家似乎慢慢有點明白了,其實RS232串口和UART串口,它們的協議類型是一樣的,只是電平標準不同而已,而MAX232這個芯片起到的就是中間人的作用,它把UART電平轉換成RS232電平,也把RS232電平轉換成UART電平,從而實現標準RS232接口和單片機UART之間的通信連接。
USB轉串口通信
隨著技術的發展,工業上還有RS232串口通信的大量使用,但是商業技術的應用上,已經慢慢的使用USB轉UART技術取代了RS232串口,絕大多數筆記本電腦已經沒有串口這個東西了,那我們要實現單片機和電腦之間的通信該怎么辦呢?
們只需要在電路上添加一個USB轉串口芯片,就可以成功實現USB通信協議和標準UART串行通信協議的轉換,在我們的開發板上,我們使用的是CH340T這個芯片
我們需要用跳線帽把中間和下邊的針短接在一起。右側的CH340T這個電路很簡單,把電源、晶振接好后,6腳和7腳的DP和DM分別接USB口的2個數據引腳上去,3腳和4腳通過跳線接到了我們單片機的TXD和RXD上去。
CH340T的電路里3腳位置加了個4148的二極管,是一個小技巧。因為這個單片機下載程序時需要冷啟動,就是先點下載后上電,上電瞬間單片機會先檢測需要不需要下載程序。雖然單片機的VCC是由開關來控制,但是由于CH340T的3腳是輸出引腳,如果沒有此二極管,開關后級單片機在斷電的情況下,CH340T的3腳和單片機的P3.0(即RXD)引腳連在一起,有電流會通過這個引腳流入后級電路并且給后級的電容充電,造成后級有一定幅度的電壓,這個電壓值雖然只有兩三伏左右,但是可能會影響到正常的冷啟動。加了二極管后,一方面不影響通信,另外一個方面還可以消除這種不良影響。這個地方可以暫時作為了解,大家如果自己做這類電路,可以參考一下。
IO口模擬UART串口通信
UART串口波特率,常用的值是300、600、1200、2400、4800、9600、14400、19200、28800、38400、57600、115200等速率。IO口模擬UART串行通信程序是一個簡單的演示程序,我們使用串口調試助手下發一個數據,數據加1后,再自動返回。
串口調試助手,這里我們直接使用STC-ISP軟件自帶的串口調試助手,先把串口調試助手的使用給大家說一下,如圖11-6所示。第一步要選擇串口助手菜單,第二步選擇十六進制顯示,第三步選擇十六進制發送,第四步選擇COM口,這個COM口要和自己電腦設備管理器里的那個COM口一致,波特率按我們程序設定好的選擇,我們程序中讓一個數據位持續時間是1/9600秒,那這個地方選擇波特率就是選9600,校驗位選N,數據位8,停止位1。
串口調試助手的實質就是利用電腦上的UART通信接口,發送數據給我們的單片機,也可以把我們的單片機發送的數據接收到這個調試助手界面上。
因為初次接觸通信方面的技術,所以我把后面的IO模擬串口通信程序進行一下解釋,大家可以邊看我的解釋邊看程序,把底層原理先徹底弄懂。
變量定義部分就不用說了,直接看main主函數。首先是對通信的波特率的設定,在這里我們配置的波特率是9600,那么串口調試助手也得是9600。配置波特率的時候,我們用的是定時器T0的模式2。模式2中,不再是TH0代表高8位,TL0代表低8位了,而只有TL0在進行計數,當TL0溢出后,不僅僅會讓TF0變1,而且還會將TH0中的內容重新自動裝到TL0中。這樣有一個好處,就是我們可以把想要的定時器初值提前存在TH0中,當TL0溢出后,TH0自動把初值就重新送入TL0了,全自動的,不需要程序中再給TL0重新賦值了,配置方式很簡單,大家可以自己看下程序并且計算一下初值。
波特率設置好以后,打開中斷,然后等待接收串口調試助手下發的數據。接收數據的時候,首先要進行低電平檢測while(PIN_RXD),若沒有低電平則說明沒有數據,一旦檢測到低電平,就進入啟動接收函數()。接收函數最開始啟動半個波特率周期,初學可能這里不是很明白。大家回頭看一下我們的圖11-2里邊的串口數據示意圖,如果在數據位電平變化的時候去讀取,因為時序上的誤差以及信號穩定性的問題很容易讀錯數據,所以我們希望在信號最穩定的時候去讀數據。除了信號變化的那個沿的位置外,其它位置都很穩定,那么我們現在就約定在信號中間位置去讀取電平狀態,這樣能夠保證我們讀的一定是正確的。
一旦讀到了起始信號,我們就把當前狀態設定成接收狀態,并且打開定時器中斷,第一次是半個周期進入中斷后,對起始位進行二次判斷一下,確認一下起始位是低電平,而不是一個干擾信號。以后每經過1/9600秒進入一次中斷,并且把這個引腳的狀態讀到RxdBuf里邊。等待接收完畢之后,我們再把這個RxdBuf加1,再通過TXD引腳發送出去,同樣需要先發一位起始位,然后發8個數據位,再發結束位,發送完畢后,程序運行到while(PIN_RXD),等待第二輪信號接收的開始。
uart模塊介紹
IO口模擬串口通信,讓大家了解了串口通信的本質,但是我們的單片機程序卻需要不停的檢測掃描單片機IO口收到的數據,大量占用了單片機的運行時間。這時候就會有聰明人想了,其實我們并不是很關心通信的過程,我們只需要一個通信的結果,最終得到接收到的數據就行了。這樣我們可以在單片機內部做一個硬件模塊,讓它自動接收數據,接收完了,通知我們一下就可以了,我們的51單片機內部就存在這樣一個UART模塊,要正確使用它,當然還得先把對應的特殊功能寄存器配置好。
51單片機的UART串口的結構由串行口控制寄存器SCON、發送和接收電路三部分構成,先來了解一下串口控制寄存器SCON。
SCON串行控制器的位分配(地址:0x98)
位:符號:復位值: 0:RI:0;1:TI:0;2:RB8:0;3:TB8:0;4:REN:0;5:SM2:0;6:SM1:0;7:SM0:0;
0位RI:接收中斷標志位,當接收電路接收到停止位的中間位置時,RI由硬件置1,必須通過軟件清零
1位TI:發送中斷標志位,當發送電路發送到停止位的中間位置時,TI由硬件置1,必須通過軟件清零。
2位RB8:模式2和3中接收到的第9位數據(很少用),模式1用來接收停止位。
3位TB8:模式2和3中要發送的第9位數據(很少用)。
4位REN:使能串行接收。由軟件置位使能接收,軟件清零則禁止接收。
5位SM2:多機通信控制位(極少用),模式1直接清零。
6位SM1和7位SM0:
這兩位共同決定了串口通信的模式0~模式3共4種模式。我們最常用的就是模式1,也就是SM0=0,SM1=1,下邊我們重點就講模式1,其它模式從略。
對于串口的四種模式,模式1是最常用的,就是我們前邊提到的1位起始位,8位數據位和1位停止位。下面我們就詳細介紹模式1的工作細節和使用方法,至于其它3種模式與此也是大同小異,真正遇到需要使用的時候大家再去查閱相關資料就行了。
在我們使用IO口模擬串口通信的時候,串口的波特率是使用定時器T0的中斷體現出來的。在硬件串口模塊中,有一個專門的波特率發生器用來控制發送和接收數據的速度。對于單片機來講,這個波特率發生器只能由定時器T1或定時器T2產生,而不能由定時器T0產生,這和我們模擬的通信是完全不同的概念。
如果用定時器2,需要配置額外的寄存器,默認是使用定時器1的,我們本章內容主要就使用定時器T1作為波特率發生器來講解,方式1下的波特率發生器必須使用定時器T1的模式2,也就是自動重裝載模式,定時器的重載值計算公式為:
TH1 = TL1 = 256 - 晶振值/12 /2/16 /波特率
和波特率有關的還有一個寄存器,是一個電源管理寄存器PCON,他的最高位可以把波特率提高一倍,也就是如果寫PCON |= 0x80以后,計算公式就成了:
TH1 = TL1 = 256 - 晶振值/12 /16 /波特率
公式中數字的含義這里解釋一下,256是8位定時器的溢出值,也就是TL1的溢出值,晶振值在我們的開發板上就是,12是說1個機器周期等于12個時鐘周期,值得關注的是這個16,我們來重點說明。在IO口模擬串口通信接收數據的時候,采集的是這一位數據的中間位置,而實際上串口模塊比我們模擬的要復雜和精確一些。他采取的方式是把一位信號采集16次,其中第7、8、9次取出來,這三次中其中兩次如果是高電平,那么就認定這一位數據是1,如果兩次是低電平,那么就認定這一位是0,這樣一旦受到意外干擾讀錯一次數據,也依然可以保證最終數據的正確性。
串口通信的發送和接收電路在物理上有2個名字相同的SBUF寄存器,它們的地址也都是0x99,但是一個用來做發送緩沖,一個用來做接收緩沖。意思就是說,有2個房間,兩個房間的門牌號是一樣的,其中一個只出人不進人,另外一個只進人不出人,這樣的話,我們就可以實現UART的全雙工通信,相互之間不會產生干擾。但是在邏輯上呢,我們每次只操作SBUF,單片機會自動根據對它執行的是“讀”還是“寫”操作來選擇是接收SBUF還是發送SBUF,后邊通過程序,我們就會徹底了解這個問題。
UART串口程序:
一般情況下,我們編寫串口通信程序的基本步驟如下所示:
1、配置串口為模式1。
2、配置定時器T1為模式2,即自動重裝模式。
3、根據波特率計算TH1和TL1的初值,如果有需要可以使用PCON進行波特率加倍。
4、打開定時器控制寄存器TR1,讓定時器跑起來。
這里還要特別注意一下,就是在使用T1做波特率發生器的時候,千萬不要再使能T1的中斷了。
我們先來看一下由IO口模擬串口通信直接改為使用硬件UART模塊時的程序代碼,看看程序是不是簡單了很多,因為大部分的工作硬件模塊都替我們做了。程序功能和IO口模擬的是完全一樣的。
通信實例與ASCLL碼
先拋開我們使用的漢字不談,那么我們常用的字符就包含了0~9的數字、A~Z/a~z的字母、還有各種標點符號等。那么在單片機系統里面我們怎么來表示它們呢?ASCII碼(hange,即美國信息互換標準代碼)可以完成這個使命:我們知道,在單片機中一個字節的數據可以有0~255共256個值,我們取其中的0~127共128個值賦予了它另外一層涵義
我們用字符格式發送一個小寫的a,返回一個十六進制的0x61,數碼管上顯示的也是61,ASCII碼表里字符a對應十進制是97,等于十六進制的0x61;我們再用字符格式發送一個數字1,返回一個十六進制的0x31,數碼管上顯示的也是31,ASCII表里字符1對應的十進制是49,等于十六進制的0x31。這下大家就該清楚了:所謂的十六進制發送和十六進制接收,都是按字節數據的真實值進行的;而字符格式發送和字符格式接收,是按ASCII碼表中字符形式進行的,但它實際上最終傳輸的還是一個字節數據。這個表格,當然不需要大家去記住,理解它,用的時候過來查就行了。
51單片機串口通信實例(字符串接收和發送)
#include《reg52.h》
//------------------串口通信協議-----------------//
/*
客戶端數據包格式解釋(長度恒為15):
例如:#
A--------數據包的開始標記(可以為A到Z,意味著數據包可以有26種)
01-----設備代號
--------指令(長度恒為10),指令的前4個人字符是指令頭部,指令的后6個字符是指令尾部
#---------數據包的結束標記
服務器端數據包格式解釋(長度恒為15):
例如:#
A--------數據包的開始標記(可以為A到Z,意味著數據包可以有26種)
02-----設備代號
--------指令(長度恒為10),指令的前4個人字符是指令頭部,指令的后6個字符是指令尾部
#---------數據包的結束標記
*/
char [16]; //定義數據包長度為15個字符
#define ‘0’ //用于串口通信時,定義本地設備ID的第1位
#define ‘2’ //用于串口通信時,定義本地設備ID的第2位
#define ‘A’ //用于串口通信時,定義數據包頭部的驗證標記
char [16]={,,,‘_’,‘S’,‘e’,‘n’,‘T’,‘X’,‘X’,‘X’,‘X’,‘X’,‘X’,‘#’};
char [16]={,,,‘_’,‘B’,‘e’,‘a’,‘t’,‘X’,‘X’,‘X’,‘X’,‘X’,‘X’,‘#’};
//----------------------------------------------//
/*******************************
串口通信
MCU:89C52RC 11.0592MHz
//11.0592MHz 0xd0 1200bps
//12MHz 0xcc 1200bps
//11.0592MHz 0xfa 9600bps
//0xf4 11.0592MHz 0xf3 12MHz 4800bps
//均在SMOD=1的情況下(波特率倍增模式)
*******************************/
//串口發送函數
void ( char *TXStr)
ES=0;
while(*TXStr!=0)
SBUF=*TXStr;
while(TI==0);
TI=0;
TXStr++;
ES=1;
//串口接收函數
bit ()
char * RecStr=;
char num=0;
char count=0;
loop:
*RecStr=SBUF;
count=0;
RI=0;
if(num《14) //數據包長度為15個字符,嘗試連續接收15個字符
num++;
RecStr++;
while(!RI)
count++;
if(count》130)return 0; //接收數據等待延遲,等待時間太久會導致CPU運算閑置,太短會出現“數據包被分割”,默認count=130
goto loop;
return 1;
//定時器1用作波特率發生器
void ()
SCON=0x50; //串口方式1,使能接收
TMOD|=0x20; //定時器1工作方式2(8位自動重裝初值)
TMOD&=~0x10;
TH1=0xfa; //9600bps
TL1=0xfa;
PCON|=0x80; //SMOD=1
TR1=1;
TI=0;
RI=0;
//PS=1; //提高串口中斷優先級
ES=1; //開啟串口中斷使能
//比較指令頭部
bit (char [])
char CharNum;
for(CharNum=0;CharNum《4;CharNum++) //指令長度為10個字符
if(?。ǎ跜harNum+4]==[CharNum]))
return 0; //指令頭部匹配失敗
return 1; //指令頭部匹配成功
//比較指令尾部(start:從哪里開始比較,quality:比較多少個字符,[]:要比較的字符串)
bit ( char start, char quality,char [])
char CharNum;
for(CharNum=0;CharNum《quality;CharNum++)
if(?。ǎ踫tart+CharNum]==[CharNum]))
return 0;
return 1;
bit () //處理串口接收數據包函數(成功處理數據包則返回1,否則返回0)
//();
if([0]==&&[14]==‘#’) //進行數據包頭尾標記驗證
switch([1]) //識別發送者設備ID的第1位數字
case ‘0’:
switch([2]) //識別發送者設備ID的第2位數字
case ‘3’:
if((“Ligt”)) //判斷指令頭部是否為“Ligt”
//下面是指令尾部分析
switch([8])
case ‘0’:
switch([9])
case ‘0’:
return 0;
case ‘1’:
if((10,3,“Off”)) //#
//要執行的代碼
return 1;
if((10,3,“On_”))
return 1;
return 0;
default:
return 0;
case ‘1’:
default:
return 0;
if((“SenT”))
if((“jdq_”))
if((“Try!”))
return 0;
default:
return 0;
default:
return 0;
return 0;
/************************
中斷函數
************************/
//串口中斷服務函數-----------
void USART() 4 //標志位TI和RI需要手動復位,TI和RI置位共用一個中斷入口
if(())
//數據包長度正確則執行以下代碼
();
else
//數據包長度錯誤則執行以下代碼
//LED1=~LED1;
RI=0; //接收并處理一次數據后把接收中斷標志清除一下,拒絕響應在中斷接收忙的時候發來的請求
/***************************
主函數
***************************/
void main()
EA=1;
();
while(1)
//();//空格20H,回車0DH
*請認真填寫需求信息,我們會在24小時內與您取得聯系。