51單片機學習總結實例
接下來,就把我總結的結果跟大家分享下。
本文引用地址:http://m.butianyuan.cn/article/201611/318259.htm這一章是51單片機,主要是程序實例。代碼參考郭天祥的單片機教材。
是總括性的,適合于有一定單片機基礎的同學,也可以給初學者做一個系統(tǒng)的學習主線。
這個博客里的涉及的源代碼大家可以在這里下載http://download.csdn.net/detail/zhaole20094463/4427745
1.流水燈
/流水燈/
#include
#include
#define uint unsigned int //宏定義 定義uint 在大項目中更加的方便
#define uchar unsigned char
void delayms(uint);
uchar aa;
void main()
{
aa=0xfe;
while(1)
{
P1=aa;
delayms(500);
aa=_crol_(aa,1);//aa左移一位
}
}
void delayms(uint xms)//延時時間約為1ms通過程序單步執(zhí)行,看運行時間可得到
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
通過調用系統(tǒng)函數(shù)的方式使流水燈實現(xiàn)循環(huán)左移。
2.數(shù)碼管顯示程序
/*鎖存器用的是74HC573高電平通,低電平鎖存*/
/*作用:高阻態(tài) ,數(shù)據(jù)鎖存 ,數(shù)據(jù)緩沖 (加強驅動能力)*/
/*循環(huán)顯示1-A的數(shù)據(jù)*/
#include
#define uint unsigned int
#define uchar unsigned char
sbit dula=P2^6;
sbit wela=P2^7;
uchar num;
uchar code table[]={數(shù)碼管段碼表};
void main()
{
wela=1;//打開鎖存器
P0=0xc0;//送入鎖存信號
wela=0;//關閉鎖存器
while(1)
{
for(num=0;num<16;num++)
dula=1;
P0=table[num];
delayms(500);
}
}
void delayms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
3.獨立鍵盤檢測
/*通過按鍵控制led亮滅*/
#include
#define uint unsigned int
#define uchar unsigned char
sbit key1=P3^4;
sbit led=P1^0;
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void main()
{
P3=0xff;//在對51單片機進行按鍵檢測時,先要將引腳置1;
while(1)
{
if(key1==0)
{
delay(5);
if(key1==0)//按鍵消抖
{
led=0;
num++;
if(num==2)
num=0;
led=1;
}
}
4.矩陣鍵盤檢測
/*檢測矩陣鍵盤數(shù)碼管顯示0-F/
矩陣鍵盤的四列與P3.4~P3.7相連
4行與P3.0~P3.3
#include
#define uchar unsinged char
#define uint unsigned int
sbit dula=P2^6;
sbint wela=P2^7;
uchar code table[]={段碼表};
void delayms(uint xms);
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void display(uchar num)顯示函數(shù)
{
P0=table[num];
dula=1;
dula=0;
}
void matrixkeyscan()//矩陣鍵盤的檢測
{
uchar temp,key;
P3=0xfe;//打開P3.7,P3.7置0.檢測高四位
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)//是否有鍵被按下
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)//按鍵消抖
{
temp=P3;
switch(temp)
{
case 0xee:key=0;break;
case 0xde:key=1;break;
case 0xbe:key=2;break
case 0x7e:key=3:break;
}
while(temp!=0xf0)//檢測按鍵是否彈起,如果沒有彈起循環(huán)停在這里。
{
temp=P3;
temp=temp&0xf0;
}
display(key);
}
}
。。。。。。。。。。。。。。。。。。。
如下程序依次增加
令P3=0xfd,打開P3.6,P3.6置低,檢測高四位 依次為 4,5,6,7
然后令P3= 0xfb 0xf7
}
void main()
{
P0=0;//關閉所有數(shù)碼管的段選
dula=1;
dula=0;
P0=0xc0;
wela=1;
wela=0;
while(1)
{
matrixkeyscan();//不停調用鍵盤掃描程序
此程序關鍵在于這個函數(shù),矩陣鍵盤的掃描算法
}
}
5.1602液晶屏顯示子函數(shù)
//
這個程序主要是有這樣幾個子函數(shù),關于lcd寫命令和寫數(shù)據(jù)的
#include
#define uchar unsigned char
#define uint unsigned int
uchar code table[]="zhaole20094463";
uchar code table1[]="love Embedded systems";
sbit lcden=P3^4;//lcd使能端
sbit lcdrs=P3^5;//lcd數(shù)據(jù)命令選擇端
uchar num;
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void write_com(uchar com)//lcd寫命令函數(shù)
{
lcdrs=0;
P0=com;
delay(5);
lcden=1;
delay(5);
lceen=0;
}
void write_data(uchar date)//lcd寫數(shù)據(jù)
{
lcdrs=1;
P0=date;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void init()
{
lcden=0;
write_com(0x38);//設置16*2顯示,5*7點陣,8位數(shù)據(jù)接口
write_com(0x0c);//設置開顯示,不顯示光標
write_com(0x06);//寫第一個字符后地址指針加1
write_com(0x01);//顯示清0,數(shù)據(jù)指針清0
}
void main()
{
init();
write_com(0x80);//數(shù)據(jù)指針定位在第一行的第一個字處
for(num=0;num<14;num++)
{
write_data(table[num]);
delay(5);
}
write_com(0x80+0x40);//數(shù)據(jù)指針定位在第二行的第一個字處
for(num=0;num<21;num++)
{
write_data(table1[num]);
delay(5);
}
while(1);
}
5.2 12864顯示子函數(shù)
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define LCD_data P0;//數(shù)據(jù)口
sbit LCD_RS=P3^5; //數(shù)據(jù)選擇輸入
sbit LCD_RW=P3^6; //液晶讀寫控制
sbit LCD_EN=P3^4; //液晶使能控制端
sbit LCD_PSB=P3^7; //串并方式控制
void delay_1ms(uint )
{
uint i,j;
for(j=0j
}
/*PS=L,RW=L,E=高脈沖,D0~D7=指令碼*/
void write_cmd(uchar cmd)
{
LCD_RS=0;
LCD_RW=0;
LCD_EN=0;
P0=cmd;
delay_1ms(5);
LCD_EN=0;
}
/*寫顯示數(shù)據(jù)到LCD*/
/*RS=H,RW=L,E=高脈沖,D0~D7=數(shù)據(jù)
void write_dat(uchar dat)
{
LCD_RS=1;
LCD_RW=0;
LCD_EN=0;
P0=dat;
delay_1ms(5);
LCD_EN=1;
dealy_1ms(5);
LCD_EN=0;
}
/*設定顯示位置*/
void LCD_pos(uchar X,uchar Y)
{
uchar pos;
if(X==0)
{X=0x80;}
else if(X==1);
{X=0x90;}
else if(x==2)
{X=0x88;}
else if(X==3)
{X=0x98;}
pos=X+Y;
write_cmd(pos);//顯示地址
}
/*LCD初始化設置*/
void lcd_init()
{
LCD_PSB=1; //并口方式
write_cmd(0x30);//基本指令操作
delay_1ms(5);
write_cmd(0x0C);//先是開,關光標
delay_1ms(5);
write_cmd(0x01);//清除LCD的顯示內容
delay_1ms(5);
}
6.定時器中斷
/*用定時器0的方式1實現(xiàn)第一個發(fā)光管以1s亮滅*/
定時器/計數(shù)器4種工作方式
1,方式0 13位計數(shù)器 高8位和低5位
2,方式1 16位計數(shù)器
3,方式2 自動恢復初值 8位
4,方式3 只適用于T0,不適用于T1 8位
45872初值的計算
晶振:11.0592M
機器周期:計數(shù)一次的時間=12*(1/11.0592M)51單片機是12個時鐘周期為一個機器周期
=1.09US 指令周期由整數(shù)個機器周期組成
45872 =50Ms/1.09us
//
#include
#define uchar unsigned char
#define uint unsigned int
sbit led1=P1^0;
uchar num;
void main()
{
TMOD=0x01;//設置定時器0工作方式1
TH0=(65535-45872)/256;//裝初值11.0592M晶振定時50ms為45872
TL0=(65535-45872)%256;
EA=1; //開總中斷
ET0=1; //開定時器0中斷
TR0=1; //啟動定時器0
while(1) //程序停在這里等待中斷的發(fā)生
}
void T0_time() interrupt 1
{
TH0=(65535-45872)/256;
TL0=(65535-45872)%256;
num++;
if(num==20)
{
num=0;
led1=~led1;
}
}
7.串口通信
//
上位機通過串口調試助手發(fā)送一個字符x,單片機收到字符后返回給上位機“I get x”
串口波特率設置為9600bps。
#include
#define uchar unsigned char
#define uint unsigned int
uchar flag,a,i;
uchar code table[]="I get";
void init()
{
TMOD=0x20;//設置T1定時器工作方式為2 8位初值自動重裝的8位定時器
TH1=0xfd;//T1定時器裝初值(高八位) 控制串口通信的波特率(由定時器1的溢出率控制)
TL1=0xfd;///T1定時器裝入初值(低八位)
TR1=1; //定時器1運行控制位 置一啟動定時器
REN=1; //允許串口接收
SM0=0;
SM1=1; //設置串口工作方式為一
EA=1; //全局中斷允許位 置一開全局中斷
ES=1; //串口中斷允許位
}
void main()
{
init();
` while(1)
{
if(flag==1)
{
ES=0;
for(i=0;i<6;i++)
{
SBUF=table[i];
while(!TI)
TI=0;//發(fā)送中斷標志 發(fā)送數(shù)據(jù)完成后觸發(fā)中斷 硬件置一 必須軟件清零
}
SBUF=a;// 串口發(fā)送數(shù)據(jù)
while(!TI);
TI=0;
ES=1;
flag=0;
}
}
void ser() interrupt 4 //串口中斷服務程序
{
RI=0; // 收到數(shù)據(jù)硬件置一,由軟件清零
a=SBUF; //將寄存器的值 賦給a
flag=1; //標志位
}
注意:51單片機的特殊之處在于,SBUF既是串口接收寄存器也是串口發(fā)送寄存器,取決于
SBUF所在賦值符號“=”左右的位置
此程序中共用的串口中斷,
定時器1中斷 特殊功能寄存器SBUF
中斷響應的條件
中斷源有中斷請求 此中斷源允許位為1 cpu開中斷(即EA=1)以上三個條件同時滿足
串口通信設置
確定串口通信波特率(編程TMOD寄存器定時器工作方式寄存器)
計算定時器初值轉載THX TLX
確定串行口工作方式(編程SCON寄存器串行口控制方式寄存器)
串行口工作在中斷方式時,要進行中斷設置如TI,RI軟件清零
8.I2C總線通信模擬子函數(shù)
/*/
因為51單片機沒有I2C總線,所以采用模擬I2C通信
1,總線初始化
void init()
{
SCL=1;
delay();
SDA=1;
delay();
}
2,啟動信號
void start()
{
SDA=1;
delay(1);
SCL=1;
delay();
SDA=0;
delay();
}
3,應答信號
void respons()
{
uchar i=0;
SCL=1;
delay();
while((SDA==1)&&(i<255))
i++;
SCL=0;
delay();
}
4,停止信號
void stop()
{
SDA=0;
delay();
SCL=1;
delay();
SDA=1;
delay();
}
5,寫一個字節(jié)
void writebyte(uchar date)
{
uchar i,temp;
temp=date;
for(i=0;i<8;i++)
{
temp=temp<<1;
SCL=0;
delay();
SDA=CY;
delay();
SCL=1;
delay();
}
SCL=0;
delay();
SDA=1;
delay();
}
6,讀一個字節(jié)
uchar readbyte()
{
uchar i,k;
SCL=0;
delay();
SDA=1;
for(i=0;i<8;i++)
{
SCL=1;
delay();
k=(k<<1)|SDA;
SCL=0;
delay();
}
delay();
return k;
}
9.看門狗
//
關于51單片機中的看門狗寄存器
D5 EN_WDT 看門狗允許位
D4 CLK_WDT 看門狗清零
D3 IDLE_WDT 看門狗IDLE模式位
PS0 PS1 PS2 看門狗定時器預分頻值
這三個寄存器置不同的值決定不同的看門狗溢出時間
看門狗溢出時間=(N*預分頻數(shù)*32768)/晶振頻率
#include
#define uchar unsigned char
#define uint unsigned int
sfr WDT_CONTR=0xe1;//定義看門狗寄存器 reg52.h中沒有對它進行定義
sbit led1=P1^0;
void delayms(uint xms)
{
uint i,j;
for(i=xms;j>0;j--)
for(j=110;j>0;j--);
}
void main()
{
WDT_CONTR=0x35;//0x35=0011 0101設置預分頻數(shù)為 64 溢出時間為2.0971s
led1=0;
delayms(500);
led1=1;
while(1)
{
delayms(1000);
WDT_CONTR=0x35;//喂狗語句將D4不斷置1,一旦D4被清零則看門狗復位
}
}
10.軟件實現(xiàn)系統(tǒng)復位
STC ISP/IAP控制寄存器(ISP_CONTR)
D7 ISPEN 功能允許位 0禁止編程改變flash 1允許
D6 SWBS 0軟件選擇從用戶應用程序區(qū)啟動 1從ISP程序區(qū)啟動
D5 SWRST 不操作 1產生系統(tǒng)軟件復位,硬件自動清零
D2 WT2
D1 WT1
D0 WT0 編程設定CPU等待的最長時間,對flash進行讀操作,寫操作,擦除操作必須在這個時間內
SWBS=0 SWRST=1(軟復位) 從用戶應用程序區(qū)(AP區(qū))軟件復位并切換到用戶應用程序區(qū)開始執(zhí)行程序
從哪里復位取決于程序在哪里執(zhí)行
SWBS=1 SWRST=1 從ISP監(jiān)控程序區(qū)軟件復位并切換ISP監(jiān)控程序區(qū)開始執(zhí)行程序
使用方法:
進行軟件復位時
首先
sfr ISP_CONTR=0xe7;定義ISP/IAP控制寄存器
然后再需要軟件復位的地方加入如下語句即可
ISP_CONTR=0x20//用軟件復位到用戶應用程序區(qū)(AP),重新開始執(zhí)行程序
11.DS18B20編程應用子函數(shù)
//
Ds18B20的編程子函數(shù)
DS18B20是單總線接口的溫度傳感器,應用范圍很廣泛
#include
#include
#define uchar unsigned char
#define uint unsigned int
sbit ds=P2^2;//溫度傳感器信號線
uint temp;//定義整型的溫度數(shù)據(jù)
float temp;//定義浮點型的溫度數(shù)據(jù)
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}
void dsreset(void)//DS18B20復位,初始化函數(shù)
{
uint i;
ds=0;
i=103;
while(i>0)i--;
ds=1;
i=4;
while(i>0)i--;
}
bit tempreadbit(void)//讀1位數(shù)據(jù)函數(shù)
{
uint i;
bit dat;
ds=0;i++;//i++起延時作用
ds=1;i++;i++;
dat=ds;
i=8;while(i>0)i--;
return(dat);
}
uchar tempread(void)//讀1個字節(jié)數(shù)據(jù)函數(shù)
{
uchar i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{
j=tempreadbit();
dat=(j<<7)|(dat>>1);//讀出的數(shù)據(jù)最低位在最前面,這樣剛好一個字節(jié)在dat里
}
return(dat);
}
void tempwritebyte(uchar dat)//向DS18B20寫一個字節(jié)數(shù)據(jù)函數(shù)
{
uint i;
uchar j;
bit testb;
for(j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb)//寫1
{
ds=0;
i++;i++;
ds=1;
i=8;while(i>0)i--;
}
else//寫0
{
ds=0;
i=8;while(i>0)i--;
ds=1;
i++;i++;
}
}
}
void tempchange(void)//DS18B20 開始獲取溫度并轉換
{
dsreset();
delay(1);
tempwritebyte(0xcc);//寫 跳過讀ROM指令
tempwritebyte(0x44);//寫 溫度轉換指令
}
uint get_temp()
{
uchar a,b;
dsreset();
delay(1);
tempwritebyte(0xcc);
tempwritebyte(0xbe);//讀內部ram中的9字節(jié)
a=tempread();//讀低8位
b=tempreda();//讀高8位
temp=b;
temp<<=8;
temp=temp | a;//兩個字節(jié)組合為1個字
f_temp = temp*0.0625;//溫度在寄存器中為12位,分辨率為0.0625 廠家出廠時默認精度為12位,所以乘以0.0625
temp=f_temp*10+0.5; //乘以10表示小數(shù)點后面只取一位,加0.5是為了四舍五入
f_temp=f_temp+0.05;
return temp;
}
12.內部ram的擴展
51單片機存儲器模式一共有三種
small模式 默認變量存儲在單片機內部的128B RAM中
無聲明均以這個模式存儲
compact模式 默認變量存儲在單片機內部的 256B RAM中
關鍵字 pdata :unsinged char pdata a[100]
large 模式 默認變量均存儲在64K的RAM區(qū)包括內部ram和外部ram
關鍵字 xdata:unsingned char xdata a[100]
三種模式的不同:訪問速度由快到慢
這個博客里的涉及的源代碼大家可以在這里下載http://download.csdn.net/detail/zhaole20094463/4427745
評論