蓝桥杯之单片机设计与开发(24)——2017_第八届_蓝桥杯_省赛——“基于单片机的电子钟”

版权声明:让结局不留遗憾,让过程更加完美。 https://blog.csdn.net/Xiaomo_haa/article/details/88173499

2019年3月5日下午更新。

想了想,找到了怎么解决在显示温度的时候数码管闪烁的问题。

问题原因:因为我是在定时器中断中显示数码管,然后在读写DS18B0时会关总中断,这个时间是ms级别的,这时数码管就不会显示。

解决方法:只在DS18B20时序要求十分严格的地方关总中断,其他地方开总中断。


昨天晚上写好的,但是懒得发了,今天给发出来。。

还是一样,先贴题目。

1、题目

根据题目,我们需要用到的就是数码管、独立按键、DS1302、DS18B20、LED

2、代码

这一个题目代码我写的有些许麻烦,而且现在还有一点小BUG,就是按下S4显示温度,数码管很抖,后续再解决吧。

在这可以直接下载 https://download.csdn.net/download/xiaomo_haa/10997235

main.c

#include <stc15.h>
#include "sys.h"

u8 Led_dat = 0xff;
u8 mode = 0;
bit flag_1s = 0, flag_100ms = 0;
bit flag_temp = 0;
bit flag_led = 0;
bit flag_alarm = 1;

u8 AlarmTime[] = {0x00, 0x00, 0x00};			//闹钟时间
signed char OptionTime[] = {0, 0, 0, 0, 0, 0};		//0-2时间 3-5闹钟

u8 bufTime[] = {  //2019年3月2日 星期六 23:59:50
			0x19, 0x03, 0x02, 0x23, 0x59, 0x50, 0x06
	};	//日期时间缓冲区
	
void main(void)
{
	bit res = 0;
	int Temp = 0;						//读取当前的温度值
	int Temp_int = 999;			//温度值的整数部分
	static u8 index = 0;
	
	All_Init();
	Timer0Init();
	Start18B20();
	InitDS1302(&bufTime);
	EA = 1;
	while(1)
	{
		KeyPress();
		Led_illume(Led_dat);
		
		if(flag_100ms == 1)		//每100ms更新一次时间
		{
			index ++;
			flag_100ms = 0;
			GetRealTime(&bufTime);					//获取当前时间
			if(flag_temp == 0)
				RefreshTime(&bufTime);					//显示日期
			
			IsAlarm(&bufTime);							//是否到了闹钟时间
			
			if(index >= 5)			//每500ms更新温度
			{
				index = 0;

			}
		}
		
		if((S4 == 0) && (mode == 0) && (flag_alarm == 1) && (flag_led == 0))
		{
			flag_temp = 1;							//显示温度标志
			res = Get18B20Temp(&Temp);	//读取当前温度
			if(res)											//如果读取到
				Temp_int = Temp >> 4;			//分离出温度值整数部分
			Start18B20();								//重新启动下一次转换
			Num_deal(Temp_int);					//显示温度
		}
		else
			flag_temp = 0;
	}
}




sys.c

#include "sys.h"

//关闭所有外设
void All_Init(void)
{
	P2 = (P2 & 0x1f) | 0x80;	//打开Y4C(LED)
	P0 = 0xff;					//关闭LED
	P2 = (P2 & 0x1f) | 0xe0;	//打开Y7C(数码管)
	P0 = 0xff;					//关闭数码管
	P2 = (P2 & 0x1f) | 0xa0;	//打开Y5C
	P0 = 0x00;					//关闭蜂鸣器、继电器
	P2 = P2 & 0x1f;
}

//定时器0初始化
void Timer0Init(void)		//2毫秒@11.0592MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x9A;		//设置定时初值
	TH0 = 0xA9;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
}

//定时器0中断程序
void Time0(void) interrupt 1
{
	static unsigned char t01 = 0, t02 = 0;
	static unsigned int t03 = 0, t04 = 0;
	
	t01 ++;
	t03 ++;
	if(t01 >= 50)
	{
		flag_100ms = 1;
		t01 = 0;
		
		if(mode == 0)
			TimeBackup(&bufTime);					//备份时间
	}
	
	if(flag_led == 1)
	{
		t02 ++;
		if(t02 <= 100)
			Led_dat = 0xfe;
		else
		{
			Led_dat = 0xff;
			if(t02 >= 200)
				t02 = 0;
		}
	}
	else if(flag_led == 0)
	{
		t02 = 0;
		Led_dat = 0xff;
	}
	
	if(t03 >= 500)
	{
		t03 = 0;
		flag_1s  = ~flag_1s;
	}
	
	if(flag_led == 1)
	{
		t04 ++;
		
		if(t04 >= 2500)
		{
			t04 = 0;
			flag_led = 0;
		}
	}
	else
		t04 = 0;
	
	KeyScan();
	Nixie_Show();
	Nixie_Scan();
}

