基于STM32开发板的多功能四面钟设计
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
基于STM32开发板的多功能四面钟设计
多功能四面钟
生产组长:郭壮
设计组长:刘赛
设计小组成员:侯化安、李瑞芹、李德志、付强、
房韶轩、刘国磊、刘长战、胡新洋
2012年9月12日
一.概述
本设计基于STM32F107/207基板,该基板集成了OLED 显示、8563日历时钟芯片、DS18B20温度传感器、LIS35DE 加速度计、蜂鸣器、按键等多种外扩硬件,本设计充分利用基板上的资源,并将其结合在一起,设计成多功能四面钟。
利用加速度计,进行阈值划分,在上下左右四个方向上分别调用不同的程序,分别实现日历、闹钟、秒表、时间温度的功能,并用OLED 显示器将四个功能显示出来。
二.总体设计
1. 系统方案(1)方案介绍
本系统充分利用STM32F107/207基板和核心板,将板上的资源充分开发,将LED 显示、8563日历时钟芯片、DS18B20温度传感器、LIS35DE 加速度计、蜂鸣器、按键等多种外扩硬件结合到一起,设计成可以在OLED 四个面上分别显示日历、闹钟、秒表、时间温度不同的内容的多功能四面钟。
本系统能够实现的功能为在四个面上分别显示不同的内容。
首先,通过调用加速度计程序,将得到的数据进行阈值划分,分成四个部分,来分别调用不同的程序,以实现在不同的方向上显示不同内容。
在正面时显示日历,此部分依靠调用8563日历芯片程序实现;在左侧面显示秒表,依靠定时器程序实现;在右侧面显示闹钟,依靠调用8563日历芯片程序实现;在反面显示时间及温度,依靠调用8563时钟芯片程序以及DS18B20温度传感器程序实现。
当然每一部分的显示都离不开OLED 部分的程序调用,也可以通过调用按键程序对日期和时间进行
修改,对闹钟进行设定,对秒表进行分时计时控制,通过蜂鸣器实现闹铃以及整点报时。
(2)方案结构
STM32F107ARM 单片机
按键
加速度计
DS18B20温度传感器
8563时钟芯片
OLED 显示器
C A N 总线
蜂鸣器
图 1 多功能四面钟系统总体设计框图
2. 模块化分
本系统根据实现功能划分为几大模块,分别为:加速度计部分、8563日历时钟芯片部分、温度传感器部分、OLED显示部分、定时器部分、按键部分。
3. 人员分工
李德志、胡新洋:完成了对加速度计的研究,编写此部分程序,李德志设计系统的整体框架,对全组所有子程序进行整合,并解决按键的消抖问题、蜂鸣器的使
用;
刘赛、李瑞芹:完成了对OLED的研究学习,实现在四个方面的显示;
刘长战、刘国磊:研究ARM定时器,完成秒表功能;
郭壮、付强、房韶轩:研究了8563芯片,完成时钟、日历、闹钟功能;
侯化安:研究DS18B20温度传感器芯片,完成实时温度检测功能。
三.关键模块设计
1. 不同面的显示设计,由于OLED的设计为倒序纵向点阵,所以要显示横向就在代
码点整应按照横向输入生成代码,经过我们的实验完成其他三面的字符和汉字代码。
2. 不同面的切换要刷掉当前页面的内容,而如果采用简单的清屏则会使得在同一
面时也实时刷屏这不能达到我们的设计,解决这个问题我们做了标记变量解决了刷屏问题。
3. 实际中我们校对时钟时对应的数字是闪烁的,所以我们的设计也采用这一理念,
我们具体的做法是在对应的校对时,只让其对应的点阵清屏加延时已达到闪烁的效果
4. 为了达到按键在对应的界面校对其参数,我们的按键做了判断条件使得在其相
应界面进行调整并退出时其参数刷零以至不影响其它面的校对。
四.测试结果
通过测试,我们顺利实现预期功能,主要功能叙述如下:
1.在四个界面上显示出不同的内容:日历(包括年、月、日、星期)、闹钟、秒表、
温度及时间;
2.修改日期(包括年、月、日)
按键1为选择键,每按四次循环一周,按下按键1后,需要调整的时间就一直处于闪烁状态,此时可以用按键2、按键3进行修改,按键表示日期加,按键3表
示日期减;
3.修改时间(包括时、分、秒)
按键1为选择键,也是每四次循环一周,需要修改的处于闪烁状态,通过按键
2、按键3进行时间加、减;
4.整点报时,每次到整点时,就会有铃声响起,响起时间为一分钟;如果在此时按
下按键4时,闹铃就停止;
5.设定闹钟时间,按键1为选择键,按键2、3分别表示时间加或减;
6.闹钟铃响,如果没有按下按键4,在设定的时间的那一分钟里会一直响;
7.秒表的设定。
按键1为启动/停止键,按键2为清零键。
五.设计不足
时间仓促,本设计并不是尽善尽美,仍有很多不足之处。
主要是在以下几个方面需要进一步完善:
1.断电后,闹钟的设定值会消失,原则上调用8563时钟芯片的数据,断电以
后,在一定的时间内,是不会消失的,这个问题需要找清楚原所在。
2.加速度计利用不完善,加速度计有X、Y、Z三个轴,本设计中,只是利用
了X、Y两个轴,致使显示的时候,并不是很稳定。
这个问题的解决,需要将X、
Y、Z三个轴都计算进去,利用三维坐标,求出其重力加速度方向,据此确定所显
示的界面。
3.以下几点为答辩过程中,本小组存在的问题:
1)问:OLED显示器的尺寸发生变化,本设计能否适应不同的尺寸显示?
对应的显示比例能否跟着改变?
答:本设计中OLED显示采用的是最基本的办法:按照每个界面所需要
的内容不同,用字模取字软件生成不同的代码,然后用显示函数调用不同
的代码,实现不同界面的显示。
如果OLED的屏幕尺寸发生改变,本设
计中的显示内容,不能根据屏幕尺寸的变化而发生相对应的改变。
因为,
如果屏幕尺寸改变,需要的显示代码就发生对应的改变,本设计无法对显
示内容的代码进行操作,使之适应不同尺寸的屏幕。
2)问:秒表的晶振准确吗?误差是多少?
答:定时器所采用的中断的晶振是存在一定的误差的,通过我们计算其
经过分频后三分钟与实际差一秒,所以误差为:1/(3*60) * 100% = 0.56%
3)闹钟是如何实现报警的?是比较吗?
答:本设计中的闹钟是利用8563时钟芯片中的报警功能,采用硬件报警,
而不是软件报警。
采用例程中所给的函数设定报警的时间之后,就会将此
时间传递给8563时钟芯片,如果芯片内检测到报警时间与真实时间相等
的话,就会触发报警标志,我们根据得到的报警标志来使蜂鸣器响。
六.总结
本次系统设计,我们小组充分利用STM32F107基板以及ARM核心板上的资源,设计完成了多功能四面钟。
在设计的过程中,我们小组,首先对已有的学习资料进行了研究,对DS18B20温度传感器、8563时钟芯片、加速度计、OLED显示等芯片的原理以及程序使用方法进行了一定程度的了解。
随后,我们分工合作,将整个系统的程序分成了五个模块,由五个设计小队分别完成,这样,既可以提高整体进度,又可以让每一位同学参与到设计过程中,学习到更多的知识,享受团队合作的乐趣和成就感。
当然,设计的过程并不可能一帆风顺,我们的知识有限,对整个程序的了解程度也不是很多,编写程序的过程中还是遇到了很多的问题。
将各个子程序编写完成之后,进行整合的难度更大。
就拿一点来说,开始时,整个系统程序的结构不合理,导致了按键的循环延时时间影响了OLED的显示,出现了画面不断闪
现的现象。
后来,我们不得不对整个程序框架进行重新调整,才是问题得到解决。
虽然难题不可避免,我们还是努力调试,修改程序,在这个过程中,我们也学习到了很多的知识。
通过这次实习设计,我们充分认识到了实践的重要性,也认识到了要想完成一个设计,只是设想是没用的,要的是不断的实践。
只有在实践的过程中,才会遇到各种问题,解决问题就是实践的过程。
动手之后才明白存在哪些问题,并积极想办法解决,这就是丰富经验的过程。
同时,我们也清楚的认识到,团队合作的重要性,一个人的思考能力和知识面是有限的,很多问题,一个人可能深陷其中,百思不得其解,但是在队友的帮
助下,这些问题就不再那么令人头疼了。
正是通过团队合作,我们小组才能顺利解决设计路上的种种难题,顺利完成了设计任务。
七.附录
1.关键程序代码
①主程序
int main (void)
{
SystemInit();//设置系统时钟
I2C_GPIOInit();//i2c总线设置
OLED_init();//oled初始化
Timer4Init();//定时器4设置
RealTime_Init();
NVIC_Configuration();
TIMER3_Init();//定时器3初始化
MEMS_Init();//重力加速度计初始化
DS18B20_Init();//温度传感器初始化
GetTime(&RealTime);//读取当前时间
while(1)
{
keyscan(); // 按键扫描
mems_change();//获取xyz坐标值alarm_running();//闹钟判断
}
}
②按键扫描程序
void keyscan()
{
if(status == Time_change)//时间按键设置{
if(KEY1Read())
{
select++;
DelayMs(100);
if(select>=4)
select = 0;
}
if(select==1)
{
if(KEY2Read())
RealTime.hour ++;
if(KEY3Read())
RealTime.hour --;
if(RealTime.hour > 23)
RealTime.hour = 0;
SetTime(&RealTime);
}
else if(select==2)
{
if(KEY2Read())
RealTime.minute ++;
if(KEY3Read())
RealTime.minute --;
if(RealTime.minute > 59) RealTime.minute = 0;
SetTime(&RealTime);
}
else if(select==3)
{
if(KEY2Read())
RealTime.second = 0;
if(KEY3Read())
RealTime.second = 0;
SetTime(&RealTime);
}
}
if(status == ALARM_change)//闹钟按键{
if(KEY1Read())
{
select++;
DelayMs(100);
if(select>=3)
select = 0;
}
if(select==1)
{
if(KEY2Read())
AlarmTime.hour ++;
if(KEY3Read())
AlarmTime.hour --;
if(AlarmTime.hour > 23)
AlarmTime.hour = 0;
SetAlarmHour(AlarmTime.hour,TRUE);
}
else if(select==2)
{
if(KEY2Read())
AlarmTime.minute ++;
if(KEY3Read())
AlarmTime.minute --;
AlarmTime.minute = 0; SetAlarmMinute(AlarmTime.minute,TRUE); }
}
if(status == DA TE_change)//日期按键{
if(KEY1Read())
{
select++;
DelayMs(100);
if(select>=4)
select = 0;
}
if(select==1)
{
if(KEY2Read())
RealDate.year ++;
if(KEY3Read())
RealDate.year --;
if(RealDate.year > 2500)
RealDate.year = 1990;
SetDate(&RealDate);
}
else if(select==2)
{
if(KEY2Read())
RealDate.month ++;
if(KEY3Read())
RealDate.month --;
RealDate.month = 1;
SetDate(&RealDate);
}
else if(select==3)
{
if(KEY2Read())
RealDate.day ++;
if(KEY3Read())
RealDate.day --;
if(RealDate.day > 31)
RealDate.day = 0;
SetDate(&RealDate);
}
}
if(status == MIAOBIAO_change)//秒表设定{ if(KEY1Read())
{
select++;
DelayMs(200);
if(select==2)
select = 0;
}
if(select==0)
{
flag=STOP;
}
else if(select==1)
{
flag=START;
}
if(KEY2Read())
{
flag=CLEAR;
}
}
}
③获取XYZ轴坐标程序
void mems_change()
{
MEMS_Get(&mems); //获取加速度计坐标
DelayMs(80);
if( (mems.x>=40&&mems.x<=60)&&((mems.y>=0&&mem s.y<=30)||(mems.y>=230&&me ms.y<=255)) )
{
status = DATE_change;
if(down==1)
WriteData(0,0,132,0,8,0); //清屏
down_display();
}
else
if( (mems.y>=30&&mems.y<=60)&&((mems.x>=0&&mem
s.x<=39)||(mems.x>=230&&me ms.x<=255)) )
{
status = ALARM_change;
if(right==1)
{
select=0;
WriteData(0,0,132,0,8,0); //清屏
}
right_display();
}
else
if( (mems.x>=200&&mems.x<=230)&&((mems.y>=0&&m ems.y<=30)||(mems.y>=230&& mems.y<=255)) )
{
status = Time_change;
if(up==1)
{
select=0;
WriteData(0,0,132,0,8,0); //清屏
}
up_display();
}
else
if((mems.x>=200&&mems.x<=255)||(mems.x>=0&&mems. x<=39)&&(mems.y>=200&&m ems.y<=229))
{
status = MIAOBIAO_change;
if(left==1)
{
select=0;
}
left_display();
}
else
{
status = Time_change;
if(up==1)
{
select=0;
WriteData(0,0,132,0,8,0); //清屏
}
up_display();
}
}
④显示程序
//向上显示
void up_display()
{
up = 0;
right =1;
down =1;
left =1;
GetTime(&RealTime);
f_temp = (unsigned int)((DS18B20_Get_Temp()*10)); char mark = 0;
if(f_temp < 0)
{f_temp = 0-f_temp;mark = 1;}
if(select==1)
{
DelayMs(1000);
}
if(select==2)
{
WriteData(0,37,54,5,7,0); //清屏
DelayMs(1000);
}
if(select==3)
{
WriteData(0,10,27,5,7,0); //清屏
DelayMs(1000);
}
WriteData(hanzi[16],99,115,5,7,1);//时
WriteData(hanzi[17],82,98,5,7,1);//间
WriteData(downnumber[RealTime.hour/10],73,81,5,7,1); WriteData(downnumber[RealTime.hour%10],64,72,5,7,1); WriteData(downnumber[10],55,63,5,9,1);
WriteData(downnumber[RealTime.minute/10],46,54,5,7,1); WriteData(downnumber[RealTime.minute%10],37,45,5,7,1); WriteData(downnumber[10],28,36,5,7,1);
WriteData(downnumber[RealTime.second/10],19,27,5,7,1); WriteData(downnumber[RealTime.second%10],10,18,5,7,1); WriteData(hanzi[18],96,112,2,4,1);//温
WriteData(hanzi[19],79,95,2,4,1);//度
if(mark==1)
WriteData(downnumber[13],70,78,2,4,1);
WriteData(downnumber[f_temp/100],61,69,2,4,1);
WriteData(downnumber[(f_temp-
(f_temp/100)*100)/10],52,60,2,4,1);
WriteData(downnumber[11],43,51,2,4,1);
WriteData(downnumber[f_temp%10],34,42,2,4,1);
WriteData(hanzi[20],17,33,2,4,1);//℃
}
//向下显示
void down_display()
{
up = 1;
right =1;
down =0;
left =1;
if(select==1)
{
WriteData(0,3,38,1,3,0); //清屏
DelayMs(1000);
}
if(select==2)
{
WriteData(0,56,73,1,3,0); //清屏
DelayMs(1000);
}
if(select==3)
{
WriteData(0,91,108,1,3,0); //清屏
DelayMs(1000);
}
GetDate(&RealDate);
WriteData(upnumber[RealDate.year/1000],3,11,1,3,1);
WriteData(upnumber[(RealDate.year-(RealDate.year/1000)*1000)/100],12,20,1,3,1);
WriteData(upnumber[(RealDate.year-
(RealDate.year/100)*100)/10],21,29,1,3,1);
WriteData(upnumber[RealDate.year%10],30,38,1,3,1);
WriteData(hanzi[0],39,55,1,3,1); //年
WriteData(upnumber[RealDate.month/10],56,64,1,3,1);
WriteData(upnumber[RealDate.month%10],65,73,1,3,1);
WriteData(hanzi[1],74,90,1,3,1);//月
WriteData(upnumber[RealDate.day/10],91,99,1,3,1);
WriteData(upnumber[RealDate.day%10],100,108,1,3,1);
WriteData(hanzi[2],109,125,1,3,1);//日
WriteData(hanzi[3],34,50,4,6,1);//星
WriteData(hanzi[4],51,67,4,6,1);//期
WriteData(hanzi[5+RealDate.weekday],68,84,4,6,1); }
//向左显示
void left_display()
{
up = 1;
right =1;
down =1;
left =0;
// WriteData(0,0,132,0,8,0); //清屏
WriteData(hanzi[12],96,112,2,4,1);//秒
WriteData(hanzi[13],96,112,5,7,1);//表
WriteData(leftnumber[min/10],64,80,2,3,1);
WriteData(leftnumber[min%10],64,80,3,4,1);
WriteData(leftnumber[10],64,80,4,5,1);
WriteData(leftnumber[sec/10],64,80,5,6,1);
WriteData(leftnumber[sec%10],64,80,6,7,1);
WriteData(leftnumber[_10us/10],32,48,3,4,1);
WriteData(leftnumber[_10us%10],32,48,4,5,1);
}
//向右显示
void right_display()
{
up = 1;
right =0;
down =1;
left =1;
if(select==1)
{
WriteData(0,48,64,5,7,0); //清屏
DelayMs(1000);
}
if(select==2)
{
WriteData(0,48,64,2,4,0); //清屏
DelayMs(1000);
}
WriteData(hanzi[14],17,33,2,4,1);//闹
WriteData(hanzi[15],17,33,5,7,1);//钟
WriteData(rightnumber[AlarmTime.hour/10],48,64,6,7,1); WriteData(rightnumber[AlarmTime.hour%10],48,64,5,6,1); WriteData(rightnumber[10],48,64,4,5,1);
WriteData(rightnumber[AlarmTime.minute/10],48,64,3,4,1); WriteData(rightnumber[AlarmTime.minute%10],48,64,2,3,1); }
⑤闹钟判断
void alarm_running()
{ if(!alarmflag)
alarmflag = Alarm_Get()&&(AlarmTime.hour ==
RealTime.hour);
if(alarmflag)
{
AlarmTime.minute = RealTime.minute;
BuzzOn();
DelayMs(200);
BuzzOff();
DelayMs(200);
if((AlarmTime.minute != RealTime.minute ) || (KEY4Read())) alarmflag = FALSE;
}
while(RealTime.minute == 0) {
Timer4OutEnable();
SetBellF(MusicData[0]);
DelayMs(400);
SetBellF(MusicData[0]);
DelayMs(400);
SetBellF(MusicData[4]);
DelayMs(400);
SetBellF(MusicData[4]);
DelayMs(400);
SetBellF(MusicData[5]);
DelayMs(400);
SetBellF(MusicData[5]);
DelayMs(400);
SetBellF(MusicData[4]);
DelayMs(400);
DelayMs(400);
SetBellF(MusicData[3]);
DelayMs(400);
SetBellF(MusicData[3]);
DelayMs(400);
SetBellF(MusicData[2]);
DelayMs(400);
SetBellF(MusicData[2]);
DelayMs(400);
SetBellF(MusicData[1]);
DelayMs(400);
SetBellF(MusicData[1]);
DelayMs(400);
SetBellF(MusicData[0]);
DelayMs(400);
DelayMs(400);
Timer4OutDisable();
DelayMs(100);
keyscan();
if(mems.x>=30&&mems.x<=57)
{
status = DA TE_change;
if(down==1)
{
select=0;
WriteData(0,0,132,0,8,0); //清屏
}
down_display();
}
else
if((mems.x>=230&&mems.x<=255)&&(mems.y>=30&&m ems.y<=55))
{
status = ALARM_change;
if(right==1)
{
select=0;
WriteData(0,0,132,0,8,0); //清屏
}
right_display();
}
else if(mems.x>=180&&mems.x<=200)
status = Time_change;
if(up==1)
{
select=0;
WriteData(0,0,132,0,8,0); //清屏
}
up_display();
}
else
if((mems.x>=200&&mems.x<=255)&&(mems.y>=160&& mems.y<=225))
{
status = MIAOBIAO_change;
if(left==1) {
select=0;
WriteData(0,0,132,0,8,0); //清屏
}
left_display();
}
else
{
status = Time_change;
if(up==1)
{
select=0;
WriteData(0,0,132,0,8,0); //清屏}
up_display();。