单片机课程设计项目:智能秒表的设计与制作 一、项目任务:智能秒表的设计与制作 用四位LED数码管制作带小数显示的秒表,前两位显示整数部分(秒),后两位显示小数部分(百分之一秒)。 所设计的秒表最大计时时间为99秒99。 开始时,数码管显示“00. 00”,表明从0开始计时。 本智能秒表有两种时间计时模式。(注意:模式1与模式2用同一个程序实现,即当程序下载至单片机芯片后,只需要按下相应按键,该秒表可工作在任意一种模式下) (一)基本部分: 1、模式1:累积计时模式 (1)按按键1可实现秒表开始、停止、再开始、再停止计时(时间累计); (2)计时完毕,按按键2计时归零。 2、模式2:间隔计时模式 (1) 按按键1开始计时; (2) 按按键2一次暂停秒表并显示当前时间。间隔时间显示2秒后秒表将持续计时。(例如在3秒钟时秒表被暂停,2秒钟后秒表从5秒开始继续计时。显示2秒是为了方便记录当前时间); (3) 在下一点终止时再按按键2记录此时相关信息,如此重复; (4) 当计时停止时按按键1; (5)可通过按键3查看前面操作所记录的时间,最多可查看10个记录; (6) 计时完毕,按按键2键计时归零。 (二)发挥部分 (1)用电脑时间校准秒表的时间,保证秒表走时的准确性(100秒的误差小于0.1秒),思考如何修改程序? (2)利用内部E2PROM存储器(STC89C52自带),使智能秒表具有掉电数据不丢失的功能,思考如何修改程序? (3)仿真无误后,购买元件制作该智能秒表,从商业价值考虑该智能秒表的美观性和实用性。 二、硬件电路及仿真效果图 三、STC90C52RC单片机引脚排列图 四、四位共阴极数码管引脚排列图 五、元器件清单 七、调试方法和步骤 1、通电前先用万用表欧姆档(1KΩ档)测量电源两端正反向电阻,正向电阻约80KΩ,反向电阻约6.5KΩ。如果电阻很小,则说明有短路性故障,不能通电,排除短路故障后才能通电,如果电阻为无穷大,则有开路性故障。 2、通电后,测量单片机40脚与20脚之间的电压应该有5V左右,如果电压不正常,则说明单片机供电不正常,应重点检查单片机供电电路。 3、测量单片机晶振引脚18和19脚电压,应该在1.9~2.3V左右,两个引脚电压相差0.2V左右,如果这两个引脚电压不正常,说明晶振电路没有起振,重点检查晶振和补偿电容是否焊接好。 4、测量单片机复位引脚9脚,正常工作时电压为0V,按复位键时为5V,松开手后由5V逐渐降至0V。如果该脚电压不正常,说明复位电路有故障,需要检查该脚的电阻、电容的连接和焊接是否正确。 5、以上步骤的测量如果正常,则多数是单片机的软件问题,可以通过编写几个小程序单独调试I/O口、按键、数码管和存储器的功能,测试到底是软件问题还是硬件问题。 六、C语言程序 - /********************************/
- /* 智能秒表(使用内部EEPROM)*/
- /*--------------------------------------------*/
- /**(1)启动/停止功能 **/
- /**(2)复位/间隔功能 **/
- /**(3)两种工作模式 **/
- /**(4)查看数据功能 **/
- /**(5)断电记忆功能 **/
- /********************************/
- #include <reg52.h>
- #include <intrins.h>
- #define uint unsigned int
- #define uchar unsigned char
-
- #define RdCommand 0x01 //定义内部EEPROM存储器部分
- #define PrgCommand 0x02
- #define EraseCommand 0x03
- #define Error 1
- #define Ok 0
- #define WaitTime 0x01
- sfr ISP_DATA=0xe2;
- sfr ISP_ADDRH=0xe3;
- sfr ISP_ADDRL=0xe4;
- sfr ISP_CMD=0xe5;
- sfr ISP_TRIG=0xe6;
- sfr ISP_CONTR=0xe7;
-
- sbit key1=P2^0; //定义"启动/暂停"按钮
- sbit key2=P2^1; //定义"复位"按钮
- sbit key3=P2^2; //定义"模式"按钮
- sbit key4=P2^3; //定义"查看"按钮
- sbit key5=P1^5; //定义"清除"按钮
-
- sbit wei1=P2^4; //定义位选
- sbit wei2=P2^5;
- sbit wei3=P2^6;
- sbit wei4=P2^7;
-
- bit flag_start,flag_mode,flag_2sec,flag_CLR;
- uchar temp,temp1,aa,aa1,shi,ge,shifen,baifen,num,i=0;
- uchar time_data1[11],time_data2[11];
- uchar code table[]={ //共阴极数码管编码
- 0x3f,0x06,0x5b,0x4f,
- 0x66,0x6d,0x7d,0x07,
- 0x7f,0x6f,0x77,0x7c,
- 0x39,0x5e,0x79,0x71,0x40};
-
- void display(uchar shi,uchar ge,uchar shifen,uchar baifen); //声明显示子函数
- void delay(uint z); //声明延时子函数
- void init(); //声明初始化函数
- void key(); //声明键扫描函数
- void display_process(); //声明显示处理函数
- void delayus(); //声明延时微秒函数
- void data_read(); //声明内部EEPROM数据读出函数
- void clear(); //声明清除EEPROM数据函数
-
- void ISP_IAP_enable(void); //声明EEPROM操作函数
- void ISP_IAP_disable(void);
- void ISPgoon(void);
- uchar byte_read(uint byte_addr);
- void SectorErase(uint sector_addr);
- void byte_write(uint byte_addr, uchar original_data);
- void memory_read(); //声明EEPROM读函数
- void memory_write(); //声明EEPROM写函数
-
- void main() //主函数
- {
- init(); //调用初始化子程序
- while(1)
- {
- key(); //调用按键扫描函数
- if(num==0&&i!=0) //如果一开机就按查看键
- memory_read(); //调用EEPROM数据读出函数
- if(flag_CLR==1) //如果按了清除键
- clear(); //调用清除EEPROM数据函数
- display_process(); //调用显示处理函数
- display(shi,ge,shifen,baifen); //调用显示函数
- }
- }
-
- void delay(uint z) //延时毫秒函数
- {
- uint x,y;
- for(x=z;x>0;x--)
- for(y=110;y>0;y--);
- }
-
- void display(uchar shi,uchar ge,uchar shifen,uchar baifen) //显示程序
- {
- P0=table[shi]; //使用动态扫描的方法实现数码管显示
- wei1=0;
- delay(1);
- wei1=1;
-
- if(flag_CLR==0) //正常显示时,第二位显示小数点
- P0=table[ge]|0x80;
- else //按清除键时,不显示小数点
- P0=table[ge];
- wei2=0;
- delay(1);
- wei2=1;
-
- P0=table[shifen]; //使用动态扫描的方法实现数码管显示
- wei3=0;
- delay(1);
- wei3=1;
-
- P0=table[baifen];
- wei4=0;
- delay(1);
- wei4=1;
- }
-
- void init() //初始化子程序
- {
- temp=0;
- TMOD=0x01; //使用定时器T0的方式1
- TH0=(65536-10000)/256;
- TL0=(65536-10000)%256; //定时10ms中断一次
- EA=1; //中断总允许
- ET0=1; //允许定时器T0中断
- }
-
- void key() //按键扫描函数
- {
- uchar j;
- if(key1==0) //检测"启动"按钮是否按下
- {
- delay(5); //延时去抖动
- if(key1==0) //再次检测"启动"按钮是否按下
- {
- while(!key1) //松手检测(按下时数码管不灭)
- {
- display_process(); //调用显示处理函数
- display(shi,ge,shifen,baifen); //调用显示函数
- }
- flag_start=~flag_start;
- if(flag_start==1)
- TR0=1; //启动定时器开始工作
- else
- TR0=0;
- }
- }
- if(key2==0) //检测"间隔"按钮是否按下
- {
- delay(5);
- if(key2==0)
- {
- while(!key2) //松手检测
- {
- display_process(); //调用显示处理函数
- display(shi,ge,shifen,baifen); //调用显示函数
- }
- if(flag_mode==0||i!=0)
- {
- shi=0;
- ge=0;
- shifen=0;
- baifen=0;
- aa=0;
- temp=0;
- TR0=0;
- i=0;
- num=0;
- for(j=0;j<=10;j++)
- {
- time_data1[j]=0;
- time_data2[j]=0;
- }
- }
- else
- {
- num++;
- if(num==11)
- num=1;
- memory_write();
- }
- }
- }
- if(key3==0)
- {
- delay(5);
- if(key3==0)
- {
- while(!key3) //松手检测
- {
- display_process(); //调用显示处理函数
- display(shi,ge,shifen,baifen); //调用显示函数
- }
- flag_mode=~flag_mode;
- }
- }
- if(key4==0)
- {
- delay(5);
- if(key4==0)
- {
- while(!key4) //松手检测
- {
- display_process(); //调用显示处理函数
- display(shi,ge,shifen,baifen); //调用显示函数
- }
- TR0=0;
- i++;
- if(i==11)
- i=1;
- data_read();
- }
- }
- if(key5==0)
- {
- delay(5);
- if(key5==0)
- {
- while(!key5) //松手检测
- {
- display_process(); //调用显示处理函数
- display(shi,ge,shifen,baifen); //调用显示函数
- }
- flag_CLR=~flag_CLR;
- }
- }
- }
-
- void display_process() //显示处理函数
- {
- shifen=aa%100/10; //正常走秒的处理
- baifen=aa%10;
- shi=temp%100/10;
- ge=temp%10;
- if(flag_mode==1&&num!=0&&flag_2sec==0) //间隔2秒停顿的处理
- {
- shi=time_data2[num]/10;
- ge=time_data2[num]%10;
- shifen=time_data1[num]/10;
- baifen=time_data1[num]%10;
- if(temp-time_data2[num]==2)
- flag_2sec=~flag_2sec;
- }
- if(i!=0) //查看存储数据的处理
- {
- shi=time_data2[i]/10;
- ge=time_data2[i]%10;
- shifen=time_data1[i]/10;
- baifen=time_data1[i]%10;
- }
- }
-
- void timer0() interrupt 1 //定时器T0中断服务函数
- {
- TH0=(65536-10000)/256; //重新赋初值
- TL0=(65536-10000)%256;
- aa++; //中断一次10ms变量aa的值加1
- if(aa==100) //中断100次后,定时时间100*10ms=1000ms=1s,变量temp的值加1
- {
- aa=0;
- temp++;
- if(temp==100) //秒表到达100s后回零
- temp=0;
- }
- }
-
- /* EEPROM存储器操作函数 */
- /* ================================= */
- void ISP_IAP_enable(void)
- {
- EA = 0;
- ISP_CONTR = ISP_CONTR & 0x18;
- ISP_CONTR = ISP_CONTR | WaitTime;
- ISP_CONTR = ISP_CONTR | 0x80;
- }
- /* ================================= */
- void ISP_IAP_disable(void)
- {
- ISP_CONTR=ISP_CONTR&0x7f;
- ISP_TRIG=0x00;
- EA=1;
- }
- /* ==================================== */
- void ISPgoon(void)
- {
- ISP_IAP_enable();
- ISP_TRIG = 0x46;
- ISP_TRIG = 0xb9;
- _nop_();
- }
- /* ============================================ */
- uchar byte_read(uint byte_addr)
- {
- ISP_ADDRH = (uchar)(byte_addr >> 8);
- ISP_ADDRL = (uchar)(byte_addr & 0x00ff);
- ISP_CMD = ISP_CMD & 0xf8;
- ISP_CMD = ISP_CMD | RdCommand;
- ISPgoon();
- ISP_IAP_disable();
- return (ISP_DATA);
- }
- /* ========================================== */
- void SectorErase(uint sector_addr)
- {
- uint iSectorAddr;
- iSectorAddr = (sector_addr & 0xfe00);
- ISP_ADDRH = (uchar)(iSectorAddr >> 8);
- ISP_ADDRL = 0x00;
- ISP_CMD = ISP_CMD & 0xf8;
- ISP_CMD = ISP_CMD | EraseCommand;
- ISPgoon();
- ISP_IAP_disable();
- }
- /* ============================================ */
- void byte_write(uint byte_addr, uchar original_data)
- {
- ISP_ADDRH = (uchar)(byte_addr >> 8);
- ISP_ADDRL = (uchar)(byte_addr & 0x00ff);
- ISP_CMD = ISP_CMD & 0xf8;
- ISP_CMD = ISP_CMD | PrgCommand;
- ISP_DATA = original_data;
- ISPgoon();
- ISP_IAP_disable();
- }
-
- void data_read() //读数组数据函数
- {
- aa=time_data1[i];
- temp=time_data2[i];
- }
-
- void memory_write() //写EEPROM数据函数
- {
- aa1=aa;
- temp1=temp;
- time_data1[num]=aa1;
- time_data2[num]=temp1;
- if(num==1)
- SectorErase(0x2000); //擦除扇区
- byte_write(0x2000+num,aa1); //重新写入数据
- byte_write(0x2000+num+10,temp1); //重新写入数据
- flag_2sec=0;
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
完整论文下载(word格式 可编辑)还带仿真和源码:
|