当前位置:文档之家› [PIC单片机] 吴坚鸿单片机程序风格赏析【6】

[PIC单片机] 吴坚鸿单片机程序风格赏析【6】

[PIC单片机] 吴坚鸿单片机程序风格赏析【6】
[PIC单片机] 吴坚鸿单片机程序风格赏析【6】

第三十六节:EPROM篇--利用单片机内部自带EPROM存储数据

开场白:

这一节最有价值的可能不是我下面写的源代码讲解,而是我现在正在讲的开场白。单片机内部自带EPROM存储数据,曾经是我去年一个挥之不去的噩梦。去年我接了一个小项目,本来预计一个月内搞完的,结果因为小小的EPROM害得我阴沟里翻了船,拖了半年才验收通过。事情是这样的,这个系统是工作在3.3V,利用单片机内部自带EPROM存储数据,我花了三个星期就交付给客户了,客户做了十几套样板在测试,发现数据偶尔会丢失,逼得我不得不想尽各种办法,比如备份数据等多种方法双管齐下,最终花了半年时间才弄好。至于我是怎么弄好的我也不想多分享,因为这个系统有特殊性,我仅仅也是巧妙地利用它自身的特殊性才解决,这种解决问题的方法没有复制的价值。不过从此之后我总结出了以下3条个人教训。

(a)凡是在低于5V电压工作的系统中,千万不要用单片机内部自带EPROM,宁可多花几毛钱也要用外挂的EPROM。否则凶多吉少。

(b)在5V电压工作的系统中,如果要用单片机内部自带EPROM,那么这款单片机必须自带上电延时和掉电复位功能,同时烧录配置位时必须把这两项功能都打开。因为单片机自带EPROM最容易丢失数据的时刻就是发生在掉电和上电那段“权力真空”的时间里。(c)不管是工作在5V还是工作在低于5V的系统中,最保险的方法还是尽量选用外挂的EPROM。因为去年那个项目给我留下了“成年阴影”,宁可错杀一千也不放过一个。注:

上电延时在PIC的烧录配置位是指power up timer

掉电复位在PIC的烧录配置位是指brown out reset

那个项目是去年年初开始弄,半年后才验收通过。之后跟那个老总就几乎没什么联系了,到了年底的时候,突然有一天我接到了他的电话,他问我周末有没有空,他说想专门请我去酒楼吃海鲜,就我和他两个人。我很开心地答应了,因为我想他肯定有什么大项目要跟我合作了。到了周末,他真的专门从南山科技园那边开车到宝安西乡来接我去吃海鲜。在吃饭的时候,我才知道,他找我真的没有什么“公事”,纯粹是因为“爱慕”我,年底了想跟我这个朋友聊聊天。他夸我做事认真,沉得住气,遇到难题敢于迎难而上,颇有“帝王之气”,以后肯定有出息的。同时他还点拨了我很多经验与方向。他也很年轻,比我大两三年,09年才创业,我去过他公司,七八个人,环境还不错,每个月写字楼的租金五六千,不过他说这个负担也不重,因为他已经有批量的产品在出货。他专门搞电脑方面的软件编程,用的是delphi,C#的开发工具。他走的也是技术路线,不抽烟不喝酒,也不善于应酬,靠着诚信经营一个小公司,在圈内的朋友越来越多,口碑也越来越好,每两三个月都会跟一帮圈内的人自驾车到附近各地去旅游,他说他很知足了。

(1)功能需求:

开机后两位数码管显示0到99之间的某一个数值,用一个按键可以不断的递增,用另外一个按键可以不断的递减。断电后此数据可保存,下次通电开机的时候还是从那个保存的数据开始显示。

(2)硬件原理:

(a)动态扫描两位数码管的电路请参考第二十一节。

(d) 独立按键的电路请参考第二节。

(c)烧录程序时记得把配置位PWRT和BOREN同时开打(Enable)。

(3)源码适合的单片机: PIC18f4520,晶振为3.579545MHz。内部自带EPROM。

(4)单片机的C语言源代码讲解如下:

#include //包含芯片相关头文件

#define cnt_delay_cnt1 25 //按键去抖动延时阀值

#define cnt_voice_time 60 //蜂鸣器响的声音长短的延时阀值

#define seg_0_dr LATB6 //任意7个IO口接数码管的seg引脚

#define seg_1_dr LATB5