void IsAlarm(u8 *time)
{
	if((time[5] == AlarmTime[2]))
	{
		if(time[4] == AlarmTime[1])
		{
			if(time[3] == AlarmTime[0])
			{
				if(flag_alarm)
					flag_led = 1;
			}
		}
	}
	else
		flag_alarm = 1;
}


sys.h

#ifndef _SYS_H_
#define _SYS_H_

#include <stc15.h>
#include <intrins.h>
#include "ds1302.h"
#include "ds18b20.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

//外部变量
extern u8 Led_dat;
extern bit flag_100ms, flag_1s;
extern u8 mode;
extern bit flag_temp;
extern bit flag_led;
extern bit flag_alarm;
extern u8 RealTime[];				//时间
extern u8 AlarmTime[];			//闹钟时间
extern signed char OptionTime[];
extern u8 bufTime[];

extern u8 time_index, alarm_index;

//管脚定义
sbit S7 = P3^0;
sbit S6 = P3^1;
sbit S5 = P3^2;
sbit S4 = P3^3;

//函数声明
void All_Init(void);
void Timer0Init(void);
void IsAlarm(u8 *time);

void Nixie_Scan(void);
void Nixie_Show(void);
unsigned char BCDToNum(unsigned char bcd);
unsigned char NumToBCD(unsigned char num);
void Num_deal(int temp_int);
void RefreshTime(unsigned char *time);
void Led_illume(u8 dat);

void KeyScan(void);
void KeyPress(void);
void KeyAction(u8 key);
void TimeBackup(u8 *time);
void Updatatime(u8 *time);


#endif



display.c

#include "sys.h"

u8 code Nixie[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf};	//共阳数码管码字
u8 NixieBuff[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};	//数码管显示缓冲区,初值0xff确保启动时都不亮

u8 smg1,smg2,smg3,smg4,smg5,smg6,smg7,smg8;

//数码管显示
void Nixie_Scan(void)
{
	static u8 index = 0;
	
	P2 = (P2 & 0x1f) | 0xe0;	//数码管消隐
	P0 =0xff;
	
	P2 = (P2 & 0x1f) | 0xc0;	//数码管片选
	P0 = 0x01 << index;
	
	P2 = (P2 & 0x1f) | 0xe0;	//数码管段选
	P0 = NixieBuff[index];
	P2 &= 0x1f;
	index ++;
	index &= 0x07;
}

//数码管显示
void Nixie_Show(void)
{	
	NixieBuff[0] = Nixie[smg1];
	NixieBuff[1] = Nixie[smg2];
	NixieBuff[2] = Nixie[smg3];
	NixieBuff[3] = Nixie[smg4];
	NixieBuff[4] = Nixie[smg5];
	NixieBuff[5] = Nixie[smg6];
	NixieBuff[6] = Nixie[smg7];
	NixieBuff[7] = Nixie[smg8];
}

unsigned char BCDToNum(unsigned char bcd)
{
	unsigned char a, b;
	
	a = (bcd >> 4);
	b = bcd & 0x0f;
	
	return (a * 10 + b);
}

unsigned char NumToBCD(unsigned char num)
{
	unsigned char a, b, bcd;
	
	a = (num % 10) & 0x0f;
	b = ((num / 10) << 4) & 0xf0;
	
	bcd = a | b;
	
	return bcd;
}

void Num_deal(int temp_int)
{
	smg1 = smg2 = smg3 = smg4 = smg5 = 16;
	smg6 = (temp_int % 100) / 10; 
	smg7 = temp_int % 10;
	smg8 = 12;
}	

