新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 基于ARM7 TDMI-S CPU的LPC2103的多功能电子钟

基于ARM7 TDMI-S CPU的LPC2103的多功能电子钟

作者:时间:2016-11-18来源:网络收藏
一、课程设计内容与要求

1)时钟显示功能,采用数码管的动态扫描工作方式,可通过键盘分别选择显示年或者月/日或者时/分或者分/秒,

本文引用地址:http://www.eepw.com.cn/article/201611/315901.htm

2)在键盘电路中,第一次中断判断有无键按下,若有按下则实行定时一次,下一次中断开始扫描。

3)具有校准年、月、日、时、分的功能。

4)闹钟功能,可按设定的时间闹时,随时开启或关闭闹钟。

二、元器件的介绍

我在这次的课程设计中使用的主芯片是LPC2103,下面对其进行简要的介绍。

LPC2103是一个基于支持实时仿真的16/32位ARM7 TDMI-S CPU的微控制器,并带有32kB的嵌入高速Flash存储器,128位宽度的存储器接口和独特的加速结构使32位代码能够在最大时钟速率下运行。

较小的封装和极低的功耗使LPC2103适用于访问控制器和POS机等小型应用系统中;由于内置了宽范围的串行通信接口(2个UART、SPI、SSP和2个I2C)和8KB的片内SRAM,LPC2103也适合用在通信网关和协议转换器中。32/16位定时器、增强型10位ADC、定时器输出匹配PWM特性、多达13个边沿、电平触发的外部中断、32条高速GPIO,使得LPC2103微控制器特别适用于工业控制和医疗系统中。

器件信息:

管脚信息:(LQFP48管脚配置 )

三、设计方案

硬件电路设计介绍:

1、键盘模块

整个硬件上总共使用了10个独立键盘,4个是开发板上原有的按键,6个是我利用多余的IO引脚焊接的按键。按键在没有被按下时,10个按键(P0.16 P0.14 P0.15 P0.18 P0.2 P0.3 P0.4 P0.5 P0.27 P0.6 P0.7)通过10K的电阻连接到3.3V的高电平,当按键被按下时,和GND相连接,被置为低电平。

2、数码管显示模块

使用串行输入/并行输出的8位移位寄存器74HC164,该移位寄存器是上升沿有效,将8位笔段码送给共阳数码管。(注:DATA为数据输入端,接P0.26;CLK为时钟输入端,接P0.13),与三极管相连的是数码管的位选端,选择哪位数码管被点亮,用于数码管的动态显示。

3、蜂鸣器模块

该蜂鸣器是一个无源蜂鸣器,要使用PWM驱动。其连接的是芯片的P0.5端。

4、LED模块

LED的连接如图所示,当管脚置为低电平时,可以将LED点亮,6个LED分别与LPC2103的P0.25 P0.12 P0.11 P0.10 P0.24 P0.23相连。

5、UART接口电路模块

由于开发板是3.3V供电系统,需要使用电平转换芯片SP3232E来进行RS-232电平转换。SP3232E的工作电压为3.3V,电平转换电路如图所示。当使用ISP功能下载程序时,需要将PC机的串口与开发板的串口相连,按下与P0.14相连的按键,短接P0.14,在系统复位时,进入ISP状态,用于下载HEX文件。这个开发板有两个串口,一个用于下载程序进主芯片,另外一个用于串口在上位机上输出显示各个变量的值,可用于调试程序。

按键功能介绍:(用户使用说明书)

Key1(P0.16):对显示的数码管进行年月日时分秒的显示切换

Key2(P0.14):在时间设置或者闹钟设置按键有效的情况下,进行自加1的向上调节,对时间或者闹钟进行设置,其余情况下该按键是无效状态

Key3(P0.15):在时间设置或者闹钟设置按键有效的情况下,进行自减1的向下调节,对时间或者闹钟进行设置,其余情况下该按键是无效状态

Key4(P0.18):时间设置按键,按下表示开始进行时间设置或跳出时间设置

Key5(P0.2 ):闹钟设置按键,按下表示开始进行闹钟设置

Key6(P0.3 ):闹钟设置移位按键,按下表示进行闹钟时和分的切换设置

Key7(P0.4 ):闹钟设置结束按键,按下表示跳出闹钟设置模式;另外可以关闭蜂鸣器

Key8(P0.27):闹钟开启按键,在Key5按下的情况下按下此键表示开启该组闹钟

Key9(P0.6 ):闹钟关闭按键,在Key5按下的情况下按下此键表示关闭该组闹钟

Key10(P0.7):用于串口输出信息,可以输出当前的时间和显示开启的闹钟设置时间

当Key10按下时,会在串口中输出信息如图所示:(RTC为输出当前时间,接下来是闹钟)

程序设计及流程图:
主程序流程图:

时间和闹钟设置程序流程图:

IO口的初始化模块:

