前言:
今天有兩個項目,分別為:
串口接收:
串口發送:
如上圖將文件放在Keli5 中即可,然后燒錄在單片機中就行了
燒錄軟件用的是STC-ISP,不知道怎么安裝的可以去看江科大的視頻:
【51單片機入門教程-2020版 程序全程純手打 從零開始入門】https://www.bilibili.com/video/BV1Mb411e7re?p=2&vd_source=ada7b122ae16cc583b4add52ad89fd5e
串口接收源代碼:
頭文件要記得宏定義和重定義,避免重復調用:
#ifndef _Timer0_h_//名字根據文件名定義即可
#define _Timer0_h_//聲明函數……#endif
?main.c
//接收程序
#include <STC89C5xRC.H>
#include "Delay.h"
#include "UART.h"unsigned char Sec;void main(){UATE_Init();//初始化while(1){UATE_SendByte(Sec);//發送字節Sec++;//每秒遞增Delay(1000);}
}
UART.c
#include <STC89C5xRC.H>//初始化串口
//void UART_Init(){
// SCON=0x40;//0100 0000
// PCON=0;
// //&只有在兩個位都為1時結果位才是1,而|只要有一個位為1結果位就是1
// TMOD &= 0x0F; //設置定時器模式,高四位清0,低四位不變
// TMOD |= 0x20; //設置定時器模式,高四位設計成0010,低四位不變,定時器1,模式2
// TL0 = 0x66; //設置定時初值
// TH0 = 0xFC; //設置定時初值
// TF0 = 0; //清除TF0標志
// TR0 = 1; //定時器0開始計時
// ET0=1;//允許中斷
// EA=1;//允許總中斷
// PT0=0;//低優先級
//}
void UATE_Init() //4800bps@11.0592MHz
{PCON &= 0x7F; //波特率不倍速SCON = 0x50; //8位數據,可變波特率
// AUXR &= 0xBF; //定時器1時鐘為Fosc/12,即12T
// AUXR &= 0xFE; //串口1選擇定時器1為波特率發生器TMOD &= 0x0F; //清除定時器1模式位TMOD |= 0x20; //設定定時器1為8位自動重裝方式TL1 = 0xFA; //設定定時初值TH1 = 0xFA; //設定定時器重裝值ET1 = 0; //禁止定時器1中斷TR1 = 1; //啟動定時器1
}//發送字節(只發送,不接受)
void UATE_SendByte(unsigned char Byte){SBUF=Byte;//傳入字節數據while(TI==0);//發送循環,發送完TI=1;TI=0;//軟件復位
}
UART.h
//UART.h#ifndef __UART_H__
#define __UART_H__void UATE_Init();
void UATE_SendByte(unsigned char Byte);
#endif
Delay.c?
//Delay.c#include <STC89C5xRC.H>
#include <INTRINS.H>//延時函數
void Delay(unsigned int xms) //@11.0592MHz
{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}
}
?Delay.h
//Delay.h#ifndef __Delay_H__
#define __Delay_H__//延時函數頭文件
void Delay(unsigned int xms);
#endif
?串口發送源代碼:
頭文件要記得宏定義和重定義,避免重復調用:
#ifndef _Timer0_h_//名字根據文件名定義即可
#define _Timer0_h_//聲明函數……#endif
?main.c
#include <STC89C5xRC.H>
#include "Delay.h"
#include "UART.h"void main(){UATE_Init();//初始化while(1){}
}
//中斷函數
void UART_Routine() interrupt 4{if(RI==1){//RI等于1表示可以中斷P2=~SBUF;//發送數據UATE_SendByte(SBUF);//接收數據RI=0;//軟件復位}
}
UART.c
#include <STC89C5xRC.H>//初始化串口
//void UART_Init(){
// SCON=0x40;//0100 0000
// PCON=0;
// //&只有在兩個位都為1時結果位才是1,而|只要有一個位為1結果位就是1
// TMOD &= 0x0F; //設置定時器模式,高四位清0,低四位不變
// TMOD |= 0x20; //設置定時器模式,高四位設計成0010,低四位不變,定時器1,模式2
// TL0 = 0x66; //設置定時初值
// TH0 = 0xFC; //設置定時初值
// TF0 = 0; //清除TF0標志
// TR0 = 1; //定時器0開始計時
// ET0=1;//允許中斷
// EA=1;//允許總中斷
// PT0=0;//低優先級
//}
void UATE_Init() //4800bps@11.0592MHz
{PCON &= 0x7F; //波特率不倍速SCON = 0x50; //8位數據,可變波特率
// AUXR &= 0xBF; //定時器1時鐘為Fosc/12,即12T
// AUXR &= 0xFE; //串口1選擇定時器1為波特率發生器TMOD &= 0x0F; //清除定時器1模式位TMOD |= 0x20; //設定定時器1為8位自動重裝方式TL1 = 0xFA; //設定定時初值TH1 = 0xFA; //設定定時器重裝值ET1 = 0; //禁止定時器1中斷TR1 = 1; //啟動定時器1EA=1;//啟動總中斷ES=1;//啟動串口中斷}//發送字節(只發送,不接受)
void UATE_SendByte(unsigned char Byte){SBUF=Byte;//傳入字節數據while(TI==0);//發送循環,發送完TI=1;TI=0;//軟件復位
}串口中斷模版
中斷函數
//void UART_Routine() interrupt 4{
// if(RI==1){//RI等于1表示可以中斷
// P2=~SBUF;//發送數據
// UATE_SendByte(SBUF);//接收數據
// RI=0;//軟件復位
// }
//}
UART.h
//UART.h#ifndef __UART_H__
#define __UART_H__void UATE_Init();
void UATE_SendByte(unsigned char Byte);
#endif
Delay.c?
//Delay.c#include <STC89C5xRC.H>
#include <INTRINS.H>//延時函數
void Delay(unsigned int xms) //@11.0592MHz
{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}
}
?Delay.h
//Delay.h#ifndef __Delay_H__
#define __Delay_H__//延時函數頭文件
void Delay(unsigned int xms);
#endif
?注意兩個項目的代碼有部分不同!!!!
第一個項目是串口接收:
- 主程序代碼:
#include <STC89C5xRC.H> #include "Delay.h" #include "UART.h"unsigned char Sec;void main(){UATE_Init();//初始化while(1){UATE_SendByte(Sec);//發送字節Sec++;//每秒遞增Delay(1000);} }
注意:接收是串口向電腦發送數據,發送一個16進制,每秒增加,電腦接收。而且發送是沒有中斷的!!!
UART.c代碼:
void UATE_Init() //4800bps@11.0592MHz {PCON &= 0x7F; //波特率不倍速SCON = 0x50; //8位數據,可變波特率 // AUXR &= 0xBF; //定時器1時鐘為Fosc/12,即12T // AUXR &= 0xFE; //串口1選擇定時器1為波特率發生器TMOD &= 0x0F; //清除定時器1模式位TMOD |= 0x20; //設定定時器1為8位自動重裝方式TL1 = 0xFA; //設定定時初值TH1 = 0xFA; //設定定時器重裝值ET1 = 0; //禁止定時器1中斷TR1 = 1; //啟動定時器1 }//發送字節(只發送,不接受) void UATE_SendByte(unsigned char Byte){SBUF=Byte;//傳入字節數據while(TI==0);//發送循環,發送完TI=1;TI=0;//軟件復位 }
沒有中斷,因此ET1=0,禁止中斷。
運行:
- 單片機沒有任何變化,電腦端STC-ISP的串口助手接收緩沖區會有逐漸增加的數據。
串口接收數據
第二個項目是串口發送:
- 主程序代碼:
#include <STC89C5xRC.H> #include "Delay.h" #include "UART.h"void main(){UATE_Init();//初始化while(1){} } //中斷函數 void UART_Routine() interrupt 4{if(RI==1){//RI等于1表示可以中斷P2=~SBUF;//發送數據UATE_SendByte(SBUF);//接收數據RI=0;//軟件復位} }
注意:發送是電腦向串口發送數據,利用中斷來控制LED燈(P2),RI=1打開中斷。UART.c代碼:
void UATE_Init() //4800bps@11.0592MHz {PCON &= 0x7F; //波特率不倍速SCON = 0x50; //8位數據,可變波特率 // AUXR &= 0xBF; //定時器1時鐘為Fosc/12,即12T // AUXR &= 0xFE; //串口1選擇定時器1為波特率發生器TMOD &= 0x0F; //清除定時器1模式位TMOD |= 0x20; //設定定時器1為8位自動重裝方式TL1 = 0xFA; //設定定時初值TH1 = 0xFA; //設定定時器重裝值ET1 = 0; //禁止定時器1中斷TR1 = 1; //啟動定時器1EA=1;//啟動總中斷ES=1;//啟動串口中斷}//發送字節(只發送,不接受) void UATE_SendByte(unsigned char Byte){SBUF=Byte;//傳入字節數據while(TI==0);//發送循環,發送完TI=1;TI=0;//軟件復位 }
有中斷,因此EA,ES啟動中斷。
?運行:
- 發送對應的16進制,用LED燈來檢測,如發送0f,就是0000 1111,也就是后四個燈亮,單片機對應的燈會亮。
串口發送數據
代碼解析與教程:
?Dealy模塊
- 包含源代碼與頭文件,不需要知道怎么實現的會用即可,后續使用,直接將頭文件和源代碼拿過來用即可;
xms是定義的毫秒,1000毫秒就是1秒;模版生成的是1毫秒的,因此xms等于1000
?UART模塊
- 包含源代碼與頭文件,需要知道怎么實現,會用
- 51單片機串口通訊十分重要,要理解怎么用,要知道原理是什么,要結合原理圖來分析怎么做,先看代碼
#include <STC89C5xRC.H>//初始化串口 //void UART_Init(){ // SCON=0x40;//0100 0000 // PCON=0; // //&只有在兩個位都為1時結果位才是1,而|只要有一個位為1結果位就是1 // TMOD &= 0x0F; //設置定時器模式,高四位清0,低四位不變 // TMOD |= 0x20; //設置定時器模式,高四位設計成0010,低四位不變,定時器1,模式2 // TL0 = 0x66; //設置定時初值 // TH0 = 0xFC; //設置定時初值 // TF0 = 0; //清除TF0標志 // TR0 = 1; //定時器0開始計時 // ET0=1;//允許中斷 // EA=1;//允許總中斷 // PT0=0;//低優先級 //} void UATE_Init() //4800bps@11.0592MHz {PCON &= 0x7F; //波特率不倍速SCON = 0x50; //8位數據,可變波特率 // AUXR &= 0xBF; //定時器1時鐘為Fosc/12,即12T // AUXR &= 0xFE; //串口1選擇定時器1為波特率發生器TMOD &= 0x0F; //清除定時器1模式位TMOD |= 0x20; //設定定時器1為8位自動重裝方式TL1 = 0xFA; //設定定時初值TH1 = 0xFA; //設定定時器重裝值ET1 = 0; //禁止定時器1中斷TR1 = 1; //啟動定時器1 }//發送字節(只發送,不接受) void UATE_SendByte(unsigned char Byte){SBUF=Byte;//傳入字節數據while(TI==0);//發送循環,發送完TI=1;TI=0;//軟件復位 }
最上面的代碼:主要是理解SCON和PCON,其他的就是定時器/計數器,中斷的內容
?Timer0模塊
- 包含源代碼與頭文件,需要知道怎么實現,會用
- 51單片機的定時器和計數器十分重要,要理解怎么用,要知道原理是什么,要結合原理圖來分析怎么做,先看代碼
#include <STC89C5xRC.H>//void Timer0_Init() //{ // TF0=0;TR0=1;//TCON,寄存器 // //TMOD=0x01;//0000 0001,寄存器 // TMOD=TMOD&0xF0;//把TMOD的低四位清零,高四位不變,方便使用兩個定時器 // TMOD=TMOD&0x01;//把TMOD的最低位置1,高四位不變,方便使用兩個定時器 // TH0=64535/256;//高電位,寄存器,1毫秒 // TL0=64535%256;//低電位,寄存器,1毫秒 // ET0=1;EA=1;PT0=0;//打開中斷開關 // //} //定時器0初始化函數 void Timer0_Init() //1毫秒@11.0592MHz { // AUXR &= 0x7F; //定時器時鐘12T模式TMOD &= 0xF0; //設置定時器模式TMOD |= 0x01; //設置定時器模式TL0 = 0x66; //設置定時初值TH0 = 0xFC; //設置定時初值TF0 = 0; //清除TF0標志TR0 = 1; //定時器0開始計時ET0=1;//允許中斷EA=1;//允許總中斷PT0=0;//低優先級 }中斷程序函數 //中斷函數模版 //void Timer0_Routine() interrupt 1 //{ // static unsigned int T0Count; // TL0 = 0x66; //設置定時初值 // TH0 = 0xFC; //設置定時初值 // T0Count++; // if(T0Count>=1000){ // T0Count=0; // //下面是代碼區 // } //}
- 最上面注釋掉的代碼是要求理解的;中間的代碼是STC-ISP軟件生成的;最下面的代碼是中斷函數模版,拿到main.c中可直接使用,但是也要了解原理:
定時器/計數器、中斷教程(重點!!!!)
- 首先,要理解原理,會認原理圖:
(本篇均是我自己理解的,只是幫助大家理解,若像深學深究,請去自行找資源,如有不對,希望大家指出)
先看官方解釋:
- 紅色部分是定時器,作用是自己設定一個最大值,讓計數器達到時,完成什么什么,比如執行中斷
- 黃色部分是計數器,作用是自己設定,讓其變化,比如+1,毫秒,微妙,完成時繼續怎么怎
- 藍色部分是中斷器,中斷當前執行,執行自己設定的東西,中斷這部分可以嵌套,像if函數一樣,低優先級讓高優先級
他們三者的關系非常微妙,僅僅相連,相輔相成:
- 定時器就是設定一個時間,計數器開始計時,到點了中斷器開始執行;舉個例子:你訂一個10.00的鬧鐘(定時器)提醒你起床,時間一點一點的過去(計數器),10.00的時候鬧鐘提醒你(定時器),隨后你開始起床(中斷器);那么他們是怎么實現的呢
?定時器/計數器(模式一)
- 先看原理圖:
- 相關寄存器:
- 官方解釋
- 模式一官方解釋
- 下面開始來解釋我的理解(再看原理圖):
- 序號1是非門:反向輸出,例如:GATE是1,輸出0,是0,輸入1。
- 序號2是或門:符號為梯形(或弧形缺口),邏輯上滿足 “有 1 出 1,全 0 出 0”。
- 序號3是與門:符號為矩形缺口,邏輯上滿足 “全 1 出 1,有 0 出 0”。
1,2,3序號均是TMOD里的,請看原理圖:![]()
- 代碼TMOD=0x01,是16進制,轉化為二進制為0000 0001,前(高)四位對應著定時器1;后(低)四位對應著定時器0;本代碼使用的是定時器0, 0001分別對應GATE,C/T,M1,M0,由上面的官方解釋可得,M1=0,M0=1時,就是使用并啟動本寄存器。
但是這樣定義有弊端,也就是定時器1和定時器0不能一起使用,因次,使用下面的代碼:TMOD &=0xF0,也就是TMOD = TMOD & 0xF0(1111 0000)。看不懂沒關系,舉個例子:1010 0101 &? 1111 0000 = 1010 0000,也就是有0出0;
1010 0101 |? 1111 0000 = 1111?0101,也就是有1出1;因此使用代碼:就可以定義定時器0;
TMOD &= 0xF0; //設置定時器模式
TMOD |= 0x01; //設置定時器模式
- 因此,當GATE=0時,通過序號1,輸出1;然后通過序號2,輸出1;因此,只需要將TR0設定成1,輸出就是1,就可以啟動寄存器了。
- 序號4是高電位和低電位寄存器:用來設置定時初值,如圖:
![]()
- 先解釋上面的代碼,可幫助理解,下面的代碼可以不理解(后續可生成):TH0和TL0,最大位就是65535,為了分開儲存,用TH0和TL0分別儲存,例如:現在有一個數123,但是一個盒子只能裝進2位數,因此分成123/100=1和123%100=23儲存,同理身為16進制,就要用256來做除數;代碼中設定為64535的目的是因為1000毫秒就是1秒,65535-64535=1000,用來表示1秒,計時器每次加1秒。
- 序號7.8(圖中忘記標了(TR0))是TCON(定時器控制)寄存器:TF0=0時,可以理解成初始化;TR0=1時,表示允許計時;反之兩個就是反義理解
- 序號5.6是TMOD(定時器模式)寄存器:用來控制定時器和計數器的模式,本篇只講用的多的模式一
?中斷器
- 先看原理圖:
這里我們定義好定時器0后,TF0=1,ET0=1,打開中斷,隨后PT0=0,接入低級優先:理解上面的東西后,再看中斷函數:
運用靜態局部變量T0Count,來表示定時區間,達到1000毫秒(1秒)后重新執行該函數
P20行代碼是代碼區,也就是放你想每1秒就重復執行的代碼。
?串口通訊教程(方式1)(接收和發送)(重點!!!!!!)
主要是理解SCON和PCON,其他的就是定時器/計數器,中斷的內容
SCON(方式1)
SCON先看官方解釋:
SCON解釋:
如圖:SCON正好是個16進制,8個變量;我們只用方式1;根據官方解釋來看,SM0=0,SM1=1,就是方式1;SM2,TB8,RB8不是方式1的制定為0;REN=1是啟動串口接收;TI,RI默認為0,表示串口通訊(中斷)允許開啟(后續可變);PCON
PCON官方解釋:
?
- PCON解釋:
SBUF就是接收數據的,如圖:
根據我標的圖來看,你需要將數據寫進SBUF里,然后通過設置定時器初值波特率倍率,然后從發送端到接收端。//發送字節(只發送,不接受) void UATE_SendByte(unsigned char Byte){SBUF=Byte;//傳入字節數據while(TI==0);//發送循環,發送完TI=1;TI=0;//軟件復位 }
其中TI就是SCON中的其中一個變量,發送的時候TI=0,發送完TI=1,因此要自定義軟件復位。EA,ES分別置于1,作用如下:
EA=1;//啟動總中斷ES=1;//啟動串口中斷
下面來看:
ET1 = 0; //禁止定時器1中斷TR1 = 1; //啟動定時器1
TR1是定時器的教程(放在下面了),作用是打開定時器1
ET1是中斷的內容作用是禁止定時器1中斷,如圖:
現在來看中斷函數:
RI=0是接收數據用的,當接收時RI=1,接收完畢后進行軟件復位。
?LED教程:
- 先看原理圖:
LED是高電位,設定為0就是通電
?
- P2就是16進制的LED燈總控制,如:0xFF=1111 1111,就是全關,例如代碼中的:
P2=0xFE,就是1111 1110,就是L1亮,左移一位就是1111 1101,就是L2亮,其他同理,配合中斷函數,就可以每秒移動一次
?舉一反三(新項目):
在第二個項目的基礎上,改一下main.c文件
#include <STC89C5xRC.H>
#include "Delay.h"
#include "UART.h"void main(){UATE_Init();//初始化UATE_SendByte(SBUF);//發送字節(新增)while(1){}
}
//中斷函數
void UART_Routine() interrupt 4{if(RI==1){//RI等于1表示可以中斷P2=~SBUF;//發送數據UATE_SendByte(SBUF);//接收數據RI=0;//軟件復位}
}
就可以做到發送緩沖區發送后, 接收緩沖區也顯示:
注意選擇同樣的模式(文本模式或者HEX模式)?,支持的文本如下:
效果視頻:?
串口的接收和發送
?注:該代碼是本人自己所寫,可能不夠好,不夠簡便,歡迎大家指出我的不足之處。如果遇見看不懂的地方,可以在評論區打出來,進行討論,或者聯系我。上述內容全是我自己理解的,如果你有別的想法,或者認為我的理解不對,歡迎指出!!!如果可以,可以點一個免費的贊支持一下嗎?謝謝各位彥祖亦菲!!!!!