STM8单片机课程设计报告闹钟+定时器解析.docx
《STM8单片机课程设计报告闹钟+定时器解析.docx》由会员分享,可在线阅读,更多相关《STM8单片机课程设计报告闹钟+定时器解析.docx(22页珍藏版)》请在冰豆网上搜索。
STM8单片机课程设计报告闹钟+定时器解析
课程设计内容与要求
课程设计目的:
1、巩固和加深对单片机原理知识的理解和运用。
2、进一步提高学生综合运用所学知识的能力。
3、培养学生综合分析问题、发现问题和解决问题的能力
基本要求:
1)显示时间和日期
2)可设置多个闹钟
3)可以调整时间
扩展功能:
1)按键复用共有六个按键,每个按键在不同模式下有不同的功能。
2)秒表功能进入秒表界面后,再按一下就秒表开始计时,再按一下秒表停止。
3)省电模式在每天00:
00会自动进入省电模式,8个数码管会自动关闭,到5:
00时会自动退出省电模式;也可以手动进入和退出省电模式。
4)查看/删除闹钟查看闹钟时间为五秒,五秒内按键无动作则自动退出。
器件介绍
本次设计主要用到的硬件有:
1、51实验板1块
2、stm8s105c6开发板1快
51实验板:
主要用到实验板上的硬件有:
1)两个四位一体的共阴LED数码管,电路图如下:
2)两个74HC573的锁存芯片:
3)8个独立按键,用了其中6个
5)一个蜂鸣器:
Stm8s105c6开发板:
总共有48个引脚,本设计共用引脚20个引脚。
PB0~PB7和PE6~PE7用于数码管显示控制,PD0~PD5用于键盘输入,PD6、PE0、PE3用于LED指示灯控制。
PD7用于蜂鸣器控制。
设计方案
本次设计采用51实验板和stm8s105c6开发板来完成,没有做实物。
显示电路采用8个共阳数码管显示;控制按键采用六个独立按键,每一个按键都有复用功能;闹钟提醒采用蜂鸣器;外加四个不同颜色的LED发光二极管,当进入相应功能模式时对应的指示灯就会亮。
1、键盘设计:
在键盘电路中,使用的是6个独立键盘。
键盘在源程序中的功能安排如下:
1)当扫描到有键盘按下时,设置相应标志位,然后延时一定时间消抖。
2)键盘的设置键盘表面如图:
其中各键设置为:
(待机状态:
正常的时间显示模式)
待机状态下是“设置闹钟”,在其他模式下是“递增功能”
待机状态下是“调整时间”,在其他模式下是“递减功能”。
待机状态下是“秒表功能”,在其他模式下是“左移功能”。
待机状态下是“查看/删除闹钟”,在其他模式下是“右移功能”
OK待机状态下是“切换显示”,在省电模式下是“退出功能”,在其他模式下是“确认功能”
Esc待机状态下是“省电模式”,在查看/删除闹钟模式下是“删除功能”,在其他模式下是“退出功能”
2、数码管显示:
采用按位扫描的动态显示方式。
3、其他设计:
定时器使用stm8s105c6中的Tim2定时器,每1ms产生一次溢出中断。
闹铃响的时间为15秒。
查看/删除闹钟模式下,5s内键盘无动作则自动退出。
每次按键按下后,会延时240ms,此时间内不会再对键盘进行扫描。
在每天00:
00会自动进入省电模式,8个数码管会自动关闭,到5:
00时会自动退出省电模式;也可以手动进入和退出省电模式。
4、设计思路:
是
否
是
否
是
否
是
否
是
否
是是
否
5、以下是部分程序:
//**************标志位************//
_Booloff;//省电模式
_Booltab;//切换显示模式
_Boolrun;//控制秒表
_Boolleft;//左移
_Boolright;//右移
_Boolset;//设置闹钟
_Booladjust;//调整时间
_Boolok;//确定
_Boolesc;//取消或删除
_Boolduan@PE_ODR:
7;//PE6作为段锁存
_Boolwei@PE_ODR:
6;//PE7作为位锁存
_Boolset_c@PE_ODR:
3;
_Booladjust_t@PE_ODR:
0;
_Boolcheck_clock@PD_ODR:
6;
_Boolc_alarm@PD_ODR:
7;
//*****************相关变量*******************//
unsignedcharmonth_day[]={31,28,31,30,31,30,31,31,30,31,30,31};
unsignedcharwei_ma[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//显示位码
unsignedcharduan_ma[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//共阴极显示段码
unsignedcharsec_duan_ma[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
unsignedcharhour_[5],minute_[5],second_[5]={1,2,3,4,5};//用于秒表
unsignedcharsec_time[8];//用于秒表
unsignedcharclocks[5][8];//可存5个闹钟信息
unsignedchartimes[8];//存放当前时间
unsignedchardays[8];//存放当前日期
unsignedintT1=1000;//200x10ms=1s秒计算
unsignedintT2=0;//20x10ms=200ms
unsignedcharc=0;//用于记录闹钟个数
unsignedcharhour=8,minute=55,second;
unsignedcharmonth=12,day=12;intyear=2011;
unsignedchar*ud;//ud加减指针
unsignedcharlr;//记录指针所指位
charA,B,C,D,E,F,G,H,I,J,K,L;
intT3=0,T4=0;//其他定时,闹钟显示和蜂鸣器
intsec1,sec2;//用于秒表的变量
//********************函数声明************************//
unsignedcharleapyear(intb);//闰年判断
voidTIM2_initial(void);//初始化定时器
voidGPIO_initial(void);//初始化I/O引脚
voiddisplay(unsignedcharx[8]);//时间显示函数
voidtime_process(void);//时间处理函数
voidtimes_bi_duan_ma(unsignedchar,unsignedchar,unsignedchar);
voiddays_bi_duan_ma(int,unsignedchar,unsignedchar);
voidkeyscan(void);//键盘扫描函数
voidset_clock(void);//设置闹钟
voidadjust_times(void);//
voidclock_display(void);//闹钟显示函数
voidalarm(void);
voidsecond_clock(void);
//*************主函数*****************//
main()
{
GPIO_initial();
TIM2_initial();
leapyear(year);
while
(1)
{
time_process();
alarm();
if(T2==0)//消抖,延时100ms
keyscan();
if(hour==0&minute==0&second==0)
off=1;
if(hour==5&minute==0&second==0)
off=0;
if(set==0&adjust==0&left==0&right==0&ok==0&off==1)
{
PB_ODR=0xff;//不显示时间,省电模式
wei=1;
wei=0;
}
elseif(set==1&adjust==0)//设置闹钟模式
{
set_c=0;
set_clock();
}
elseif(set==0&adjust==1)//调整时间模式
{
adjust_t=0;
adjust_times();
}
elseif(left==1)//秒表模式
second_clock();
elseif(right==1)//查看/删除闹钟模式
{
check_clock=0;
clock_display();
}
elseif(tab==0)
{
times_bi_duan_ma(hour,minute,second);
display(times);//时间显示
}
elseif(tab==1)
{
days_bi_duan_ma(year,month,day);
display(days);
}
}
}
//**********************秒表函数***************************//
voidsecond_clock(void)
{
if(sec1==999)sec1=999;
sec_time[0]=0x00;
sec_time[1]=0x00;
sec_time[2]=0x00;
sec_time[3]=duan_ma[sec1/100];
sec_time[4]=duan_ma[sec1%100/10];
sec_time[5]=sec_duan_ma[sec1%10];
sec_time[6]=duan_ma[sec2/10];//小数位
sec_time[7]=duan_ma[sec2%10];//小数位
display(sec_time);
if(esc==1)
{
left=0;
run=0;
sec1=0;
sec2=0;
esc=0;
}
}
//**********************闹钟设置****************************//
voidset_clock(void)
{
unsignedchari,j;
display(times);
if(lr==0)ud=&G;//指针指向G
if(lr==1)ud=&H;
if(lr==3)ud=&I;
if(lr==4)ud=&J;
if(lr==6)ud=&K;
if(lr==7)ud=&L;
times[lr]=duan_ma[*ud];//*ud即是ud所指变量的当前值
if(ok==1)
{
hour_[c]=G*10+H;
minute_[c]=I*10+J;
second_[c]=K*10+L;
if(c==4)c=0;
elsec++;
set=0;
ud=0;
lr=0;
right=0;
left=0;
ok=0;
set_c=1;
}
}
//**********************校时函数****************************//
voidadjust_times(void)
{
if(ok==0)
{
display(days);
if(lr==0)ud=&A;
if(lr==1)ud=&B;
if(lr==3)ud=&C;
if(lr==4)ud=&D;
if(lr==6)ud=&E;
if(lr==7)ud=&F;
days[lr]=duan_ma[*ud];
}
if(ok==1)
{
display(times);
if(lr==0)ud=&G;
if(lr==1)ud=&H;
if(lr==3)ud=&I;
if(lr==4)ud=&J;
if(lr==6)ud=&K;
if(lr==7)ud=&L;
times[lr]=duan_ma[*ud];
}
if(esc==1&ok==1)
{
year=2000+A*10+B;
month=C*10+D;
day=E*10+F;
hour=G*10+H;
minute=I*10+J;
second=K*10+L;
ok=0;
esc=0;
lr=0;
ud=0;
adjust=0;
T1=100;
adjust_t=1;
}
}
//********************时间处理函数**************************//
voidtime_process(void)
{
if(second>=60)//秒到60,分钟加1
{
second=0;
minute++;
if(minute>=60)//分钟到60,小时加1
{
minute=0;
hour++;
if(hour>=24)//小时到24,回零
{hour=0;
leapyear(year);//闰、平年计算
if(day>=month_day[month-1])//是否到了每月的最后一天
{
day=1;
if(month>=12)
{
month=1;
year++;
}
elsemonth++;
}
elseday++;
}
}
}
}
//*******************时间数据的转换*************************//
voidtimes_bi_duan_ma(unsignedcharhour,unsignedcharminute,unsignedcharsecond)
{
G=hour/10;H=hour%10;
I=minute/10;J=minute%10;
K=second/10;L=second%10;
times[0]=duan_ma[G];//时//数据的转换,因我们采用数码管0~9的显示,将数据分开
times[1]=duan_ma[H];
times[2]=0x40;//加入"-"
times[3]=duan_ma[I];//分
times[4]=duan_ma[J];
times[5]=0x40;
times[6]=duan_ma[K];//秒
times[7]=duan_ma[L];
}
//*******************日期数据的转换************************///
voiddays_bi_duan_ma(intyear,unsignedcharmonth,unsignedcharday)
{
A=((year%100)/10);B=((year%100)%10);
C=month/10;D=month%10;
E=day/10;F=day%10;
days[0]=duan_ma[A];//年
days[1]=duan_ma[B];
days[2]=0x40;
days[3]=duan_ma[C];//月
days[4]=duan_ma[D];
days[5]=0x40;
days[6]=duan_ma[E];//日
days[7]=duan_ma[F];
}
//******************时间显示函数*******************************//
voiddisplay(unsignedchartime[8])
{
unsignedchara=0,i;
while(a<8)
{
PB_ODR=0x00;//确保清除之前的数据,防止重影
duan=1;
duan=0;
if((set==1|adjust==1)&a==lr&T1<=50)
PB_ODR=0xff;//指针所指的位闪烁
else
PB_ODR=wei_ma[a];
wei=1;
wei=0;
PB_ODR=time[a++];
duan=1;
duan=0;
for(i=100;i>0;i--);//延时
}
a=0;
}
//*********************查看/删除闹钟函数***************************//
voidclock_display(void)
{
unsignedchara=0,i;
times_bi_duan_ma(hour_[lr],minute_[lr],second_[lr]);
display(times);
if(esc==1)
{
esc=0;
for(i=0;i<8;i++)
hour_[lr]=0;
minute_[lr]=0;
second_[lr]=lr+1;
}
if(T3==0)right=0;//5s内键盘无输入,切换到原来的显示模式
}
//*******************键盘扫描函数***************************//
voidkeyscan(void)
{
T2=241;//延时241ms,消抖
if((PD_IDR&0x01)==0x00)//加或设置闹钟按键
{
if(set==1|adjust==1)//闹钟或校时模式下
{
if(*ud==9)
*ud=0;
else
(*ud)++;//ud所指的变量加1
}
else//正常显示模式下
{
set=1;
esc=0;
ok=0;
}
}
elseif((PD_IDR&0x02)==0x00)//减或调整时间
{
if(set==1|adjust==1)//闹钟或校时模式下
{
if(*ud==0)
*ud=9;
else
(*ud)--;//ud所指的变量减1
}
else//正常显示模式下
{
adjust=1;
esc=0;
ok=0;
}
}
elseif((PD_IDR&0x04)==0x00)//左移/查看闹钟
{
if(set==1|adjust==1)//闹钟或校时模式下
{
lr--;
if(lr==255)lr=7;
elseif(lr==5)lr=4;//跳过第3和6个数码管
elseif(lr==2)lr=1;
}
elseif(right==1)//查看闹钟模式下
{
T3=5000;
if(lr==0)
lr=4;
else
lr--;
}
elseif(left==1)run=run^1;//为1时秒表开始计时
else
{
left=1;
esc=0;
ok=0;
}
}
elseif((PD_IDR&0x08)==0x00)//右移/查看闹钟
{
if(set==1|adjust==1)//闹钟或校时模式下
{
lr++;
if(lr==8)lr=0;//跳过第3和6个数码管
elseif(lr==2)lr=3;
elseif(lr==5)lr=6;
}
elseif(right==1)//查看闹钟模式下
{
T3=5000;
if(lr==4)
lr=0;
else
lr++;
}
else
{
right=1;
T3=5000;
esc=0;
ok=0;
}
}
elseif((PD_IDR&0x10)==0x00)//确定
{
off=0;//退出省电模式
tab=tab^1;
if(ok==0)ok=1;
elseif(ok==1&adjust==1)esc=1;
}
elseif((PD_IDR&0x20)==0x00)//取消删除或进入省电模式
{
if(adjust==1|set==1)
{
set=0;
ok=0;
lr=0;
ud=0;
adjust=0;
set_c=1;
adjust_t=1;
check_clock=1;
}
elseif(set==0&adjust==0&left==0&right==0)
{
off=1;
ok=0;
}
elseesc=1;
}
else1;
}
调试过程
因为此次程序比较大,实现的功能比较多,所以从一开始我便一个功能一个功能的做,边做边调试,做完一个功能之后才做下一个。
第一次做数码管显示的时候就出现问题,8个数码管一直显示乱码。
当时我就把程序用原来的动态显示改为静态显示,可是还是不行。
于是我就再把8个数码管同时显示改为一个数码管显示,这下就可以了。
然后我又再把显示的位数慢慢增加,当增加到第5位的时候发现,乱码又来了,之后我又改用其他引脚,发现原来之前写的程序时没有错的,是因为芯片的一个引脚出问题了,不知道为什么,那个引脚总是高电平。
所以才导致了之前的乱码一直出来。
数码管调试好之后,其他的程序调试起来就顺利多了,在用到蜂鸣器的时候还遇到了一个问题。
因为之前课程设计做蜂鸣器电路的时候都是做成有源蜂鸣器,所以,给个电平就可以控制蜂鸣器的开和关。
但这次我在实验板上做,上面的蜂鸣器是无源的,要给一定1KHz以上频率的方波才可以响。
这个问题当时拖了很久都没解决,如果要产生1KHz的频率,那我必须修改内部时钟,这样会导致定时器无法正常工作,最后经过一番苦思终于想出了一个办法,就是把定时中断改为1ms一次,然后在定时中断中修改引脚的输出,这样就可以产生1KHz的方波信号了。
还有在控制功能指示灯的时候,一开始没注意选用了PE0~PE3引脚,结果出现两个指示灯总是不亮,刚开始还以为灯坏了,但换了其他灯还是一样,所以我就怀疑是引脚出问题了,但最后才发现不是引脚问题,而是PE1和PE2这两个引脚不可以初始化为推挽输出。
最后我