LPC2103控制器的引脚都具有多种功能,但是每个引脚在某一时刻只能选择一种功能。当使用一个功能外设时,如果需要相应的引脚参与(如GPIO等),则必须在实现这一功能之前先设置好引脚的功能,否则无法实现该外设功能。LPC2103具有两个PINSEL寄存器,PINSEL0和PINSEL1,PINSEL0和PINSEL1寄存器中的每两个位控制着一个引脚的功能,所以一个引脚最多可以有4种不同的功能选择。当引脚选择GPIO功能时,使用寄存器IODIR可以控制引脚的方向,另外IOCLR、IOSET两个寄存器分别可以将引脚初始化设置为低电平或者高电平。所以,在主程序开始是时的GPIO初始化中包含如下几个子函数:

void GPIO_init(UINT8 GPIO_num); 将管脚初始为GPIO功能

void GPIO_inout(UINT8 GPIO_num,UINT8 in_out); 设置GPIO为输入或者输出

UINT8 GPIO_READ(UINT8 GPIO_num); 读出GPIO当前的状态

void GPIO_SET(UINT8 GPIO_num,UINT8 High_Low); GPIO初始化后置1或者置0

定时器的初始化模块:

该程序中使用了3个定时器,定时器0、1、2。定时器0设置为PWM输出模式,产生2kHZ的方波,用于驱动无源蜂鸣器,当设置的闹钟时间和当前时间匹配时,将定时器0启动,产生方波驱动蜂鸣器发出响声提醒。

定时器1用于时间计数,即当前时钟的计时器,它设置为1秒钟产生一次中断,其中断程序分配为最高的优先级,当一秒的定时时间到时,在服务程序中首先清除中断标志位,然后秒变量进行自加1(miao++),另外还进行秒闪烁标志位(g_Dot)的取反标志,使秒标志位能够闪烁。

定时器2的初始化有多个功能。定时器2的优先级是次于定时器1的,定时器设置为5毫秒产生一次中断,首先是用于四位八段LRD的动态扫描显示,使四位数码管的刷新频率为50Hz,另外是10mS的标志位(time10)自加1,当10mS标志位计时时间到时,就执行一次键盘扫描程序,即10个独立键盘每10mS被扫描一次。最后是清除中断标志位,通知中断服务程序结束。

时间更新子函数模块:

该函数主要是对当前时间的处理,定时器1对秒变量进行了计数,而此程序就是对时分秒年月日变量的值进行判断和限制,使其超过最大值后对其进行归零,比如分秒的值不能大于59,时的最大值为23。大小月以及二月份天数的处理是通过调用UINT8 yue_deal()这个函数实现的。该函数主要是使用一个switch case 语句,对大月份返回一个值31,小月份返回30,另外通过调用UINT8 nian_deal()来判断是平年还是闰年【闰年条件:if((nian%4==0&&nian%100!=0)||nian%400==0)】,闰年返回29,平年返回28。具体的程序见附录。

数码管动态显示模块程序:

四位数码管的刷新频率为50Hz,即一位显示时间为(1∕50×4)即5mS,用定时器2每5ms产生一次中断进行刷新,每次中断产生时只将一位数码管的位选端点亮,用void Refresh_LED(UINT16 dat)函数实现这一功能,由于数码管的笔段码输入端接的是8位移位寄存器74HC164,故需要使用移位将笔段码送到数码管笔段输入端【void data_shift(UINT8 dat)函数】。

注:显示程序负责当前时间的显示和闹钟设置时的显示内容,需要改变的是将要显示的数值用变量display进行保存成为十进制的四位数。

键盘扫描程序:

如果10毫秒定时时间到的标志位有效,就进入键盘扫描的子程序UINT8 GetKey()中。通过查找资料,该部分的程序设计采用的是状态机的思想,用如下图所示,其中系统的输入信号是与按键连接的I/O口电平,“1”表示按键处于开放状态,“0”表示按键处于闭合状态。而系统的输出信号则表示检测和确认到一次按键的闭合操作,用“1”表示。