#define seg_2_dr LATB4

#define seg_3_dr LATB3

#define seg_4_dr LATC7

#define seg_5_dr LATB0

#define seg_6_dr LATB1

#define com_left_dr LATB2 //任意2个IO口接数码管的com引脚

#define com_right_dr LATB7

#define beep_dr LATA1 //蜂鸣器输出

#define key_sr1 RD6 //独立按键输入

#define key_sr2 RD7 //独立按键输入

void eewrite(unsigned char ee_addr,unsigned char ee_data); //写入单片机内部eprom, unsigned char ee_read(unsigned char ee_addr); //从单片机内部eprom读取出数据

void initial();//初始化

void delay1(unsigned int de) ;//小延时程序,时间不宜太长,因为内部没有喂看门狗

void display_drive(); //数码管驱动程序,放在定时中断里

void display_seg(unsigned char seg); //编码转换程序,放在display_drive里

//补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中

//断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序放在中断里,别听他们,信鸿哥无坎坷。

void key_scan(); //按键扫描函数,放在定时中断里

void key_service(); //按键服务函数,放在main函数循环里

//补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名

//后缀都用_lock表示。

unsigned char key_lock1=0; //按键自锁标志

unsigned char key_lock2=0; //按键自锁标志

unsigned int delay_cnt1=0; //延时计数器的变量

unsigned int delay_cnt2=0; //延时计数器的变量

unsigned int voice_time_cnt; //蜂鸣器响的声音长短的计数延时

unsigned char number_left=0; //左边数码管显示的内容

unsigned char number_right=0; //右边数码管显示的内容

unsigned char dis_step=1; //扫描的步骤

unsigned char set_data; //显示,设置和保存的数

unsigned char key_sec=0; //哪个按键被触发

main() //主程序

{

initial(); //初始化

while(1)

{

CLRWDT();

}

}

void eewrite(unsigned char ee_addr,unsigned char ee_data) //写入单片机内部eprom,

{

EEADR=ee_addr; //每种单片机都会有它固定的指令来配置相关的寄存器,来达到操作内部自带EPROM的目的,不必深究此时序

EEDATA=ee_data;

EEPGD=0;

CFGS=0;

WREN=1;

GIE=0;

EECON2=0x55;

EECON2=0xaa;

WR=1;

asm("nop");asm("nop");asm("nop");asm("nop");

asm("nop");asm("nop");asm("nop");asm("nop");

GIE=1;

asm("nop");asm("nop");asm("nop");asm("nop");

asm("nop");asm("nop");asm("nop");asm("nop");

WREN=0;

WR=0;

}

unsigned char ee_read(unsigned char ee_addr) //从单片机内部eprom读取出数据

{

unsigned char ee_data;

EEADR=ee_addr; //每种单片机都会有它固定的指令来配置相关的寄存器,来达到操作内部自带EPROM的目的,不必深究此时序

EEPGD=0;

CFGS=0;

RD=1;

asm("nop");asm("nop");asm("nop");asm("nop");

asm("nop");asm("nop");asm("nop");asm("nop");

ee_data=EEDATA;

RD=0;

return ee_data;

}

void key_scan() //按键扫描函数