void RefreshTime(unsigned char *time)
{
	if(mode == 0)
	{
		smg1 = BCDToNum(time[3]) / 10;		//时钟十位
		smg2 = BCDToNum(time[3]) % 10;		//时钟个位	
		smg4 = BCDToNum(time[4]) / 10;		//分钟十位
		smg5 = BCDToNum(time[4]) % 10;		//分钟个位	
		smg7 = BCDToNum(time[5]) / 10;		//秒针十位
		smg8 = BCDToNum(time[5]) % 10;		//秒针个位
		
		if(smg8 % 2 == 0)
			smg3 = smg6 = 16;
		else
			smg3 = smg6 = 17;
	}
	else if(mode == 1)
	{
		smg1 = OptionTime[0] / 10;		//设置时钟十位
		smg2 = OptionTime[0] % 10;		//设置时钟个位
		smg3 = 17;
		smg4 = OptionTime[1] / 10;		//设置分钟十位
		smg5 = OptionTime[1] % 10;		//设置分钟个位
		smg6 = 17;
		smg7 = OptionTime[2] / 10;		//设置秒钟十位
		smg8 = OptionTime[2] % 10;		//设置秒钟个位
		
		if(flag_1s == 0)
		{
			switch(time_index)
			{
				case 1: smg1 = smg2 = 16; break;
				case 2: smg4 = smg5 = 16; break;
				case 3: smg7 = smg8 = 16; break;
			}
		}
	}
	else if(mode == 2)
	{
		smg1 = OptionTime[3] / 10;		//闹钟时钟十位
		smg2 = OptionTime[3] % 10;		//闹钟时钟个位
		smg3 = 17;                     
		smg4 = OptionTime[4] / 10;		//闹钟分钟十位
		smg5 = OptionTime[4] % 10;		//闹钟分钟个位
		smg6 = 17;                     
		smg7 = OptionTime[5] / 10;		//闹钟秒钟十位
		smg8 = OptionTime[5] % 10;		//闹钟秒钟个位
		
		if(flag_1s == 0)
		{
			switch(alarm_index)
			{
				case 4: smg1 = smg2 = 16; break;
				case 5: smg4 = smg5 = 16; break;
				case 6: smg7 = smg8 = 16; break;
			}
		}
	}
}

void Led_illume(u8 dat)
{
	P2 = (P2 & 0x1f) | 0x80;	//打开Y4C(LED)
	P0 = dat;					//点亮LED
	P2 = P2 & 0x1f;
}

ds1302.c

#include "sys.h"

sbit DS1302_IO = P2^3;
sbit DS1302_CK = P1^7;
sbit DS1302_CE = P1^3;

/*******************************************************************************
* 函数名	:DS1302ByteWrite
* 输入值	:unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:发送一个字节到DS1302通信总线上
* 备注		:
*******************************************************************************/
void DS1302ByteWrite(unsigned char dat) 
{
	unsigned char mask;
	DS1302_IO = 1;					//拉低IO总线
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在前,逐位移出
	{
		if((dat&mask) != 0)		//首先输出该位数据
			DS1302_IO = 1;
		else
			DS1302_IO = 0;
		DS1302_CK = 1;				//拉高时钟线
		DS1302_CK = 0;				//拉低时钟线,完成一个位的操作
	}
	DS1302_IO = 1;  				//写完之后确保释放IO总线
}  

/*******************************************************************************
* 函数名	:DS1302ByteRead
* 输入值	:none
* 返回值	:unsigned char dat
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:由DS1302通信总线上读取一个字节
* 备注		:返回读到的字节数据
*******************************************************************************/
unsigned char DS1302ByteRead(void)
{
	unsigned char mask, dat = 0;
	
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在前,逐位读取
	{
		if(DS1302_IO)			//首先读取此时的IO引脚,并设置dat中的对应位
			dat |= mask;

		DS1302_CK = 1;		//拉高时钟
		DS1302_CK = 0;		//再拉低时钟,完成一个位的操作
	}
	return dat;					//返回读到的字节数据
} 

/*******************************************************************************
* 函数名	:DS1302SingleWrite
* 输入值	:unsigned char reg, unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用单次写操作向某一寄存器写入一个字节
* 备注		:reg为寄存器地址,dat为待写入字节
*******************************************************************************/
void DS1302SingleWrite(unsigned char reg, unsigned char dat)     
{
	DS1302_CE = 1;					//使能片选信号
	DS1302ByteWrite((reg << 1) | 0x80);	//发送写寄存器指令
	DS1302ByteWrite(dat);		//写入字节数据
	DS1302_CE = 0;					//除能片选信号
}

/*******************************************************************************
* 函数名	:DS1302SingleRead
* 输入值	:unsigned char reg
* 返回值	:unsigned char dat
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用单次读操作从某一寄存器读取一个字节
* 备注		:reg为寄存器地址,返回值dat为读到的字节
*******************************************************************************/
unsigned char DS1302SingleRead(unsigned char reg)
{
	unsigned char dat;
	
	DS1302_CE = 1;					//使能片选信号
	DS1302ByteWrite((reg << 1) | 0x81);	//发送读寄存器指令
	dat = DS1302ByteRead();	//读取字节数据
	DS1302_CE = 0;					//除能片选信号
	
	DS1302_IO = 0;					//单字节读必须加的!
	
	return dat;         
}

