如果学习51单片机,郭天祥的开发板是比较经典的一款,学习到最后可能会需要做一个综合应用的作业,我们的综合作业就是在单片机上实现实时监测环境温度,并且当温度改变时通过串口通信发送给串口调试助手,同时需要将温度通过I2C储存。
程序有三个模式:
第一个模式是单片机刚启动时,显示上次断电时的温度;
第二个模式是显示实时温度,同时在温度变化时通过串口通信发送给串口调试助手,并且通过I2C储存。
第三个模式是PC通过串口调试助手发送0-7来调节DA芯片控制的LED灯的亮度,亮度为8个等级,通过开发板上的8个LED灯来表示目前处于第几级亮度。
模式的切换通过外部中断1来控制,可以将外部中断1口与P3.7通过杜邦线连接起来,这样就可以通过独立按键的最后一个来切换模式。
实现的代码如下:
#include <reg52.h>
#define uchar unsigned char;
#define uint unsigned int;
//码字表部分
//段选码字表0~9
uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//ASCII码字表
uchar code tableAscii[]={'0','1','2','3','4','5','6','7','8','9'};
//把电流分为八个大等级,形成码字表(32是一个大等级)
uchar code electricLevel[]={0x1f,0x3f,0x5f,0x7f,0x9f,0xbf,0xdf,0xff};
//LED灯指示等级
uchar code showLevel[]={0x7f,0x3f,0x1f,0x0f,0x07,0x03,0x01,0x00};
//位选码字表
uchar code tablewela[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
//显示数组--AD
uchar ToDisplay[6]={0,0,0,0,0,0};
//位定义部分
sbit dula=P2^6; //段选
sbit wela=P2^7; //位选
sbit sda=P2^0; //数据线
sbit scl=P2^1; //时钟线
sbit csda=P3^2; //DA片选
sbit wr=P3^6; //DA写入
sbit DS18B20_IO=P2^2; //温度传感器io口
//全局变量定义部分
uchar buf; //承接接收的数据
uchar current=0; //用于数码管动态显示
uint temperture; //温度
uchar last_temperture; //记录上次温度
uchar ge,shi,bai; //将显示的温度分为三位
uchar flag=0; //运行模式
uchar number1,number2; //帮助掉电重连
//函数申明部分
void initial(); //初始化
void Display(); //数码管显示
void whenGetValue(); //当收到串口调试助手数据时
void IIC_init(); //IIC初始化
void write(); //写入数据
void read(); //读取数据
void update(); //温度数据更新
void DS18B20_change(); //开始温度转换
void restart(); //掉电重连
//延时函数部分
void someNop(){ //微秒级延时
;;;
}
void delay8ms(){ //8ms的延时
unsigned int a,b;
for(a=24;a>0;--a){
Display();
for(b=0;b<300;++b);
}
}
void delayXms(unsigned int x){ //延时Xms
unsigned char a,b,c;
for(a=x;a>0;a--)
for(b=10;b>0;b--)
for(c=100;c>0;c--);
}
//主函数部分
void main(){
initial();
read();
restart();
while(1){
while(flag==0){
delay8ms();
}
while(flag==1){
Display();
update();
}
wela=1;
P0=0xff;
wela=0;
dula=1;
P0=0;
dula=0;
while(flag==2);
}
}
//掉电重连部分
void restart(){ //上电后将掉电前的值发送给电脑
char i;
for(i=0;i<10;i++){
if(ToDisplay[0]==table[i]){
number1=i;
break;
}
}
for(i=0;i<10;i++){
if(ToDisplay[1]==table[i]){
number2=i;
break;
}
}
SBUF=number1*16+number2;
delay8ms();
delay8ms();
delay8ms();
for(i=0;i<10;i++){
if(ToDisplay[2]==table[i]){
number1=i;
break;
}
}
SBUF=number1;
}
//DA转换部分
void Exchange(){ //将ASCII码转换成对应码字表位
if(buf>=48&&buf<=57) //'0'<buf<'9'
{
buf=buf-48; //buf-'0'
}
}
void DA_SignalControl_LED(unsigned char input_DA){ //进行DA转换
csda=0;
wr=0;
P0=input_DA;
}
void initial(){ //初始化
TMOD=0x20; //设置定时器1为工作方式2
TH1=0xfd; //设置常量值
TL1=0xfd; //设置初值
TR1=1; //打开定时器1
SCON=0x50; //选择方式1,REN置1
EA=1; //打开总中断
ES=1; //打开串口中断
EX1=1; //外部中断1
IT1=1; //跳边沿触发
IIC_init(); //IIC初始化
DS18B20_change(); //开始温度转换
}
//串行口中断程序
void ser() interrupt 4{ //串口中断
if(RI==1)
{
RI=0; //数据
buf=SBUF; //承接接收到的数据
Exchange(); //将ASCII码转换成对应码字表位
DA_SignalControl_LED(electricLevel[buf]);
P1=showLevel[buf];
}else if(TI==1){
TI=0;
}
}
void INT_1() interrupt 2{ //外部中断1
flag++;
if(flag==3){
flag=1;
}
P1=~P1;
}
//DS18B20部分
unsigned char DS18B20_delay; //用于温度传感器分脉冲延时
void DS18B20_reset(){ //DS18B20复位
DS18B20_IO=1;
someNop();
DS18B20_IO=0;
DS18B20_delay=108;
while(DS18B20_delay>0)DS18B20_delay--;
DS18B20_IO=1;
DS18B20_delay=8;
while(DS18B20_delay>0)DS18B20_delay--;
}
bit DS18B20_read_bit(){ //DB18B20读取一位
bit B;
DS18B20_IO=0;DS18B20_delay++;
DS18B20_IO=1;
DS18B20_delay++;
DS18B20_delay++;
B=DS18B20_IO;
DS18B20_delay=8;
while(DS18B20_delay>0)DS18B20_delay--;
return B;
}
uchar DS18B20_read_byte(){ //DB18B20读取一个字节
unsigned char i,j,byte_data;
byte_data=0;
for(i=0;i<8;i++){
j=DS18B20_read_bit();
byte_data=(j<<7)|(byte_data>>1);//把获得的字节逆序
}
return byte_data;
}
void DS18B20_write_byte(unsigned char byte_data){//DB18B20写入一个字节
unsigned char i,j;
bit tmp_bit;
for(i=0;i<8;i++){
tmp_bit=byte_data&0x01;
byte_data=byte_data>>1;
if(tmp_bit){
DS18B20_IO=0;
j++;j++;
DS18B20_IO=1;
DS18B20_delay=8;
while(DS18B20_delay--);
}else{
DS18B20_IO=0;j++;j++;
DS18B20_delay=8;
while(DS18B20_delay--);
DS18B20_IO=1;
DS18B20_delay++;
DS18B20_delay++;
}
}
}
void DS18B20_change(){ //开始温度转换
DS18B20_reset(); //复位
delayXms(1);
DS18B20_write_byte(0xcc); //跳过
DS18B20_write_byte(0x44); //开始温度转换
}
uint DS18B20_getTemperature(){ //获得温度
unsigned char a,b;
float real_temperture;
DS18B20_reset(); //复位
delayXms(1);
DS18B20_write_byte(0xcc);//跳过ROM选择
DS18B20_write_byte(0xbe);//获取温度
a=DS18B20_read_byte();
b=DS18B20_read_byte();
temperture=b; //获取高位
temperture<<=8;
temperture=temperture|a;//获取低位
real_temperture=temperture*0.0625;//获取真实温度
temperture=real_temperture*10+0.5;//四舍五入法保留一位小数
return temperture;
}
void Display(){ //数码管显示
wela=1;
P0=tablewela[current]; //取位选值
wela=0;
P0=0x0;
dula=1;
P0=ToDisplay[current]; //取段选值
dula=0;
P0=0xff;
current++;
if(current==3)
{
current=0;
}
}
void update(){ //更新ToDisplay中的值
int tmp;
DS18B20_change(); //开始温度转换
Display(); //延时一下
tmp=DS18B20_getTemperature();
bai=tmp/100;
shi=tmp/10%10;
ge=tmp%10;
ToDisplay[0]=table[bai];
ToDisplay[1]=table[shi]+0x80;
ToDisplay[2]=table[ge];
if(tmp!=last_temperture){
last_temperture=tmp;
tmp=bai*16+shi;
SBUF=tmp;
write();
SBUF=ge;
}
}
//IIC部分
void IIC_init(){ //IIC初始化
sda=1;
someNop();
scl=1;
someNop();
}
void start(){ //开始接收数据
sda=1;
someNop();
scl=1;
someNop();
sda=0;
someNop();
scl=0;
someNop();
}
void stop(){ //停止接收数据
sda=0;
someNop();
scl=1;
someNop();
sda=1;
someNop();
scl=0;
someNop();
}
void ack(){ //等待应答
unsigned char i=0;
IIC_init();
while((sda==0)&&(i<250))
i++;
scl=0;
someNop();
}
void write_byte(unsigned char _data){ //写入数据
unsigned char tmp,i;
tmp=_data;
scl=0;
someNop();
for(i=0;i<8;++i){
tmp=tmp<<1;
sda=CY;
someNop();
scl=1;
someNop();
scl=0;
someNop();
}
}
uchar read_byte(){ //读取数据
unsigned char i,j,k;
scl=0;
someNop();
for(i=0;i<8;++i){
scl=1;
someNop();
j=sda;
someNop();
k=(k<<1)|j;
scl=0;
someNop();
}
return k;
}
void write(){ //将数码管内容写入24C02
unsigned char i;
for(i=0;i<3;++i){
start();
write_byte(0xa0);
ack();
write_byte(i);
ack();
write_byte(ToDisplay[i]);
ack();
stop();
delay8ms();
}
}
void read(){ //从24C02中读出六位数据
unsigned char i;
for(i=0;i<3;++i){
start();
write_byte(0xa0);
ack();
write_byte(i);
ack();
start();
write_byte(0xa1);
ack();
ToDisplay[i]=read_byte();
stop();
}
}
由于时间问题,以上的代码有些细节未经过处理,如外部中断可能触发多次,这个问题可以通过延时来解决,可以延时200ms或者更多的时间,只要保证在这个时间段内使用者不会想要连续按两次即可。或者通过按键消抖加上松手检测也可以解决。