上图给出了一个简单按键状态机的状态转换图。在图中,将一次按键完整的操作过程分解为3个状态,采用时间序列周期为10ms。下面对该图做进一步的分析和说明,并根据状态图给出软件的实现方法。首先,要充分体会时间序列的作用。在这个系统中,采用的时间序列周期为10ms,它意味着,每隔10ms检测一次按键的输入信号,并输出一次按键的确认信号,同时按键的状态也发生一次转换。图中“状态0”为按键的初始状态,当按键输入为“1”时,表示按键处于开放,输出“0”(1/0),下一状态仍旧为“状态0”。当按键输入为“0”,表示按键闭合,但输出还是“0”(0/0)(没有经过消抖,不能确认按键真正按下),下一状态进入“状态1”。“状态1”为按键闭合确认状态,它表示了在10ms前按键为闭合的,因此当再次检测到按键输入为“0”时,可以确认按键被按下了(经过10ms的消抖),输出“1”表示确认按键闭合(0/1),下一状态进入“状态2”。而当再次检测到按键的输入为“1”时,表示按键可能处在抖动干扰,输出为“0”(1/0),下一状态返回到“状态0”。这样,利用状态1,实现了按键的消抖处理。“状态2”为等待按键释放状态,因为只有等按键释放后,一次完整的按键操作过程才算完成。从对上图的分析中可以知道,在一次按键操作的整个过程,按键的状态是从“状态0”->“状态1”->“状态2”,最后返回到“状态0”的。并且在整个过程中,按键的输出信号仅在“状态1”时给出了唯一的一次确认按键闭合的信号“1”(其它状态均输出“0”)。所以上面状态机所表示的按键系统,不仅克服了按键抖动的问题,同时也确保在一次按键整个的过程中,系统只输出一次按键闭合信号(“1”)。换句话讲,不管按键被按下的时间保持多长,在这个按键的整个过程中都只给出了一次确认的输出,因此在这个设计中,按键没有“连发”功能,它是一个最简单和基本的按键。一旦有了正确的状态转换图,就可以根据状态转换图编写软件了。在软件中实现状态机的方法和程序结构通常使用多分支结构(IF-ELSEIF-ELSE、CASE等)实现。下面是根据上图、基于状态机方式编写的简单按键接口函数GetKey()。

该简单按键接口函数GetKey()在整个系统程序中应每隔10ms调用执行一次,每次执行时进入用switch结构构成的状态机。switch结构中的case语句分别实现了3个不同状态的处理判别过程,在每个状态中将根据状态的不同,以及key4的值(状态机的输入)确定输出值(keyreturn),和确定下一次按键的状态值(keystate)。函数GetKey()的返回参数提供上层程序使用。返回值为0时,表示按键无动作;而返回1表示有一次按键闭合动作,需要进入按键处理程序做相应的键处理。在函数GetKey()中定义了2个局部变量,其中keyreturn为一般普通的局部变量,每次函数执行时,keyreturn为函数的返回值,总是先初始化为0,只有在状态1中重新置1,作为表示按键确认的标志返回。变量keystate非常重要,它保存着按键的状态值,该变量的值在函数调用结束后不能消失,必须保留原值,因此在程序中定义为“局部静态变量”。通过对按键的扫描程序后,就知道了哪一个独立键盘的按键被按下了,通过void keysure()函数来定义每一个按键的功能。

中断优先级的定义:

LPC2103的向量中断控制器VIC(Vectored Interrupt Controller)具有32个中断请求输入。可将这些中断编程分为3类:FIQ、向量IRQ、非向量IRQ。其中快速中断请求FIQ(Fast Interrupt reQuest)具有最高的优先级。向量IRQ(Vectored IRQ)具有中等优先级。该级别可分配32个中断请求中的16个。32个请求中的任意一个都可分配到16个向量IRQ slot中的任意一个,其中slot0具有最高优先级,而slot15则为最低优先级。非向量IRQ(Non-vectored IRQ)的优先级最低。

在这个多功能时钟的程序设计中,使用了两个中断都是分配为向量IPQ。定时器1作为时钟的基准计数时钟,有最高的优先级;定时器2分配为下一个优先级。相关的语句设置如下:

VICVectCntl0 = 0x20 | 5; /* 定时器1分配为向量IRQ通道0 */

VICVectAddr0 = (UINT32) Timer1ISR; /* 分配中断服务程序地址0 */

VICVectCntl1 = 0x20 | 26; /* 定时器2分配为向量IRQ通道1 */

VICVectAddr1 = (UINT32) Timer2ISR; /* 分配中断服务程序地址1 */

VICIntEnable = 1 << 26; /* 定时器2中断使能 */

VICIntEnable = 1 << 5; /* 定时器1中断使能 */

四、调试过程

由于这次的设计使用的是LPC2103的开发板,所以在硬件上不需要太多的去调试。在利用开发板硬件资源的基础上,由于这次的多功能闹钟设计使用的模块较多,基本上各个模块的调试是分开进行的。主要包括初始化的程序调试、按键子程序调试、LED显示调试这几部分子程序的调试。将这三部分调试成功,那么整个设计的软件部分也就基本完成了。在该课程设计中,采用的集成开发环境是uVision,在软件设计过程中,有时候不小心插入了一个中文的符号(如分号),就会使软件编译不通过,开始在这个错误上浪费不少时间,但后来注意到了这个情况,避免了类似的错误的发生。

我首先进行的是键盘扫描模块的程序设计及调试。该模块主要由按键的状态确认函数(UINT8 GetKey())和按键确认函数(void keysure())共同完成。按键的状态函数主要是由一个返回值来确认按键的状态,开始的时候,由于缺少一个语句:keyreturn=0;即每次进入函数的时候要将按键的状态清零,导致按键在一次按下之后就出现了不正常的情况,最后找到问题的所在,就纠正了过来。由于这个模块是我最先进行的,所以开始的时候我并没有给每个按键定义相应的功能,每个按键的功能我都是定义为每次按下对相应的LED进行取反操作,在之后的设计中,再在相应的按键确认语句下增加入相应的功能,这也是模块化程序设计的一个体现。