{

if(key_sr1==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位{

key_lock1=0; //按键自锁标志清零

delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙

}

else if(key_lock1==0) //有按键按下,且是第一次被按下

{

++delay_cnt1; //延时计数器

if(delay_cnt1>cnt_delay_cnt1)

{

delay_cnt1=0;

key_lock1=1; //自锁按键置位,避免一直触发

key_sec=1; //触发1号键

}

}

if(key_sr2==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位{

key_lock2=0; //按键自锁标志清零

delay_cnt2=0; //按键去抖动延时计数器清零,此行非常巧妙

}

else if(key_lock2==0) //有按键按下,且是第一次被按下

{

++delay_cnt2; //延时计数器

if(delay_cnt2>cnt_delay_cnt1)

{

delay_cnt2=0;

key_lock2=1; //自锁按键置位,避免一直触发

key_sec=2; //触发2号键

}

}

}

void key_service() //按键服务函数

{

switch(key_sec) //按键服务状态切换

{

case 1:// 1号键加

// 补充说明:voice_time_cnt只要不为0蜂鸣器就会响,中断里判断voice_time_cnt不为0 //时,会不断自减,一直到它为0时,自动把蜂鸣器关闭

++set_data;

if(set_data>99)

{

set_data=0;

}

eewrite(128,set_data); //把刚刚修改的数据保存进单片机内部自带的eprom

voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停

key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,避免一直触发

break;

case 2:// 2号键减

--set_data;

if(set_data>99) //字节类型的数据,当0减去1时会变成255(0xff),当然会比99大了。

{

set_data=99;

}

eewrite(128,set_data); //把刚刚修改的数据保存进单片机内部自带的eprom

voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停

key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,避免一直触发

break;

}

}

void interrupt timer1rbint(void) //中断程序入口

{

if(TMR1IE==1&&TMR1IF==1) //定时中断程序

{

TMR1IF=0;

TMR1ON=0;

key_scan(); //按键扫描函数

if(voice_time_cnt) //控制蜂鸣器声音的长短

{

beep_dr=1; //蜂鸣器响

--voice_time_cnt; //蜂鸣器响的声音长短的计数延时

}

else

{

beep_dr=0; //蜂鸣器停止

}

display_drive(); //数码管驱动程序,放在定时中断里

TMR1H=0xFF;

TMR1L=0xC8;

TMR1ON=1;

}

}

void initial()//初始化

{

ADCON0=0x00;

ADCON1=0x0f;

ADCON2=0x00;

RBPU=0;

TRISB=0x00; //数码管IO口设置成输出

TRISC7=0;

TRISA1=0; //蜂鸣器IO口设置成输出

TRISD6=1; //独立按键IO口设置成输入

TRISD7=1;

SSPEN=0;

T1CON=0x24;

TMR1H=0xFE;

TMR1L=0xEF;

INTCON=0xC0;

TMR1IF=0;

TMR1IE=1;

PEIE=1; //外围中断允许

GIE=1;

TMR1ON=1;

set_data=ee_read(128); //上电开机读取内部EPROM数据,128是一个我任意选的一个存储地址

if(set_data>99) //说明是这个单片机是第一次烧录程序之后,内部数据是默认值,以前没操作过。

{

eewrite(128,0); //如果从来没操作过此EPROM,则默认写入一个0的数据}

}

void display_drive() //数码管驱动程序,放在定时中断里

{

if(set_data>=10)

{

number_left=set_data/10; //如果数据大于或者等于10,则分解出显示数据的十位去显示

}

else

{

number_left=10; //如果数据小于10,则十位什么也不显示,显示空。

}

number_right=set_data%10; //分解出显示数据的个位

seg_0_dr=0;

seg_1_dr=0;

seg_2_dr=0;

seg_3_dr=0;

seg_4_dr=0;

seg_5_dr=0;

seg_6_dr=0;

com_left_dr=1;

com_right_dr=1; //在即将更换下一位数码管时,先把让它两个什么都不显示,让显示过度更加平稳

asm("nop"); //空指令延时

asm("nop");

asm("nop");

asm("nop");

asm("nop");

asm("nop");

switch(dis_step)

{

case 1: //扫描左边的数码管

display_seg(number_left); //如果不是任意IO口,可以直接用查表的方式取代此子程序

com_left_dr=0; //选中左数码管

com_right_dr=1;

break;

case 2: //扫描右边的数码管

display_seg(number_right); //如果不是任意IO口,可以直接用查表的方式取代此子程序

com_left_dr=1;

com_right_dr=0; //选中右数码管

break;

}

delay1(50); //每一位数码管显示的停留延时时间,有疑问的朋友请自己尝试改成计数延时的方式,

//鸿哥认为在此种环境下,在定时中断里用死延时delay1(50)是最佳的方式

++dis_step; //下一次中断扫描另外一位的数码管,轮流扫描

if(dis_step>2)

{

dis_step=1;

}

}

//不是鸿哥不懂爱,如果不是用任意IO口,而是直接用一个并口(比如51单片机中的P1口),那么就不用那么费力,

//直接用查数组(俗称查表)的方式就可以替代display_seg这个编码转换程序,

void display_seg(unsigned char seg) //编码转换程序,,放在display_drive里

{

switch(seg) //switch指令,单片机中的战斗机,鸿哥的最爱!

{

case 0: //显示"0"

seg_0_dr=1;

seg_1_dr=1;

seg_2_dr=1;

seg_3_dr=1;

seg_4_dr=0;

seg_5_dr=1;

seg_6_dr=1;

break;

case 1: //显示"1"

seg_0_dr=1;

seg_1_dr=1;

seg_2_dr=0;

seg_3_dr=0;

seg_4_dr=0;

seg_5_dr=0;

seg_6_dr=0;

break;

case 2: //显示"2"

seg_0_dr=1;

seg_1_dr=0;

seg_2_dr=1;

seg_3_dr=1;

seg_4_dr=1;

seg_5_dr=1;

seg_6_dr=0;

break;

case 3: //显示"3"

seg_0_dr=1;

seg_1_dr=1;

seg_2_dr=0;

seg_3_dr=1;

seg_4_dr=1;

seg_5_dr=1;

seg_6_dr=0;

break;

case 4: //显示"4"

seg_0_dr=1;

seg_1_dr=1;

seg_2_dr=0;

seg_3_dr=0;

seg_4_dr=1;

seg_5_dr=0;

seg_6_dr=1;

break;

case 5: //显示"5"

seg_0_dr=0;

seg_1_dr=1;

seg_2_dr=0;

seg_3_dr=1;

seg_4_dr=1;

seg_5_dr=1;

seg_6_dr=1;

break;

case 6: //显示"6"

seg_0_dr=0;

seg_1_dr=1;

seg_2_dr=1;

seg_3_dr=1;

seg_4_dr=1;

seg_5_dr=1;

seg_6_dr=1;

break;

case 7: //显示"7"

seg_0_dr=1;

seg_1_dr=1;

seg_2_dr=0;

seg_3_dr=0;

seg_4_dr=0;

seg_5_dr=1;

seg_6_dr=0;

break;

case 8: //显示"8"

seg_0_dr=1;

seg_1_dr=1;

seg_2_dr=1;

seg_3_dr=1;

seg_4_dr=1;

seg_5_dr=1;

seg_6_dr=1;

break;

case 9: //显示"9"

seg_0_dr=1;

seg_1_dr=1;

seg_2_dr=0;

seg_3_dr=1;

seg_4_dr=1;

seg_5_dr=1;

seg_6_dr=1;

break;

case 10: //什么也不显示,空

seg_0_dr=0;

seg_1_dr=0;

seg_2_dr=0;

seg_3_dr=0;

seg_4_dr=0;

seg_5_dr=0;

seg_6_dr=0;

break;

}

}

void delay1(unsigned int de)

{

unsigned int t;

for(t=0;t

}

(5)下集预告:

EPROM篇--利用AT24C02存储数据。

第三十七节:EEPROM篇--利用AT24C02存储数据

开场白:

一个AT24C02可以存储256个字节,地址范围是(0至255)。

利用AT24C02存储数据时,有4个地方最容易出错。

(a) 在写入或者读取完一个字节之后,一定要加上一段延时时间。在11.0592M晶振的系统中,写入数据时经验值用Delay_time(2000),读取数据时经验值用Delay_time(800)。否则在连续写入或者读取一串数据时容易丢失数据。如果一旦发现丢失数据,应该适当继续

把这个时间延长,尤其是在写入数据时。

(b) 在时序中,发送ACK确认信号时,要记得把数据线sda设置为输入的状态。对于51单片机来说,只要把sda=1就可以。而对于PIC或者A VR单片机来说,它们都是带方向寄存器的,就不能直接sda=1,而要直接修改方向寄存器,把它设置为输入状态,比如TRISC4=1.在本驱动程序中,鸿哥没有对ACK信号进行出错判断,因为鸿哥这么多年一直都是这样用也没出现过什么问题。

(c) 单片机跟A T24C02通讯的2根IO口都要加上一个15K左右的上拉电阻。

(d) AT24C02的WP引脚一定要接地,否则存不进数据。

(1)功能需求:

开机后两位数码管显示0到99之间的某一个数值,用一个按键可以不断的递增,用另外一个按键可以不断的递减。断电后此数据可保存,下次通电开机的时候还是从那个保存的数据开始显示。

(2)硬件原理:

(a)动态扫描两位数码管的电路请参考第二十一节。

(d) 独立按键的电路请参考第二节。

(c) AT24C02的A0,A1,A2引脚都接地,芯片ID为”000”。

(3)源码适合的单片机: PIC18f4520,晶振为11.0592MHz。

(4)单片机的C语言源代码讲解如下:

#include //包含芯片相关头文件

#define cnt_delay_cnt1 25 //按键去抖动延时阀值

#define cnt_voice_time 60 //蜂鸣器响的声音长短的延时阀值

#define seg_0_dr LATB6 //任意7个IO口接数码管的seg引脚

#define seg_1_dr LATB5

#define seg_2_dr LATB4

#define seg_3_dr LATB3

#define seg_4_dr LATC7

#define seg_5_dr LATB0

#define seg_6_dr LATB1

#define com_left_dr LATB2 //任意2个IO口接数码管的com引脚

#define com_right_dr LATB7

#define beep_dr LATA1 //蜂鸣器输出

#define key_sr1 RD6 //独立按键输入

#define key_sr2 RD7 //独立按键输入

#define eeprom_scl_dr LATC5 //时钟线

#define eeprom_sda_dr LATC4 //数据输出线

#define eeprom_sda_sr RC4 //数据输入线

void start24(); //开始位

void ack24(); //确认位

void stop24(); //停止位

unsigned char read24(); //读取一个字节的时序

void write24(unsigned char dd); //发送一个字节的时序

unsigned char read_eeprom(unsigned int address); //从一个地址读取出一个字节数据

void write_eeprom(unsigned int address,unsigned char dd); //往一个地址存入一个字节数据

void initial();//初始化

void delay1(unsigned int de) ;//小延时程序,时间不宜太长,因为内部没有喂看门狗

void display_drive(); //数码管驱动程序,放在定时中断里

void display_seg(unsigned char seg); //编码转换程序,放在display_drive里

void Delay_time(unsigned int Delay11_MS); //延时程序

//补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中

//断里,凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序放在中断里,别听他们,信鸿哥无坎坷。

void key_scan(); //按键扫描函数,放在定时中断里

void key_service(); //按键服务函数,放在main函数循环里

//补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名

//后缀都用_lock表示。

unsigned char key_lock1=0; //按键自锁标志

unsigned char key_lock2=0; //按键自锁标志

unsigned int delay_cnt1=0; //延时计数器的变量

unsigned int delay_cnt2=0; //延时计数器的变量

unsigned int voice_time_cnt; //蜂鸣器响的声音长短的计数延时

unsigned char number_left=0; //左边数码管显示的内容

unsigned char number_right=0; //右边数码管显示的内容

unsigned char dis_step=1; //扫描的步骤

unsigned char set_data; //显示,设置和保存的数

unsigned char key_sec=0; //哪个按键被触发

main() //主程序

{

initial(); //初始化

while(1)

{

CLRWDT();

key_service(); //按键服务函数

}

}

//AT24C02驱动程序

void start24() //开始位

{

TRISC4 = 0; // sda输出

eeprom_sda_dr=1;

eeprom_scl_dr=1;

Delay_time(15);

eeprom_sda_dr=0;

Delay_time(15);

eeprom_scl_dr=0;

}

//在本驱动程序中,鸿哥没有对ACK信号进行出错判断,因为鸿哥这么多年一直都是这样用也没出现过什么问题。

void ack24() //确认位

{

TRISC4 = 1; // sda输入,很关键!!!

eeprom_scl_dr=1;

Delay_time(15);

eeprom_scl_dr=0;

Delay_time(15);

}

void stop24() //停止位

{

TRISC4 = 0; // sda输出

eeprom_sda_dr=0;

eeprom_scl_dr=1;

Delay_time(15);

eeprom_sda_dr=1;

TRISC4 = 1; // 结束后释放总线,sda输入}

unsigned char read24() //读取一个字节的时序

{

unsigned char outdata,tempdata;

TRISC4 = 1; // sda输入

outdata=0;

asm("nop");asm("nop");

for(tempdata=0;tempdata<8;tempdata++)

{

eeprom_scl_dr=0;

asm("nop");asm("nop");

eeprom_scl_dr=1;

asm("nop");asm("nop");

outdata<<=1;

if(eeprom_sda_sr==1)outdata++;

asm("nop");asm("nop");

}

return(outdata);

}

void write24(unsigned char dd) //发送一个字节的时序{

unsigned char tempdata;

TRISC4 = 0; // sda输出

for(tempdata=0;tempdata<8;tempdata++)

{

if(dd>=0x80)eeprom_sda_dr=1;

else eeprom_sda_dr=0;

dd<<=1;

asm("nop");

eeprom_scl_dr=1;

asm("nop");asm("nop");asm("nop");asm("nop");

eeprom_scl_dr=0;

}

}

unsigned char read_eeprom(unsigned int address) //从一个地址读取出一个字节数据

{

unsigned char dd,cAddress;

cAddress=address; //把低字节地址传递给一个字节变量。

GIE=0; //禁止中断

start24(); //开始

write24(0xA0); //此字节包含读写指令和芯片地址两方面的内容。

//指令为写指令。地址为"000"的信息,此信息由A0,A1,A2的引脚决定

ack24(); //发送应答信号

write24(cAddress); //发送读取的存储地址(范围是0至255)

ack24(); //发送应答信号

start24(); //开始

write24(0xA1); //此字节包含读写指令和芯片地址两方面的内容。

//指令为读指令。地址为"000"的信息,此信息由A0,A1,A2的引脚决定ack24(); //发送应答信号

dd=read24(); //读取一个字节

ack24(); //发送应答信号

stop24(); //停止

GIE=1; //允许中断

Delay_time(800); //此处最关键,此处的延时时间一定要,而且要足够长

return(dd);

}

void write_eeprom(unsigned int address,unsigned char dd) //往一个地址存入一个字节数据{

unsigned char cAddress;

cAddress=address; //把低字节地址传递给一个字节变量。

GIE=0; //禁止中断

start24(); //开始

write24(0xA0); //此字节包含读写指令和芯片地址两方面的内容。

//指令为写指令。地址为"000"的信息,此信息由A0,A1,A2的引脚决定ack24(); //发送应答信号

write24(cAddress); //发送写入的存储地址(范围是0至255)

ack24(); //发送应答信号

write24(dd); //写入存储的数据

ack24(); //发送应答信号

stop24(); //停止

GIE=1; //允许中断

Delay_time(2000); //此处最关键,此处的延时时间一定要,而且要足够长

}

void key_scan() //按键扫描函数

{

if(key_sr1==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位

{

key_lock1=0; //按键自锁标志清零

delay_cnt1=0; //按键去抖动延时计数器清零,此行非常巧妙

}

else if(key_lock1==0) //有按键按下,且是第一次被按下

{

++delay_cnt1; //延时计数器

if(delay_cnt1>cnt_delay_cnt1)

{

delay_cnt1=0;

key_lock1=1; //自锁按键置位,避免一直触发

key_sec=1; //触发1号键

}

}

if(key_sr2==1) //IO是高电平,说明按键没有被按下,这时要及时清零一些标志位{

key_lock2=0; //按键自锁标志清零

delay_cnt2=0; //按键去抖动延时计数器清零,此行非常巧妙

}

else if(key_lock2==0) //有按键按下,且是第一次被按下

{

++delay_cnt2; //延时计数器

if(delay_cnt2>cnt_delay_cnt1)

{

delay_cnt2=0;

key_lock2=1; //自锁按键置位,避免一直触发

key_sec=2; //触发2号键

}

}

}

void key_service() //按键服务函数

{

switch(key_sec) //按键服务状态切换

{

case 1:// 1号键加

// 补充说明:voice_time_cnt只要不为0蜂鸣器就会响,中断里判断voice_time_cnt不为0

//时,会不断自减,一直到它为0时,自动把蜂鸣器关闭

++set_data;

if(set_data>99)

{

set_data=0;

}

write_eeprom(128,set_data); //把刚刚修改的数据保存进eeprom

voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停

key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,避免一直触发

break;

case 2:// 2号键减

--set_data;

if(set_data>99) //字节类型的数据,当0减去1时会变成255(0xff),当然会比99大了。

{

set_data=99;

}

write_eeprom(128,set_data); //把刚刚修改的数据保存进eeprom

voice_time_cnt= cnt_voice_time; //蜂鸣器响“滴”一声就停

key_sec=0; //相应完按键处理程序之后,把按键选择变量清零,避免一直触发

break;

}

}

void interrupt timer1rbint(void) //中断程序入口

{

if(TMR1IE==1&&TMR1IF==1) //定时中断程序

{

TMR1IF=0;

TMR1ON=0;

key_scan(); //按键扫描函数

if(voice_time_cnt) //控制蜂鸣器声音的长短

相关主题
文本预览
相关文档 最新文档