第一次大更新:删除了一部分冗余的代码,将代码部分改得更为规范了些。
——2020年2月23日
第二次更新:在C语言相关中增添了“数组”及“指针”的部分知识点。
——2020年3月18日
第三次大更新:在C语言相关中增添了“sprintf函数”的部分知识点;增添了运用sprintf函数完成数码管显示及左移操作的代码,增添了利用定时器中断完成数码管动态扫描的代码,增添了目录并对一些文章阅读的细节进行了优化,增添了“代码块阅读说明”;对代码块内一些有小bug的代码进行了修复。
——2020年3月30日
第四次大更新:在C语言相关中增添了“DS1302实时时钟”、“DS18B20温度测量”、“I2C协议通信”和“长时操作和短时操作”的相关知识点;增添了DS1302实时时钟和DS18B20温度测量的相关代码;对所有代码的注释进行了优化,更方便阅读;优化了字体大小。
——2020年4月22日
第五次小更新:更正了C语言相关中“DS1302部分”和“sprintf”函数部分的错误和不当的描述。
DS1302部分:
错误语句:DS1302的数据是以压缩BCD码的格式输出的(初始化数据的写入格式则是非压缩BCD码)
正确语句:DS1302的数据是以压缩BCD码的格式输出的(初始化数据的写入格式也是压缩BCD码)
sprintf函数部分:
错误语句:该函数为标准输入输出函数,包含于“stdio.h”头文件中,其作用是将输入数字格式化为字符形式输出至字符串中。
正确语句:该函数为标准输入输出函数,包含于“stdio.h”头文件中,不严谨地说,其作用是将输入数字格式化为字符形式输出至字符串中。
——2020年4月29日
第六次小更新:在C语言相关中增添了“PCF8591”芯片的相关知识点;简化了所有代码中键盘消抖的部分;将所有代码中“int main(void)…return 0;”形式改为了“void main(void)…”形式,消除了“unreachable code”的警告。
——2020年6月18日
第七次小更新:在C语言相关中修改了“sprintf”函数相关说明部分,增添了新的使用技巧,增添了一些解释和示例代码,更方便读者理解;修改了“循环左/右移”部分,增添了“intrins.h”头文件中循环左/右移函数的声明及使用说明,对一些细节部分进行了优化。
——2020年6月19日
第八次大更新:在C语言相关中增添了“AT24C02”芯片的相关知识点。
寄语:蓝桥杯省赛已结束,我就是在AT24C02上栽跟头的,没怎么练习这个模块,希望大家引以为戒,一定要把所有模块好好掌握,每个模块至少编程3遍才行!加油!
——2020年7月5日
注意:
当短接帽接至“IO”口时,使用的“IO模式”需要手动锁存地址。当短接帽接至“MM”口时,使用的“MM模式”可借助“XBYTE”命令(一定要大写)自动锁存地址。
数组计算元素的个数是从“1”开始计算的,但是,选择元素时数组则是从“0”开始计算。
例如:Example [ 3 ] = { a , b , c } ;
Example数组共有3个元素,其中“a”为第0个元素,“b”为第1个元素,“c”为第2个元素。
利用初始化器初始化:利用初始化器进行数组初始化:
int a[8]={[6]=12};//把数组a中的第6个元素初始化为12,其余都为0。
注意:①如果初始化器后面有更多的值,那么就那些值就会被初始化在其之后。②后面的初始化会覆盖前面的初始化。
int a[8]={1,2,[4]=7,7,7,[0]=3};//其结果是a[8]={3,2,0,0,7,7,7,0};
“*p”表示地址为p存储单元的内容;“p”表示地址;“&a”表示取a的地址;a为变量。
int *p;//初始化指针,但是该指针无指向地址!如果直接使用会有问题。
int *p=&a;//初始化指针,该形式只在定义时正确,表示以p指针所指向地址的值为变量a的值。
p=&a;//表示指针p所指向的地址就是a的地址。即地址间的赋值。
*p=a;//表示指针p所指向地址的内容就是内容a。
在C中,指针一定要初始化,指针加1指的是增加一个存储单元。对数组而言,这意味着把加1后的地址是下一个元素的地址,而不是下一个字节的地址,这是为什么必须声明指针所指向对象类型的原因之一。
该函数为标准输入输出函数,包含于“stdio.h”函数中,不严谨的说,其作用是将输入的量(可为数字,可为字符)转为字符形式输出至字符串中,其输出格式和语法等都与“printf”函数一样,故可利用此函数实现许多功能。
但在使用时,若输入的量为数字型,则需注意C51编译器和C语言编译器的区别,因C51编译器十分“节省”,故需要加一个“unsigned int”进行强制转换,详情可看之后代码部分的注释。
大概格式如下:
sprintf(字符串地址,“占位符(可加上其他字符)”,(unsigned int)需要转换数字);
示例代码如下:
unsigned char zfsz[9];//第九个字符为“\0”,用来存放由sprintf函数自动加上的字符串结束标志。
unsigned int num=5555;
sprintf(zfsz,”QWQ %d”,(unsigned int)num);
此时字符数组里的内容应如下:
zfsz={‘Q’,‘W’,‘Q’,‘ ’,‘5’,‘5’,‘5’,‘5’};
若输入的量不是数字,而是字符,则不需要加“unsigned int”强制转换,但需要注意占位符“%d”要改为“%c”。
大概格式如下:
sprintf(字符串地址,“%c(可加上其他字符)”,字符量);
示例代码如下:
unsigned char zfsz[9]; //第九个字符为“\0”,用来存放由sprintf函数自动加上的字符串结束标志。
unsigned char wenzi=’a’;
sprintf(zfsz,”^_^ …%c”,wenzi);
此时字符数组里的内容应如下:
zfsz={‘^’,‘_’,‘^’,‘ ’,‘.’,‘.’,‘.’,‘a’};
长时操作:如让灯亮1秒,灭1秒。元件动作的同时也在计时。工作模式类似于数码管动态扫描。格式:动作(时段1)→停止(时段2)→动作(时段1)→停止(时段2)…
例如:
//代码实现的功能是根据jg(间隔)的值让LED灯亮jg秒、灭jg秒。
//jg通过按键控制赋予其不同的值。
unsigned int cjsj;//采集时间参数。
unsigned char cjsjfz;//采集时间辅助参数。
void T0_Ser(void) interrupt 1
{
cjsj++;//开始计时。
if(jg==60)//因为60s的计算超出了65536的范围,因此另外处理。
{
if((cjsj<60000)&&(cjsjfz==0))
XBYTE[0x8000]=0;//LED灯亮。
else if((cjsj<60000)&&(cjsjfz==1))
XBYTE[0x8000]=0xff;//LED灯灭。
if(cjsj>=60000)
{
cjsj=0;//初始化cjsj。
cjsjfz++;//同时将cjsjfz加一,即让下次计时与上次计时区分开来。
}
if(cjsjfz==2)
cjsjfz=0;//初始化cjsjfz参数。
}
else if((jg==1)||(jg==5)||(jg==30))//如果jg不为60s,则用常用的方法处理即可。
{
if(cjsj<1000*jg)//前1秒亮灯。
XBYTE[0x8000]=0;
else if((cjsj>=1000*jg)&&(cjsj<2000*jg))//后一秒灭灯。
XBYTE[0x8000]=0xff;
else if(cjsj>=2000*jg)
cjsj=0;//初始化cjsj参数。
}
}
短时操作:如,每隔1s对温度进行一次采样。注意,采样这个动作的时间是极短的,可以看作为瞬时
的。先计时,元件再动作,且元件动作的时间极短,可看作元件动作时不计时。格式:计时→动作(时刻1)→计时→动作(时刻1)…
例如:
//代码实现的功能为每隔jg(间隔)秒,对环境温度进行一次采样并存储采样数据。
//jg通过按键控制赋予其不同的值。
void T0_Ser(void) interrupt 1 //T0中断服务函数。
{
if(zt==1)//进入采集温度状态,开始采集温度。
{
cjsj++;//当进入采集温度数据界面时,开始计时。
if((cjsj>=1000*jg)&&(wdgs<10))//当cjsj(采集时间)达到jg设置的时间时,执行以下操作。
{
wdhc[wdgs]=wd;//将温度数据传递给wdhc(温度缓存)数组。
cjsj=0;//cjsj清零,进行下一轮计时。
wdgs++;//wdhc数组地址序号wdgs(温度个数)加1,数组等待下一次赋值。
}
if(wdgs>=10)//当wdgs(温度数据个数)等于10个时,执行以下操作。
{
zt=2;//工作状态切换至状态2。
wdgs=0;//同时将wdgs初始化,方便进行下一轮测温。
}
}
}
总结,在对某个器件编写间断动作的代码时,一定要分清元件是进行长时动作还是短时动作,否则会出现辛辛苦苦编出来的代码并不符合要求的情况。且在编写代码时,一定要注意变量的类型大小,不可溢出。
循环左/右移功能,在keil 5自带的intrins.h头文件中有相关的函数,但在keil 5中无法找到其对相关函数的定义,着实可惜。
打开头文件,可看到如下声明:
extern unsigned char _cror_ (unsigned char, unsigned char);//8位循环右移
extern unsigned int _iror_ (unsigned int, unsigned char);//16位循环右移
extern unsigned long _lror_ (unsigned long, unsigned char);//32位循环右移
extern unsigned char _crol_ (unsigned char, unsigned char);//8位循环左移
extern unsigned int _irol_ (unsigned int, unsigned char);//16位循环左移
extern unsigned long _lrol_ (unsigned long, unsigned char);//32位循环左移
函数使用方式为(以_cror_为例):
_cror_(被移动的8位变量,需移动的位数);
而以下是我参照网上代码写的左/右移函数,在此给各位读者做参考,以方便理解intrins.h头文件里左/右移函数。
/*循环左移*/
unsigned rol(unsigned val, int size)
{
unsigned res = val << size;
res |= val >> (32 - size);
return res;
}
/*循环右移*/
unsigned ror(unsigned val, int size)
{
unsigned res = val >> size;
res |= val << (32 - size);
return res;
}
//其中的“32”为int的位数,若需修改数据类型,以此类推。
片选信号以一个极高的频率在不同数码管之间不断切换,通过利用人眼的暂留效应,让数码管“全部显示”。
工作的一般模式为:全灭→管1亮→全灭→管2亮… (发光二极管有余辉效应,因此要消隐全灭)
如此反复。消隐全灭不需要延迟时间。
注意:
在代码中,段选语句不需要延迟时间(若需加,一定要小于片选语句的延迟时间),但是,其一定嵌在“全灭”片选语句和“切换”片选语句之间。
例如:
for(i=0;i<8;i++)//循环显示全部数码管。
{
XBYTE[0xE000]=0xff;//段选信号初始化。
XBYTE[0xC000]=0;//片选信号初始化,片选信号的“全灭”语句。
XBYTE[0xE000]=smg_Dxdata[i];//段选信号的“切换”语句。
XBYTE[0xC000]=smg_Pxdata[i];//片选信号的“切换”语句。
Delay10us();“切换”片选语句的延迟时间。
}
根据数码管动态扫描的原理,每个i都对应着一片数码管,因此,可以根据i的值来选中数码管。当i为特定值时,再让数码管消隐,即可控制指定数码管消隐。同时再用另一个变量(如times)来控制数码管消隐的时间即可。
简而言之,就是使用if语句,将i和times相与作为条件(如(i==1)&&(times<125))
),判断是否需要进入数码管消隐语句块。
注意:
当指定数码管消隐时,别忘记其他未指定的数码管需要正常显示。具体请看代码中的注释。
例如:
for(i=0;i<8;i++)
{
XBYTE[0XE000]=0xff;//数码管消隐,段选信号。
XBYTE[0xC000]=0;//数码管消隐,片选信号。
XBYTE[0xE000]=smg_Dxdata[i];//段选信号不变,通过只改变片选信号让数码管闪烁。
if(times<125)//循环的次数,当小于125次时,选中的数码管消隐。
{
if((i>5)||(i<2)||((i==4)||(i==3)))//选中需要闪烁的数码管。
{
XBYTE[0xC000]=0;//消隐选中的数码管。
}
else
XBYTE[0xC000]=smg_Pxdata[i];//让其他数码管照常显示。
}
else
XBYTE[0xC000]=smg_Pxdata[i];//当大于125次时,所有数码管都照常显示。
Delay1ms();//数码管动态扫描延迟时间。
}
times++;//完成一次循环后,times加1。
if(times==250)//当times=250时,初始化times的值。
times=0;
按键按下去时有两个时间段存在抖动,分别在刚按下去的时候和松开按键的时候。所以,为了保证按键功能稳定,需要去两次抖动。
例如:
if(key==0)
{
Delay10ms;//刚按下去时进行延时防抖,第一次去抖动。
if(key==0)
{
segment1;//按下去后的相关功能模块。
while(!key);//此处括号里内容可替换为“key==0”。等待按键释放,或判断按键是否释放。
// Delay10ms;//刚松手时进行延时防抖,第二次去抖动。
// while(!key);//判断按键是否完全释放。
segment2;//按键释放之后的相关功能模块。
}
}
/*/while 循环的条件是值为1,而其中key的值为0,因此“while(key);
”语句等同于“while(0);
”,是不循环的。所以对key取非,值为1,可循环,即“while(!key)
”等同于“while(1)
”。补充,当括号内为式子时,式子成立则为1,不成立则为0。
被注释的语句在实际编程中可以省略,省略之后也可消抖,目前尚不清楚原因。在代码中,除“独立键盘(常用方法)”未省略,其他键盘程序代码均已省略。
可参照独立键盘的编程思路,进而推理出矩阵键盘的代码。矩阵键盘可分为行扫描和列扫描(当然二者差不多)。思路和独立键盘一样,独立键盘只有一列,而矩阵键盘有四列,因此只是需要加一个条件去判断到底是哪列(或哪行)的按键按下了。
简而言之,基本原理就是8个端口只让1个端口赋值为低电平,任一按键按下,一定会有另1个端口被拉为低电平。
代码过长且复杂,为方便理解,便不在此赘述,其相关说明在“实验代码”部分中以注释的形式写出。
DS1302可以显示日期、星期、时间等。在使用DS1302前,先要初始化,写入初始时间等数据并启动,然后再读出实时时间数据即可。在DS1302有关日历、时间的寄存器里,每个单位都有各自的读地址和写地址。进行相关操作时,都要先传输地址。此外,在DS1302中,还有一个写保护位,在对DS1302进行任何写操作之前,都必须要解锁写保护,操作完毕后也必须要重锁写保护。
当从DS1302中读出时钟数据时,务必注意,DS1302的数据是以压缩BCD码的格式输出的(初始化数据的写入格式也是压缩BCD码)。一般情况下,要对读出的数据进行解压和转换,通常将1个字节的数据解压为2个字节。当读地址顺序为时、分、秒时,转换压缩BCD码应秉承“时低秒高”“十低个高”的原则,即在数组中,“时”的数据应放在数组低地址上,“秒”的数据应放在数组的高地址上。在只对一个单位进行转换时(如:秒),应该将秒的十位放至低字节,个位应放至高字节。
例如:
void RTC_Init(void)//DS1302初始化函数。
{
Write_Ds1302(0x8e,0);//解锁写保护。
Write_Ds1302(0x84,0x23);//时。
Write_Ds1302(0x82,0x59);//分。
Write_Ds1302(0x80,0x50);//秒。
Write_Ds1302(0x8e,0x80);//锁定写保护。
}
void RTC_Read(void)//DS1302时间读取函数。
{
unsigned char rr1,rr2,temp;//rr1、rr2是循环参数,temp是临时缓存时间数据参数。
for(rr1=0;rr1<3;rr1++)//从DS1302中读取并转换时、分、秒时间数据。
{
/*当DS1302读地址的顺序为时、分、秒时,转换数据的原则为“时低秒高”“十低个高”,即在数组中,转换
顺序为时~秒,即时→低地址,秒→高地址;且在转换单个时间单位时,即十位→低字节,个位→高字节。*/
temp=Read_Ds1302(RTC_Adr[rr1]);//读取数据。
bcdzh[rr1*3]=(temp&0xf0)>>4;//转换十位。
bcdzh[rr1*3+1]=temp&0x0f;//转换个位。
}
for(rr2=0;rr2<8;rr2++)
{
//将时间数据输入zfhc(字符缓存)数组,进而显示至数码管上。
sprintf(zfhc+rr2,"%u",(unsigned int)bcdzh[rr2]);
}
}
DS18B20是一个单总线通信的温度测量芯片,其通信方式十分便捷。其可通过单总线获取电源,也可通过VDD引脚外接一个电源,更加详细的介绍请参看芯片手册。
DS18B20的事件序列为:初始化→写入命令:ROM命令(紧跟任何数据交换请求)→写入命令:功能命令(紧跟任何数据交换请求)。单总线上的所有事件都必须以初始化开始。
当从DS18B20中读出数据时,需要注意温度数据格式的转换,其读出的温度数据为2个字节,且首先读出的数据为温度的低字节数据,其次才是温度的高字节数据。虽然蓝桥杯官方会给出DS18B20的驱动程序,但用户仍要自己编写一部分驱动代码。
用户可通过配置暂存寄存器调整测温精度(即每一个二进制位代表的温度),可分别调为0.5℃、0.25℃、0.125℃、0.0625℃。DS18B20输出的温度数据格式为16进制,需转换为十进制数,再乘以分辨率才能得到温度实际值(负数是以补码形式输出,需要将输出的数据进行取反再+1的换算),转换时务必要分清浮点数和整型数!
必要时可采取“1元=100分”类似的方法来避免浮点数的计算。
例如:
//以下部分为所需编写的驱动代码。
unsigned int rd_temperature(void)//务必注意函数的类型。
{
unsigned char low,high;//分别用于存储温度数据的低字节和高字节。
unsigned int temperature;//最终返回值。
init_ds18b20();//初始化。
Write_DS18B20(0xcc);//ROM命令——跳过ROM。
Write_DS18B20(0x44);//对温度进行模数转换。
//Delay_OneWire(1000);//延时,等待转换完成,一般情况下可删除。
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);//读取RAM里温度暂存寄存器的数据。
low=Read_DS18B20();//低字节数据传输给low。
high=Read_DS18B20();//高字节数据传输给high。
temperature=high;//①整合。将2个字节的char类型数据整合成1个int类型的数据。
temperature<<=8;//②整合。
temperature|=low;//③整合。
return temperature;//将数据返回给上层函数。
}
//以下为测量转换温度的程序。
//方式一。
void get_Temperature(void)//获取实际温度数据函数。
{
unsigned int rt;//rt(real temperature)用于存放实际的温度。
//从ds18b20中读取温度数据,保留2位小数,因室内温度一般都高于0度,故此处不判断温度的正负。
rt=rd_temperature();
sprintf(zfhc,"%4.2f",rt/16.0);//除“16.0”就是乘以0.0625,“.0”一定要加!
}
//方式二。采取了“1元=100分”类似的方法进行温度转换。
void get_Temperature(void)//获取实际温度数据函数。
{
unsigned int rt;//rt(real temperature)用于存放实际的温度。
//从ds18b20中读取温度数据,保留2位小数,因室内温度一般都高于0度,故此处不判断温度的正负。
rt=rd_temperature()*0.0625*100;
sprintf(zfhc,"%8u",(unsigned int)rt);//加“8”是为了让数码管显示的字符靠右对齐。
}
写通信格式:
单字节:主机发送起始位→主机发送元件片选写字节信号→等待从机回应→主机发送写地址→等待从机回应→主机发送第一个字节→等待从机回应→主机发送终止位
多字节:主机发送起始位→主机发送元件片选写字节信号→等待从机回应→主机发送写地址→等待从机回应→主机发送第一个字节→等待从机回应→主机发送第二个字节→等待从机回应→…→主机发送终止位
读通信格式:
单字节:主机发送起始位→主机发送元件片选写字节信号→等待从机回应→主机发送读地址→等待从机回应→主机发送起始位→主机发送元件片选读字节信号→等待从机回应→主机接收第一个字节→主机不响应从机→主机发送终止位
多字节:主机发送起始位→主机发送元件片选写字节信号→等待从机回应→主机发送读地址→等待从机回应→主机发送起始位→主机发送元件片选读字节信号→等待从机回应→主机接收第一个字节→主机响应从机→主机接收第二个字节→主机响应从机→…→主机不响应从机→主机发送停止位
注意:①请尽量将I2C通信程序放入中断服务程序中,否则中断会干扰I2C协议的通信!
②若无法将I2C通信程序放入中断服务程序中,请务必要在I2C协议通信时关闭中断,以尽可能减小中断对I2C协议的影响。③有的使用I2C协议的芯片需要一定时间的缓冲,请务必在芯片缓冲完毕后再进行下一轮通信。如AT24C02芯片,每次写一字节需5ms的时间,这5ms即为缓冲时间。
PCF8591是一个兼A/D、D/A转换功能为一体的8位精度的芯片,其最多可接8个同类芯片。PCF8591的A/D转换原理为逐次逼近式,具有4个模拟量输入通道(可通过编程选择单端或差分输入)。其与单片机的交流方式为I2C通信,A/D转换后的数字量输出以及D/A转换时的数字量输入,均通过I2C通信传输。
更多详细资料请参看数据手册。
使用PCF8591的步骤可简要概括如下:①发送PCF8591片选信号字。②发送控制字。③(D/A转换)发送数字量;(A/D转换)接收数字量。以上步骤为简略步骤,具体使用时,务必注意I2C协议的通信特点。
片选信号字:该字节分为固定部分和可编程部分,高4位为固定部分(1001),低四位为可编程部分,其中,D0为读/写位,D1、D2、D3位为芯片片选信号位(根据外部硬件A0、A1、A2接线图来确定),一般情况下,若只有一个PCF8591,A0、A1、A2直接接地,故D1、D2、D3位都为0。
控制字:发送到PCF8591的第二个字节将被存储到控制寄存器,用于控制器件功能。D7和D3位为固定位,恒为0;D6位为D/A和A/D转换功能的选择位,0为A/D转换,1为D/A转换;D5和D4位为模拟量四种输入方式的选择位;D2位为通道自动增加位,为1,芯片每次A/D转换后自动将通道号+1,选择下一个通道,为0则恒保持一个通道;D1和D0位则为通道输出选择位。
D/A转换输入字:发送给PCF8591的第三个字节将被存储至DAC数据寄存器,并使用片上D/A转换器转换成对应的模拟电压。
例如:
void adzh(void)
{
IIC_Start();//主机发送起始位。
IIC_SendByte(0x90);//主机发送PCF8591芯片片选及写信号。
IIC_WaitAck();//等待从机响应。
IIC_SendByte(0x03);//主机发送控制字,模式:D/A转换,输入方式单端输入,输出通道3。
IIC_WaitAck();//等待从机响应。
IIC_Start();//主机发送起始位。
IIC_SendByte(0x91);//主机发送PCF8591芯片片选及读信号。
IIC_WaitAck();等待从机响应。
v=IIC_RecByte();//主机接收电压数字量数据。
IIC_SendAck(1);//主机发送“无应答”信号。
IIC_Stop();//结束通信。
v=(5*v/256)*100;//换算成实际电压的100倍,方便显示。
sprintf(zfhc,"u%3d",(u16)v);//将电压输入字符缓存数组,用于数码管显示。
}
AT24C02的使用方法类似于PCF8591,因为它们都是采用I2C通信协议。
AT24C02提供2048位串行电可擦除和可编程只读存储器(EEPROM),其大小为256个字节,每个字节8位。 该装置低功耗和低压操作应用于各种场合。通过两线串行接口访问,使用I2C与单片机通信。其还具有一个写保护引脚,当该引脚接高电平时,禁止对AT24C02进行写操作,低电平则允许写操作。更多详细资料请参看数据手册。
其有“字节写”和“页写”两种写入方式和“立即地址读”、“选择性读”和“连续读”三种读取方式。通过I2C是否应答来区分它们。
其与单片机通信过程可简要概括如下:①发送AT24C02芯片片选信号字。②发送地址。③写入数据/读出数据。
片选信号字:该字节分为固定部分和可编程部分,高4位为固定部分(1010),低四位为可编程部分,其中,D0为读/写位(低电平写,高电平读),D1、D2、D3位为芯片片选信号位(根据外部硬件A0、A1、A2接线图来确定),一般情况下,若只有一个AT24C02,A0、A1、A2直接接地,故D1、D2、D3位都为0。
写操作:不管是“字节写”还是“页写”,都需要传输一个存储地址给AT24C02,不同的是,对于“页写”方式来说,该地址为首地址。传输完地址后,在传输数据时,“字节写”在传输完一个字节后就等待从机回应并发送停止通信信号
,而“页写”则在等待从机回应后再发送数据,每回应一次就再发送一个字节数据给AT24C02,最多可发送16字节数据,每次传输前从机都会把之前的地址+1,以接收新数据。
示例代码:
//字节写
void write_E2(void)//写入存储函数。
{
IIC_Start();
IIC_SendByte(0xa0);//选中AT24C02并选择写模式。
IIC_WaitAck();
IIC_SendByte(0);//输入写地址0x00。
IIC_WaitAck();
IIC_SendByte(0x34);//写入数据。
IIC_WaitAck();
IIC_Stop();
// Delayms(5);//可省略,写入至少需要5毫秒的时间。
}
//页写
void write_E2(void)//写入存储函数。
{
IIC_Start();
IIC_SendByte(0xa0);//选中AT24C02并选择写模式。
IIC_WaitAck();
IIC_SendByte(0);//输入写地址0x00。
IIC_WaitAck();
IIC_SendByte(0x34);//写入第1个数据。
IIC_WaitAck();
IIC_SendByte(0x35);//写入第2个数据。
IIC_WaitAck();
……
IIC_Stop();
// Delayms(5);//可省略,写入至少需要5毫秒的时间。
}
读操作:“立即地址读”时,不需要输入地址,直接读取1字节数据即可,地址默认为上次读/写操作的地址。“选择性读”时,则和PCF8591一样,需先写入地址,再读出1字节数据。“连续读”则是读取完一个字节后,主机发送一个“0”应答信号告诉从机需要更多数据,AT24C02则将地址+1,继续向主机发送数据,当地址达到最大后,AT24C02将会回到0地址,继续发送数据。需要注意的是,凡是读操作,在结束时,主机都需发送一个“1”不应答信号,再发送终止传输信号
。
示例代码:
//立即地址读
void read_E2(void)//读取存储函数。
{
u8 tem;
IIC_Start();
IIC_SendByte(0xa1);//选中AT24C02并选择读模式。
IIC_WaitAck();
tem=IIC_RecByte();//读取设定的参数值。
IIC_SendAck(1);
IIC_Stop();
}
//选择性读
void read_E2(void)//读取存储函数。
{
u8 tem;
IIC_Start();
IIC_SendByte(0xa0);//选中AT24C02并选择写模式。
IIC_WaitAck();
IIC_SendByte(0);//输入读地址0x00。
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xa1);//选中AT24C02并选择读模式。
IIC_WaitAck();
tem=IIC_RecByte();//读取设定的参数值。
IIC_SendAck(1);
IIC_Stop();
}
//连续读
void read_E2(void)//读取存储函数。
{
u8 sz[8];
IIC_Start();
IIC_SendByte(0xa0);//选中AT24C02并选择写模式。
IIC_WaitAck();
IIC_SendByte(0);//输入读地址0x00。
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xa1);//选中AT24C02并选择读模式。
IIC_WaitAck();
sz[0]=IIC_RecByte();//读取设定的参数值。
IIC_SendAck(0);
sz[1]=IIC_RecByte();//读取设定的参数值。
IIC_SendAck(0);
sz[2]=IIC_RecByte();//读取设定的参数值。
IIC_SendAck(0);
…
IIC_SendAck(1);
IIC_Stop();
}
一、下载代码时,请务必看清代码中单片机的时钟频率为多少,以免程序运行出错。
二、因笔记在不同时间段分多次完成,故有些代码中功能相同的函数会有一些区别。
三、在按键代码中,给P3口赋值以及“key”的赋值有两种写法:①“P3=0x0f”“key=P3”。②“P3|=0x0f”“key=(P3&0x0f)”。在此说明一下,②的写法是更为谨慎的写法,其使用了位运算符号“|=”,尽量保证了P3口的初始状态不变,防止在按键与其他元件共同使用该口时,因P3口状态改变而导致元件使用出错。但①的写法更方便读者理解按键消抖的原理,便未修改。
//实验效果:所有LED灯同时点亮,并以每100毫秒亮一次的频率不断闪烁。
#include
#include
void buzz_clr(void);//声明函数。
void led_l(void);
void Delay100ms(void);
void main(void)
{
buzz_clr();//让蜂鸣器不动作。
while(1)
{
led_l();//LED灯闪烁程序。
}
}
void buzz_clr(void)//函数定义。
{
P2=(P2&0x1F|0xA0);//先初始化再置数,通过38译码器选定元器件对应的锁存器。
P0=0;//控制信号的数据传输。
P&=0x1f;//信号锁存。
}
void led_l(void)
{
P2=(P2&0x1F|0x80);
P0=0; //所有LED灯亮。
Delay100ms();//延时100毫秒。
P0=0xFF;//所有LED灯灭。
Delay100ms();
P2&=0x1f;
}
void Delay100ms(void) //单片机的内部时钟频率为@11.0592MHz。
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 5;
j = 52;
k = 195;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//实验预期效果:8个led灯先依次点亮。
#include
#include
void buzz_clr(void);//声明函数。
void led_l(void);
void Delay100ms(void);
unsigned char led_data1[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//LED依次点亮的信号数组。
void main(void)
{
while(1)//该循环是不断重复整个程序的循环。
{
buzz_clr();//让蜂鸣器不动作。
led_l();//LED灯闪烁程序。
}
}
void buzz_clr(void)//函数定义。
{
P2=(P2&0x1f|0xa0);//先初始化再置数,通过38译码器选定元器件对应的锁存器。
P0=0;//控制信号的数据传输。
P&=0x1f;//信号锁存。
}
void led_l(void)
{
int i;
for(i=0;i<8;i++)
{
P2=(P2&0x1F|0x80);
P0=led_data1[i];//输入数组里的控制信号数据。
Delay100ms();
P2&=0x1f;
}
}
void Delay100ms(void)//单片机的内部时钟频率为@11.0592MHz。
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 5;
j = 52;
k = 195;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
/*实验预期效果:8个led灯先依次点亮,然后再分别2个、3个、4个、5个、6个、7个依次点亮;接着,再4个4个交
替闪烁5次,最后全部闪烁5次。*/
#include
#include
void buzz_clr(void);//声明函数。
void led_l(void);
void Delay100ms(void);
unsigned char led_data1[36]={ //流水灯依次点亮的信号数组。
0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0x3f,
0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x1f,0x8f,0xc7,
0xe3,0xf1,0xf8,0x0f,0x87,0xc3,0xe1,0xf0,0x07,
0x83,0xc1,0xe0,0x03,0x81,0xc0,0x01,0x80,0x00};
unsigned char led_data2[2]={0x0f,0xf0};
void main(void)
{
buzz_clr();//让蜂鸣器不动作。
while(1)//该循环是不断重复整个程序的循环。
{
led_l();//LED灯闪烁程序。
}
}
void buzz_clr(void)//函数定义。
{
P2=(P2&0x1F|0xA0);//先初始化再置数,通过38译码器选定元器件对应的锁存器。
P0=0;//控制信号的数据传输。
P&=0x1f;//信号锁存。
}
void led_l(void)
{
int i=1,j=0,k;
for(i=0;i<37;i++)
{
P2=(P2&0x1F|0x80);
P0=led_data1[i];//输入数组里的控制信号数据。
Delay100ms();
P2&=0x1f;
}
for(k=0;k<5;k++)
{
for(i=0;i<2;i++)
{
P2=(P2&0x1F|0x80);
P0=led_data2[i];
Delay100ms();
P2&=0x1f;
}
}
for(k=0;k<5;k++)
{
P2=(P2&0x1F|0x80);
P0=0;
Delay100ms();
P0=0xff;
Delay100ms();
P2&=0x1f;
}
}
void Delay100ms(void) //单片机的内部时钟频率为@11.0592MHz。
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 5;
j = 52;
k = 195;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
/*实验预期效果:8个led灯先依次点亮,然后再分别2个、3个、4个、5个、6个、7个依次点亮;接着,再4个4个交
替闪烁5次,最后全部闪烁5次。*/
//该实验采用了部分地址译码的方式选中锁存器,故在下载程序至单片机上之前,短接帽J13需要短接至MM位置。
#include
#include
#include
void buzz_clr(void);//声明函数。
void led_l(void);
void Delay100ms(void);
unsigned char led_data1[36]={ //流水灯依次点亮的信号数组。
0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0x3f,
0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x1f,0x8f,0xc7,
0xe3,0xf1,0xf8,0x0f,0x87,0xc3,0xe1,0xf0,0x07,
0x83,0xc1,0xe0,0x03,0x81,0xc0,0x01,0x80,0x00};
unsigned char led_data2[2]={0x0f,0xf0};
void main(void)
{
buzz_clr();//让蜂鸣器不动作。
while(1)//该循环是不断重复整个程序的循环。
{
led_l();//LED灯闪烁程序。
}
}
void buzz_clr(void)//函数定义。
{
XBYTE[0xA000]=0;
}
void led_l(void)
{
int i=1,j=0,k;
for(i=0;i<37;i++)
{
XBYTE[0x8000]=led_data1[i];//输入数组里的控制信号数据。
Delay100ms();
}
for(k=0;k<5;k++)
{
for(i=0;i<2;i++)
{
XBYTE[0x8000]=led_data2[i];//输入数组里的控制信号数据。
Delay100ms();
}
}
for(k=0;k<5;k++)
{
XBYTE[0x8000]=0;
Delay100ms();
XBYTE[0x8000]=0xff;
Delay100ms();
}
}
void Delay100ms(void) //单片机的内部时钟频率为@11.0592MHz。
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 5;
j = 52;
k = 195;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//实验预期效果:LED灯逐颗逐颗点亮
//利用循环移位功能实现。
#include
#include
#include
void buzz(void);
void led(void);
void Delay500ms(void);
unsigned char rol(unsigned name,char times);
void main(void)
{
while(1)
{
buzz();
led();
}
}
void buzz(void)
{
XBYTE[0xA000]=0;
}
void led(void)
{
int i;
for(i=0;i<8;i++)
{
XBYTE[0x8000]=rol(0xfe,i);
Delay500ms();
}
}
unsigned char rol(unsigned name,char times)//循环左移函数
{
char res;
res=name<<times;
res|=name>>(8-times);
return res;
}
void Delay500ms(void) //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 22;
j = 3;
k = 227;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//实验预期效果:数码管从左至右显示76543210。
#include
#include
#include
unsigned char smg_Pxdata[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
unsigned char smg_Dxdata[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void buzz(void);
void Delayus(unsigned char us);
void smg(void);
void main(void)
{
while(1)
{
buzz();
smg();
}
}
void buzz(void)
{
XBYTE[0xA000]=0;
}
void smg(void)
{
int i;
for(i=0;i<8;i++)
{
XBYTE[0xE000]=0xff;
XBYTE[0xC000]=0;
XBYTE[0xE000]=smg_Dxdata[i];
XBYTE[0xC000]=smg_Pxdata[i];
Delayus(10);
}
}
void Delayus(unsigned char us) //@11.0592MHz
{
while(us)
{
_nop_();
_nop_();
_nop_();
us--;
}
}
//实验预期效果:数码管上从左至右第1、2、4、5、7、8片数码管以每秒一次的频率闪烁。
#include
#include
#include
void Delay1ms(void);
unsigned char smg_Pxdata[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
unsigned char smg_Dxdata[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void smg(void);
unsigned char times=0;//times为循环的次数,用于控制数码管的闪烁频率。
void main(void)
{
XBYTE[0xA000]=0;//初始化P0口所有元器件。
while(1)
{
smg();
}
}
void smg(void)
{
int i;
for(i=0;i<8;i++)
{
XBYTE[0XE000]=0xff;//数码管消隐,段选信号。
XBYTE[0xC000]=0;//数码管消隐,片选信号。
XBYTE[0xE000]=smg_Dxdata[i];//段选信号不变,通过只改变片选信号让数码管闪烁。
if(times<125)//循环的次数,当小于125次时,选中的数码管消隐。
{
if((i>5)||(i<2)||((i==4)||(i==3)))//选中需要闪烁的数码管。
{
XBYTE[0xC000]=0;//消隐选中的数码管。
}
else
XBYTE[0xC000]=smg_Pxdata[i];//让其他数码管照常显示。
}
else
XBYTE[0xC000]=smg_Pxdata[i];//当大于125次时,所有数码管都照常显示。
Delay1ms();//数码管动态扫描延迟时间。
}
times++;//完成一次循环后,times加1。
if(times==250)//当times=250时,初始化times的值。
times=0;
}
void Delay1ms(void) //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
/*实验预期功能:数码管默认熄灭,按下S7键,最左边的数码管开始显示并+1,松开S7键,则显示0;按下S6键,数
码管开始-1,松开S6键,则会显示1。*/
#include
#include
#include
unsigned char smg_Pxdata[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
unsigned char smg_Dxdata[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void buzz(void);
void button(void);
void smg(unsigned char tem);
sbit s7=P3^0;
sbit s6=P3^1;
void Delayms(unsigned char ms);
void Delayus(unsigned char us);
unsigned char btdb1,btdb2,smgct;
void main(void)
{
while(1)
{
buzz();
button();
}
}
void buzz(void)
{
XBYTE[0xA000]=0;
}
void smg(unsigned char tem)
{
XBYTE[0xE000]=0xff;
XBYTE[0xC000]=0;
XBYTE[0xE000]=smg_Dxdata[tem];
XBYTE[0xC000]=smg_Pxdata[7];
Delayus(10);
}
void button(void)
{
P3=0xff;
if(s7==0)
{
Delayms(10);
if(s7==0)
{
smg(smgct+1);
smgct=smgct+1;
while(!s7);
Delayms(10);
while(!s7);
smg(0);
}
}
if(s6==0)
{
Delayms(10);
if(s6==0)
{
smg(smgct-1);
smgct=smgct-1;
while(!s6);
Delayms(10);
while(!s6);
smg(1);
}
}
}
void Delayms(unsigned char ms) //@11.0592MHz
{
unsigned char i, j;
while(ms)
{
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
}
while (--i);
ms--;
}
}
void Delayus(unsigned char us) //@11.0592MHz
{
while(us)
{
_nop_();
_nop_();
_nop_();
us--;
}
}
/*实验预期效果:依次按下S4,分别会出现:①LED全亮;②LED全灭;③蜂鸣器响;④继电器吸合;⑤蜂鸣器关闭;
⑥继电器关闭;⑦回到初始状态。按下S5,回到初始状态。S6、S7无功能。*/
//这是独立键盘的另外一种编程方式,该方式与矩阵键盘的方式相似。
#include
#include
#include
void Delay5ms(void);
unsigned char led_Data[2]={0xff,0x00};//LED控制数据。
void P0ctrl(unsigned char temp);
void button(void);
void s4ctrl(void);
unsigned char i=0x00,mtfn;//i为P0口的控制变量,mtfn(mutifunction)为按键函数的输出变量。
void main(void)
{
XBYTE[0xA000]=0;//初始化P0口所有元器件。
while(1)
{
button();
s4ctrl();
}
}
void P0ctrl(unsigned char temp)//P0口的控制函数。
{
if(temp==0)
i=i&0xbf;//关闭蜂鸣器。
else if(temp==1)
i=i|0x40;//打开蜂鸣器。
else if(temp==2)
i=i&0xef;//关闭继电器。
else if(temp==3)
i=i|0x10;//打开继电器。
XBYTE[0xA000]=i;
}
void led(unsigned char teml)//led控制函数。
{
XBYTE[0x8000]=led_Data[teml];
}
void button(void)//按键控制函数。
{
unsigned char key;
P3=0x0f;//给P3口赋予初值。
key=P3;//用变量获取实际P3口的实际值,防止P3口的实际值因受影响而改变。
if(key!=0x0f)//第一次消抖开始,消除前沿抖动,即按下抖动。
{
Delay5ms();
if(key!=0x0f)
{//第一次消抖结束。
switch(key)
{
case 0x0e:break;
case 0x0d:break;
case 0x0b:mtfn=0;break;//S5设置为复位按键。
case 0x07:mtfn++;break;//S4每按下一次,功能就切换一次。
}
while(key!=0x0f)//第二次消抖开始,消除后沿抖动,即松手抖动。
{
Delay5ms();
key=P3;
}//第二次消抖结束。
}
}
if (mtfn==7)//设置循环,让功能复位。
mtfn=0;
}
void s4ctrl(void)//S4按键功能切换函数。
{
if(mtfn==0)
{
led(0);
XBYTE[0xA000]=0;
}
else if(mtfn==1)
led(1);
else if(mtfn==2)
led(0);
else if(mtfn==3)
P0ctrl(1);
else if(mtfn==4)
P0ctrl(3);
else if(mtfn==5)
P0ctrl(0);
else if(mtfn==6)
P0ctrl(2);
}
void Delay5ms(void) //单片机内部晶振频率@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
/*实验预期效果:依次按下S6,从左至右,可以分别让第1、2片,第4、5片,第7、8片数码管闪烁;第四次按下,
数码管正常显示。*/
//这是独立键盘的另外一种编程方式,该方式与矩阵键盘的方式相似。
#include
#include
#include
void Delayms(unsigned char ms);
unsigned char smg_Pxdata[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
unsigned char smg_Dxdata[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
void button(void);
void smg(void);
unsigned char times,temqh;/*times(次数)为控制数码管闪烁频率的变量,temqh(temp切换)为切换闪烁
数码管的变量。*/
void main(void)
{
XBYTE[0xA000]=0;//初始化P0口所有元器件。
while(1)
{
button();
smg();
}
}
void smg(void)
{
int i;
for(i=0;i<8;i++)
{
XBYTE[0XE000]=0xff;
XBYTE[0xC000]=0;
XBYTE[0xE000]=smg_Dxdata[i];
if(times<125)//将消隐变量单独提取出来,作为一个大前提。
{
//让i和tempqh配对,进而通过给予tempqh不同的值来选中不同的数码管。
if(((i>5)&&(temqh==1))||(((i==4)||(i==3))&&(temqh==2))||((i<2)&&(temqh==3)))
{
XBYTE[0xC000]=0;//消隐选中的数码管。
}
else
XBYTE[0xC000]=smg_Pxdata[i];//其他未选中的数码管正常显示。
}
else
XBYTE[0xC000]=smg_Pxdata[i];//当times>=125时,所有数码管正常显示。
Delayms(1);
}
times++;//循环完一次,times+1。
if(times==250)//当times=250时,初始化times的值。
times=0;//当times=0时,所有数码管均正常显示。
}
void button(void)//按键控制函数。
{
unsigned char key;
P3=0x0f;//给P3口赋予初值。
key=P3;//用变量获取实际P3口的实际值,防止P3口的实际值因受影响而改变。
if(key!=0x0f)//第一次消抖开始,消除前沿抖动,即按下抖动。
{
Delayms(5);
if(key!=0x0f)
{//第一次消抖结束。
switch(key)
{
case 0x0e:break;
case 0x0d:temqh++;break;//设置S6的功能为在不同数码管之间切换。
case 0x0b:;break;
case 0x07:;break;
}
while(key!=0x0f)//第二次消抖开始,消除后沿抖动,即松手抖动。
{
Delayms(5);
key=P3;
}//第二次消抖结束。
}
}
if(temqh==4)
temqh=0;
}
void Delayms(unsigned char ms) //@11.0592MHz
{
unsigned char i, j;
while(ms)
{
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
}
while (--i);
ms--;
}
}
//实验预期效果:依次按下S4~S19,倒数第四个数码管会依次显示0~F。
//不足:除S4~S7外,剩下的按键在按下时都会出现消隐的情况。有时会出现按键与数码管的显示值不匹配的情况。
//注意:J5的短接帽一定要接到1、2口,即左边和中间。
#include
#include
#include
unsigned char smg_Pxdata[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
unsigned char smg_Dxdata[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
void buzz(void);
void button(void);
void smg(unsigned char tem);
void Delay5ms(void);
void Delay10us(void);
unsigned char num;
void main(void)
{
while(1)
{
buzz();
button();
smg(num);
}
}
void buzz(void)
{
XBYTE[0xA000]=0;
}
void smg(unsigned char tem)
{
XBYTE[0XE000]=0Xff;
XBYTE[0xC000]=0;
XBYTE[0xE000]=smg_Dxdata[tem];
XBYTE[0xC000]=smg_Pxdata[3];
Delay10us();
}
void button(void)
{
unsigned char key;
P44=0,P42=1,P3=0x7f; //8个端口只让1个端口赋值为低电平。
key=P3;//获取实际P3口的值
key=key&0x0f;//相与,方便之后的判断。
if(key!=0x0f)//第一次消抖开始,消除前沿抖动,即按下抖动。
{
Delay5ms();
if(key!=0x0f)
{
key=P3;//第一次消抖结束。
switch(key)//第一列按键开始判断。根据Key的值,赋予num不同的值。
{
case 0x7e:num=3;break;
case 0x7d:num=2;break;
case 0x7b:num=1;break;
case 0x77:num=0;break;
}
while(key!=0x0f)//第二次消抖开始,消除后沿抖动,即松手抖动。
{
Delay5ms();
key=P3;
key=key&0x0f;
}//第二次消抖结束。
}
}
P44=1,P42=0,P3=0xbf;
key=P3;
key=key&0x0f;
if(key!=0x0f)
{
Delay5ms();
if(key!=0x0f)
{
key=P3;
switch(key)//第二列按键开始判断。
{
case 0xbe:num=7;break;
case 0xbd:num=6;break;
case 0xbb:num=5;break;
case 0xb7:num=4;break;
}
while(key!=0x0f)
{
Delay5ms();
key=P3;
key=key&0x0f;
}
}
}
P44=1,P42=1,P3=0xdf;
key=P3;
key=key&0x0f;
if(key!=0x0f)
{
Delay5ms();
if(key!=0x0f)
{
key=P3;
switch(key)//第三列按键开始判断。
{
case 0xde:num=11;break;
case 0xdd:num=10;break;
case 0xdb:num=9;break;
case 0xd7:num=8;break;
}
while(key!=0x0f)
{
Delay5ms();
key=P3;
key=key&0x0f;
}
}
}
P44=1,P42=1,P3=0xef;
key=P3;
key=key&0x0f;
if(key!=0x0f)
{
Delay5ms();
if(key!=0x0f)
{
key=P3;
switch(key)//第四列按键开始判断。
{
case 0xee:num=15;break;
case 0xed:num=14;break;
case 0xeb:num=13;break;
case 0xe7:num=12;break;
}
while(key!=0x0f)
{
Delay5ms();
key=P3;
key=key&0x0f;
}
}
}
}
void Delay5ms(void) //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay10us(void) //@11.0592MHz
{
unsigned char i;
_nop_();
i = 25;
while (--i);
}
//实验预期效果:依次按下S4~S19,倒数第四个数码管会依次显示0~F。
//不足:所有按键在按下时那一刹那都会出现消隐的情况,若长按,数码管一直保持消隐状态,直至松手。
//注意:J5的短接帽一定要接到1、2口,即左边和中间。
#include
#include
#include
void Delay5ms(void);
void Delay10us(void);
//↓数码管片选数据,共有八片,为共阳极数码管。
unsigned char smg_Pxdata[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
//↓数码管段选数据,依次为0~f。
unsigned char smg_Dxdata[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
void buzz(void);
void button(void);
void smg(unsigned char tem);
unsigned char num;//全局变量,使用于按键函数和数码管显示函数中。
void main(void)
{
while(1)
{
buzz();
button();
smg(num);
}
}
void buzz(void)
{
XBYTE[0xA000]=0;//关闭蜂鸣器。
}
void smg(unsigned char tem)//数码管显示程序,和按键函数结合使用,用于判断按下哪个键。
{
XBYTE[0xE000]=0xff;
XBYTE[0xC000]=0;//片选信号——消隐。
XBYTE[0xE000]=smg_Dxdata[tem];//段选信号。
XBYTE[0XC000]=smg_Pxdata[3];//片选信号——始终显示一片数码管。
Delay10us();
}
void button(void)//P3.0~P3.3为行线,P3.4、P3.5、P4.2、P4.4为列线。
{
unsigned int key;
P44=0,P42=1,P3=0x7f;/*所有8个端口中,只令1个端口为低电平,若1个按键按下,那么肯定有另外1个端口
会被拉低。因此,通过扫描就可以知道是哪个按键按下了。*/
key=P3&0x0f;//和0x0f相与,让高四位在任何情况下始终为0000,即让case后的值始终是0x0****形式。
P44=1,P42=0,P3=0xbf;
key=(key<<4)|(P3&0x0f);/*为保留上一行扫描的数据,故将数据左移4位。此处“P3&0x0f”是为了将高四位
清0,进而再进行位运算时不会清除之前的数据。*/
P44=1,P42=1,P3=0xdf;
key=(key<<4)|(P3&0x0f);
P44=1,P42=1,P3=0xef;
key=(key<<4)|(P3&0x0f);
if(key!=0x0ffff)//判断是否和未按下按键时的值相等。
{
Delay5ms();//第一次消抖开始:消除按键前沿抖动,即按下抖动。
if(key!=0x0ffff)
{//第一次消抖结束。
switch(key)//开始判断key的值,并依此赋予num不同的值,然后将值传递给数码管显示函数。
{
case 0x0fffe:num=15;break;
case 0x0fffd:num=14;break;
case 0x0fffb:num=13;break;
case 0x0fff7:num=12;break;
case 0x0ffef:num=11;break;
case 0x0ffdf:num=10;break;
case 0x0ffbf:num=9;break;
case 0x0ff7f:num=8;break;
case 0x0feff:num=7;break;
case 0x0fdff:num=6;break;
case 0x0fbff:num=5;break;
case 0x0f7ff:num=4;break;
case 0x0efff:num=3;break;
case 0x0dfff:num=2;break;
case 0x0bfff:num=1;break;
case 0x07fff:num=0;break;
default:/*倘若有的按键按下没有达到预期效果,根据这个来判断key的值是否正确。*/
{
XBYTE[0x8000]=0xf5;//LED亮。
Delay5ms();
XBYTE[0x8000]=0xff;//LED灭。
}
}
while (key!=0x0ffff)//第二次消抖开始:消除后沿抖动,即松手抖动。
{
Delay5ms();
P44=0,P42=1,P3=0x7f;
key=P3&0x0f;
P44=1,P42=0,P3=0xbf;
key=(key<<4)|(P3&0x0f);
P44=1,P42=1,P3=0xdf;
key=(key<<4)|(P3&0x0f);
P44=1,P42=1,P3=0xef;
key=(key<<4)|(P3&0x0f);
}//第二次消抖结束。
}
}
}
void Delay5ms(void) //单片机内部晶振频率@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay10us(void) //单片机内部晶振频率@11.0592MHz
{
unsigned char i;
_nop_();
i = 25;
while (--i);
}
/*实验预期效果:一上电,最右边三位数码管都亮,并且最后两位数码管从60开始减计时。减至0后,会从255开始
减。按S6键,可以更改计时方向,改为加计时。*/
//本实验采用了中断的方式进行计时,并且为方便计算,单片机频率设为12MHZ,务必注意。
#include
#include
#include
//↓数码管片选数组。
unsigned char smg_Pxdata[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
//↓数码管段选数组。
unsigned char smg_Dxdata[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff};
unsigned char time[8]={10,10,10,10,10,10,10,10};//显示时间的数组。
void button(void);//矩阵键盘函数。
void smg(void);//数码管函数。
void A_Ctrl(void);//地址为0xA000的一组元器件控制函数。
//↓数值转换替换函数,将值转换成单个的数字,并依次替换time数组里的元素。
void zhuanhuan(unsigned int zh);
void Delayms(unsigned int ms);//软件延时函数,以微秒为单位。
void T0zd_Init(void);//定时器T0初始化函数。
unsigned char num,t=60;//全局变量,num存储键值,t存储时间。
unsigned int c;//全局变量,用于统计中断次数,用于延长时间。
/*为何main函数里没有中断服务函数却也能执行它?因为单片机程序有两条运行路线,一个是main函数,另一个就
是中断。*/
void main(void)
{
T0zd_Init();//初始化定时器。
A_Ctrl();//初始化蜂鸣器等无关部件。
EA=1;//开启总中断。
while(1)
{
button();
smg();
}
}
void T0zd_Init(void)//定时器T0初始化函数。
{
AUXR &=0x7f;//使用内部时钟,即f/12模式。
TMOD &=0xf0;//初始化TMOD,保持高四位不变,初始化低四位。
TMOD |=0x02;//设定定时器0的工作方式为方式1。
TH0=0x9C;//初值高位写入,总共定时100us。
TL0=0x9C;//初值低位写入,总共定时100us。
TF0=0;//清除标志位(硬件虽会自己清除,但保险起见,还是手动清除一下)。
TR0=1;//启动定时器。
ET0=1;//开启定时器0中断。
}
void T0_Service(void) interrupt 1 //定时器T0中断服务程序
{
c++;
if (c==10000)//即定时器计时到了1s。
{
if (num==2)//按下S6,改变计时方向,加计时。
t++;
else
t--;//否则按下其他按键,一直都是减计时。
c=0;//初始化c的值,开始新的1秒计时。
}
}
void A_Ctrl(void)//地址为0xA000的一组元器件控制函数(蜂鸣器等)。
{
XBYTE[0xA000]=0;//让该组元件全部不动作。
}
void button(void)//矩阵键盘函数。
{
unsigned int key;
P44=0, P3=0xff;/*因为单片机一上电复位,其所有引脚均被初始化为高电平,原只需要修改引脚值即可。
但P3口中P3.4脚比较特殊,需给高电平。为方便,将整个P3口都置位。*/
key=(P3&0x0f);
P44=1,P42=0;
key=((key<<4)|(P3&0x0f));
P42=1,P3=0xdf;
key=((key<<4)|(P3&0x0f));
P3=0xef;
key=((key<<4)|(P3&0x0f));
if(key!=0x0ffff)//第一次消抖开始。
{
Delayms(5);
if(key!=0x0ffff)
{//第一次消抖结束。
switch(key)
{
case 0x07fff:num=0;break;
case 0x0bfff:num=1;break;
case 0x0dfff:num=2;break;
case 0x0efff:num=3;break;
case 0x0f7ff:num=4;break;
case 0x0fbff:num=5;break;
case 0x0fdff:num=6;break;
case 0x0feff:num=7;break;
case 0x0ff7f:num=8;break;
case 0x0ffbf:num=9;break;
case 0x0ffdf:num=10;break;
case 0x0ffef:num=11;break;
case 0x0fff7:num=12;break;
case 0x0fffb:num=13;break;
case 0x0fffd:num=14;break;
case 0x0fffe:num=15;break;
}
while(key!=0x0ffff)//第二次消抖开始。
{
Delayms(5);
P44=0, P3=0xff;
key=(P3&0x0f);
P44=1,P42=0;
key=((key<<4)|(P3&0x0f));
P42=1,P3=0xdf;
key=((key<<4)|(P3&0x0f));
P3=0xef;
key=((key<<4)|(P3&0x0f));
}//第二次消抖结束。
}
}
}
void smg(void)//数码管函数。
{
unsigned char i;
zhuanhuan(t);
for(i=0;i<8;i++)
{
XBYTE[0xE000]=0xff;
XBYTE[0xC000]=0;
XBYTE[0xE000]=smg_Dxdata[time[i]];
XBYTE[0xC000]=smg_Pxdata[i];
Delayms(1);
}
}
void zhuanhuan(unsigned int zh)//转换替换函数。
{
//对zh值取余,再用余数除以其所在的数位,便可将其数位的值转换为单个数字。
//因为zh是整型函数,所以会强制转换数据类型,浮点值的小数部分会被直接省略。
time[2]=(zh%1000)/100;
time[1]=(zh%100)/10;
time[0]=zh%10;
}
void Delayms(unsigned int ms)//@12.000MHz,请注意频率,为了方便计算定时器初值,频率改为12MHZ。
{
while(ms)
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
ms--;
}
}
//实验预期效果:分别按下s4、s5、s6、s7,最右边两片数码管会显示9,10,11,12。
/*实验重点:①利用定时器进行数码管动态扫描,消除了之前按键按下就会消隐的缺陷。②利用了sprintf函数实现数
值的转换以及数字左移效果。*/
//左移效果:当9变成10时,1会左移至倒数第二片数码管上。
#include
#include
#include
#include
void button(void);//独立键盘函数。
void smg(unsigned char sg);//数码管函数。
void Timer0Init(void);//定时器T0初始化函数。
void T0_Ser(void);//定时器T0中断服务函数。
void Delayms(unsigned char ms);//软件延时函数,单位为毫秒。
unsigned char i,num;//全局变量。i为T0服务程序中切换数码管段、片选信号的参数;num为存储键值的参数。
unsigned char smg_Pxdata[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管片选信号数组。
unsigned char zfhc[9];/*数码管字符缓存数组,用于存储数值被分离成个、十、百等数位后的字符型数字,设
置9个元素是因为sprintf函数会默认在数组末尾加"\0"结束符号。*/
void main(void)
{
XBYTE[0xA000]=0;//关闭其他无关元件。
Timer0Init();//定时器初始化函数。
EA=1;//开启总中断。
while(1)
{
button();
}
}
void button(void)//独立键盘函数。
{
unsigned char key;
P3|=0x0f;
key=(P3&0x0f);
if(key!=0x0f)//第一次消抖开始。
{
Delayms(5);
key=(P3&0x0f);
if(key!=0x0f)
{
key=(P3&0x0f);//第一次消抖结束。
switch(key)
{
case 0x07:num=9;break;
case 0x0b:num=10;break;
case 0x0d:num=11;break;
case 0x0e:num=12;break;
default:
XBYTE[0x8000]=0;//用于判断键值是否出错,在程序成功运行后可删除节省单片机空间。
}
while(key!=0x0f)//第二次消抖开始。
{
Delayms(5);
key=(P3&0x0f);//第二次消抖结束。
}
}
}
sprintf(zfhc,"%8d",(unsigned int)num);/*将整型数字转换成字符并存入zfhc数组中,加(unsigned
int)的原因是因为C51编译器和C编译器不一样,没有内存对
齐。加"%8d"是为了让字符可以呈现和输入密码一样左移的效
果,利用了函数字符不够加空格(空格在接下来的"switch"
判断中会被判断至"default"里去)的特性。*/
}
void smg(unsigned char sg)
{
unsigned char dx;//临时变量,用于缓存数码管段选信号。
XBYTE[0xE000]=0xff;
XBYTE[0xC000]=0;
switch(zfhc[sg])//字符型数字转换为整型数字,依次为0~9。
{
case '0':dx=0xc0;break;
case '1':dx=0xf9;break;
case '2':dx=0xa4;break;
case '3':dx=0xb0;break;
case '4':dx=0x99;break;
case '5':dx=0x92;break;
case '6':dx=0x82;break;
case '7':dx=0xf8;break;
case '8':dx=0x80;break;
case '9':dx=0x90;break;
default:dx=0xff;//对其他数码管进行消隐处理。
}
XBYTE[0xE000]=dx;
XBYTE[0xC000]=smg_Pxdata[sg];
}
void Timer0Init(void) //100微秒@12.000MHz,定时器初始化函数。
{
AUXR &= 0x7F; //定时器时钟12T模式。
TMOD &= 0xF0; //设置定时器模式。
TMOD |= 0x02; //设置定时器模式,8位自动重装模式。
TL0 = 0x9C; //设置定时初值,100us。
TH0 = 0x9C; //设置定时重载值,100us。
TF0 = 0; //清除TF0标志。
TR0 = 1; //定时器0开始计时。
ET0=1; //开启定时器0中断。
}
void T0_Ser(void) interrupt 1//定时器0的中断服务程序。
{
smg(i);
i++;
if(i==8)
i=0;//让信号只在0~7之间切换。
}
void Delayms(unsigned char ms) //@12MHz
{
while(ms)
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
ms--;
}
}
//实验预期效果:上电后,数码管开始显示实时时间,从“21-15-21”开始计时。
//实验重点:利用sprintf函数转换段选信号执行数码管显示。
#include
#include
#include
#include
#include
//————————————————————————————————以下为硬件驱动函数声明。
void smg(unsigned char sg);//数码管函数。
void Timer0Init(void);//定时器0初始化函数,8位重装。
void Ds1302_Init(void);//Ds1302实时时钟初始化函数。
//————————————————————————————————以下为功能函数声明。
void Timer0_Ser(void);//定时器0中断服务函数。
void Ds1302_Read(void);//读取Ds1302的时间函数。
//————————————————————————————————以下为全局变量声明。
//
unsigned char i;//全局变量。i在定时器0服务程序中,用于数码管片选、段选切换显示。
//
//————————————————————————————————以下为数组声明。
//因以另一种形式传递段码信号,故此处无数码管段选信号数组。
unsigned char smg_Pxdata[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管片选信号数组。
unsigned char Ds1302_Radr[3]={0x85,0x83,0x81};//Ds1302读动作的目标地址数组。分别是时、分、秒。
unsigned char bcdzh[8]={10,10,10,10,10,10,10,10};/*压缩BCD码转换缓存数组,用于缓存从Ds1302中读出
来后被转换的数据。*/
unsigned char zfhc[8];//字符缓存数组,用于缓存从sprintf函数中分离转换出来的字符数字。
void main(void)
{
XBYTE[0xA000]=0;//初始化无关器件。
EA=1;//开总中断。
Timer0Init();//定时器0初始化函数。
Ds1302_Init();//Ds1302初始化函数。
while(1)
{
Ds1302_Read();//不断从Ds1302元气件中读取时间。
}
}
//—————————————————————————————————以下部分为硬件驱动函数。
void smg(unsigned char sg)//数码管函数。
{
unsigned char dx;//用于临时缓存段选信号。
XBYTE[0xE000]=0xff;
XBYTE[0xC000]=0;
if((sg==2)||(sg==5))//因为“-”不是数字,故无法通过sprintf函数转换,因此只能利用片选信号来输出。
dx=0xbf;//第2位和第5位数码管恒定显示“-”。
else
{
switch(zfhc[sg])//对字符缓存数组里的字符进行转换,转换成相应的段选信号。
{
case '0':dx=0xc0;break;
case '1':dx=0xf9;break;
case '2':dx=0xa4;break;
case '3':dx=0xb0;break;
case '4':dx=0x99;break;
case '5':dx=0x92;break;
case '6':dx=0x82;break;
case '7':dx=0xf8;break;
case '8':dx=0x80;break;
case '9':dx=0x90;break;
}
}
XBYTE[0xE000]=dx;
XBYTE[0xC000]=smg_Pxdata[sg];
}
void Timer0Init(void) //定时器0初始化函数,1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式。
TMOD &= 0xF0; //设置定时器模式。
TMOD |= 0x02; //设置定时器模式。
TL0 = 0x9C; //设置定时初值,100us。
TH0 = 0x9C; //设置定时重载值,100us。
TF0 = 0; //清除TF0标志。
TR0 = 1; //定时器0开始计时。
ET0=1; //开启定时器T0中断。
}
void Ds1302_Init(void)//Ds1302初始化函数,用于设定时间以及启动计时。
{
Write_Ds1302_Byte(0x8e,0x00);//解锁保护,允许数据写入。
Write_Ds1302_Byte(0x84,0x21);//写入数据,时初始化——21时(选择24小时模式)。
Write_Ds1302_Byte(0x82,0x15);//写入数据,分初始化——15分。
Write_Ds1302_Byte(0x80,0x21);//写入数据,秒初始化——21秒,同时启动时间计时。
Write_Ds1302_Byte(0x8e,0x80);//重启保护,禁止数据写入。
}
void Ds1302_Read(void)//Ds1302读取数据函数,用于将时钟数据读取出来。
{
unsigned char ir,ir2,temp;//ir1和ir2为循环参数;temp为临时参数,用于缓存读出来的时钟数据。
for(ir=0;ir<3;ir++)
{
/*因为读出来的参数是压缩BCD码,因此要对其进行解压缩转换。当DS1302的读地址顺序为时、分、秒时,转换数据
的原则为“时低秒高”“十低个高”,即在数组中,转换顺序为时~秒,即时→低地址,秒→高地址;且在转换单个时间单
位时,即十位→低位,个位→高位。在转换时同时进行格式化处理,将格式改为“23059059”类型。*/
temp=Read_Ds1302_Byte(Ds1302_Radr[ir]);//将读取出的时钟数据缓存至temp,读取顺序,时~秒。
bcdzh[(ir*3)+1]=(temp&0x0f);//此行转换的是时、分、秒的个位,同时进行格式化处理。
bcdzh[ir*3]=((temp&0xf0)>>4);//此行转换的是时、分、秒的十位,同时进行格式化处理。
}
for(ir2=0;ir2<8;ir2++)
{
sprintf(zfhc+ir2,"%d",(unsigned int)bcdzh[ir2]);/*将bcd转换数组里的元素转换成字符,依次
传递给字符缓存数组。*/
}
}
//—————————————————————————————————以下部分为功能函数。
void Timer0_Ser(void) interrupt 1//定时器T0的中断服务程序。
{
smg(i);
i++;
if(i==8)
i=0;//让信号只在0~7之间切换。
}
//该文件蓝桥杯官方会下发。
#ifndef _ds1302_h
#define _ds1302_h
void Write_Ds1302(unsigned char temp);
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte ( unsigned char address );
void Set_RTC(unsigned char* pucRtc);
void Read_RTC(unsigned char* pucRtc);
#endif
//该文件蓝桥杯官方会下发。
#include
#include
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST = P1^3; // DS1302复位
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_();
SDA=0; _nop_();
return (temp);
}
// 设置时钟
void Set_RTC(unsigned char* pucRtc)
{
unsigned char temp;
Write_Ds1302_Byte(0x8E, 0); // WP=0:允许写操作
temp = ((pucRtc[0]/10)<<4)+pucRtc[0]%10;
Write_Ds1302_Byte(0x84, temp); // 设置时
temp = ((pucRtc[1]/10)<<4)+pucRtc[1]%10;
Write_Ds1302_Byte(0x82, temp); // 设置分
temp = ((pucRtc[2]/10)<<4)+pucRtc[2]%10;
Write_Ds1302_Byte(0x80, temp); // 设置秒
Write_Ds1302_Byte(0x8E, 0x80); // WP=1:禁止写操作
}
// 读取时钟
void Read_RTC(unsigned char* pucRtc)
{
unsigned char temp;
temp = Read_Ds1302_Byte(0x85); // 读取时
pucRtc[0] = (temp>>4)*10+(temp&0xf);
temp = Read_Ds1302_Byte(0x83); // 读取分
pucRtc[1] = (temp>>4)*10+(temp&0xf);
temp = Read_Ds1302_Byte(0x81); // 读取秒
pucRtc[2] = (temp>>4)*10+(temp&0xf);
}
/*实验预期效果:方案一,右边四位数码管以“21-5”的格式实时显示实际温度,其中“-”为整数和小数的分割线;方
案二,右边三位数码管以“21.5”的格式实时显示实际温度(该方案将需修改的函数以注释的形式写在最后)。*/
/*实验重点:①sprintf函数的拓展用法,解决想要移位某片数码管的问题。如将“215”格式中的“5”右移一位,在之
前的位置加上“-”,变成“21-5”的格式。②通过“或”的位运算,将小数点加入,无需再创建数组。*/
#include
#include
#include
#include
#include
void smg(unsigned char sg);//数码管函数。
void Timer0Init(void);//定时器T0初始化函数。
void T0_Ser(void);//定时器T0中断服务函数。
void get_Temperature(void);//获取实际温度函数。
unsigned char i;//全局变量。i在定时器T0的服务函数中用于切换段选、片选信号。
unsigned char smg_Pxdata[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管片选信号。
unsigned char zfhc[8];//字符缓存数组,缓存经sprintf函数转换后的数字字符。
void main(void)
{
XBYTE[0xA000]=0;//让其他无关元件初始化。
EA=1;//开启总中断。
Timer0Init();//定时器初始化函数。
while(1)
{
get_Temperature();//实时获取温度数据。
}
}
//可替换函数部分开始。方案一,以“21-5”的形式实时显示实际温度。
void smg(unsigned char sg)//数码管函数。
{
unsigned char dx;//临时缓存段选信号参数。
XBYTE[0xE000]=0xff;
XBYTE[0xC000]=0;
if (sg==6)
{
dx=0xbf;//让第6片数码管始终显示“-”,成为整数和小数的分割线。
}
else
{
switch(zfhc[sg])//将字符缓存数组中的字符转换为相应的段选信号。
{
case '0':dx=0xc0;break;
case '1':dx=0xf9;break;
case '2':dx=0xa4;break;
case '3':dx=0xb0;break;
case '4':dx=0x99;break;
case '5':dx=0x92;break;
case '6':dx=0x82;break;
case '7':dx=0xf8;break;
case '8':dx=0x80;break;
case '9':dx=0x90;break;
default:
dx=0xff;//消隐其他无关数组。
}
}
XBYTE[0xE000]=dx;
XBYTE[0xC000]=smg_Pxdata[sg];
}
void get_Temperature(void)//获取实际温度数据函数。
{
unsigned int rt;//rt(real temperature)用于存放实际的温度。
rt=read_Temperature();//从ds18b20中读取温度数据。
rt=rt*0.625;//因室内温度一般都高于0度,因此此处不判断温度的正负。
sprintf(zfhc,"%7d%d",(unsigned int)rt,(unsigned int)rt%10);/*重复打印实际温度的小数部分,并
将小数部分放在整数部分后面,再
一齐靠右对齐,解决第6片数码管
小数部分被覆盖的问题。*/
}
//可替换函数部分结束。
void Timer0Init(void) //100微秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式。
TMOD &= 0xF0; //设置定时器模式,8位自动重载。
TMOD |= 0x02; //设置定时器模式。
TL0 = 0x9C; //设置定时初值,100us。
TH0 = 0x9C; //设置定时重载值,100us。
TF0 = 0; //清除TF0标志。
TR0 = 1; //定时器0开始计时。
ET0=1; //开启定时器T0中断。
}
void T0_Ser(void) interrupt 1//定时器T0中断服务函数。
{
smg(i);
i++;
if(i==8)
i=0;//初始化i的值,让段选、片选信号只能在0~7之间切换。
}
////可替换函数部分开始。方案二,以“21.5”的格式实时显示实际温度。
//void smg(unsigned char sg)//数码管函数。
//{
// unsigned char dx;//临时缓存段选信号参数。
// XBYTE[0xE000]=0xff;
// XBYTE[0xC000]=0;
// switch(zfhc[sg])//将字符缓存数组中的字符转换为相应的段选信号。
// {
// case '0':dx=0xc0;break;
// case '1':dx=0xf9;break;
// case '2':dx=0xa4;break;
// case '3':dx=0xb0;break;
// case '4':dx=0x99;break;
// case '5':dx=0x92;break;
// case '6':dx=0x82;break;
// case '7':dx=0xf8;break;
// case '8':dx=0x80;break;
// case '9':dx=0x90;break;
// default:
// dx=0xff;//消隐其他无关数组。
// }
// if(sg==6)//给第6片数码管加小数点。
// {
// dx&=0x7f;//加小数点。
// }
// XBYTE[0xE000]=dx;
// XBYTE[0xC000]=smg_Pxdata[sg];
//}
//void get_Temperature(void)//获取实际温度数据函数。
//{
// unsigned int rt;//rt(real temperature)用于存放实际的温度。
// rt=read_Temperature();//从ds18b20中读取温度数据。
// rt=rt*0.625;//因室内温度一般都高于0度,因此此处不判断温度的正负。
// sprintf(zfhc,"%8d",(unsigned int)rt);//加“8”是为了让数码管显示的字符靠右对齐。
//}
//可替换函数部分结束。
//该文件蓝桥杯官方会下发。
#ifndef __ONEWIRE_H
#define __ONEWIRE_H
unsigned int read_Temperature(void); //; ;
#endif
//该文件蓝桥杯官方会下发。
/*
程序说明: 单总线驱动程序
软件环境: Keil uVision 4.10
硬件环境: CT107单片机综合实训平台(外部晶振12MHz) STC89C52RC单片机
日 期: 2011-8-9
*/
#include "stc15f2k60s2.h"
sbit DQ = P1^4; //单总线接口
//单总线延时函数
void Delay_OneWire(unsigned int t) //STC89C52RC——注:已修正适应IAP15F2K60系列芯片。
{
unsigned char x;
while(t--)
for(x=0;x<12;x++);
}
//通过单总线向DS18B20写一个字节
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);
}
//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//DS18B20设备初始化
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;
}
unsigned int read_Temperature(void)
{
unsigned char low,high;
unsigned int sum;
init_DS18b20();//初始化DS18B20。
Write_DS18B20(0xCC);//因只有一个元件,可跳过ROM存储识别。
Write_DS18B20(0X44);//对温度进行模数转换。
Delay_OneWire(4000);
init_DS18b20();//初始化DS18B20。
Write_DS18B20(0xCC);//因只有一个元件,可跳过ROM存储识别。
Write_DS18B20(0XBE);//读取温度暂存器。
low=Read_DS18B20();//温度数据的低字节暂存于low中。
high=Read_DS18B20();//温度数据的高字节暂存于low中。
sum=high;
sum<<=8;
sum|=low;
return sum;//将数据返回给上层函数。
}