四位的数码管采用的是动态显示模式,刷新频率为50Hz,另外,由于开发板的硬件采用的是移位寄存器送笔段码,所以在软件设计方面要用到将8位笔段码不断循环右移,增加了设计的难度。起初我认为一切程序都编写得差不多的时候,烧进芯片的程序使数码管显示一片模糊,基本上显示的都是8,后来从程序的开始查起,终于找到原因,原来动态显示程序中,每次只能是一位的数码管点亮,但在程序设计中,在点亮下一位数码管时,忘记了将上一次点亮的位给关掉,导致四位数码管时同时被点亮的,最后,在点亮数码管前首先将四位数码管全部关闭,再以50Hz的频率进行点亮刷新,显示方回到正常状态。

最后就是在这两个模块的基础上来对整体的程序进行相关的调试和完善。例如在原先设置有当进入时间设置或者闹钟挂起时有LED点亮进行提示,设置时间时相关的设置超过相应的极限值,这些在最后的调试过程中得到完善,使其工作在正常的状态,调试过程也就这样基本得到完成。

五、结论

这次的课程设计基本上完成了任务书中所提出的要求。最终的成品具有如下功能:

通过数码管动态扫描的工作方式实现时分秒年月日星期的显示,利用键盘实现利用四位数码管可以进行如上时间的自由切换,时钟具有时间设置和随时的校准功能。有三组可随意设置打开或者关闭的闹钟,闹钟匹配时有蜂鸣器响和LED闪烁两种方式进行提醒。利用串口可以输出当前时间和被挂起的闹钟。

综上所述,本次的课程设计达到预期的设计要求。

六、小结与讨论

通过为期一个星期以来的单片机课程设计,感觉自己在这么的一个过程下来,还是很有收获的。这个设计的题目老师是在亚运放假之前就给我们的,我知道老师是希望我们用亚运放假的时间去思考、去准备的过程。

从暑假以来,我就利用暑假空闲的时间,来自学ARM7TDMI-S内核的一款芯片LPC2103,其与单片机相识的一些功能我都有涉及性的学习过了,在9月份,我买了它的开发板,进行相关的实践性学习。基于上述的原因,所以这次的课程设计我就想用这款LPC2103的芯片开进行这次的课程设计,也是对我之前的学习的一种检验。以前用开发板学习的时候,都是针对其的单独一个功能进行相关程序的设计,而这次的课程设计会涉及比较多的功能模块的综合应用,所以,开始的时候有点摸不着头脑的感觉。最后经过几番的思考,我就决定,针对每一个模块,一个个进行攻破,最终实现了多功能电子时钟的全部的功能。

首先,我利用LPC2103的实时时钟功能(RTC),先对其进行初始化,使其的时钟跑起来,在这个基础上,我最先是开始键盘模块的设计,这就涉及到:键盘扫描,键盘去抖动,键盘判断相关的问题,最后经过两天的编程,设计,调试,思考才把键盘模块的问题顺利地解决了。从最初的模型规划,到具体功能的实现,直到最后的软件设计和调试过程,每一个环节都让我加深了对实际问题的思考,有利于自己动手能力的提高。

这次的课程设计让我学会了系统地去解决一个实际的问题,了解到了单片机设计的基本步骤、开发设计过程中需要注意的问题、学会处理调试过程中出现的问题.,学会了巧妙运用模块化设计的思想,在整个的程序化设计过程中,学会将功能细化,分成一个小功能来实现,在设计好一个功能之后,再在这个基础上去增加其他的功能,最后完成整个多功能电子时钟的设计。总之,通过这次的课程设计,检验了我所学习的知识,使我将平时书本上的理论知识与实践很好地结合起来,利于加深对理论知识的理解和提高自己的实际动手操作能力。

七、参考文献

1、《ARM微控制器基础与实战》 出版社:北京航空航天大学出版社 2005年

2、《ARM7易学通》 出版社:人民邮电出版社 2006年

3、《EasyARM2103教材》 出版社:北京航空航天大学出版社 2008年

4、《新编单片机原理与应用》 出版社:西安电子科技大学出版社 2007年

5、《新编单片机原理与应用实验》 出版社:西安电子科技大学出版社 2005年

附录:

开发板实物图

