CH451驅(qū)動(dòng)的數(shù)碼管+按鍵+電子表+溫度程序
//已經(jīng)在51單片機(jī)開發(fā)板上測(cè)試通過,帶有溫度報(bào)警,有詳細(xì)的注釋.
//程序的完整版本下載地址:http://www.51hei.com/f/ch451.rar
//本程序版權(quán)所無您可以隨意修改.
#include reg51.h>
#include math.h>
#include intrins.h>
/*************************************************************
_crol_ 字符循環(huán)左移
_cror_ 字符循環(huán)右移
_irol_ 整數(shù)循環(huán)左移
_iror_ 整數(shù)循環(huán)右移
_lrol_ 長(zhǎng)整數(shù)循環(huán)左移
_lror_ 長(zhǎng)整數(shù)循環(huán)右移
_nop_ 空操作8051 NOP 指令
*************************************************************/
#define uchar unsigned char
#define uint unsigned int
/******************************DS1302宏定義******************/
#define WRITE_SECOND 0x80
#define WRITE_MINUTE 0x82
#define WRITE_HOUR 0x84
#define READ_SECOND 0x81
#define READ_MINUTE 0x83
#define READ_HOUR 0x85
#define WRITE_PROTECT 0x8E
/***********************CH451宏定義************************/
#define CH451_RESET 0x0201 //復(fù)位
#define CH451_LEFTMOV 0x0300 //設(shè)置移動(dòng)方式-作移
#define CH451_LEFTCYC 0x0301 //設(shè)置移動(dòng)方式-左循
#define CH451_RIGHTMOV 0x0302 //設(shè)置移動(dòng)方式-右移
#define CH451_RIGHTCYC 0x0303 //設(shè)置移動(dòng)方式-右循
#define CH451_SYSOFF 0x0400 //關(guān)顯示、鍵盤、看門狗
#define CH451_SYSON1 0x0401 //開顯示
#define CH451_SYSON2 0x0403 //開顯示、鍵盤
#define CH451_SYSON3 0x0407 //開顯示、鍵盤、看門狗功能
#define CH451_DSP 0x0500 //設(shè)置默認(rèn)顯示方式
#define CH451_BCD 0x0580 //設(shè)置BCD譯碼方式
#define CH451_TWINKLE 0x0600 //設(shè)置閃爍控制——正常顯示
#define CH451_DIG0 0x0800 //數(shù)碼管位0顯示
#define CH451_DIG1 0x0900 //數(shù)碼管位1顯示
#define CH451_DIG2 0x0a00 //數(shù)碼管位2顯示
#define CH451_DIG3 0x0b00 //數(shù)碼管位3顯示
#define CH451_DIG4 0x0c00 //數(shù)碼管位4顯示
#define CH451_DIG5 0x0d00 //數(shù)碼管位5顯示
#define CH451_DIG6 0x0e00 //數(shù)碼管位6顯示
#define CH451_DIG7 0x0f00 //數(shù)碼管位7顯示
/*************************************************************/
sbit ACC_7=ACC^7; //位尋址寄存器定義
sbit SCLK=P2^1; //ds1302管腳定義
sbit IO=P2^0;
sbit RST=P1^7;
sbit DQ=P1^6; //ds18b20定義
sbit P26=P2^6;
sbit ch451_dclk=P3^4; //串行數(shù)據(jù)時(shí)鐘上升延激活
sbit ch451_din=P3^3; // 串行數(shù)據(jù)輸出,接CH451的數(shù)據(jù)輸人
sbit ch451_load=P2^3; //串行命令加載,上升延激活
sbit ch451_dout=P2^2; //鍵值數(shù)據(jù)輸入,接ch451數(shù)據(jù)輸出
int temp;
int tt; //真實(shí)溫度變量
uchar ch451_key=0; // 存放鍵盤中斷中讀取的鍵值
uint display[8]={0x0a00,0x0900,0x0b00,0x0800,
0x0c00,0x0d00,0x0e00,0x0f00};
uchar BCD[10]={0xbe,0x24,0xea,0xe6, //0,1,2,3,
0x74,0xd6,0xde,0xa4, //4,5,6,7,
0xfe,0xf6 //8,9
};
/*************精確延時(shí)函數(shù)*****************/
void delay(uint i)
{
while(--i);
}
/************************蜂鳴器程序********/
void speaker(void)
{
uchar i,j;
for(j=0;j250;j++)
for(i=0;i250;i++)
{
P26=~P26;
for(i=0;i250;i++);
}
P26=1; //防止結(jié)束時(shí)候是低電平
}
/**********************4*******************/
/*****************DS18B20******************/
void Init_Ds18b20(void) //DS18B20初始化
{
DQ=0; //單片機(jī)拉低總線
delay(500); //精確延時(shí),維持至少480us
DQ=1; //釋放總線,即拉高了總線
delay(200); //此處延時(shí)有足夠,確保能讓DS18B20發(fā)出存在脈沖。
}
/*****************************************************************************************/
uchar Read_One_Byte() //讀取一個(gè)字節(jié)的數(shù)據(jù)
//讀數(shù)據(jù)時(shí),數(shù)據(jù)以字節(jié)的最低有效位先從總線移出
{
uchar i=0;
uchar dat=0;
for(i=8;i>0;i--)
{
DQ=0; //將總線拉低,要在1us之后釋放總線
//單片機(jī)要在此下降沿后的15us內(nèi)讀數(shù)據(jù)才會(huì)有效。
_nop_();
_nop_(); //至少維持了1us,表示讀時(shí)序開始
dat>>=1; //讓從總線上讀到的位數(shù)據(jù),依次從高位移動(dòng)到低位。
DQ=1; //釋放總線,此后DS18B20會(huì)控制總線,把數(shù)據(jù)傳輸?shù)娇偩€上
delay(1); //延時(shí)7us,此處參照推薦的讀時(shí)序圖,盡量把控制器采樣時(shí)間放到讀時(shí)序后的15us內(nèi)的最后部分
if(DQ) //控制器進(jìn)行采樣
{
dat|=0x80; //若總線為1,即DQ為1,那就把dat的最高位置1;若為0,則不進(jìn)行處理,保持為0
}
delay(60); //此延時(shí)不能少,確保讀時(shí)序的長(zhǎng)度60us--很重要
}
return (dat);
}
/********************************************************/
void Write_One_Byte(uchar dat)
{
uchar i=0;
for(i=8;i>0;i--)
{
DQ=0; //拉低總線
_nop_();
nop_(); //至少維持了1us,表示寫時(shí)序(包括寫0時(shí)序或?qū)?時(shí)序)開始
DQ=dat0x01; //從字節(jié)的最低位開始傳輸
//指令dat的最低位賦予給總線,必須在拉低總線后的15us內(nèi),
//因?yàn)?5us后DS18B20會(huì)對(duì)總線采樣。
delay(60); //必須讓寫時(shí)序持續(xù)至少60us ----很重要
DQ=1; //寫完后,必須釋放總線,
/* _nop_(); */
dat>>=1;
//因?yàn)?5us后DS18B20會(huì)對(duì)總線采樣。
delay(1);
}
}
/*************************************************************************/
uint Get_Tmp() //獲取溫度
{
float t;
uchar a=11,b=0;
Init_Ds18b20(); //初始化
Write_One_Byte(0xcc); //忽略ROM指令
Write_One_Byte(0x44); //溫度轉(zhuǎn)換指令
Init_Ds18b20(); //初始化
Write_One_Byte(0xcc); //忽略ROM指令
Write_One_Byte(0xbe); //讀讀RAM的存儲(chǔ)器指令
a=Read_One_Byte(); //讀取到的第一個(gè)字節(jié)為溫度LSB
b=Read_One_Byte(); //讀取到的第二個(gè)字節(jié)為溫度MSB
temp=b; //先把高八位有效數(shù)據(jù)賦于temp
temp=8; //把以上8位數(shù)據(jù)從temp低八位移到高八位
temp=temp|a; //兩字節(jié)合成一個(gè)整型變量
t=temp*0.0625; //得到真實(shí)十進(jìn)制溫度值
//因?yàn)镈S18B20可以精確到0.0625度
//所以讀回?cái)?shù)據(jù)的最低位代表的是0.0625度
temp=t*10+0.5; //此處放大十倍
//這樣做的目的將小數(shù)點(diǎn)后第一位也轉(zhuǎn)換為可顯示數(shù)字
//同時(shí)進(jìn)行一個(gè)四舍五入操作。
return(temp);
}
/************************************************************************
以上實(shí)現(xiàn)溫度傳感器的讀取數(shù)據(jù)
*************************************************************************/
/******************************DS1302*************************************/
//地址、數(shù)據(jù)發(fā)送子程序
void Write1302(uchar addr,uchar dat )
{
uchar i,t;
RST=0; //RST 引腳為低,數(shù)據(jù)傳送終止
SCLK=0; //清零時(shí)鐘總線
RST=1; //RST 引腳為高,邏輯控制有效
//發(fā)送地址
for(i=8;i>0;i--) //循環(huán)8次移位
{
SCLK=0;
t=addr;
IO=(bit)(t0x01); //每次傳輸?shù)妥止?jié)
addr>>=1; //右移一位
SCLK=1;
}
//發(fā)送數(shù)據(jù)
for(i=8;i>0;i--)
{
SCLK=0;
t=dat;
IO=(bit)(t0x01);
dat>>=1;
SCLK=1;
}
RST=0;
}
//數(shù)據(jù)讀取子程序
uchar Read1302(uchar addr )
{
uchar i,t,dat1,dat2;
RST=0;
SCLK=0;
RST=1;
//發(fā)送地址
for(i=8;i>0;i--) //循環(huán)8次移位
{
SCLK=0;
t=addr;
IO=(bit)(t0x01); //每次傳輸?shù)妥止?jié)
addr>>=1; //右移一位
SCLK=1;
}
//讀取數(shù)據(jù)
for(i=8;i>0;i--)
{
ACC_7=IO;
SCLK=1;
ACC>>=1; //右移
SCLK=0;
}
RST=0;
dat1=ACC;
dat2=dat1/16; //數(shù)據(jù)進(jìn)制轉(zhuǎn)換
dat1=dat1%16; //十六進(jìn)制轉(zhuǎn)十進(jìn)制
dat1=dat1+dat2*10;
return (dat1);
}
//初始化DS1302
void Initial(void)
{
Write1302 (WRITE_PROTECT,0x00); //禁止寫保護(hù)
Write1302 (WRITE_SECOND,0x00); //秒位初始化,開始計(jì)時(shí)
Write1302 (WRITE_MINUTE,0x03); //分鐘初始化
Write1302 (WRITE_HOUR,0x23); // 小時(shí)初始化——24小時(shí)模式
//Write1302 (WRITE_PROTECT,0x80); // 允許寫保護(hù)
// 調(diào)試證明必須禁止寫保護(hù)
//不然修改不了初始值
}
/*********************************CH451**********************/
//********************************************
//設(shè)置定時(shí)器中斷__定時(shí)掃描鍵盤
void ch451_init()
{
ch451_din=0; //先低后高,選擇4線輸入
ch451_din=1;
TMOD=0x10; //設(shè)置定時(shí)器T1工作在16位計(jì)時(shí)狀態(tài)
EA=1; //開中斷總開關(guān)
ET1=1; //允許中斷
TR1=1; //開中斷
PT1=0; //設(shè)置低優(yōu)先級(jí)
TL1=1; //裝載計(jì)數(shù)初值
TH1=0; //此計(jì)數(shù)初值用來調(diào)試
}
//*****************************************************
//定義一無符號(hào)整型變量存儲(chǔ)12字節(jié)的命令字。
void ch451_write(uint command)
{
uchar i;
TR1=0; //禁止T1中斷
ch451_load=0; //命令開始
for(i=0;i12;i++){ //送入12位數(shù)據(jù),低位在前
ch451_din=command1;
ch451_dclk=0;
command>>=1;
ch451_dclk=1; //上升沿有效
}
ch451_load=1; //上升沿加載數(shù)據(jù)
TR1=1; // 開啟T1中斷
}
//*************************************************
//*************************************************
//中斷子程序 使用定時(shí)器中斷T1,寄存器組1
void ch451_inter() interrupt 3 using 1
{
uchar i; //定義循環(huán)變量
uchar command,keycode; //定義控制字寄存器,和中間變量定時(shí)器
command=0x07; //讀取鍵值命令的高4位0111B
ch451_load=0; //命令開始
for(i=0;i4;i++){
ch451_din=command1; //低位在前,高位在后
ch451_dclk=0;
command>>=1; //右移一位
ch451_dclk=1; //產(chǎn)生時(shí)鐘上升沿鎖通知CH451輸入位數(shù)據(jù)
}
ch451_load=1; //產(chǎn)生加載上升沿通知CH451處理命令數(shù)據(jù)
keycode=0; //清除keycode
for(i=0;i7;i++){
keycode=1; //數(shù)據(jù)作移一位,高位在前,低位在后
keycode|=ch451_dout; //從高到低讀入451的數(shù)據(jù)
ch451_dclk=0; //產(chǎn)生時(shí)鐘下升沿通知CH451輸出下一位
ch451_dclk=1;
}
ch451_key=keycode; //保存上次的鍵值
TL1=1; //重新裝載計(jì)數(shù)初值
TH1=0; //此計(jì)數(shù)初值用來調(diào)試
}
//***********************************************
void main()
{
int HIGH=270; //定義溫度上限變量
uchar A0,A1,A2,A3; //記錄溫度各位數(shù)的變量
uchar b,c; //定義存放DS1302中數(shù)據(jù)的變量
P26=1; //關(guān)閉蜂鳴器
Initial(); //初始化DS1302
//初始化以后此語(yǔ)句必須關(guān)掉 防止每次上電初始化DS1302
//關(guān)掉此語(yǔ)句編譯時(shí)會(huì)為此產(chǎn)生一個(gè)警告
/*改動(dòng)*/
ch451_init(); // 設(shè)置定時(shí)器中斷__定時(shí)掃描鍵盤
ch451_write(CH451_RESET); //CH451復(fù)位
ch451_write(CH451_SYSON2); //開顯示、鍵盤
ch451_write(CH451_DSP); //設(shè)置BCD不譯碼方式 由于板畫錯(cuò)了 此設(shè)置很重要
ch451_write(CH451_TWINKLE); //設(shè)置閃爍控制——正常顯示
while(1)
{
tt=Get_Tmp(); //讀出當(dāng)前溫度
A3=tt/1000; //千位
A2=(tt%1000)/100; //百位
A1=(tt%100)/10; //十位——這么處理的目的是顯示小數(shù)點(diǎn),因?yàn)锳1實(shí)際是溫度的個(gè)位
A0=tt%10; //個(gè)位
ch451_write(display[0]+BCD[A0]); //顯示溫度小數(shù)位
ch451_write(display[1]+BCD[A1]+1); //顯示溫度個(gè)位 此處加一為了加入小數(shù)點(diǎn)
ch451_write(display[2]+BCD[A2]); //顯示溫度十位
ch451_write(display[3]+BCD[A3]); //顯示溫度百位
if(Get_Tmp()>HIGH) //如果溫度大于上限度
{
delay(10); //延時(shí)是為了防止脈沖不穩(wěn)定干擾
if(Get_Tmp()>HIGH) //可以消除蜂鳴器非正常響
/*合理的改動(dòng)*/
{
Write1302(0xc0,Read1302(READ_MINUTE)/10*16+Read1302(READ_MINUTE)%10); //寫入當(dāng)前記錄的數(shù)據(jù)所對(duì)應(yīng)的時(shí)間——分
Write1302(0xc2,Read1302(READ_HOUR)/10*16+Read1302(READ_HOUR)%10); //寫入當(dāng)前記錄的數(shù)據(jù)所對(duì)應(yīng)的時(shí)間——小時(shí)
/**寫入的應(yīng)該是16進(jìn)制而不是十進(jìn)制**/
speaker(); //如果溫度大于 HIGH度,蜂鳴器報(bào)警
}
}
if(Get_Tmp()0) //如果溫度小于0度
{
delay(10); //延時(shí)是為了防止脈沖不穩(wěn)定干擾
if(Get_Tmp()0) //可以消除溫度非正常顯示為負(fù)
/*合理的改動(dòng)*/
{
tt=abs(tt);
ch451_write(display[0]+BCD[A0]); //顯示溫度小數(shù)位
ch451_write(display[1]+BCD[A1]+1); //顯示溫度個(gè)位 此處加一為了加入小數(shù)點(diǎn)
ch451_write(display[2]+BCD[A2]); //顯示溫度十位
ch451_write(display[3]+BCD[A2]); //顯示溫度百位
ch451_write(display[4]+0x40); //顯示負(fù)號(hào)
}
}
//對(duì)各個(gè)按鍵進(jìn)行定義
switch(ch451_key)
{
case 0x40: {
b=Read1302(0xc1); //讀出數(shù)據(jù)所對(duì)應(yīng)的時(shí)間——分
c=Read1302(0xc3); //讀出數(shù)據(jù)所對(duì)應(yīng)的時(shí)間——小時(shí) */
ch451_write(display[4]+BCD[b%10]); //顯示分鐘個(gè)位
ch451_write(display[5]+BCD[b/10]); //顯示分鐘十位
ch451_write(display[6]+BCD[c%10]); //顯示小時(shí)個(gè)位
ch451_write(display[7]+BCD[c/10]); //顯示小時(shí)十位
} break; //按下第1個(gè)鍵時(shí)調(diào)出DS1302中產(chǎn)生溫度報(bào)警的時(shí)間
case 0x43: { ch451_write(display[4]+0); //關(guān)顯示分鐘個(gè)位
ch451_write(display[5]+0); //關(guān)顯示分鐘十位
ch451_write(display[6]+0); //關(guān)顯示小時(shí)個(gè)位
ch451_write(display[7]+0); //關(guān)顯示小時(shí)十位;
} break; //按下第2個(gè)鍵關(guān)時(shí)間顯示
case 0x41: speaker(); break; //按下第3個(gè)鍵時(shí)蜂鳴器響
case 0x42: {
A3=HIGH/1000; //千位
A2=(HIGH%1000)/100; //百位
A1=(HIGH%100)/10; //十位——這么處理的目的是顯示小數(shù)點(diǎn),因?yàn)锳1實(shí)際是溫度的個(gè)位
A0=HIGH%10; //個(gè)位
ch451_write(display[4]+BCD[A0]); //顯示溫度小數(shù)位
ch451_write(display[5]+BCD[A1]+1); //顯示溫度個(gè)位 此處加一為了加入小數(shù)點(diǎn)
ch451_write(display[6]+BCD[A2]); //顯示溫度十位
ch451_write(display[7]+BCD[A3]); //顯示溫度十位
} break; //按下第4個(gè)鍵顯示上限溫度
case 0x48: {
delay(65000); //使按鍵增大溫度上限值得過程變慢
HIGH=HIGH+1;
A3=HIGH/1000; //千位
A2=(HIGH%1000)/100; //百位
A1=(HIGH%100)/10; //十位——這么處理的目的是顯示小數(shù)點(diǎn),因?yàn)锳1實(shí)際是溫度的個(gè)位
A0=HIGH%10; //個(gè)位
ch451_write(display[4]+BCD[A0]); //顯示溫度小數(shù)位
ch451_write(display[5]+BCD[A1]+1); //顯示溫度個(gè)位 此處加一為了加入小數(shù)點(diǎn)
ch451_write(display[6]+BCD[A2]); //顯示溫度十位
ch451_write(display[7]+BCD[A3]); //顯示溫度百位
} ; break; //按下第5個(gè)鍵增加溫度上限值
case 0x4b: {
delay(65000); //使按鍵減小溫度上限值得過程變慢
HIGH=HIGH-1;
A3=HIGH/1000; //千位
A2=(HIGH%1000)/100; //百位
A1=(HIGH%100)/10; //十位——這么處理的目的是顯示小數(shù)點(diǎn),因?yàn)锳1實(shí)際是溫度的個(gè)位
A0=HIGH%10; //個(gè)位
ch451_write(display[4]+BCD[A0]); //顯示溫度小數(shù)位
ch451_write(display[5]+BCD[A1]+1); //顯示溫度個(gè)位 此處加一為了加入小數(shù)點(diǎn)
ch451_write(display[6]+BCD[A2]); //顯示溫度十位
ch451_write(display[7]+BCD[A3]); //顯示溫度十位
} ; break; //按下第6個(gè)鍵減小溫度上限值
case 0x49: {
while(ch451_key!=0x43)
{
ch451_write(display[0]+BCD[Read1302(READ_SECOND)%10]); //顯示分鐘的個(gè)位
ch451_write(display[1]+BCD[Read1302(READ_SECOND)/10]); //顯示秒的十位
ch451_write(display[2]+0x40); //顯示負(fù)號(hào)
ch451_write(display[3]+BCD[Read1302(READ_MINUTE)%10]); //顯示分鐘的個(gè)位
ch451_write(display[4]+BCD[Read1302(READ_MINUTE)/10]); //顯示分鐘的十位
ch451_write(display[5]+0x40); //顯示負(fù)號(hào)
ch451_write(display[6]+BCD[Read1302(READ_HOUR)%10]); //顯示小時(shí)的個(gè)位
ch451_write(display[7]+BCD[Read1302(READ_HOUR)/10]); //顯示分鐘的十位
}
} ; break; //按下第7個(gè)鍵動(dòng)態(tài)顯示當(dāng)前的時(shí)間——電子表
case 0x4a: ; break; //按下第8個(gè)鍵無效
}
/****************************************************************/
}
}
蜂鳴器相關(guān)文章:蜂鳴器原理 溫濕度控制器相關(guān)文章:溫濕度控制器原理
評(píng)論