/*******************************************************************************
* 函数名	:DS1302BurstWrite
* 输入值	:unsigned char *dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用突发模式连续写入8个寄存器数据
* 备注		:reg为寄存器地址,返回值dat为读到的字节
*******************************************************************************/
void DS1302BurstWrite(unsigned char *dat)
{
	unsigned char i;
	
	DS1302_CE = 1;
	DS1302ByteWrite(0xBE);				//发送突发写寄存器指令
	for(i = 0; i < 7; i ++)				//连续写入8字节数据
		DS1302ByteWrite(*dat++);        
	DS1302_CE = 0;      
}

/*******************************************************************************
* 函数名	:DS1302BurstRead
* 输入值	:unsigned char *dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:用突发模式连续读取8个寄存器的数据
* 备注		:dat为读到的字节
*******************************************************************************/
void DS1302BurstRead (unsigned char *dat)
{
	unsigned char i;
	
	DS1302_CE = 1;
	DS1302ByteWrite(0xBF);				//发送突发读寄存器指令
	for(i = 0; i < 7; i++)				//连续读取8个字节
		dat[i] = DS1302ByteRead();      
	DS1302_CE = 0;  
	
	DS1302_IO = 0;								//突发读必须加
}	

/*******************************************************************************
* 函数名	:GetRealTime
* 输入值	:struct sTime *time
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:读取DS1302时间
* 备注		:
*******************************************************************************/
void GetRealTime(unsigned char *time)
{
	unsigned char buf[8];
	
	DS1302BurstRead(buf);
	time[0] = buf[6];
	time[1] = buf[4];
	time[2] = buf[3];
	time[3] = buf[2];
	time[4] = buf[1];
	time[5] = buf[0];
	time[6] = buf[5];
}

/*******************************************************************************
* 函数名	:SetRealTime
* 输入值	:u8 *time
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:设置DS1302时间
* 备注		:
*******************************************************************************/
void SetRealTime(unsigned char *time)
{
	unsigned char buf[8];
	
	buf[7] = 0;
	buf[6] = time[0];
	buf[4] = time[1];
	buf[3] = time[2];
	buf[2] = time[3];
	buf[1] = time[4];
	buf[0] = time[5];
	buf[5] = time[6];
	DS1302BurstWrite(buf);
}

/*******************************************************************************
* 函数名	:InitDS1302
* 输入值	:struct sTime *time
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年3月1日
* 功能描述:DS1302初始化
* 备注		:
*******************************************************************************/
void InitDS1302(unsigned char *time)
{
	unsigned char dat;
	signed char i = 7;
	unsigned char timeinit[8];
	
	while(i >= 0)
	{
		timeinit[i] = time[i];
		i --;
	}
	
	DS1302_CE = 0;								//初始化DS1302通信引脚
	DS1302_CK = 0;
	dat = DS1302SingleRead(0);			//读取秒寄存器
	DS1302SingleWrite(7, 0x00);  	//撤销写保护以允许写入数据
	SetRealTime(&timeinit);					//设置DS1302时间
//	DS1302SingleWrite(7, 0x80);  	//写保护以禁止写入数据
}

ds1302.h

#ifndef __DS1302_H
#define __DS1302_H

void DS1302ByteWrite(unsigned char dat);
unsigned char DS1302ByteRead(void);
void DS1302SingleWrite(unsigned char reg, unsigned char dat);
unsigned char DS1302SingleRead(unsigned char reg);
void DS1302BurstWrite(unsigned char *dat);
void DS1302BurstRead (unsigned char *dat);
void GetRealTime(unsigned char *time);
void SetRealTime(unsigned char *time);
void InitDS1302(unsigned char *time);


#endif

ds18b20.c

 #include "sys.h"

sbit DS18B20_IO = P1^4;

/*******************************************************************************
* 函数名	:Delayus
* 输入值	:unsigned int us
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月17日
* 功能描述:1T单片机延时指定us
* 备注		:最大形参65535,即最大延时65ms
*******************************************************************************/
void Delayus(unsigned int us)
{
	do{
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
	}while(--us);
}

/*******************************************************************************
* 函数名	:Get18B20Ack
* 输入值	:none
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:复位总线,获取18B20存在脉冲,以启动一次读写操作
* 备注		:
*******************************************************************************/
bit Get18B20Ack(void)
{
	bit ack;
	
	DS18B20_IO = 0;			//产生500us的复位脉冲
	Delayus(500);
	DS18B20_IO = 1;			//延时60us
	EA = 0;							//禁止总中断
	Delayus(60);
	ack = DS18B20_IO;		//读取存在脉冲
	while(!DS18B20_IO);	//等待存在脉冲结束
	
	EA = 1;							//重新使能总中断
	
	return ack; 	
}