以下是完整的源程序:
以下是名为main.c的文件
#include /* prototype declarations for I/O functions */
#include /* LPC21xx definitions */
#include "main.h"
#define LED (1<<23)
UINT8 keystate=0; //按键的状态标志有0、1、2三种状态
UINT16 key4=1023;//按键的扫描输入端
UINT8 keyreturn=0; //按键的返回值
UINT8 time10=0;//10ms标志位
UINT8 dis_bit=0; //显示切换标志位
UINT8 settime=0; //进入时间设置标志位
UINT8 alarm=0;//闹钟设置有效标志位
UINT8 al_shi1=0; // 以下是闹钟的序号和用数组储存的闹钟时、分位
UINT8 al_num=0;
UINT8 al_shi[4];
UINT8 al_fen[4];
UINT8 al_yunxu=0; //闹钟才开始比较,确保设置期间不比较
UINT8 LS=0; //闹钟时间到,铃声有效位,0为无效状态
UINT8 al_on3off[4]={0,0,0,0}; //用数组记录闹钟是否开启
UINT8 al_flag=0;//闹钟组数1.2.3,0表示没有闹钟被挂起
UINT8 al_fen1=0;
static UINT8 g_Dot = 0;
static UINT16 display = 0;//数码管显示缓存

UINT16 nian=2008;//与时间相关的变量
UINT8 yue=8;
UINT8ri=8;
UINT8week=5;
UINT8shi=20;
UINT8 fen=0;
UINT8miao;
//--------------------------------------------------------------------------------
void UART1_Init (void)
{
UINT16 Fdiv;
PINSEL0 = 0x00050000;
U1LCR = 0x83; // DLAB = 1,可设置波特率
Fdiv = (Fpclk / 16)/ UART_BPS; // 设置波特率
U1DLM = Fdiv / 256;
U1DLL = Fdiv % 256;
U1LCR = 0x03;
}

void GPIO_init(UINT8 GPIO_num) //初始为GPIO功能
{
if(GPIO_num < 16)
PINSEL0&= ~(0x03 << (GPIO_num * 2));
else
PINSEL1&= ~(0x03 << ((GPIO_num-16) * 2));
}
void GPIO_inout(UINT8 GPIO_num,UINT8 in_out) //设置GPIO为输入0或者输出1
{
if(in_out)
IODIR |= (0x01 << GPIO_num);//output 1
else
IODIR &= ~(0x01 << GPIO_num);//input 0
}
UINT8 GPIO_READ(UINT8 GPIO_num) //读出GPIO的状态
{
if(IOPIN & (1 << GPIO_num))
return 1;
else
return 0;
}
void GPIO_SET(UINT8 GPIO_num,UINT8 High_Low) //GPIO置1或者置0
{
if(High_Low)
IOSET |= (0x01 << GPIO_num);
else
IOCLR |= (0x01 << GPIO_num);
}
void LEDchange(UINT8 GPIO_num)//对二极管进行取反
{
if ((IO0PIN & (0x01 << GPIO_num)) == 0) {
IO0SET |= (0x01 << GPIO_num); // 点亮发光二极管
}
else IO0CLR |= (0x01 << GPIO_num);
}
UINT8 nian_deal()
{ if((nian%4==0&&nian%100!=0)||nian%400==0)
return 1;
else return 0;
}
UINT8 yue_deal()
{
switch(yue)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:return 31;
case 2:
{
if(nian_deal())
return 29;
else return 28;
}
case 4:
case 6:
case 9:
case 11: return 31;
default: return 1;
}
}

