標(biāo)題: 藍(lán)僑杯單片機(jī)編程筆記 [打印本頁(yè)]
作者: wudibabagha 時(shí)間: 2018-3-19 08:55
標(biāo)題: 藍(lán)僑杯單片機(jī)編程筆記
藍(lán)僑杯單片機(jī)編程筆記
一、 IO口編程
二、 數(shù)碼管動(dòng)態(tài)掃描和定時(shí)器
三、 矩陣鍵盤
四、 串口通訊和串口中斷
五、 外部中斷的使用
六、 實(shí)時(shí)時(shí)鐘DS1302的使用
七、 PCF8591與IIC總線的使用
八、 DS18B20溫度芯片的使用
九、 超聲波傳感器的使用
十、 步進(jìn)電機(jī)與直流電機(jī)的使用
十一、 擴(kuò)展:宏定義編程方法(推薦)
十二、 注意事項(xiàng)(常見編程錯(cuò)誤)
IO編程,該開發(fā)板使用了573鎖存器,通過P2口的5,6,7位連接3-8譯碼器,擴(kuò)展出了8個(gè)口,其中4個(gè)口分別連接4個(gè)573鎖存器,這里以LED的鎖存器來舉例:
原理圖573:
分析代碼:
P2=((P2&0x1f)|0x80);
其中0x1f=0001。保保保,P2與0x1f進(jìn)行與運(yùn)算,高三位清零,其余位保持原來狀態(tài),不改變,即把控制3-8譯碼器的高三位留出來:
接著再或上0x80;容易發(fā)現(xiàn)0x80=1000。埃埃埃;或運(yùn)算,與1或結(jié)果為1,與0或結(jié)果不變,所以或上0x80只需看P2的高三位,則高三位為100,對(duì)應(yīng)3-8譯碼器的話,P2^7=1;P2^6=0;P2^5=0;
所以輸出Y4=0;Y4再經(jīng)過與非運(yùn)算,看下圖示:
則輸出Y4C=1;即LED對(duì)應(yīng)的鎖存器的片選信號(hào)被選中,鎖存器打通,接下來就可以對(duì)P0口進(jìn)行操作,操作完之后,
P2=P2&0x1f;P2高三位直接清零,此時(shí)Y4C=0,則把鎖存器鎖上了。
類似的方法,數(shù)碼管、蜂鳴器等都是如此操作,
選中鎖存器代碼:
P2=((P2&0x1f)|(這里填對(duì)應(yīng)鎖存器的位移號(hào)))。
- 數(shù)碼管動(dòng)態(tài)掃描和定時(shí)器
數(shù)碼管顯示分為段選和位選,
數(shù)碼管定義和顯示函數(shù):
code unsigned char tab[] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned char dspbuf[]={10,10,10,10,10,10,10,10};
unsigned char dspcom=0;
void display()
{
//段選,消隱
P2=((P2&0x1f)|0xe0);
P0=0xff;
P2=P2&0x1f;
//位選
P2=((P2&0x1f)|0xc0);
P0=(1<<dspcom);
P2=P2&0x1f;
//段碼輸入
P2=((P2&0x1f)|0xe0);
P0=tab[dspbuf[dspcom]];
P2=P2&0x1f;
if(++dspcom==8)
dspcom=0;
}注意:這里1左移dspcom位,剛開始dspcom=0,則1左移dspcom位依舊為1,接著dspcom每次自增1,1對(duì)應(yīng)二進(jìn)制0000 0001,即把1每次向左移,每次都比上一次多移一位,直至8位移完,對(duì)應(yīng)8個(gè)數(shù)碼管。
定時(shí)器配置:
這里只需記住定時(shí)器的配置,知道怎么使用就可以了。首先有兩個(gè)定時(shí)器,T0和T1,(也有的單片機(jī)有T2),定時(shí)器有4種工作方式0,1,2,3;其中最常用的是方式1(16位),其次是方式2(8位自動(dòng)重裝,串口通訊中斷會(huì)用到)。
定時(shí)器需要配置:TMOD |=0x01;配置成使用定時(shí)器0,工作方式為1;同理使用定時(shí)器1工作方式1:TMOD |=0x10;則同時(shí)使用兩個(gè)定時(shí)器且工作方式為1,那么可以:TMOD |=0x11;
定時(shí)器1配置成工作方式2:TMOD |=0x20;
接著配置(以定時(shí)器0舉例):
TH0=(65535-2000)/256;//配置初值
TL0=(65535-2000)%256;
ET0=1;
TR0=1;//定時(shí)0中斷
EA =1;//總中斷
定時(shí)器1也是同理的,只不過0要改成1.
接著定時(shí)中斷函數(shù)和優(yōu)先級(jí):
定時(shí)器0
void isr_timer_0(void) interrupt 1 //默認(rèn)中斷優(yōu)先級(jí) 1
{
TH0 = (65536-2000)/256;
TL0 = (65536-2000)%256; //定時(shí)器重載
display();
}
定時(shí)器1:
void isr_timer_1(void) interrupt 3 //默認(rèn)中斷優(yōu)先級(jí) 3
{
TH0 = (65536-2000)/256;
TL0 = (65536-2000)%256; //定時(shí)器重載
display();
}
注意:定時(shí)器0優(yōu)先級(jí)為1,定時(shí)器1為3,串口中斷優(yōu)先級(jí)為4,總共有5個(gè)中斷源,后面還會(huì)介紹外部中斷和串口中斷。
數(shù)碼管動(dòng)態(tài)掃描,顯示函數(shù)放在定時(shí)中斷函數(shù)里面,2ms掃一次是最穩(wěn)定的!
矩陣鍵盤需要死記了!這里不再講獨(dú)立鍵盤。
第二種單片機(jī)鍵盤掃描代碼(沒有消抖):
sfr P4^4=0xC0;
//鍵盤定義
sbit r1=P3^0; //4行
sbit r2=P3^1;
sbit r3=P3^2;
sbit r4=P3^3;
//4列
sbit c1=P4^4;
sbit c2=P4^2;
sbit c3=P3^5;
sbit c4=P3^4;
//讀取矩陣鍵盤鍵值
unsigned char key_scan()
{
unsigned char key_value;
r1=0;
r2=r3=r4=1;
c1=c2=c3=c4=1;
if(!c1) key_value=0;
else if(!c2) key_value=1;
else if(!c3) key_value=2;
else if(!c4) key_value=3;
r2=0;
r1=r3=r4=1;
c1=c2=c3=c4=1;
if(!c1) key_value=4;
else if(!c2) key_value=5;
else if(!c3) key_value=6;
else if(!c4) key_value=7;
r3=0;
r2=r1=r4=1;
c1=c2=c3=c4=1;
if(!c1) key_value=8;
else if(!c2) key_value=9;
else if(!c3) key_value=10;
else if(!c4) key_value=11;
r4=0;
r2=r3=r1=1;
c1=c2=c3=c4=1;
if(!c1) key_value=12;
else if(!c2) key_value=13;
else if(!c3) key_value=14;
else if(!c4) key_value=15;
return key_value;
}
串口中斷配置只需記住幾個(gè)寄存器就行了,
初始化:
SCON =0x50; //串口配置成模式1
TMOD |=0x20;//定時(shí)器1,方式2,8位自動(dòng)重裝
TH1=256-(unsigbedchar)(SYSTEMCLOK/BAUDRATE/384+0.5);//定時(shí)初值
ES=1; //串口中斷打開
TR1=1; //啟動(dòng)定時(shí)器1
EA=1; //總中斷打開
這里必須使用定時(shí)器1,不能用定時(shí)器0.
下面是模塊化的函數(shù):
void Uart_Init()
{
SCON = 0x50;
TMOD |=0x20;
TH1=256-(SYSREMCLOCK/BAUDRATE/384+0.5);
ES=1;
TR1=1;
EA=1;
}
void UartSend(unsigned char*pBuff,int length)
{
unsigned char c;
int i=0;
for(i=0;i<length;i++)
{
c=pBuff[ i];
SBUF=c;
while(TI==0);
TI=0;
}
}
接收數(shù)據(jù)可以這樣寫:
定義全局變量:
unsigned char uart_buf[100];//串口緩沖區(qū)
unsigned int uart_Count=0;//串口數(shù)據(jù)長(zhǎng)度
void uart_inte() interrupt 4
{
unsigned char c;
if(RI)
{
RI=0;
c=SBUF;
uart_buf[uart_Count]=c;
uart_Count++;
}
}
如果可以指定的接收,可以這樣寫
//串口中斷服務(wù)函數(shù)
void isr_uart(void) interrupt 4{
if(RI){
RI = 0; //清除接收標(biāo)志位
rxbuf[rxcnt] = SBUF;
if(rxbuf[rxcnt] == '\n'){
rxcnt = 0;
rx_over = 1;
ES = 0;
//回車為接收結(jié)束標(biāo)志,檢測(cè)到回車符后,關(guān)閉串口中斷
}
else{
rxcnt++;
}
}
}
當(dāng)接收完一幀數(shù)據(jù)時(shí)關(guān)閉串口中斷,設(shè)一個(gè)標(biāo)志位,處理完之后再打開。
#include "reg51.h"
#include "intrins.h"
typedef unsigned char BYTE;
typedef unsigned int WORD;
BYTE code_tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};
char arry[10]="I CAN PLAY";
unsigned char x;
#define FOSC 11059200//12000000L //系統(tǒng)頻率
#define BAUD 115200 //串口波特率
#define NONE_PARITY 0 //無(wú)校驗(yàn)
#define ODD_PARITY 1 //奇校驗(yàn)
#define EVEN_PARITY 2 //偶校驗(yàn)
#define MARK_PARITY 3 //標(biāo)記校驗(yàn)
#define SPACE_PARITY 4 //空白校驗(yàn)
#define PARITYBIT NONE_PARITY //定義校驗(yàn)位
sfr AUXR = 0x8e; //輔助寄存器
sfr P_SW1 = 0xA2; //外設(shè)功能切換寄存器1
#define S1_S0 0x40 //P_SW1.6
#define S1_S1 0x80 //P_SW1.7
sbit P22 = P2^2;
bit busy;
void SendData(BYTE dat);
void SendString(char *s);
void main()
{
ACC = P_SW1;
ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=0
P_SW1 = ACC; //(P3.0/RxD, P3.1/TxD)
// ACC = P_SW1;
// ACC &= ~(S1_S0 | S1_S1); //S1_S0=1 S1_S1=0
// ACC |= S1_S0; //(P3.6/RxD_2, P3.7/TxD_2)
// P_SW1 = ACC;
//
// ACC = P_SW1;
// ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=1
// ACC |= S1_S1; //(P1.6/RxD_3, P1.7/TxD_3)
// P_SW1 = ACC;
//#if (PARITYBIT == NONE_PARITY)
SCON = 0x50; //8位可變波特率
//#elif (PARITYBIT == ODD_PARITY) || (PARITYBIT == EVEN_PARITY) || (PARITYBIT == MARK_PARITY)
// SCON = 0xda; //9位可變波特率,校驗(yàn)位初始為1
//#elif (PARITYBIT == SPACE_PARITY)
// SCON = 0xd2; //9位可變波特率,校驗(yàn)位初始為0
//#endif
AUXR = 0x40; //定時(shí)器1為1T模式
TMOD = 0x20; //定時(shí)器1為模式2(8位自動(dòng)重載)
TL1 = (256 - (FOSC/32/BAUD)); //設(shè)置波特率重裝值
TH1 = (256 - (FOSC/32/BAUD));
TR1 = 1; //定時(shí)器1開始工作
ES = 1; //使能串口中斷
EA = 1;
while(1)
{
// SendString(arry);
SendString("I CAN PLAY~~\r\n");//上位機(jī)顯示接收文本模式
// SendData(x);
}
}
/*----------------------------
UART 中斷服務(wù)程序
-----------------------------*/
void Uart() interrupt 4 using 1
{
if (RI)//單片機(jī)接收數(shù)據(jù),發(fā)送數(shù)字0~9,可在數(shù)碼管上顯示,發(fā)送hex模式
{
RI = 0; //清除RI位
// P0 = SBUF;
x=SBUF;//將緩存器的數(shù)據(jù)賦值給x
P0=0xff; //消隱
P2|=0xe0;
P2&=0x1f;
P0=code_tab[x]; //段選
P2|=0xe0;
P2&=0x1f;
P0=0x01; //位選第一位
P2|=0xc0;
P2&=0x3f;
}
if (TI)
{
TI = 0; //清除TI位
busy = 0; //清忙標(biāo)志
}
}
/*----------------------------
發(fā)送串口數(shù)據(jù)
----------------------------*/
void SendData(BYTE dat)
{
while (busy); //等待前面的數(shù)據(jù)發(fā)送完成
ACC = dat; //獲取校驗(yàn)位P (PSW.0)
if (P) //根據(jù)P來設(shè)置校驗(yàn)位
{
#if (PARITYBIT == ODD_PARITY)
TB8 = 0; //設(shè)置校驗(yàn)位為0
#elif (PARITYBIT == EVEN_PARITY)
TB8 = 1; //設(shè)置校驗(yàn)位為1
#endif
}
else
{
#if (PARITYBIT == ODD_PARITY)
TB8 = 1; //設(shè)置校驗(yàn)位為1
#elif (PARITYBIT == EVEN_PARITY)
TB8 = 0; //設(shè)置校驗(yàn)位為0
#endif
}
busy = 1;
SBUF = ACC; //寫數(shù)據(jù)到UART數(shù)據(jù)寄存器
}
/*----------------------------
發(fā)送字符串
----------------------------*/
void SendString(char *s)
{
while (*s) //檢測(cè)字符串結(jié)束標(biāo)志
{
SendData(*s++); //發(fā)送當(dāng)前字符
}
}
記不住可以看手冊(cè)。
#include "reg51.h"
#include "intrins.h"
typedef unsigned char BYTE;
typedef unsigned int WORD;
#define FOSC 11059200L
#define BAUD 115200
sfr AUXR=0x8e; //輔助寄存器
sbit P22=P2^2;
bit busy;
void SendData(BYTE dat);
void SendString(char *s);
void main()
{
SCON=0x50;
AUXR=0x40; //設(shè)置定時(shí)器T1為1T,即一個(gè)機(jī)器周期模式
TMOD=0x20;
TL1=(256-(FOSC/32/BAUD));
TH1=(256-(FOSC/32/BAUD));
TR1=1;
ES=1;
EA=1;
SendString("Hello");
while(1);
}
void Uart() interrupt 4 using 1
{
if(RI)
{
RI=0;
P0=SBUF;
}
if(TI)
{
TI=0;
busy=0;
}
}
void SendData(BYTE dat)
{
while(busy);
busy=1;
SBUF=dat;
}
void SendString(char *s)
{
while(*s)
{
SendData(*s++);
}
}
#include <reg52.h>
sbit L1=P0^0;
int main(){
IT0=1; //IT0=1,下降沿觸發(fā)外部中斷0,IT0=0邊沿觸發(fā)
EX0=1;//使用外部中斷0
EA=1;
while(1){
}
}
void Ex_int0() interrupt 0 //外部中斷優(yōu)先級(jí)最高
{
P2=((P2&0x1f)|0x80);
L1=~L1;
P2=(P2&0x1f);
}
其中,外部中斷的引腳控制是P3^2,P3^3,即對(duì)應(yīng)獨(dú)立按鍵的S5,S4。
- 實(shí)時(shí)時(shí)鐘DS1302的使用
藍(lán)橋杯提供函數(shù),解釋為:
里面的命令和寫入的數(shù)據(jù)可以看芯片手冊(cè):
左側(cè)的READ、WRITE分別是讀寫的命令,BIT7-BIT0是要寫入的數(shù)據(jù),根據(jù)需要進(jìn)行配置。DS1302只需記住這兩個(gè)函數(shù)即可:Write_Ds1302( , )與Read_Ds1302(x),配置看手冊(cè)。
重點(diǎn):芯片表說明:第一行:秒->因?yàn)槊氲姆秶?-59,所以6,5,4位表示秒的十位,3,2,1,0表示個(gè)位,十位最大是5,所以三位即可。
第二行:跟上面一樣;
第三行:7位:1為12小時(shí)制,0為24小時(shí)制;5位:12小時(shí)制時(shí)為0表示上午,1表示下午,24小時(shí)制時(shí),和4位一起表示小時(shí)的十位;
其余的時(shí)間一樣的表示。
倒數(shù)第二行:只看7位:為1時(shí)禁止寫數(shù)據(jù),所以開始寫數(shù)據(jù)時(shí)必須置0;
讀數(shù)時(shí):
!!需要加“寫操作這一行代碼”。
讀的話直接按照命令讀即可。
DS1302進(jìn)階(BCD碼轉(zhuǎn)換):解決之前60秒不能進(jìn)位的問題。
- 寫入初始值時(shí),要把10十進(jìn)制數(shù)轉(zhuǎn)換為BCD碼,
例:寫入時(shí)間->17:58:50
Ds1302_Single_Byte_Write(0x8e, 0x00);//寫操作
Ds1302_Single_Byte_Write(0x85, ((17/10)<<4 | (17%10)));//寫時(shí)
Ds1302_Single_Byte_Write(0x83, ((58/10)<<4 | (58%10)));//寫分
Ds1302_Single_Byte_Write(0x81, ((50/10)<<4 | (50%10)));//寫秒
Ds1302_Single_Byte_Write(0x8e, 0x80);//寫保護(hù)
即轉(zhuǎn)換的公式是:((Value/10)<<4 | (Value%10)),可以寫一個(gè)settime()函數(shù)。
2 ) 讀數(shù):讀回來的數(shù)要進(jìn)行轉(zhuǎn)換成十進(jìn)制數(shù)
((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
八進(jìn)制轉(zhuǎn)十進(jìn)制->
ReadValue=Ds1302_Single_Byte_Read(0x85);
hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
!!(這句一定不要。 Ds1302_Single_Byte_Write(0x00, 0x00);//寫操作
ReadValue=Ds1302_Single_Byte_Read(0x83);
minute=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
Ds1302_Single_Byte_Write(0x00, 0x00);//寫操作
ReadValue=Ds1302_Single_Byte_Read(0x81);
sec=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
Ds1302_Single_Byte_Write(0x00, 0x00);//寫操作
顯示:
dspbuf[0]=hour/10;
dspbuf[1]=hour%10;
dspbuf[2]=minute/10;
dspbuf[3]=minute%10;
dspbuf[4]=sec/10;
dspbuf[5]=sec%10;
比賽提供了IIC的兩個(gè)庫(kù)文件,IIC.h;IIC.c,其中需要注意的函數(shù)是:
其中,該函數(shù)是初始化的,當(dāng)使用AD轉(zhuǎn)換的時(shí)候需要在main函數(shù)開始時(shí)調(diào)用,該函數(shù)內(nèi)部只需看這句代碼即可:i2c_sendbyte(0x03);//ADC通道3,板上有4個(gè)模擬輸入口,分別為0,1,2,3;設(shè)置哪一個(gè)模擬輸入口就是根據(jù)這句代碼,0x03表示通道3,這是根據(jù)芯片手冊(cè)配置的,如圖:
8位前6位不用管,都為0,最后兩位就是配置選擇哪一個(gè)通道的。
第二個(gè)函數(shù):
讀取AD轉(zhuǎn)換后的數(shù)值,這個(gè)函數(shù)直接調(diào)用就可以了,函數(shù)內(nèi)部如何實(shí)現(xiàn)不用管,但是需要注意的是:該函數(shù)掃描調(diào)用最好是100ms。
第三個(gè)函數(shù),上面的都是AD轉(zhuǎn)換,即模擬信號(hào)轉(zhuǎn)數(shù)字信號(hào),下面這個(gè)函數(shù)是DA轉(zhuǎn)換,數(shù)字信號(hào)轉(zhuǎn)換成模擬信號(hào),就是單片機(jī)輸出數(shù)字信號(hào),用萬(wàn)能表去量單片機(jī)引出的引腳,量一下電壓大小,這個(gè)估計(jì)比賽不會(huì)考,不過預(yù)防萬(wàn)一:
該函數(shù)和上面兩個(gè)函數(shù)分離開來的,一、二函數(shù)是要在一起使用,初始化后之后才能調(diào)用,第三個(gè)加入頭文件,直接調(diào)用即可,比較簡(jiǎn)單!!
上面說法有誤,A/D轉(zhuǎn)換的初始化函數(shù)和讀取轉(zhuǎn)換后的數(shù)值都需要自己寫。
這里了解一下PCF8591只需根據(jù)時(shí)序格式發(fā)送地址字節(jié)和控制字節(jié):
,這是地址字節(jié),其中A2,A1,A0硬件已經(jīng)接地,故都為0,最低位表示的是你要從IIC總線上讀數(shù)還是寫數(shù)據(jù),1表示讀,0表示寫,即讀數(shù)據(jù)發(fā)的地址是:0x91;寫數(shù)據(jù)發(fā)的地址是0x90;
控制字節(jié):
由芯片資料知,控制字節(jié)有8位,有兩位固定是0,除了第0、1位需要自己設(shè)置,其他的我們都設(shè)為0,那些位都是一些具體的功能,我們暫時(shí)用不著,不用管先,第0、1位是模擬通道選擇,PCF8591上提供了4路模擬通道,根據(jù)需求進(jìn)行選擇,如選擇通道3即發(fā)送控制字節(jié):0x03;
地址字節(jié)和控制字節(jié)都明白了,接下來根據(jù)時(shí)序要求進(jìn)行配置,A/D轉(zhuǎn)換需要一個(gè)初始化函數(shù):Init_ADpcf8591();和一個(gè)獲得AD轉(zhuǎn)換后的數(shù)值的函數(shù):adc_pcf8591(); 其中初始化函數(shù)的作用是發(fā)送AD轉(zhuǎn)換的控制字節(jié);adc_pcf8591()發(fā)送讀取得地址并讀回?cái)?shù)據(jù),先寫指令才能讀;格式如下:
這個(gè)是初始化的協(xié)議:分別是startIIC、(地址寫)發(fā)送0x90、等待應(yīng)答、發(fā)送控制字節(jié)(AD這里是選擇通道的指令,如選擇通道3,0x03)0x03、等待應(yīng)答、(達(dá)到目的,沒有后續(xù)的操作,直接停止總線)StopIIC.
初始化的函數(shù)就是如此寫;
adc_pcf8591的協(xié)議:
依次是:startIIc、發(fā)送讀地址0x91、等待應(yīng)答、讀回AD轉(zhuǎn)換后的數(shù)值、讀回后發(fā)送應(yīng)答給PCF8591,表示收到,并且不需要再返回應(yīng)答,要傳參數(shù)1,如圖紅圈示,即函數(shù)Ack(1);、最后stopIIC總線。
D/A轉(zhuǎn)換(其實(shí)挺麻煩,先前太自信了,哈):
所謂D/A轉(zhuǎn)換其實(shí)就是把數(shù)字信號(hào)轉(zhuǎn)換成模擬信號(hào)輸出,用單片機(jī)發(fā)數(shù)字通過D/A轉(zhuǎn)換成電壓輸出,檢測(cè)的方法可以用電壓表測(cè)量。
配置的方法跟A/D類似,先發(fā)地址字節(jié),再發(fā)控制字節(jié),然后把數(shù)字發(fā)出去(AD這里是接收模擬信號(hào),是相反的機(jī)制)。
控制字節(jié):
如圖示,控制字節(jié)的第6位是1的話是模擬輸出模式,其余位全為0,發(fā)送格式跟AD一樣:
代碼如一開始圖示。
- EEPROM的使用,AT24C02,可以掉電依舊保存上一次操作的數(shù)據(jù),下次上電后接著運(yùn)行。
需要注意兩個(gè)函數(shù),一個(gè)是寫進(jìn)EEPROM里面保存,再次上電再?gòu)睦锩孀x回來:
其中寫函數(shù)需要指定AT24C02的地址以及需要寫入的數(shù)據(jù),讀函數(shù)要想取回寫進(jìn)的數(shù)據(jù),需要從相同的地址里面讀:
其中AT24C02的存儲(chǔ)地址是0x00,可以是其他地址,如0x02,但是讀和寫的地址必須一致。
寫與讀的協(xié)議與AD或DA相同,
由芯片資料及原理圖知EEPROM(AT24C02)的寫地址為0xa0;讀地址為0xa1;注意:讀數(shù)的時(shí)候讀出一個(gè)數(shù)之后發(fā)送一個(gè)應(yīng)答信號(hào),若ACK(0)表示還想繼續(xù)讀下一個(gè)字節(jié),若ACK(1);則不想再讀數(shù),讓EEPROM停止發(fā)送。
比賽有提供代碼,只需記住這個(gè)函數(shù):
讀取溫度值,整數(shù)(其中,提示EA總中斷要打開、關(guān)閉,也可以不用)。
浮點(diǎn)數(shù)的表示。
注意,只有提供函數(shù),沒有提供讀取溫度的函數(shù),即上面的那個(gè),只有下面:
這幾個(gè)函數(shù)。
編寫讀取溫度的函數(shù)需要記住DS13B20的三條指令,0xCC,跳過ROM檢測(cè);然后啟動(dòng)溫度轉(zhuǎn)換:0x44;轉(zhuǎn)換需要時(shí)間,這里精確延時(shí)Delay_OneWire(200);然后再次初始化,再次執(zhí)行跳過,然后讀取溫度指令:0xBE.;注意讀出的溫度是低字節(jié)先,然后才是高字節(jié),分別用兩個(gè)變量保存還要通過公式轉(zhuǎn)換成我們需要的整數(shù)或浮點(diǎn)數(shù)。完整代碼如上圖示。
#include "reg52.h" //定義51單片機(jī)特殊功能寄存器
#include "intrins.h"
#include "absacc.h"
//12M用這個(gè)
/*#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_(); _nop_();}*/
//11.0592用這個(gè)
#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();}
sbit TX = P1^0; //發(fā)射引腳
sbit RX = P1^1; //接收引腳
code unsigned char tab[] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,\
0xff};
unsigned char dspbuf[8] = {10,10,10,10,10,10,10,10}; //顯示緩沖區(qū)
unsigned char dspcom = 0;
unsigned int intr = 0;
bit s_flag;
unsigned int t = 0;
void send_wave(void);
void display(void);
void main(void)
{
unsigned int distance;
TMOD |= 0x11; //配置定時(shí)器工作模式
TH0 = (65536-2000)/256;
TL0 = (65536-2000)%256;
TH1 = 0;
TL1 = 0;
EA = 1;
ET0 = 1; //打開定時(shí)器0中斷
TR0 = 1; //啟動(dòng)定時(shí)器
while(1){
/** 200毫秒更新一次數(shù)據(jù) */
if(s_flag)
{
s_flag = 0;
/** 關(guān)閉定時(shí)器0中斷:計(jì)算超聲波發(fā)送到返回的時(shí)間
send_wave(); //發(fā)送方波信號(hào)
TR1 = 1; //啟動(dòng)計(jì)時(shí)
while((RX == 1) && (TF1 == 0)); //等待收到脈沖
TR1 = 0; //關(guān)閉計(jì)時(shí)
//發(fā)生溢出
if(TF1 == 1)
{
TF1 = 0;
distance = 9999; //無(wú)返回
}
else
{
/** 計(jì)算時(shí)間 */
t = TH1;
t <<= 8;
t |= TL1;
distance = (unsigned int)(t*0.017); //計(jì)算距離
}
TH1 = 0;
TL1 = 0;
}
/** 數(shù)據(jù)處理 */
dspbuf[5] = distance/100;
dspbuf[6] = distance%100/10;
dspbuf[7] = distance%10;
}
}
//定時(shí)器0中斷服務(wù)函數(shù)
void isr_timer_0(void) interrupt 1 //默認(rèn)中斷優(yōu)先級(jí) 1
{
TH0 = (65536-2000)/256;
TL0 = (65536-2000)%256; //定時(shí)器重載
display(); //2ms執(zhí)行一次
if(++intr == 200){
s_flag = 1;
intr = 0;
}
}
//顯示函數(shù)
void display(void){
XBYTE[0xE000] = 0xff; //去除鬼影
XBYTE[0xC000] = (1<<dspcom);
XBYTE[0xE000] = tab[dspbuf[dspcom]];
if(++dspcom == 8){
dspcom = 0;
}
}
//TX引腳發(fā)送40KHz方波信號(hào)驅(qū)動(dòng)超聲波發(fā)送探頭
void send_wave(void)
{
unsigned char i = 8; //發(fā)送8個(gè)脈沖
do
{
TX = 1;
somenop;
TX = 0;
somenop;
}
while(i--);
}
必要時(shí)還可以加個(gè)看門狗: WDT_CONTR=0x34;
- 步進(jìn)電機(jī)與直流電機(jī)的使用
參考代碼如下:
#include <reg52.h>
sbit A1=P1^4; //定義步進(jìn)電機(jī)連接端口
sbit B1=P1^3;
sbit C1=P1^2;
sbit D1=P1^1;
void qudong1();
#define Dy_A1 {A1=1;B1=0;C1=0;D1=0;}//A相通電,其他相斷電
#define Dy_B1 {A1=0;B1=1;C1=0;D1=0;}//B相通電,其他相斷電
#define Dy_C1 {A1=0;B1=0;C1=1;D1=0;}//C相通電,其他相斷電
#define Dy_D1 {A1=0;B1=0;C1=0;D1=1;}//D相通電,其他相斷電 //采用1相勵(lì)磁
#define Dy_OFF {A1=0;B1=0;C1=0;D1=0;}//全部斷電
unsigned char Speed,Speed1;
/*------------------------------------------------
uS延時(shí)函數(shù),含有輸入?yún)?shù) unsigned char t,無(wú)返回值
unsigned char 是定義無(wú)符號(hào)字符變量,其值的范圍是
0~255 這里使用晶振12M,精確延時(shí)請(qǐng)使用匯編,大致延時(shí)
長(zhǎng)度如下 T=tx2+5 uS
------------------------------------------------*/
void DelayUs2x(unsigned char t)
{
while(--t);
}
/*------------------------------------------------
mS延時(shí)函數(shù),含有輸入?yún)?shù) unsigned char t,無(wú)返回值
unsigned char 是定義無(wú)符號(hào)字符變量,其值的范圍是
0~255
------------------------------------------------*/
void DelayMs(unsigned char t)
{
while(t--)
{
//大致延時(shí)1mS
DelayUs2x(245);
DelayUs2x(245);
}
}
/*------------------------------------------------
主函數(shù)
------------------------------------------------*/
main()
{
Dy_OFF;
for(;;)
{
qudong1();
}
}
void qudong1()
{
unsigned int i=470;//旋轉(zhuǎn)一周時(shí)間
Speed=5;
while(i--) //正向
{
Dy_A1
//遇到Coil_A1 用{A1=1;B1=0;C1=0;D1=0;}代替
DelayMs(Speed);
//改變這個(gè)參數(shù)可以調(diào)整電機(jī)轉(zhuǎn)速 ,
//數(shù)字越小,轉(zhuǎn)速越大,力矩越小
Dy_B1 //順序從A1--D1相通電如果為正轉(zhuǎn),那么順序從D1--A1相通電則為反轉(zhuǎn)
DelayMs(Speed);
Dy_C1
DelayMs(Speed);
Dy_D1
DelayMs(Speed);
}
Dy_OFF
i=512;
while(i--)//反向
{
Dy_D1 //遇到Coil_A1 用{A1=1;B1=0;C1=0;D1=0;}代替
DelayMs(Speed); //改變這個(gè)參數(shù)可以調(diào)整電機(jī)轉(zhuǎn)速 ,
//數(shù)字越小,轉(zhuǎn)速越大,力矩越小
Dy_C1
DelayMs(Speed);
Dy_B1
DelayMs(Speed);
Dy_A1
DelayMs(Speed);
}
}
直流電機(jī):
#include<reg52.h>
#define uint unsigned int
void drive();
void delay(uint);
sbit dj1=P1^0; //電機(jī),1引腳
uint set; //set為電機(jī)轉(zhuǎn)角標(biāo)志位
unsigned char angle,angle1; //angle為電機(jī)PWM變化打角
void Time0_Init() //中斷初始化
{
TMOD=0X01;
IE=0X82;
TH0=(65536-58)/256;
TL0=(65536-58)%256;
TR0=1;
}
void main() //主函數(shù)
{
set=0;
angle=62; // 改變angle的值可以改變電機(jī)的占空比
Time0_Init();
for(;;)
{
}
}
void T0_time()interrupt 1
{
TH0=(65536-58)/256;
TL0=(65536-58)%256;
if(set<angle)
dj1=1;
else
dj1=0;
set++;
if(set>165) set=0; //電機(jī)占空比
}
常用的可以宏定義,省時(shí),下面以數(shù)碼管為例:
#define rst573 P2&=0x1f
#define Y6C P2= ((P2&0x1f)|0xc0)
#define Y7C P2= ((P2&0x1f)|0xe0)
code unsigned char tab[] = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
unsigned char dspbuf[8] = {10,10,10,10,10,10,10,10}; //顯示緩沖區(qū)
unsigned char dspcom;
unsigned char flag;
void display()
{
Y7C;
P0=0xff;
rst573;
Y6C;
P0=1<<dspcom;
rst573;
Y7C;
P0=tab[dspbuf[dspcom]];
rst573;
if(++dspcom==8)
dspcom=0;
}
- 設(shè)置標(biāo)志位flag,常在定時(shí)的時(shí)候使用,一般我們常用的數(shù)據(jù)類型是unsigned char類型,有時(shí)候我們這樣判斷if(flag==500){flag=0; … };這是十分明顯的錯(cuò)誤,unsigned char是8位,范圍是0-255,500已經(jīng)超出其最大值的范圍了,但是編譯的時(shí)候是不會(huì)報(bào)錯(cuò)的,我們可以設(shè)成unsigned int 類型,16位,范圍很大。
- 模擬題中led閃爍的頻率隨著溫度值value與設(shè)定的范圍值比較而改變,這里困擾了我很久,終于在前人的代碼中找到解決方法了,這樣:設(shè)置的標(biāo)志位flag不要直接在while循環(huán)里直接如:if(flag==100){flag=0;…}不要這樣寫。應(yīng)該在定時(shí)中斷新定義一個(gè)變量,如為標(biāo)志位flag_100ms(假設(shè)進(jìn)入一次定時(shí)中斷2ms):
void time0() interrupt 1
{ unsigned int i;
if(++i==50)
{ i=0;
flag_100ms=1;
}
}
然后在需要用到的位置判斷標(biāo)志位是否為1即可。
注意:也可以設(shè)靜態(tài)變量,建議用:
void time0() interrupt 1
{
TH0=(65535-2000)/256;
TL0=(65535-2000)%256;
static unsigned int i=0;
static unsigned int j=0;
if(++i==100)
{
i=0;
flag_200ms=1;
}
if(++j==200)
{
j=0;
flag_400ms=1;
}
}
3、注意DS18b20溫度傳感器,因?yàn)槭褂玫膯纹瑱C(jī)芯片不一樣,晶振頻率不一樣,而DS18b20對(duì)時(shí)序的要求又很嚴(yán)格,所以這里記住兩種單片機(jī)的延時(shí)程序://單總線延時(shí)函數(shù)
#ifndef STC12
void Delay_OneWire(unsigned int t) //STC89C52RC
{
while(t--);
}
#else
void Delay_OneWire(unsigned int t) //STC12C5260S2
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
#endif
4、使用串口通訊傳一個(gè)數(shù)組給PC機(jī),如果是文本模式顯示字符,需要將字符的ASII碼轉(zhuǎn)換成文本字符,還需在數(shù)組末尾加一個(gè)結(jié)束符‘\0’:
Val[0]='{';
Val[1]=(hour/10)+48;
。
。
。
Val[19]='H';
Val[20]='}';
Val[21]='\0';
5、串口通訊波特率設(shè)置及初始化函數(shù):
sfr AUXR=0x8e;
//宏定義區(qū)
#define BAUD 1200//波特率;需要設(shè)置哪個(gè)波特率只需在這里更改即可
#define FOSC 11059200L //晶振頻率
void InitSerial()
{
SCON=0x50;
TMOD |=0x00;//使用16位自動(dòng)重載方式,選擇T1
AUXR=0x40;//設(shè)置定時(shí)器為一個(gè)周期,即1T
TL1=(65536-(FOSC/4/BAUD));
TH1=(65536-(FOSC/4/BAUD))>>8;
TR1=1;
ES=1;
}
6、串口中斷函數(shù)及發(fā)送、結(jié)束函數(shù):
void Uart() interrupt 4 using 1
{
if(RI){
RI=0;
value=SBUF;
}
if(TI){
TI=0;
busy=0;
}
}
void SendData(unsigned char dat)
{
while(busy);
busy=1;
SBUF=dat;
}
void SendString(unsigned char *s)
{
while(*s){
SendData(*s++);
}
}
7、簡(jiǎn)單的電路設(shè)計(jì)知識(shí)點(diǎn):
SOURCE CURRENT 電流源
SOURCE VOLTAGE 電壓源
定值無(wú)極性電容;CAP
定值有極性電容;CAP
繼電器:RELAY. LIB
運(yùn)放:OPAMP
BATTERY 直流電源
DIODE 二極管
DIODE SCHOTTKY 穩(wěn)壓二極管
NPN NPN三極管
NAND 與非門
NOR 或非門
NOT 非門
4013 D 觸發(fā)器
4027 JK 觸發(fā)器
8、LED閃爍控制程序參考:
static unsigned char LED=0xfe;
XBYTE[0x8000]=LED=LED^0x01;(和0x01進(jìn)行異或運(yùn)算,L1閃爍。。
9、數(shù)碼管的碼值(牢記):
tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf};
完整的Word格式文檔51黑下載地址:
藍(lán)僑杯單片機(jī)編程筆記新.docx
(976.4 KB, 下載次數(shù): 9)
2018-3-19 08:53 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
| 歡迎光臨 (http://www.raoushi.com/bbs/) |
Powered by Discuz! X3.1 |