/*******************************************************************************
* 函数名	:DS18B20Write
* 输入值	:unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:向18B20写入一个字节
* 备注		:dat为待写入字节
*******************************************************************************/
void DS18B20Write(unsigned char dat)
{
 	unsigned char mask;
					
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在先,依次移出8个bit
	{
	    EA = 0;									//禁止总中断	
		DS18B20_IO = 0;				//产生2us低电平脉冲
		Delayus(2);
	 	if(dat & mask)				//输出该bit值
		 	DS18B20_IO = 1;
		else
		 	DS18B20_IO = 0;
	    EA = 1;									//重新使能总中断
		Delayus(60);					//延时60us
		DS18B20_IO = 1;				//拉高通信引脚
	}
}

/*******************************************************************************
* 函数名	:DS18B20Read
* 输入值	:none
* 返回值	:unsigend char dat
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:从18B20读取一个字节
* 备注		:返回值为读取到的字节
*******************************************************************************/
unsigned char DS18B20Read(void)
{
 	unsigned char mask, dat = 0;
	
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在先,依次采集8个bit
	{
    	EA = 0;									//禁止总中断
		DS18B20_IO = 0;				//产生2us低电平脉冲
		Delayus(2);
		DS18B20_IO = 1;				//结束低电平脉冲,等待18B20输出数据
		Delayus(2);						//延时2us
	 	if(DS18B20_IO)				//读取通信引脚上的值
		 	dat |= mask;
    	EA = 1;									//重新使能总中断
		Delayus(60);					//再延时60us
	}
	
	return dat;	
}

/*******************************************************************************
* 函数名	:Start18B20
* 输入值	:none
* 返回值	:bit ~ack
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:启动一次18B20温度转换
* 备注		:返回值为是否启动成功
*******************************************************************************/
bit Start18B20()
{
 	bit ack;
	static bit flag = 1;

	ack = Get18B20Ack();		//执行总线复位,并获取18B20应答
	if(ack == 0)						//如18B20正确应答,则启动一次转换
	{
		DS18B20Write(0xCC);		//跳过ROM操作
		
		if(flag)
		{
			flag = 0;
			DS18B20Write(0x4e);			//写暂存器指令4E
			DS18B20Write(0x4b);			//写高速缓存器TH高温限值75度
			DS18B20Write(0x00);			//写高速缓存器TL低温限值0度
			DS18B20Write(0x1f);			//写配置寄存器4
				//0x1f : 0.5000°C  转换时间93.75ms
				//0x3f : 0.2000°C  转换时间187.5ms
				//0x5f : 0.1250°C  转换时间375ms
				//0x7f : 0.0625°C  转换时间750ms
		}
		
		ack = Get18B20Ack();		//执行总线复位,并获取18B20应答
		if(ack == 0)						//如18B20正确应答,则启动一次转换
		{
			DS18B20Write(0xCC);		//跳过ROM操作
			DS18B20Write(0x44);		//启动一次温度转换
		}
	}

	return ~ack;						//ack == 0 表示操作成功,所以返回值对其取反
}

/*******************************************************************************
* 函数名	:Get18B20Temp
* 输入值	:int *temp
* 返回值	:bit ~ack
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:读取18B20转换的温度值
* 备注		:返回值为是否读取成功
*******************************************************************************/
bit Get18B20Temp(int *temp)
{
 	bit ack;
	unsigned char LSB, MSB;			//16bit温度值的低字节和高字节

	ack = Get18B20Ack();				//执行总线复位,并获取18B20应答
	if(ack == 0)								//如18B20正确应答,则读取温度值
	{
	 	DS18B20Write(0xCC);				//跳过ROM操作
		DS18B20Write(0xBE);				//发送读命令
		LSB = DS18B20Read();			//读温度值的低字节
		MSB = DS18B20Read();			//读温度值的高字节
		*temp = ( MSB << 8) + LSB;	//合成16bit的整数
	}

	return ~ack;								//ack == 0 表示操作应答,所以返回值为1其取反值
} 

ds18b20.h

#ifndef __DS18B20_H
#define __DS18B20_H


//单总线延时函数
void Delayus(unsigned int us);
bit Get18B20Ack(void);
void DS18B20Write(unsigned char dat);
unsigned char DS18B20Read(void);
bit Start18B20();
bit Get18B20Temp(int *temp);


#endif

猜你喜欢

转载自blog.csdn.net/Xiaomo_haa/article/details/88173499