void keysure()//按键的确认 ,并执行相关功能
{
if(GPIO_READ(14)==0)//对每一位进行切换
{
if(settime!=0)
{
switch(dis_bit)
{case 0:
if(settime==1)
{shi++;
if(shi>23)
shi=0;
}
else {fen++;
if(fen>59)
fen=0;

}
break;
case 1:
week++;
if(week>7)
week=1;
break;
case 2:
nian++;
break;
case 3:
if(settime==1)
{yue++;
if(yue>12)
yue=1;
}
else
{ri++;
if(ri>yue_deal())
ri=1;
}
break;
}

}
if(alarm!=0)
{if(alarm==1)
{
al_shi1++;
if(al_shi1>23)
al_shi1=0;
al_shi[al_num]=al_shi1;
}
else
{al_fen1++;
if(al_fen1>59)
al_fen1=0;
al_fen[al_num]=al_fen1;
}

}

}
if(GPIO_READ(15)==0)
{
if(settime!=0)
{
switch(dis_bit)
{case 0:
if(settime==1)
{shi--;
if(shi>23)
shi=23;
}
else {fen--;
if(fen>59)
fen=59;
}
break;
case 1:
week--;
if(week==0)
week=7;
break;
case 2:
nian--;
break;
case 3:
if(settime==1)
{yue--;
if(yue==0)
yue=12;
}
else
{ri--;
if(ri==0)
ri=yue_deal();
}
break;
}
}
if(alarm!=0)
{if(alarm==1)
{
al_shi1--;
if(al_shi1>23)
al_shi1=23;
al_shi[al_num]=al_shi1;
}
else
{al_fen1--;
if(al_fen1>59)
al_fen1=59;
al_fen[al_num]=al_fen1;
}

}
}
if(GPIO_READ(16)==0)
{

if(dis_bit==3)
dis_bit=0;
elsedis_bit++;

}
if(GPIO_READ(18)==0)
{if(settime>1)
{
settime=0;
T1TCR = 0x01;
GPIO_SET(23,1);
}
else {settime++;
T1TCR = 0x00;
GPIO_SET(23,0);}
}
if(GPIO_READ(2)==0)
{al_yunxu=0;
alarm=1;
al_num++;
if(al_num>3)
al_num=1;
//al_num++;
}
if(GPIO_READ(3)==0)
{
alarm++; //对闹钟的时分为进行设置切换
if(alarm>2)
alarm=1;
}
if(GPIO_READ(4)==0)
{
if(alarm!=0)
{alarm=0;
al_shi1=0;
al_yunxu=1;
al_flag=0;
for(al_num=0;al_num<4;al_num++)
{ if(al_on3off[al_num]==1)
al_flag++;
}
if(al_flag!=0)
GPIO_SET(24,0);
if(al_flag==0)
GPIO_SET(24,1);
}
else
{

LS=0;
T0TCR=0x00; //启动对响铃进行关闭
GPIO_SET(25,1);
GPIO_SET(11,1);
GPIO_SET(12,1);

}
}
if(GPIO_READ(27)==0)
{
if(alarm!=0)
{al_on3off[al_num]=1;

}
if(settime!=0)
nian=nian+10;

//表示有闹钟挂起,要加上一些标示位
}
if(GPIO_READ(6)==0)
{ if(alarm!=0)
{
al_on3off[al_num]=0;
}
if(settime!=0)
nian=nian-10;
}
if(GPIO_READ(7)==0)
{
//启动 输出开启的闹钟信息
printf("RTC ----%02d -%02d -%02d _ %02d : %02d : %02d WEEK=%d-- n",nian,yue,ri,shi,fen,miao,week);
for(al_num=0;al_num<4;al_num++)
{
if(al_on3off[al_num]==1)
{
printf("The number%d alarm is on.---Alarm time--%d : %d --n",al_num,al_shi[al_num],al_fen[al_num]);
}
}
}
}
UINT8 GetKey()
{keyreturn=0;
key4=GPIO_READ(18)| (GPIO_READ(15)<<1)|(GPIO_READ(14)<<2)|(GPIO_READ(16)<<3)|(GPIO_READ(2)<<4)|(GPIO_READ(3)<<5)|(GPIO_READ(4)<<6)|(GPIO_READ(27)<<7)|(GPIO_READ(6)<<8)|(GPIO_READ(7)<<9);
switch (keystate)
{
case 0:
if(key4!=1023) //检测到有按键,转到状态1,相当于是消抖过程
{
keystate=1;
}
break;
case 1:
if(key4!=1023) //再次检测到有按键,确认按键按下,返回一个值,并转到状态2
{
keyreturn=1;
keystate=2;
}
else
{
keystate=0; //没有检测到按键,说明状态0检测到是一个抖动,重新转到状态0
}
break;
case 2:
if(key4==1023) //检测到按键松开,状态转到状态0,一次完整的按键过程结束
{
keystate=0;
}
break;
}
return keyreturn;
}
void data_shift(UINT8 dat)
{
UINT8 i;
for(i=0; i<8; i++)
{
GPIO_SET(CLK,LOW);
if(dat & 0x01){
GPIO_SET(DATA,0);
}
else{
GPIO_SET(DATA,1);
}

GPIO_SET(CLK,HIGH);

dat >>= 1;
}
}

