一、前言
在蓝桥杯单片机的比赛当中,很多传感器都是会经常使用到的,比如说DS18B20和DS1302等,都是会经常用到的,所以我们要把这些传感器都学会一下。在省十三的蓝桥杯单片机题目中,我自己也写了一下这个代码,可能有些地方会有点问题,但是大致的功能还是能够实现的。接下来先放出第十三届蓝桥杯单片机的省赛题目。
然后是官方给的一些库函数,我先把官方原版的库函数先放在这里,等下我才根据题目来把要用的传感器的初始化写上去。
二、官方初始代码
iic.c
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "iic.h"
#include "intrins.h"
#define DELAY_TIME 5
//
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
iic.h
#ifndef __IIC_H__
#define __IIC_H__
#include <stc15.h>
#define sda P21
#define scl P20
unsigned char ADC_Output(char channel);
#endif
ds1302.c
/* # DS1302代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "ds1302.h"
#include <STC15F2K60S2.H>
sbit SCK = P1^7;
sbit SDA = P2^3;
sbit RST = P1^3;
//
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
ds1302.h
#ifndef __DS1302_H__
#define __DS1302_H__
#endif
onewire.c
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "onewire.h"
//
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
Delay_OneWire(1);
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
onewire.h
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
#define DQ P14
#endif
三、题目分析
看了题目和硬件框图之后,我们需要用的传感器有,数码管(这个在蓝桥杯单片机中是必定会出现的),LED灯(LED灯也是会经常出现),继电器,矩阵键盘,DS1302(时钟芯片,用来记录时间),DS18B20(温度传感器)。
数码管的显示的话是有三个界面的,分别是温度显示界面、时间显示界面、参数显示界面,有这么多的显示界面的话,我们就可以使用一个显示参数切换的函数来选择我们要显示的界面,这是一种思路,你也可以每个界面都写一个显示函数,这样也可以做到显示不同的显示界面。
在时间显示上,我们可以使用一个结构体来存储我们的时间,这样我们要修改和读取的时候就能很方便了,接下来我会演示如何写这个结构体。
温度显示的话,就还是那样,就是发送相应的命令字节,让他去检测环境的温度。
按键的话,可以采用读取相应矩阵的高低电平来做到,先把某一列置为1,随后按照一定的高低位顺序来检测这一列当中是哪个按键被按下了。
四、代码讲解
分析完题目,确定了我们要写什么内容以后,我们就可以开始写代码了,我的建议是先写出各个部分的初始化代码,这样的话,等下就能直接使用这些已经写好的函数去构建我们的主函数了。
时钟芯片函数
ds1302.c
为了你们能够直接cv这个函数,所以我也把官方给的代码也cv进去了,你们省事我也省事了。
/* # DS1302代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "ds1302.h"
#include <STC15F2K60S2.H>
sbit SCK = P1^7;
sbit SDA = P2^3;
sbit RST = P1^3;
//
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
#define WP 0x8e
#define W_Sec 0x80
#define R_Sec 0x81
#define W_Min 0x82
#define R_Min 0x83
#define W_Hour 0x84
#define R_Hour 0x85
void Write_Time(ds1302 *Date)
{
unsigned char hour,min,sec;
// ds1302 Date;
//转化为十六进制
hour = Date->Hour/10*16 + Date->Hour%10;
min = Date->Min/10*16 + Date->Min%10;
sec = Date->Sec/10*16 + Date->Sec%10;
Write_Ds1302_Byte(WP,0x00);
Write_Ds1302_Byte(W_Sec,sec);
Write_Ds1302_Byte(W_Min,min);
Write_Ds1302_Byte(W_Hour,hour);
Write_Ds1302_Byte(WP,0x00);
}
ds1302 Read_Time(void)
{
ds1302 Date;
unsigned hour,min,sec;
sec = Read_Ds1302_Byte(R_Sec);
min = Read_Ds1302_Byte(R_Min);
hour = Read_Ds1302_Byte(R_Hour);
Date.Sec=sec/16*10 + sec%16;
Date.Min=min/16*10 + min%16;
Date.Hour=hour/16*10 + hour%16;
return Date;
}
ds1302.h
#ifndef __DS1302_H__
#define __DS1302_H__
#include "intrins.h"
#include <STC15F2K60S2.H>
typedef struct {
unsigned char Hour;
unsigned char Min;
unsigned char Sec;
}ds1302;
void Write_Time(ds1302 *Date);
ds1302 Read_Time(void);
#endif
时钟芯片代码讲解
现在开始对我写的这些代码讲解,首先是先创建出存储这些时间的结构体,为什么要使用结构体呢,因为使用结构体的话,我们到时候修改时间会比较方便,而且使用结构体的话我们能直接用结构体来写一个函数,然后再返回这个函数,这样时间就能修改成功了。
#define WP 0x8e 这个定义是ds1302芯片的一个保护特性,其实就是write protect,写保护来的,所以我们在把时间写入ds1302芯片的时候,首先要取消写保护,不然的话会导致你不能写入时间,在我们写完时间之后,你也要记得要再开启写保护喔,取消/开启写保护的方式就是将0x00写入到WP就行了。
#define W_Sec 0x81这个是写时间秒的地址,我在这里教你们一个很方便记忆的这些寄存器地址的方式,其实这些地址都是连续的,你只要记住第一个W_Sec是0x81就行了,然后后面的都是write read,按着秒、分、时的顺序,随后寄存器的地址再++,这样的话,整个ds1302时钟芯片你所需要记的寄存器地址就一个WP和W_Sec了,大大方便了我们的记忆。
我们先讲下Write_Time(ds1302*Data)这个函数,在我们把时间写入芯片之前,我们首先要把我们的时间先转换成十六进制的,因为ds1302时钟芯片只能识别到十六进制的数字,转换成十六进制的方式也很简单,只要我们高位/10再%上16,就能将这个数字转换成十六进制的高位了,/10就是把十进制的高位提出来,%16是将十六进制的低位去除,十六进制的低位也是差不多这样的道理,就把十进制数直接%10,得到十六进制的低位。随后就是我们上述讲的,关闭写保护,然后把我们对应的时间写进去最后关闭写保护就行了,我们函数的参数是直接用的结构体来定义的,所以我们可以直接用指针来得到我们原始的时间,这样就省事多了。
ds1302 Read_Time()这个函数就比刚刚的写时间就简单一点,就用我们定义的结构体来直接修改我们所需要修改的时间,要注意的是,我们时钟芯片里得到的是十六进制的,所以我们还要把他转换成十进制的,转换原理和上述转换十六进制的原理一致。具体的也没什么了,ds1302时钟芯片就这样了,是不是很简单。
温度传感器
onewire.c
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "onewire.h"
//
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
Delay_OneWire(1);
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
float Read_Temp(void)
{
unsigned char th,tl;
float temp;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
tl=Read_DS18B20();
th= Read_DS18B20();
temp = (th*256 + tl)*0.625;
return temp;
}
onewire.h
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
#include <STC15F2K60S2.H>
#define DQ P14
float Read_Temp(void);
#endif
温度传感器要写的东西就更少了,也没什么需要写的。在我们读取温度之前我们要先init一下,在我们写入一个寄存器之后也需要再init一次。
Write_DS18B20(0xcc);这行代码向 DS18B20 发送了一个命令,其中 0xcc是命令字节,它告诉传感器跳过地址匹配步骤,直接对所有连接的传感器执行后续命令。在这种情况下,它是在进行温度转换之前发送的准备命令。
Write_DS18B20(0x44);这行代码发送了另一个命令给 DS18B20,其中 0x44
是启动温度转换的命令字节。当传感器收到这个命令后,它会开始测量环境温度,并将结果存储在其内部寄存器中。
Write_DS18B20(0xbe);这行代码发送了另一个命令给 DS18B20,其中 0xbe
是读取温度数据的命令字节。当传感器收到这个命令后,它会将内部存储的温度值发送回给控制器。
在输入完命令之后,就要开始读取温度了,这里有一个坑需要注意的,刚开始的时候我也踩了这个坑,就是我们读取温度之前,我们要先读取那个低位的数据,然后再读取高位的数据,不然我们等下转换的数据会出错的,我之前也试过这个错误,后面才发现他要先读取低位的数据,才能正常转换数据。转换数据的时候,我们得到的温度,你要根据实际情况来修改最后面要乘的那个数,如果只是要得到温度的整数值的话,那么我们就可以直接×0.0625就行了,这样就能得到我们温度的整数值了,现在这个题目是要我们保留一位小数的,所以我们要把他放大十倍,把第一位小数也弄出来,所以我们×的是0.625,这样就可以得到一位小数了,要注意的是,我们现在的温度已经是三位数的了,所以我们等下显示这个温度的时候,要注意我们转换的时候是按三位数的形式来转换的。
由于没有用到要用到iic的传感器,所以我们也可以不用去管这个函数了。
最主要的显示函数
display.c
#include <STC15F2K60S2.H>
code unsigned char Number[]={
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, //0~9
0xc1, //U 10
0xbf //- 11
};
//0~9小数
code unsigned char FNumber[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
void Delayms(int ms) //@12.000MHz
{
unsigned char data i, j;
int k=0;
for(k=0;k<ms;k++)
{
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
void Select_HC573(unsigned char Number)
{
switch(Number)
{
case 4:P2=(P2 & 0x1f)|0x80; break; //LED
case 5:P2=(P2 & 0x1f)|0xA0; break; //蜂鸣器和继电器
case 6:P2=(P2 & 0x1f)|0xC0; break; //数码管位选
case 7:P2=(P2 & 0x1f)|0xE0; break; //段选
}
P2=(P2&0x1f)|0x00;
}
void Display_Number(unsigned char Pos,unsigned char Data)
{
P0 = 0x01<<Pos -1;
Select_HC573(6);
P0=Number[Data];
Select_HC573(7);
Delayms(1);
P0=0x01<<Pos -1;
Select_HC573(6);
P0=0xff;
Select_HC573(7);
}
void Display_FNumber(unsigned char Pos,unsigned char Data)
{
P0 = 0x01<<Pos -1;
Select_HC573(6);
P0=FNumber[Data];
Select_HC573(7);
Delayms(1); //延时过高会导致闪烁,延时要低一点
P0=0x01<<Pos -1;
Select_HC573(6);
P0=0xff;
Select_HC573(7);
}
void System_Init(void)
{
P0=0x00;
Select_HC573(5);
Select_HC573(6);
P0=0xff;
Select_HC573(4);
}
//LED开关,1为开,0为关
void Display_LED(unsigned char Pos,unsigned char ON_OFF)
{
unsigned char Temp=0xff;
if(ON_OFF)
{
switch(Pos)
{
case 1:Temp=Temp & 0xfe;break;
case 2:Temp=Temp & 0xfd;break;
case 3:Temp=Temp & 0xfb;break;
}
}
else
{
switch(Pos)
{
case 1:Temp=Temp | 0x01;break;
case 2:Temp=Temp | 0x02;break;
case 3:Temp=Temp | 0x04;break;
}
}
P0=Temp;
Select_HC573(4);
}
void Display_Relay(unsigned char ON_OFF)
{
unsigned char Temp=0xff;
if(ON_OFF)
{
Temp &= 0x10;
}
else
{
Temp &= 0x00;
}
P0 = Temp;
Select_HC573(5);
}
display.h
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
void System_Init(void);
void Delayms(int ms); //@12.000MHz
void Select_HC573(unsigned char Number);
void Display_Number(unsigned char Pos,unsigned char Data);
void Display_FNumber(unsigned char Pos,unsigned char Data);
void Display_LED(unsigned char Pos,unsigned char ON_OFF);
void Display_Relay(unsigned char ON_OFF);
#endif
我们这个STC15F2K60S2单片机和其他普通单片机的最大的不同就是这个单片机是拥有锁存的,所以在我们使用对应的传感器之前,我们首先要先把锁存器给使能了,不然我们无法使用那些传感器。我现在在这里也说一下对应的锁存器所代表的东西吧。
void Select_HC573(unsigned char Number)
首先是case 4的,4的话就是控制LED灯的传感器的,然后case 5是控制蜂鸣器那些的,我们在正式写代码之前,最后先写一个系统初始化的函数,目的就是为了讲蜂鸣器给关掉,不然如果那个蜂鸣器在你写代码的时候一直响的话,影响了你也影响了别人,不太好,所以记得把蜂鸣器给关掉。case 6是数码管的位选的锁存器,case 7是数码管的段选的锁存器。
什么叫位选和段选呢?首先我解释一下位选,在数字显示器中,数字通常是由七段LED(或其他显示技术)组成的。要显示一个数字,需要同时控制这些段中的哪些点亮以形成所需的数字。位选是指选择哪个数字的哪一位要被激活。例如,如果你有一个四位数码管,位选就是选择激活哪一位数码管来显示数字。这通常通过一个译码器或类似的设备来完成,它接收输入信号,然后选择性地激活相应的数码管。
段选是指选择要显示的数字或字符的哪些段要被点亮。每个数字或字符可以通过多个LED段来表示,例如数字"0"可能由数码管的多个段组成,每个段代表数字的一部分。段选通常是通过控制每个数字或字符的段,来显示特定的数字或字符。例如,在一个七段数码管中,需要选择性地点亮每个数字的七个段中的哪些。总的来说,位选是选择要激活的显示器的特定位置,而段选是选择要在选定位置上显示的数字或字符的具体形状。
要记住对应的锁存器的使能方式也挺简单的,因为他们的形式都是差不多的,首先把高三位清零一下,然后LED的锁存器是与上一个0x80就行了,随后那些的锁存器也就是把高位逐渐+2就好了,也挺简单的。记得最后把P2给清零了,最后这个给P2清零至关重要,因为你在写代码的过程中,你的P2会一直不断的改变的,如果你不把P2清零了,P2一直在不断的变化中,那么你就可能会出现一些BUG,比如说我遇到的BUG就是数码管的显示会非常暗,导致各种各样的问题会出现。
void System_Init(void)
把锁存器的解锁讲完之后,接下来最重要的就是初始化了,最简单的就是先把P0=0x00,然后把锁存器5 6都选上,然后再把P0=0xff,把LED也初始化一下,为什么LED是0xff呢,因为LED是共阳极接法来的,共阳极就是他们把正极都接在一起了,所以只要给LED一个低电平,他就能点亮了。初始化之后把他放主函数里面,这样你的蜂鸣器就能关闭了,具体到哪一位才能将蜂鸣器关闭我们后面说到继电器的时候再说。
void Display_Number(unsigned char Pos,unsigned char Data)
接下来就是写数码管的显示函数了。在写数码管的显示及之前,我们首先要写出每个数字都对应的编码。首先我们来了解一下数码管是如何能够显示东西在数码管上的,数码管是一种数字显示设备,用于显示数字、字母和一些特殊符号。八段数码管由八个发光二极管(LED)组成,每个LED称为一个段。这八个段可以分别表示数字0到9以及一些字母和符号。这八个段分别被标记为A、B、C、D、E、F、G和DP。数码管的每个段(A到DP)都有一个对应的输入引脚。通过控制这些引脚的电平状态,可以控制每个段的亮灭情况,从而形成不同的数字、字母或符号。通常,数码管的工作原理是:通过控制对应的输入引脚,将需要显示的数字或字符的每个段点亮,组合成所需的形状。然后这个数码管的引脚都是共阳极的,所以我们要让哪个二极管发光的话,我们要输入对应的低电平给那个引脚。然后他们的低位是从a开始的,也就是说a是低位,然后dp是最高位。比如说我们要显示一个0在数码管上的话,我们的编码就要这样,(高位)1100 0000(低位),也就是0xc0,然后就可以自己根据要求的那个数字或符号来自己进行编码了。由于我们要用到小数的显示,所以在后面我也写了一个显示小数的函数,然后他的编码其实很简单的,其实就是dp点亮嘛,所以就是我们上面定义的那些编码,在他们的高四位减个8就好了,也不用重新去计算了。
void Delayms(int ms) //@12.000MHz
这个延时我们可以使用我们烧录的那个软甲来进行,由于题目规定要使用的是12MHz的,所以我们也要注意下吗,记得修改我们的频率为12MHz。我是先先用软件生成一个1毫秒的,然后再加循环来达到我们的延时函数,这个软件延时也挺方便的,不需要我们去记什么东西。
void Display_LED(unsigned char Pos,unsigned char ON_OFF)
我们这个函数是用来控制LED的状态的,我已经在我的.h文件里定义了ON为1,OFF为0了,这个读者可自行去定义,我们的P0是不能频繁的变化的,不能可能会出一点上面BUG出来的,所以我们需要先定义一个temp来使用,这样也避免了直接修改P0从而影响到其他的东西,我们定义这个temp为0xff。在上面我说过我们的LED是共阳极的接法的,所以我们要开启一个LED灯的话就要给他对应的位置0,我们的LED灯的定义规则是,最低位为第一个LED灯,最高位为第8个LED灯,所以我们要开启第一个灯的话,我们只需要将temp &上一个0xfe,下面的就以此类推。关闭一个LED灯就是将对应位置1,详细的我就不多说了,都一样的,利用与或就能实现了。
void Display_Relay(unsigned char ON_OFF)
这个继电器的开启与关闭也是和开启关闭LED灯差不多的,有一点需要注意的是,继电器是在二进制的第五位上的(在最低位开始数起),也就是说,我们需要操作的是继电器的话,我们则是需要操作第五位的二进制数,在这里顺带提一下,我们的蜂鸣器是在第六位上的,所以到时候如果遇到了需要操作蜂鸣器的题目,你们就要第一时间想起控制蜂鸣器需要操作哪些位。由于锁存器5我们只用到了一个锁存器,所以我后面关闭的时候直接就&0x00了。
到这里我的display函数就讲完了,接下来我会讲我的主函数了,也是重头戏来的。
主函数
main.c
#include <STC15F2K60S2.H>
#include "display.h"
#include "onewire.h"
#include "ds1302.h"
unsigned char i=0;
float Temping=0; //温度
ds1302 Date ={23,59,55};
unsigned char Flag_5s; //L10的5秒计时
unsigned char Temp_Para=23; //温度参数
unsigned char Switching=1; //界面切换
unsigned char count_5s;
unsigned char Show_Time=0;
unsigned char count_100ms,Flag_100ms;
unsigned char Para_Flag;
unsigned char Control_Flag=0; //控制模式 1为温度控制模式,0为时间控制模式
unsigned char Show_Relay=0;
unsigned char LED1_Flag=0;
void Key_Board(void);
void Show_Switching(void);
void Timer2_Init(void); //20毫秒@12.000MHz
void Control_Mode(void);
void Timer1_Init(void); //10毫秒@12.000MHz
unsigned char Time_Zheng(ds1302* date);
void main(void)
{
System_Init();
Write_Time(&Date);
Timer2_Init();
Timer1_Init();
while(1)
{
Control_Mode();
Key_Board();
Show_Switching();
}
}
void Key_Board(void)
{
unsigned char Key;
P35=0; P34=1; P3 |= 0x0f;
Key = P3; Key &= 0x0c;
if(Key != 0x0c)
{
Delayms(5);
if(Key != 0x0c)
{
switch(Key)
{
case 0x04 :Switching++;
if(Switching > 3) Switching=1;
break;
case 0x08 :Control_Flag=~Control_Flag;break;
}
}
}
while(Key != 0x0c)
{
Key = P3; Key &= 0x0c;
Show_Switching();
Control_Mode();
}
P34 =0;P35=1; P3 |= 0x0f;
Key =P3; Key &= 0x0c;
if(Key != 0x0c)
{
Delayms(5);
if(Key != 0x0c)
{
switch(Key)
{
case 0x04: if(Switching ==3) Temp_Para++;
if(Temp_Para >= 99) Temp_Para =10;
break;
case 0x08: if(Switching ==3) Temp_Para--;
if(Temp_Para <=10) Temp_Para =99;
break;
}
}
}
while(Key != 0x0c)
{
Key = P3; Key &= 0x0c;
Show_Switching();
Control_Mode();
if(Key == 0x08 && Switching==2)
{
Show_Time =1;
}
}
Show_Time=0;
}
void Show_Switching(void)
{
Temping = Read_Temp();
Date=Read_Time();
if(Show_Time !=1)
{
if(Switching == 1)//温度显示界面 U1
{
Display_Number(1,10);Display_Number(2,1);Display_Number(6,(unsigned char)Temping/100);
Display_FNumber(7,(unsigned char)Temping/10%10);Display_Number(8,(unsigned char)Temping%10);
}
if(Switching == 2)//时间显示界面 U2
{
Display_Number(1,10);Display_Number(2,2);Display_Number(4,Date.Hour/10);Display_Number(5,Date.Hour%10);
Display_Number(6,11);Display_Number(7,Date.Min/10);Display_Number(8,Date.Min%10);
}
if(Switching ==3)
{
Para_Flag=1;
Display_Number(1,10);Display_Number(2,3);Display_Number(7,Temp_Para/10);Display_Number(8,Temp_Para%10);
}
}
else
{
Display_Number(1,10);Display_Number(2,2);Display_Number(4,Date.Min/10);Display_Number(5,Date.Min%10);
Display_Number(6,11);Display_Number(7,Date.Sec/10);Display_Number(8,Date.Sec%10);
}
if(Time_Zheng(&Date))
{
LED1_Flag=1;//计时5s
AUXR |= 0x10;
}
Display_LED(1,LED1_Flag);
}
unsigned char Time_Zheng(ds1302* date)
{
unsigned char hour,min,sec;
hour = date ->Hour;
min = date->Min;
sec = date->Sec;
if(min/10==0 && min%10==0 && sec/10==0 && sec%10==0)
{
return 1;
}
else return 0;
}
void Control_Mode(void)
{
if(Control_Flag == 0) //温度控制模式
{
Display_LED(2,1);
if(Temping > Temp_Para)
{
Show_Relay=1;
}
else Show_Relay=0;
}
else
{
Display_LED(2,0);
if(Time_Zheng(&Date))
{
Show_Relay=1; //计时5s
AUXR |= 0x10;
}
}
if(Show_Relay)
{
TR1 = 1;
}
else
{
TR1=0;
}
Display_Relay(Show_Relay);
}
void Timer2_Init(void) //20毫秒@12.000MHz
{
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0xE0; //设置定时初始值
T2H = 0xB1; //设置定时初始值
// AUXR |= 0x10; //定时器2开始计时
AUXR &= 0xef;
IE2 |= 0x04; //使能定时器2中断
// EA =1; //使能全局中断,确保定时器能正常中断
}
void Timer2(void) interrupt 12
{
count_5s++;
if(count_5s == 50)
{
count_5s=0;
Flag_5s++;
if(Flag_5s == 5)
{
Flag_5s=0;
Show_Relay=0;
LED1_Flag=0;
AUXR &= 0xef; //关闭定时器2
}
}
}
void Timer1(void) interrupt 3
{
count_100ms++;
if(count_100ms %10 == 0)
{
// TF1=0;
count_100ms =0;
Flag_100ms =~Flag_100ms;
if(Flag_100ms)
{
Display_LED(3,1);
}
else Display_LED(3,0);
TR1=0;
}
}
void Timer1_Init(void) //10毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0xF0; //设置定时初始值
TH1 = 0xD8; //设置定时初始值
TF1 = 0; //清除TF1标志
// TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
TR1=0;
EA =1;
}
由于主函数里面的函数比较多,所以我们先一个个函数来拆解来讲。
void Show_Switching(void)
这个显示函数是要一直显示的,所以我们要放在主函数里面一直循环,要注意,由于显示函数里面有数码管的存在,所以在这个函数里面最好不要放延时函数,如果真要放的话最好也不要超过10毫秒,不然数码管可能会一闪一闪或者显示的时候会有重影,导致被扣分。
由于我们有多个显示界面,所以我需要用一些标志位来判断当前我需要在哪个界面显示,要显示什么东西在上面,switching标志是用来检测当前处于哪个界面的标志位,然后我的Show_Time是为了处理当S17长按的情况的也就是
下面那个是另外写的的一个整点的检测函数,就是将我的时钟的分和秒都/10和%10,判断是否是整点,如果检测到了是整点的话,就会开启定时器2开始计时,这个定时器2下面再细讲。最后一行没什么好说的,就是一个LED的控制函数。
void Key_Board(void)
这个矩阵键盘的检测函数可能会稍稍有点麻烦,但是你们应该也能听懂的,在我们的矩阵的定义里面,我们的第一列,也就是从S4到S7的那一列开始,被定义位P44 P42 P35 P34。检测矩阵键盘哪个按键被按下的思想就是检测哪个按键变成0了,因为我们按键按下之后,他是会输出低电平的,所以我们要先将我们要检测的那一列置为0,随后把我们的P3的低四位置1,为什么要把我们的低四位置1呢,因为在我们的按键当中,其实我们每一列都是有高低位之分的,也就是说,我以S7到S4这一列为例,在矩阵键盘中,S7其实是作为我们P3在低四位当中的最低位的,然后我们的S4是在P3的低四位的最高位的,我们把P3的低四位置为1之后,我们就可以去检测哪个按键被按下了,本次这个题目之后用到高两位的按键,以及,P35和P34这两列的按键,所以我们处理起来也更方便了。在上文我们说到,我们不能频繁的变动我们的P3或者其他的,所以我们也要定义个Key来储存我们的P3,为什么我又把key&上0x0c呢,因为我们只用上了最高的两位,也就是0000 1100,所以我们只需要将最高两位置1就好了,其他位可以置0,避免干扰,我们可以用这个思想去检测矩阵键盘的所有位了,如果下次需要检测的是一列的按键,那么你只需要将Key&上0x0f就可以了。
在我们检测按键是否被按下的时候,我们需要进行一个消抖操作,因为你不知道我们的按键是否真的被按下了,我们需要进行一个延时,大约5ms左右就行了,而检测哪个按键被按下了也很简单,刚刚我们不是说过哪个按键被按下之后就会被置0的,所以如果我们的Key变成了0x04的话,就是我们当前这一列的第一个按键被按下了,以此类推,读者可自行去书写剩下的代码。如果我们按键是长按的话,又该如何处理呢,我们只需要将我们的Key重新初始化一边就可以了,但是要记得,我们长按的时候,我们需要持续运行的东西,比如说数码管显示什么的,都需要放到我们长按时候的操作里面,不然在我们长按的时候,我们的数码管就会消失不见的。
写完矩阵键盘按键之后,我们需要写的就是按键按下后的操作了,我们的S16按下之后是界面切换的,所以我们只需要将我们的switching操作一下就好了。S17是我们的控制模式的切换,我们控制模式的切换在下面写有代码,模式切换后需要的操作也在模式切换了有写到了。
剩下的代码逻辑也挺简单的我就不过多赘述了,我接下来要讲的是我们如何来设置我们的定时器来实现我们的操作。在我们这次的题目里,我们如果不使用定时器的话,我们的操作是无法完成的。
TImer1
我们的定时器也可以使用我们的软件来帮我们初始化,我们选定时器的时候,记得勾选定时器中断,要选12MH在和16位自动重载,定时器时钟也记得要选12T,如下所示
我们在配置定时器的时候,不能选延时太高的,不然不能生成出代码的,最好是生成10ms,20ms这些比较好,选这些整十的数字,到后面我们的延时计算也比较容易,那有人就要说了,我一次只能10ms,那我怎么能做到长时间的延时呢,这个问题就很简单了,我们只需要慢慢的把时间叠加上去就好了,比如说我要延时一秒钟再做处理,那么我只要弄一个标志位不断的计算就好了,它中断一次就++一次,当++到100的时候(这里我是按我初始化每次隔10ms就终端一次来计算的),也就是 10ms ×100=1000ms=1s,这时候再把标志位清零,再做1s的处理,这样就能做到用短时间来做到长延时了。
我们在使用定时器的时候,如果不是马上用到的话,可以先关闭掉定时器,比如像我一样,可以先把定时器1关闭,到后面需要计时的时候,再把定时器1开启来计时,这样我们就能实现计时多少秒后再处理了。有一个点需要注意,我们在使用定时器中断的时候,一定要把EA开启,因为EA是使能全局定时器中断的,如果你不将他开启的话,你是使用不到定时器中断的,这一点在我之前已经得到过教训了。。。。。
Timer2
定时器2的配置方式也和定时器1差不多的,就是在关闭定时器的时候可能不太一样,定时器2关闭定时器的时候,需要用这句话,AUXR &= 0xef;来关闭的,其他的和定时器1差不多
五、结语
讲了这么多了,如果读者你能够认真的看完我这篇文章,而且认真的看完了我讲的那些可能会踩的坑的话,我相信你肯定能够学会这个蓝桥杯单片机的,那些坑都是作者曾经踩过的坑,可能还有很多坑我还未踩过的,也请读者可以指出,我们共同学习,一起成长,最后祝愿各位看官蓝桥杯单片机能取得大捷,本单片机蓝桥杯系列出续集主要看作者心情,还有看看有没有这个时间。、
六、完整代码
main.c
#include <STC15F2K60S2.H>
#include "display.h"
#include "onewire.h"
#include "ds1302.h"
unsigned char i=0;
float Temping=0; //温度
ds1302 Date ={23,59,55};
unsigned char Flag_5s; //L10的5秒计时
unsigned char Temp_Para=23; //温度参数
unsigned char Switching=1; //界面切换
unsigned char count_5s;
unsigned char Show_Time=0;
unsigned char count_100ms,Flag_100ms;
unsigned char Para_Flag;
unsigned char Control_Flag=0; //控制模式 1为温度控制模式,0为时间控制模式
unsigned char Show_Relay=0;
unsigned char LED1_Flag=0;
void Key_Board(void);
void Show_Switching(void);
void Timer2_Init(void); //20毫秒@12.000MHz
void Control_Mode(void);
void Timer1_Init(void); //10毫秒@12.000MHz
unsigned char Time_Zheng(ds1302* date);
void main(void)
{
System_Init();
Write_Time(&Date);
Timer2_Init();
Timer1_Init();
while(1)
{
Control_Mode();
Key_Board();
Show_Switching();
}
}
void Key_Board(void)
{
unsigned char Key;
P35=0; P34=1; P3 |= 0x0f;
Key = P3; Key &= 0x0c;
if(Key != 0x0c)
{
Delayms(5);
if(Key != 0x0c)
{
switch(Key)
{
case 0x04 :Switching++;
if(Switching > 3) Switching=1;
break;
case 0x08 :Control_Flag=~Control_Flag;break;
}
}
}
while(Key != 0x0c)
{
Key = P3; Key &= 0x0c;
Show_Switching();
Control_Mode();
}
P34 =0;P35=1; P3 |= 0x0f;
Key =P3; Key &= 0x0c;
if(Key != 0x0c)
{
Delayms(5);
if(Key != 0x0c)
{
switch(Key)
{
case 0x04: if(Switching ==3) Temp_Para++;
if(Temp_Para >= 99) Temp_Para =10;
break;
case 0x08: if(Switching ==3) Temp_Para--;
if(Temp_Para <=10) Temp_Para =99;
break;
}
}
}
while(Key != 0x0c)
{
Key = P3; Key &= 0x0c;
Show_Switching();
Control_Mode();
if(Key == 0x08 && Switching==2)
{
Show_Time =1;
}
}
Show_Time=0;
}
void Show_Switching(void)
{
Temping = Read_Temp();
Date=Read_Time();
if(Show_Time !=1)
{
if(Switching == 1)//温度显示界面 U1
{
Display_Number(1,10);Display_Number(2,1);Display_Number(6,(unsigned char)Temping/100);
Display_FNumber(7,(unsigned char)Temping/10%10);Display_Number(8,(unsigned char)Temping%10);
}
if(Switching == 2)//时间显示界面 U2
{
Display_Number(1,10);Display_Number(2,2);Display_Number(4,Date.Hour/10);Display_Number(5,Date.Hour%10);
Display_Number(6,11);Display_Number(7,Date.Min/10);Display_Number(8,Date.Min%10);
}
if(Switching ==3)
{
Para_Flag=1;
Display_Number(1,10);Display_Number(2,3);Display_Number(7,Temp_Para/10);Display_Number(8,Temp_Para%10);
}
}
else
{
Display_Number(1,10);Display_Number(2,2);Display_Number(4,Date.Min/10);Display_Number(5,Date.Min%10);
Display_Number(6,11);Display_Number(7,Date.Sec/10);Display_Number(8,Date.Sec%10);
}
if(Time_Zheng(&Date))
{
LED1_Flag=1;//计时5s
AUXR |= 0x10;
}
Display_LED(1,LED1_Flag);
}
unsigned char Time_Zheng(ds1302* date)
{
unsigned char hour,min,sec;
hour = date ->Hour;
min = date->Min;
sec = date->Sec;
if(min/10==0 && min%10==0 && sec/10==0 && sec%10==0)
{
return 1;
}
else return 0;
}
void Control_Mode(void)
{
if(Control_Flag == 0) //温度控制模式
{
Display_LED(2,1);
if(Temping > Temp_Para)
{
Show_Relay=1;
}
else Show_Relay=0;
}
else
{
Display_LED(2,0);
if(Time_Zheng(&Date))
{
Show_Relay=1; //计时5s
AUXR |= 0x10;
}
}
if(Show_Relay)
{
TR1 = 1;
}
else
{
TR1=0;
}
Display_Relay(Show_Relay);
}
void Timer2_Init(void) //20毫秒@12.000MHz
{
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0xE0; //设置定时初始值
T2H = 0xB1; //设置定时初始值
// AUXR |= 0x10; //定时器2开始计时
AUXR &= 0xef;
IE2 |= 0x04; //使能定时器2中断
// EA =1; //使能全局中断,确保定时器能正常中断
}
void Timer2(void) interrupt 12
{
count_5s++;
if(count_5s == 50)
{
count_5s=0;
Flag_5s++;
if(Flag_5s == 5)
{
Flag_5s=0;
Show_Relay=0;
LED1_Flag=0;
AUXR &= 0xef; //关闭定时器2
}
}
}
void Timer1(void) interrupt 3
{
count_100ms++;
if(count_100ms %10 == 0)
{
// TF1=0;
count_100ms =0;
Flag_100ms =~Flag_100ms;
if(Flag_100ms)
{
Display_LED(3,1);
}
else Display_LED(3,0);
TR1=0;
}
}
void Timer1_Init(void) //10毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0xF0; //设置定时初始值
TH1 = 0xD8; //设置定时初始值
TF1 = 0; //清除TF1标志
// TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
TR1=0;
EA =1;
}
ds1302.c和ds1302.h
/* # DS1302代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "ds1302.h"
#include <STC15F2K60S2.H>
sbit SCK = P1^7;
sbit SDA = P2^3;
sbit RST = P1^3;
//
void Write_Ds1302(unsigned char temp)
{
unsigned char i;
for (i=0;i<8;i++)
{
SCK = 0;
SDA = temp&0x01;
temp>>=1;
SCK=1;
}
}
//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
Write_Ds1302(dat);
RST=0;
}
//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
unsigned char i,temp=0x00;
RST=0; _nop_();
SCK=0; _nop_();
RST=1; _nop_();
Write_Ds1302(address);
for (i=0;i<8;i++)
{
SCK=0;
temp>>=1;
if(SDA)
temp|=0x80;
SCK=1;
}
RST=0; _nop_();
SCK=0; _nop_();
SCK=1; _nop_();
SDA=0; _nop_();
SDA=1; _nop_();
return (temp);
}
#define WP 0x8e
#define W_Sec 0x80
#define R_Sec 0x81
#define W_Min 0x82
#define R_Min 0x83
#define W_Hour 0x84
#define R_Hour 0x85
void Write_Time(ds1302 *Date)
{
unsigned char hour,min,sec;
// ds1302 Date;
//转化为十六进制
hour = Date->Hour/10*16 + Date->Hour%10;
min = Date->Min/10*16 + Date->Min%10;
sec = Date->Sec/10*16 + Date->Sec%10;
Write_Ds1302_Byte(WP,0x00);
Write_Ds1302_Byte(W_Sec,sec);
Write_Ds1302_Byte(W_Min,min);
Write_Ds1302_Byte(W_Hour,hour);
Write_Ds1302_Byte(WP,0x00);
}
ds1302 Read_Time(void)
{
ds1302 Date;
unsigned hour,min,sec;
sec = Read_Ds1302_Byte(R_Sec);
min = Read_Ds1302_Byte(R_Min);
hour = Read_Ds1302_Byte(R_Hour);
Date.Sec=sec/16*10 + sec%16;
Date.Min=min/16*10 + min%16;
Date.Hour=hour/16*10 + hour%16;
return Date;
}
#ifndef __DS1302_H__
#define __DS1302_H__
#include "intrins.h"
#include <STC15F2K60S2.H>
typedef struct {
unsigned char Hour;
unsigned char Min;
unsigned char Sec;
}ds1302;
void Write_Time(ds1302 *Date);
ds1302 Read_Time(void);
#endif
onewire.c和onewire.h
/* # 单总线代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "onewire.h"
//
void Delay_OneWire(unsigned int t)
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
//
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
Delay_OneWire(1);
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
float Read_Temp(void)
{
unsigned char th,tl;
float temp;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
tl=Read_DS18B20();
th= Read_DS18B20();
temp = (th*256 + tl)*0.0625;
return temp;
}
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
#include <STC15F2K60S2.H>
#define DQ P14
float Read_Temp(void);
#endif
display.c和display.h
#include <STC15F2K60S2.H>
code unsigned char Number[]={
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, //0~9
0xc1, //U 10
0xbf //- 11
};
//0~9小数
code unsigned char FNumber[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
void Delayms(int ms) //@12.000MHz
{
unsigned char data i, j;
int k=0;
for(k=0;k<ms;k++)
{
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
void Select_HC573(unsigned char Number)
{
switch(Number)
{
case 4:P2=(P2 & 0x1f)|0x80; break; //LED
case 5:P2=(P2 & 0x1f)|0xA0; break; //蜂鸣器和继电器
case 6:P2=(P2 & 0x1f)|0xC0; break; //数码管位选
case 7:P2=(P2 & 0x1f)|0xE0; break; //段选
}
P2=(P2&0x1f)|0x00;
}
void Display_Number(unsigned char Pos,unsigned char Data)
{
P0 = 0x01<<Pos -1;
Select_HC573(6);
P0=Number[Data];
Select_HC573(7);
Delayms(1);
P0=0x01<<Pos -1;
Select_HC573(6);
P0=0xff;
Select_HC573(7);
}
void Display_FNumber(unsigned char Pos,unsigned char Data)
{
P0 = 0x01<<Pos -1;
Select_HC573(6);
P0=FNumber[Data];
Select_HC573(7);
Delayms(1); //延时过高会导致闪烁,延时要低一点
P0=0x01<<Pos -1;
Select_HC573(6);
P0=0xff;
Select_HC573(7);
}
void System_Init(void)
{
P0=0x00;
Select_HC573(5);
Select_HC573(6);
P0=0xff;
Select_HC573(4);
}
//LED开关,1为开,0为关
void Display_LED(unsigned char Pos,unsigned char ON_OFF)
{
unsigned char Temp=0xff;
if(ON_OFF)
{
switch(Pos)
{
case 1:Temp=Temp & 0xfe;break;
case 2:Temp=Temp & 0xfd;break;
case 3:Temp=Temp & 0xfb;break;
}
}
else
{
switch(Pos)
{
case 1:Temp=Temp | 0x01;break;
case 2:Temp=Temp | 0x02;break;
case 3:Temp=Temp | 0x04;break;
}
}
P0=Temp;
Select_HC573(4);
}
void Display_Relay(unsigned char ON_OFF)
{
unsigned char Temp=0xff;
if(ON_OFF)
{
Temp &= 0x10;
}
else
{
Temp &= 0x00;
}
P0 = Temp;
Select_HC573(5);
}
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
void System_Init(void);
void Delayms(int ms); //@12.000MHz
void Select_HC573(unsigned char Number);
void Display_Number(unsigned char Pos,unsigned char Data);
void Display_FNumber(unsigned char Pos,unsigned char Data);
void Display_LED(unsigned char Pos,unsigned char ON_OFF);
void Display_Relay(unsigned char ON_OFF);
#endif
标签:STC15F2K60S2,char,10,代码,unsigned,蓝桥,Write,nop,void
From: https://blog.csdn.net/m0_74713645/article/details/136639430