嵌入式系统课程设计报告 哈工程 万年历文档格式.docx
《嵌入式系统课程设计报告 哈工程 万年历文档格式.docx》由会员分享,可在线阅读,更多相关《嵌入式系统课程设计报告 哈工程 万年历文档格式.docx(11页珍藏版)》请在冰豆网上搜索。
进度安排
第1天系统的初步设计
第2-5天编代码,实现系统功能
第6-7天系统的调试与完善
参考资料
[1]《基于ARM的嵌入式系统设计实验与实践教程》,清华大学出版社,2011.
[2]《嵌入式系统开发原理与实践》,清华大学出版社,2005.
[3]《ARM微处理器应用开发技术详解与实例分析》电子科技大学出版社,2007
1.题目
2.系统简介
二十一世纪的今天,最具代表性的计时产品就是电子万年历,它是近代世界钟表业界的第三次革命。
使计时产品的走时日差从分级缩小到1/600万秒,从原有传统指针计时的方式发展为人们日常更为熟悉的夜光数字显示方式,直观明了,并增加了全自动日期、星期以及其他日常附属信息的显示功能,它更符合消费者的生活需求。
因此,电子万年历的出现带来了钟表计时业界跨跃性的进步。
如今电子万年历飞入了寻常百姓家,挂式、台式以及带装饰画面等丰富的电子万年历数不胜数,不但满足了精准的计时需求,还将计时带上了科技时尚的味道。
ARM嵌入式技术可进行多种功能的设置,如闹钟、报时、日历查询、语音等;
并改善了很多原有石英钟不能解决的问题,例如:
数字夜光显示、数据存储以及全自动温度检测等功能;
这给传统计时消费带来了新的动力,越来越多的消费者选择了电子万年历。
所以,我们这次嵌入式课程设计的题目选为电子万年历。
本数字万年历系统实现了以下功能:
1.显示年、月、日和时间,支持LED和LCD两种方式显示;
2.时间显示方式有两种:
文本方式,图形方式;
3.时钟加入背景图片,并且图片可以根据季节变化,整体界面美观;
4.能通过小键盘调节万年历的年、月、日、时、分、秒;
5.定时闹钟提醒功能和整点报时功能;
6.显示当月日历,显示当天星期功能;
7.掉电后无需重新设置时间和日期。
3.系统设计
本次设计是基于ARM微处理芯片并用ADS1.2开发完成的,该万年历能够显示年、月、日、星期、时、分、秒,还有闹钟和整点报时功能,并且可任意设定时间和日期。
显示年、月、日和时间的方式均支持LED和LCD显示,时间的LCD显示方式又分为两种,一种是文本方式,另一种是图形方式,再配上背景图片,界面非常美观。
它还支持触摸屏操作和小键盘,修改时间和日期可以通过小键盘来调节。
并且掉电后无需重新设置时间和日期。
本系统采用多任务设计。
除了系统已定义的任务外,再定义了Main_Task_Prio、Rtc_Disp_Task_Prio和LED_Display_Task_Prio三个任务,任务优先级分别为12、14和和11。
通过Main_Task_Prio任务中响应键盘和触摸屏消息,可以对时钟和闹钟进行设置。
通过Rtc_Disp_Task实现系统时钟和日历的显示和更新,并判断闹钟时间是否到达并做到达后的相应处理以及判断是否到达整点并进行整点报时。
通过LED_Display_Task_Prio实现LED对时间、日期的显示和更新。
使用多任务系统中的信号量保证多个任务同时对系统的一个资源(系统时钟)访问而不产生冲突。
3.1LED显示时间和日期
万年历启动后,按下“NumLock”按键可以切换LED显示日期或时间,其中年、月、日、时、分、秒均为2位数字,并且每两个数字之间用“-”隔开,并且其显示的时间和日期与LCD屏幕上的时间和日期保持一致,其实现函数是在任务LED_Display_Task_Prio中,任务实时获取系统当前的时间和星期存放于led_currenttime中,再根据显示时间或日期的标识符led_flag的数值来进入不同的分支以显示时间或日期,其中currenttime是一个自定义的结构体,它存放了当前时间的年、月、日、时、分、秒。
通过函数Get_Rtc(&
led_currenttime)取得当前的时间信息,并通过调用LED相关的API函数将时间显示出来,通过WriteSDIO(ZLG7289_CMD_DATA0|0)使得,第一个数码管亮,同样使用WriteSDIO(ZLG7289_CMD_DATA0|1)数码管以方式0译码,第二个数码管亮,按照同样的方法就可以使得其他数码管灯也亮。
最后用OSTimeDly(100)将任务挂起一段时间。
用这样的方式实现LED显示时间和日期。
主要代码如下:
voidLED_Display_Task(void*Id)
{
structTimeled_currenttime;
Zlg7289_Reset();
//zlg7289复位
for(;
;
)
{
Get_Rtc(&
led_currenttime);
//获取当前时间
ZLG7289_ENABLE();
//使zlg7289占有同步串口
if(led_flag==0)
{//进入显示时间分支
Delay(5);
//延时
WriteSDIO(ZLG7289_CMD_DATA0|5);
//数码管以方式0译码,第6个数码管亮
WriteSDIO(0x0a);
//显示-
Delay
(1);
WriteSDIO(ZLG7289_CMD_DATA0|2);
//数码管以方式0译码,第3个数码管亮
WriteSDIO(ZLG7289_CMD_DATA0|7);
//数码管以方式0译码,第8个数码管亮
WriteSDIO(led_currenttime.hour/16);
//显示十位
WriteSDIO(ZLG7289_CMD_DATA0|6);
//数码管以方式0译码,第7个数码管亮
WriteSDIO(led_currenttime.hour%16);
//显示个位
WriteSDIO(ZLG7289_CMD_DATA0|4);
//数码管以方式0译码,第5个数码管亮
WriteSDIO(led_currenttime.minute/16);
WriteSDIO(ZLG7289_CMD_DATA0|3);
//数码管以方式0译码,第4个数码管亮
WriteSDIO(led_currenttime.minute%16);
WriteSDIO(ZLG7289_CMD_DATA0|1);
//数码管以方式0译码,第2个数码管亮
WriteSDIO(led_currenttime.second/16);
WriteSDIO(ZLG7289_CMD_DATA0|0);
//数码管以方式0译码,第1个数码管亮
WriteSDIO(led_currenttime.second%16);
}
elseif(led_flag==1)
{//进入显示日期分支
WriteSDIO(led_currenttime.year/16);
WriteSDIO(led_currenttime.year%16);
WriteSDIO(led_currenttime.month/16);
WriteSDIO(led_currenttime.month%16);
WriteSDIO(led_currenttime.day/16);
WriteSDIO(led_currenttime.day%16);
Delay
(1);
ZLG7289_DISABLE();
//zlg7289放弃同步串口控制?
OSTimeDly(200);
}
3.2LCD显示文本形式的系统时间、闹钟时间、日期和星期
LCD显示文本形式的时间和日期比较简单,时间、日期、星期和闹钟时间的显示都是用文本框显示的,并且时间要实时刷新,所以放在了Rtc_Disp_Task时钟刷新任务里面,其中时间文本框为pTimeTextCtrl,日期文本框为pDateTextCtrl,闹钟文本框为pAlarmTextCtrl,星期文本框为pWeekDayTextCtrl,只要在系统初始时,将文本框初始化,并且在Rtc_Disp_Task任务里面实时提取当前系统时间,以显示系统时间为例,获取当前系统时间的函数为Get_Rtc(&
currenttime),它把时间信息存入currenttime,然后将currenttime中的时间存入strtime,最后再用SetTextCtrlText(pTimeTextCtrl,strtime,TRUE)语句便将时间显示到系统时间文本框中,类似的其他文本框显示也是用这种方式。
比较特殊的是星期的显示,需要先根据年月日计算出当前是周几,然后再根据周几设定显示的字符,其中计算星期的函数如下:
U8GetSysWeek(U8year,U8month,U8day)
U8y,m,d,w;
U8c=20;
y=(year>
>
4)*10+(year&
0x0f);
//bcd转dec
m=(month>
4)*10+(month&
d=(day>
4)*10+(day&
if(m<
2)
{
m+=12;
y-=1;
}
w=y+(y/4)+(c/4)+(26*(m+1)/10)+d-1-(2*c);
//计算星期
return(w%=7);
3.3LCD显示图形形式的时间、日历和背景图片
由于背景图片和图形形式的时间、日历位置是重叠的,同时要使背景图片实现根据日期变化图片功能,而且时钟要实时变化、日历也要根据日期点亮当天的日期。
所以要求它们全部实时刷新显示,这就牵扯到刷新的顺序问题,否则会出现显示不正常的情况。
最终确定的刷新顺序是先刷新背景图片,之后是圆形表盘,最后是日历,这样做的好处是保证全部都可以显示正常,而且不需要额外擦除表盘之前留下的秒针、分针和日历的高亮日期。
背景图片随季节变化的实现比较简单,只需要在加载图片时判断当前日期的月份即可,在1-3月就在指定位置加载春天的背景,在4-6月就加载夏天的背景,同理在6-9月和10-12月分别加载秋天和冬天的背景图片。
圆形表盘形式的显示,需要画出表盘、时针、分针、和秒针,主要包括以下过程:
(1)先画出表盘一圈的60个短刻度和12个长刻度,其中给秒针、分针各对应60个坐标,表示60秒和60分,给时针设定12个坐标对应一圈的12个小时。
(2)再根据当前系统时间画出对应的时针、分针、秒针,其中主要用了函数MoveTo()和LineTo()来有关的线画表盘主要代码如下:
m=60;
n=120;
SetDrawOrg(pdc,m,n,&
oldx,&
oldy);
//设定画板60,120为原点
for(i=0;
i<
=60;
i++)//画时钟刻度短线//短线长度为5
{
x1=50*cos(PI/30*i);
//PI/30*i为角度范围从0到360度
y1=50*sin(PI/30*i);
x2=45*cos(PI/30*i);
y2=45*sin(PI/30*i);
MoveTo(pdc,x2,y2);
LineTo(pdc,x1,y1);
/*-----------画刻度值3------*/
MoveTo(pdc,34,-4);
LineTo(pdc,38,-4);
LineTo(pdc,38,4);
LineTo(pdc,34,4);
MoveTo(pdc,34,0);
LineTo(pdc,38,0);
//根据当前时间画秒针
timeys=30*sin(PI/30*time_s-(PI/2));
timexs=30*cos(PI/30*time_s-(PI/2));
MoveTo(pdc,0,0);
SetPenColor(pdc,0xffff0000);
LineTo(pdc,timexs,timeys);
类似的可以画出分针、时针。
画日历也采用了划线的方式,把1-31这31个数字先分别计算出它所在的位置,以及要划线的坐标,然后用连线的方式画出来,过程比较繁琐,这是代码量最大的一部分,然后根据日期去高亮显示对应的日期,高亮的方式就是通过改变画线的颜色再画一遍。
举其中一个例子,画数字7
/*----------画数字7---------*/
if(currentdate.day==7){
SetPenColor(pdc,0xff00ff00);
//设置划线为绿色
MoveTo(pdc,160,8);
LineTo(pdc,165,8);
LineTo(pdc,165,18);
SetPenColor(pdc,0x00000000);
//设置划线为黑色
}else{
其中比较困难的部分是根据不同月份日历显示不同的天数,并且要考虑平年和闰年的二月天数不一致的问题。
最终采用分支策略,判断当前日期属于哪种情况,然后进入不同分支,进行日历绘制。
3.4闹钟提醒和整点报时
闹钟提醒和整点报时也是需要实时检测的,所以放在了Rtc_Disp_Task时钟更新任务中,闹钟提醒需要先从设置闹钟的文本框中获取到设定的闹钟时间,然后获取系统当前时间,并实时比较两者,当不相等时不执行任何操作,当相等时开始播放指定的音乐。
整点报时也放在了Rtc_Disp_Task时钟更新任务中,任务实时获取当前时间并且实时判断当前秒数和分数是否同时为0,如果同时为0则表明是到整点时刻,于是便再按照当前小时的数值去播放对应的语音进行整点报时。
4.系统实现
几处关键部分的实现:
(1)表盘绘制函数CreateClockFigure()
圆形表盘的实现先画一个圆,对圆12等分获得分针刻度,60等分获得表针刻度。
使用sin和cos获得坐标值,利用API函数MoveTo和LineTo画出刻度。
(2)时间设定函数SetSystemTime()
首先从文本框中取出时间,把时间从字符型转化为整型,最后Set_Rtc_Clock()把整型时间设置为系统时间。
(3)日期设定函数SetSysDate()
从日期文本框中取出时间,把时间从字符型转化为整型,最后用Set_Rtc_Date()把整型日期设置为系统时日期,同时在确认设置之前要判断输入的日期是否合法,若不合法则不允许修改。
(4)表盘指针的更新
由于涉及到背景图片的更新,为了全部显示正常,需要把表盘全部更新,具体的实现是每次更新的时候,全部重新绘制。
(4)闹钟功能
本设计用一个文本框来模拟闹钟。
先设置闹钟时间并开启闹钟,判断当前系统时间达到闹钟设置的时间,如果是闹钟响应,闹钟文本框内容变为AlarmOn!
如果闹钟播放停止,则关闭闹钟并且闹钟文本框内容变为AlarmOff。
5.系统截图
6.总结
这次实验,收获了很多,特别是对嵌入式系统有了整体的认识和了解,更加熟练了对C语言的应用编写。
本次实现的万年历功能虽然简单,但每个功能的实现却是不断失败然后继续调试的过程。
在调试时,发现闰年二月日历天数显示不正常,无法消除从其他月份跳到二月时多余的天数,经过反复修改代码、调试,发现是因为判断闰年时计算出错,用16进制的年份直接除以4导致计算出错,从而无法正确判断出闰年。
由此说明了对C语言16进制等知识点还是不熟悉,这点日后需要继续练习。
最后感谢老师的耐心指导和同学的细心帮助。
7.参考文献