void Refresh_LED(UINT16 dat)
{
static UINT8 i = 0;
UINT8 a,b,c,d;
a = dat /1000;
b = dat % 1000 / 100;
c = dat % 1000 % 100 /10;
d = dat % 1000 % 100 % 10;

switch(i)
{
case 0:
i++;
GPIO_SET(DIG_EN0,LOW);
data_shift(DIGData[d]);
break;

case 1:
i++;
GPIO_SET(DIG_EN1,LOW);
data_shift(DIGData[c]);
break;

case 2:
i++;
GPIO_SET(DIG_EN2,LOW);
if(g_Dot)
data_shift(DIGData[b] & ~(0x01 << 0));
else
data_shift(DIGData[b] | (0x01 << 0));
break;

case 3:
i = 0;
GPIO_SET(DIG_EN3,LOW);
data_shift(DIGData[a]);
break;
}
}
/*********************************************************************************************************
** Function name: Timer1_ISR
** Descriptions: 定时器1中断主程序
** input parameters: 无
** ouput parameters: 无
** Returned value: 无
*********************************************************************************************************/
void __irq Timer1ISR (void)
{
T1IR = 0x01; /* 清除中断标志 */
miao++;
if(g_Dot)
g_Dot = 0;
else
g_Dot = 1;
VICVectAddr = 0x00; /* 中断向量结束 */
}
void __irq Timer2ISR (void)
{
GPIO_SET(DIG_EN0,HIGH);
GPIO_SET(DIG_EN1,HIGH);
GPIO_SET(DIG_EN2,HIGH);
GPIO_SET(DIG_EN3,HIGH);
Refresh_LED(display);
time10++;
T2IR = 0x01; /* 清除中断标志 */
VICVectAddr = 0x00; /* 中断向量结束 */
}
void Timer1Init(void)
{
T1TCR = 0x02; /* 定时器1复位 */
T1PR = 0; /* 不设时钟分频 */
T1MCR = 0x03; /* 匹配后复位TC,并产生中断 */
T1MR0 = Fpclk ; /* 设置1秒匹配值 */
T1IR = 0x01; /* 清除中断标志 */
T1TCR = 0x01; /* 启动定时器1 */
}
void timeupdate()//时间更新函数
{

if(miao>59)
{
fen++;
if(al_flag&&al_yunxu) //表示闹钟开启,bian a!!
{
for(al_num=0;al_num<4;al_num++)
{if(fen==al_fen[al_num]&&shi==al_shi[al_num]&&al_on3off[al_num]==1)
{ al_flag--;
al_on3off[al_num]=0;
if(al_flag==0)
GPIO_SET(24,1);
LS=1;

}
}
}
miao=0;
if(fen>59)
{
shi++;
fen=0;
if(shi>23)
{
ri++;
week++;
if(week>7){week=1;}
shi=0;
if(ri>yue_deal())
{
yue++;
ri=1;
if(yue>12)
{
nian++;
yue=1;
if(nian>9999)
nian=1000;
}
}
}
}
}
}

/*********************************************************************************************************
** Function name: Timer02Init
** Descriptions: 定时器0&2初始化
** input parameters: 无
** ouput parameters: 无
** Returned value: 无
*********************************************************************************************************/
void Timer2Init(void) //这里也初始化了T0,用于pwm输出,驱动蜂鸣器
{
PINSEL0 &= ~(3 << 10);
PINSEL0 |= (2 << 10); //P0.5 作MAT0.1输出

T0TCR=0x02; //复位
T0PR =0x00; //不预分频
T0PWMCON=0x02; //MAT0.1为PWM输出,在P0.5脚

T0MR0=Fcclk/1000;
T0MCR=0x02; //每FCLK/1000复位一次T0
T0MR1=((Fcclk/2000)/10)*10;

T2TCR = 0x02; /* 定时器2复位 */
T2PR = 0; /* 不设时钟分频 */
T2MCR = 0x03; /* 匹配后复位TC,并产生中断 */
T2MR0 = Fpclk /200; /* 设置5毫秒匹配值 */
T2IR = 0x01; /* 清除中断标志 */
T2TCR = 0x01; /* 启动定时器2 */
}
void PLL_Init(void)
{
PLLCON = 1;
#if (Fcco / Fcclk) == 2
PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);
#endif
#if (Fcco / Fcclk) == 4
PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);
#endif
#if (Fcco / Fcclk) == 8
PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);
#endif
#if (Fcco / Fcclk) == 16
PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);
#endif
PLLFEED = 0xaa;
PLLFEED = 0x55;
while((PLLSTAT & (1 << 10)) == 0);
PLLCON = 3;
PLLFEED = 0xaa;
PLLFEED = 0x55;
}

int main (void)
{
PLL_Init();

UART1_Init();

GPIO_init(14); // 选择GPIO功能
GPIO_init(15);
GPIO_init(16);
GPIO_init(18);
GPIO_init(2);
GPIO_init(3);
GPIO_init(4);
GPIO_init(27);
GPIO_init(6);
GPIO_init(7);
GPIO_inout(14,0); // 设置输入
GPIO_inout(15,0);
GPIO_inout(16,0);
GPIO_inout(18,0);
GPIO_inout(2,0);
GPIO_inout(3,0);
GPIO_inout(4,0);
GPIO_inout(27,0);
GPIO_inout(6,0);
GPIO_inout(7,0);
GPIO_init(23); //LED控制端的初始化
GPIO_init(24);
GPIO_init(10);
GPIO_init(11);
GPIO_init(12);
GPIO_init(25);
GPIO_inout(23,1);
GPIO_inout(24,1);
GPIO_inout(10,1);
GPIO_inout(11,1);
GPIO_inout(12,1);
GPIO_inout(25,1);
GPIO_SET(23,1);
GPIO_SET(24,1);
GPIO_SET(10,1);
GPIO_SET(11,1);
GPIO_SET(12,1);
GPIO_SET(25,1);
GPIO_init(DATA);
GPIO_init(CLK);
GPIO_init(DIG_EN0);
GPIO_init(DIG_EN1);
GPIO_init(DIG_EN2);
GPIO_init(DIG_EN3);
GPIO_inout(DATA,OUTPUT);
GPIO_inout(CLK,OUTPUT);
GPIO_inout(DIG_EN0,OUTPUT);
GPIO_inout(DIG_EN1,OUTPUT);
GPIO_inout(DIG_EN2,OUTPUT);
GPIO_inout(DIG_EN3,OUTPUT);
GPIO_SET(CLK,HIGH);
GPIO_SET(DATA,HIGH);
//GPIO_init(5);
//GPIO_inout(5,1);
//GPIO_SET(5,1);
Timer1Init();
Timer2Init(); // 定时器初始化

VICIRQStatus=1<<26; // IRQ中断使能
VICIntSelect = 0<<26; // 定时器2分配为IRQ中断
VICVectCntl1 = 0x20 | 26; /* 定时器2分配为向量IRQ通道0 */
VICVectAddr1 = (UINT32) Timer2ISR; /* 分配中断服务程序地址 */
VICIntEnable = 1 << 26; /* 定时器2中断使能 */
VICIntSelect = 0<<5;
VICVectCntl0 = 0x20 | 5; /* 定时器1分配为向量IRQ通道0 */
VICVectAddr0 = (UINT32) Timer1ISR; /* 分配中断服务程序地址 */
VICIntEnable = 1 << 5; /* 定时器1中断使能 */

while(1)
{
timeupdate();
if(alarm!=0)
{
if(al_on3off[al_num]==0)
display=al_num*1111;
else
display=al_shi[al_num]*100+al_fen[al_num];
}
else
{ switch(dis_bit)
{ case 0:
display=shi*100+fen;
break;
case 1:
display=miao*100+week;
break;
case 2:
display=nian;
break;
case 3:
display=yue*100+ri;
break;
}
}
if(time10==1)
{time10=0;
if(GetKey()==1)
{
keysure();

}
}
if(LS==1)
{ if(miao%2==0)
{ GPIO_SET(25,1);
GPIO_SET(11,1);
GPIO_SET(12,1);
T0TCR=0x01;

}
else
{ GPIO_SET(25,0);
GPIO_SET(11,0);
GPIO_SET(12,0);
T0TCR=0x00;

}

}
}
}
以下是名为main.h的文件
typedef unsigned char UINT8 ;
typedef unsigned int UINT16 ;
typedef unsigned int UINT32 ;
//--------------------------------------------------------------------------------
#defineUART_BPS 115200
/* 系统设置, Fosc、Fcclk、Fcco、Fpclk必须定义*/
#define Fosc(11059200)//晶振频率,10MHz~25MHz,应当与实际一至
#define Fcclk(Fosc * 4) //66.3552 系统频率,必须为Fosc的整数倍(1~32),且<=60MHZ
#define Fcco(Fcclk * 4) //CCO频率,必须为Fcclk的2、4、8、16倍,范围为156MHz~320MHz
#define Fpclk(Fcclk / 4) * 1 //016.5888,VPB时钟频率,只能为(Fcclk / 4)的1 ~ 4倍
//--------------------------------------------------------------------------------
typedef enum
{
LOW,
HIGH
}en_Level;
typedef enum
{
INPUT,
OUTPUT
}en_InOut;
enum
{
FALSE,
TRUE
};
#defineSH(0x01 << 0)
#defineSG(0x01 << 1)
#defineSF(0x01 << 2)
#defineSE(0x01 << 3)
#defineSD(0x01 << 4)
#defineSC(0x01 << 5)
#defineSB(0x01 << 6)
#defineSA(0x01 << 7)
const UINT8 DIGData[] =
{
SA |SB |SC | SD |SE |SF,//0
SB | SC,//01
SA |SB |SG | SE | SD,//02
SA |SB |SC | SD | SG,//03
SB |SF | SG | SC,//04
SA |SF |SG |SC | SD,//05
SA |SC | SD | SE | SG | SF,//06
SA | SB| SC,//07
SA |SB |SC | SD | SE | SF | SG,//08
SA |SB |SC|SD | SF | SG,//09
0,//mask - 10
SG//minus - 11
};
//--------------------------------------------------------------------------------
#defineLED125
#defineLED212
#defineLED311
#defineLED410
#defineLED524
#defineDATA26
#defineCLK13
#defineDIG_EN021
#defineDIG_EN120
#defineDIG_EN219
#defineDIG_EN317
#defineINT016
#defineINT114
#defineINT215
#defineIR_IN18
#defineBUZZER23
//--------------------------------------------------------------------------------

void PLL_Init(void);
void Timer0_Init(void);
void UART1_Init (void);
UINT8 GPIO_READ(UINT8 GPIO_num) ;
void GPIO_init(UINT8 GPIO_num);
void GPIO_inout(UINT8 GPIO_num,UINT8 in_out);
//inline voidGPIO_SET(UINT8 GPIO_num,UINT8 High_Low);
void __irq IRQ_Timer0 (void);
void GPIO_SET(UINT8 GPIO_num,UINT8 High_Low);
//--------------------------------------------------------------------------------



评论


相关推荐

技术专区

关闭