评阅教师 日期
关于RTC的万年历实验
一.实验目的:
1. 通过实验加深对实时时钟RTC结构和工作原理的理解和相关寄存器的应用; 2. 通过UART传输数据并由PC机终端软件EasyARM.exe显示,复习异步串行口UART
内容;
3. 通过对《深入浅出ARM7》中万年历显示实验程序的修改添加,实现闹钟功能,以及
通过按键可实现时钟和闹钟时间的调整。
二.实验仪器:
ARM开发板一块、
装有ADS1.2及EasyJTAG仿真器的电脑一台
三.实验原理:
1.RTC功能结构如下图所示:
2.相关寄存器描述:
(1)中断位置寄存器—Interrupt Location Register(ILR – 0xE0024000)
(2)时钟控制寄存器—Clock Control Register(CCR – 0xE0024008)
(3)计数器增量中断寄存器—Counter Increment Interrupt Register(CIIR – 0xE002400C) 计数器增量中断寄存器可使计数器每次增加时产生一次中断。在中断位置寄存器的位0(ILR[0])写入1之前,该中断一直保持有效。
(4)报警屏蔽寄存器—Alarm Mask Register(AMR – 0xE0024010)
对于报警功能来说,要产生中断,非屏蔽的报警寄存器必须匹配对应的时间计数器。只有当计数器之间的比较第一次从不匹配到匹配时,才会产生中断。向中断位置寄存器(ILR)的位写入1,会清除相应的中断。如果所有屏蔽位都置位,报警将被禁止。
四.实验程序: (见附录。)
五.实验结果:
通过EasyARM的万年历功能实现了实时时钟和闹钟时间的显示。同时LED1每秒
间隔闪烁,到预设的闹钟时间(即报警时间)时,LED8闪烁三次后蜂鸣器开始播放“虹彩妹妹”的音乐,播放音乐同时LED1跟随音乐节奏闪烁。按键KEY1按一次,实时时钟计数停止,LED1长亮指示秒调整状态,可通过KEY3(减一)、KEY4(加一)调节秒。KEY1按两次,进入分钟调整状态,可通过KEY3(减一)、KEY4(加一)调节分钟。依次类推,可依次调整秒、分、时、星期、日、月、年;按键KEY2按一次,EasyARM显示闹钟时间,类似实时时钟调整功能,可通过按KEY2依次调整闹钟时间(由于程序中报警屏蔽寄存器AMR只是没屏蔽秒报警值,故只要所设的秒报警值与时钟秒计数值相等就会产生报警)。每次按键时,都有LED8闪烁一次和BEEP蜂鸣一声提示。
六.调试总结和体会:
经过整整一天的努力和老师同学的帮助,终于将实验调试成功了。
一开始,经过反复的调试终于实现了报警功能,然后在实验室通过涂老师的指导,实现了播放音乐过程中时钟实时更新功能以及实现LED灯跟随音乐节奏闪烁。再经过程序的改进,实现了实时时钟和闹钟调整功能。在同学建议下,添加了LED灯指示调整时间点(秒、分、时、星期、日、月、年)功能。
在调试过程中,总结了一些错误原因分析以及编程注意点:
①刚开始时,由于没注意格式书写,程序看起来很乱,在涂老师的建议下,将程序中函数的正反大括号对齐,这样程序变得更直观。
②经过调试以及和同学的探讨,成功实现了KEY3和KEY4按键在最小值和最大值的变化。其中关键是使用类似if(SEC==0) SEC=59;else SEC- -;和if(SEC==59) SEC=0;else SEC++; 而自己原来是将类似SEC- -和SEC++放在前面,由于SEC寄存器不能取-1值,所以SEC- -放在前面时,不能减到00的状态,直接从01到59了。原来只是个顺序问题,让程序变得如此微妙。
③在调试时,由于错在将#define KEY1 1<<16 后面多加了“;”导致调试时显示很
多错误。从中发现,有时会因一个地方出错导致显示很多错误。同样的,有时也会是调试时只显示一个错误,但其实有多个地方有错。以后要注意这点。
④在调整星期时发现,当7加一时竟变成0了。和同学讨论后得知原来星期寄存器只用了3为(26:24),所以当7加一时为8但第四位没被获取故只得0。以后多了解寄存器地址、结构等内部原理知识,这样一些问题就会迎刃而解。
⑤调试时发现,当双击“{”或“}”所在行,则其所包含的函数体会用黑色显示,如下图,这样即可快速找到“{”或“}”的匹配括号,因此也可以很方便用来查错。
⑥通过调试,掌握了按键释放程序的应用。程序中采用类似while((IO0PIN & KEY4)==0);语句,其作用与while(!(IO0PIN & KEY4));等价得知“!(IO0PIN & KEY4)”并不是取反的意思。 程序中KEY1和KEY2采用等待按键释放,而为了按住按键时自动快速加减,KEY3和KEY4不用等待按键释放程序。
这次实验,我习得了不少,从中体会到多调试多实际操作练习将会发现很多的问题,同时在解决问题过程中可以获得很多知识。
再次,感谢涂老师的指导和同学的帮助。
附录:
#include \"config.h\" #include \"music.h\" #define BEEP 1<<7
const uint32 KEY1=1<<16; const uint32 KEY2=1<<17; const uint32 KEY3=1<<18; const uint32 KEY4=1<<19;
const uint32 LED8=1<<25; const uint32 LED1=1<<18; const uint32 LED2=1<<19; const uint32 LED3=1<<20; const uint32 LED4=1<<21; const uint32 LED5=1<<22; const uint32 LED6=1<<23; const uint32 LED7=1<<24;
const uint32 LEDS8=0xFF<<18; // 定义串口模式设置的数据结构 const uint32 HCMM[] = { _LA, _SO, _MI, _LA, _SO, _MI, _LA, _LA, _SO, _LA, _LA, _SO, _MI, _LA, _SO, _MI, _RE, _RE, _DO, _RE, _MI, _MI, _SO, _LA, _DO1, _LA, _SO, _MI, _MI, _SO, _DO, _MI, _MI, _MI, _MI, _MI, _1LA,_1LA,_1SO,_1LA, };
/* 歌曲节拍 */
const uint32 HCMM_L[] = { _4, _8, _8, _4, _8, _8, _8, _4, _8, _2, _4, _8, _8, _4, _8, _8, _8, _4, _8, _2, _4, _8, _8, _8, _8, _8, _8, _8, _4, _8, _2, _4, _4, _4, _8, _8, _8, _4, _8, _2, };
typedef struct UartMode { uint8 datab; // 字长度 5/6/7/8 uint8 stopb; // 停止位 1/2 uint8 parity; // 奇偶校验 0-无校验,}UARTMODE;
void Delay(uint8 dly) {
uint32 i;
for(; dly > 0; dly--)
1-奇校验,2-偶校验
{ for(i = 0; i < 0x7FFFF; i++);} }
uint8 UART0_Init (uint32 baud, UARTMODE set) { uint32 bak; // 参数过滤 if ((0 == baud) || (baud > 115200)) return (0); if ((set.datab < 5) || (set.datab > 8)) return (0); if ((0 == set.stopb) || (set.stopb > 2)) return (0); if (set.parity > 4) return (0); // 设置串口波特率 U0LCR = 0x80; // DLAB=1 bak = (Fpclk >> 4) / baud; U0DLM = bak >> 8; U0DLL = bak & 0xff; // 设置串口模式 bak = set.datab - 5; if (2 == set.stopb) bak |= 0x04; if (0 != set.parity) { set.parity = set.parity - 1; bak |= 0x08; } bak |= set.parity << 4; U0LCR = bak; return (0); }
void SendByte (uint8 data) { U0THR = data; while ((U0LSR & 0X20) == 0); // 等待数据发送 }
void PC_DispChar (uint8 no, uint8 chr) { SendByte(0xff); SendByte(0x81); SendByte(no); SendByte(chr); SendByte(0x00); }
void abd(void)
{
IO0CLR=BEEP; IO1CLR=LED8; Delay(5);
IO0SET=BEEP; IO1SET=LED8; }
uint8 const SHOWTABLE[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
void SendTimeRtc (void) { uint32 datas; uint32 times; uint32 bak; times = CTIME0; // 读取完整的时钟寄存器 datas = CTIME1; bak = (datas >> 16) & 0xfff; // 获取 年 PC_DispChar(0, SHOWTABLE[bak / 1000]); bak = bak % 1000; PC_DispChar(1, SHOWTABLE[bak / 100]); bak = bak % 100; PC_DispChar(2, SHOWTABLE[bak / 10]); PC_DispChar(3, SHOWTABLE[bak % 10]); bak = (datas >> 8) & 0x0f; // 获取 月 PC_DispChar(4, SHOWTABLE[bak / 10]); PC_DispChar(5, SHOWTABLE[bak % 10]); bak = datas & 0x1f; // 获取 日 PC_DispChar(6, SHOWTABLE[bak / 10]); PC_DispChar(7, SHOWTABLE[bak % 10]); bak = (times >> 24) & 0x07; // 获取 星期 PC_DispChar(8, SHOWTABLE[bak]); bak = (times >> 16) & 0x1f; // 获取 小时 PC_DispChar(9, SHOWTABLE[bak / 10]); PC_DispChar(10, SHOWTABLE[bak % 10]); bak = (times >> 8) & 0x3f; // 获取 分钟 PC_DispChar(11, SHOWTABLE[bak / 10]); PC_DispChar(12, SHOWTABLE[bak % 10]);
bak = times & 0x3f; // 获取 秒钟 PC_DispChar(13, SHOWTABLE[bak / 10]); PC_DispChar(14, SHOWTABLE[bak % 10]); }
void SendTimeRtc1 (void) { PC_DispChar(0, SHOWTABLE[ALYEAR / 1000]); ALYEAR = ALYEAR % 1000; PC_DispChar(1, SHOWTABLE[ALYEAR / 100]); ALYEAR = ALYEAR % 100; PC_DispChar(2, SHOWTABLE[ALYEAR / 10]); PC_DispChar(3, SHOWTABLE[ALYEAR % 10]); PC_DispChar(4, SHOWTABLE[ALMON / 10]); PC_DispChar(5, SHOWTABLE[ALMON % 10]); PC_DispChar(6, SHOWTABLE[ALDOM / 10]); PC_DispChar(7, SHOWTABLE[ALDOM % 10]); PC_DispChar(8, SHOWTABLE[ALDOW]); PC_DispChar(9, SHOWTABLE[ALHOUR/ 10]); PC_DispChar(10, SHOWTABLE[ALHOUR% 10]); PC_DispChar(11, SHOWTABLE[ALMIN/ 10]); PC_DispChar(12, SHOWTABLE[ALMIN % 10]); PC_DispChar(13, SHOWTABLE[ALSEC / 10]); PC_DispChar(14, SHOWTABLE[ALSEC % 10]); }
void __irq RTC_Int(void) {
uint32 i,num1; if((IO1SET&LED1)==0)IO1SET=LED1; else IO1CLR=LED1; if(num1!=0)SendTimeRtc1(); else SendTimeRtc(); if(ILR==0x03) { for(i=0;i<3;i++) {
IO1CLR=LED8; Delay(3); IO1SET=LED8; Delay(3); } PINSEL0 = (PINSEL0&0xFFFF3FFF)|(0x02 << 14); // P0.7选择PWM2功能 for(i = 0; i < sizeof(HCMM)/4; i++) {
if((IO1SET&LED1)==0)IO1SET=LED1; else IO1CLR=LED1;
PWMMR0 = Fpclk / HCMM[i]; // 设置输出频率 PWMLER = 0x05; // 更新匹配值后,必须锁存 Delay(HCMM_L[i]); // 延时,控制播放速度 if(num1!=0) SendTimeRtc1(); else SendTimeRtc(); }
PINSEL0 = (PINSEL0&0xFFFF3FFF)|(0x00 << 14); // P0.7取消PWM2功能
}
ILR = 0x03; VICVectAddr = 0; }
void RTCInit (void) { YEAR = 2011; MONTH = 04; DOM = 28; DOW = 4; HOUR = 10; MIN = 29; SEC = 50; ALYEAR =2003; ALMON = 04; ALDOM = 01; ALDOW = 4; ALHOUR = 06; ALMIN = 42; ALSEC = 20; AMR =0xFE; //设置报警屏蔽寄存器,设置秒值与报警寄存器比较。 CIIR = 0x01; // 设置秒值的增量产生1次中断 ILR = 0x03; CCR = 0x11; // 启动RTC }
int main (void) { uint8 i,num=0,num1=0; UARTMODE uart0_set; PINSEL0 = 0x00000005; // 连接IO到UART0 PINSEL2 =PINSEL2&(~0x08); IO1DIR = LEDS8; IO1SET= LEDS8; IO0DIR=BEEP; IO0SET=BEEP;
PWMPR = 0x00; // 不分频,计数频率为Fpclk PWMMCR = 0x02; // 设置PWMMR0匹配时复位PWMTC PWMPCR = 0x0400; // 允许PWM2输出,单边PWM PWMMR0 = Fpclk / 1000; PWMMR2 = PWMMR0 / 2; // 50%占空比 PWMLER = 0x05; // PWM0和PWM2匹配锁存 PWMTCR = 0x02; // 复位PWMTC PWMTCR = 0x09; // 启动PWM输出 //IRQEnable(); // 中断使能,程序可用查询方式,也可以用中断方式。 VICIntSelect = 0x00; // 设置所有中断连接IRQ中断 VICVectCntl0 = 0x20 | 13; // 分配通道0
VICVectAddr0 = (int)RTC_Int; // 设置中断服务程序地址 VICIntEnable = (1 << 13); // 使能RTC中断 uart0_set.datab = 8; uart0_set.stopb = 1; uart0_set.parity = 0; UART0_Init(115200, uart0_set); U0FCR = 0x01; // FIFO使能 RTCInit();
while (1) {
if(ILR==0x01) { if((IO1SET&LED1)==0)IO1SET=LED1; else IO1CLR=LED1; if(num1!=0) SendTimeRtc1(); else SendTimeRtc(); if(ILR==0x03) { for(i=0;i<3;i++) {
IO1CLR=LED8; Delay(3); IO1SET=LED8; Delay(3); }
PINSEL0 = (PINSEL0&0xFFFF3FFF)|(0x02 << 14); // P0.7选择PWM2功能 for(i = 0; i < sizeof(HCMM)/4; i++) {
if((IO1SET&LED1)==0)IO1SET=LED1; else IO1CLR=LED1;
PWMMR0 = Fpclk / HCMM[i]; // 设置输出频率 PWMLER = 0x05; // 更新匹配值后,必须锁存 Delay(HCMM_L[i]); // 延时,控制播放速度 if(num1!=0) SendTimeRtc1(); else SendTimeRtc(); }
PINSEL0 = (PINSEL0&0xFFFF3FFF)|(0x00 << 14); // P0.7取消PWM2功能 }
ILR = 0x03; }
if((IO0PIN&KEY1)==0) {
Delay(1);
if((IO0PIN&KEY1)==0) {
abd();
CCR=0x00; num++; switch(num) {
case 1:IO1CLR=LED1;break;
case 2:IO1SET=LED1;IO1CLR=LED2;break; case 3:IO1SET=LED2;IO1CLR=LED3;break; case 4:IO1SET=LED3;IO1CLR=LED4;break; case 5:IO1SET=LED4;IO1CLR=LED5;break; case 6:IO1SET=LED5;IO1CLR=LED6;break; case 7:IO1SET=LED6;IO1CLR=LED7;break; case 8:IO1SET=LED7;num=0;CCR=0x01;break; } }
while((IO0PIN&KEY1)==0);//等待按键释放 }
if((IO0PIN&KEY2)==0) {
Delay(1);
if((IO0PIN&KEY2)==0) {
abd(); num1++;
SendTimeRtc1(); switch(num1) {
case 1:IO1CLR=LED1;break;
case 2:IO1SET=LED1;IO1CLR=LED2;break; case 3:IO1SET=LED2;IO1CLR=LED3;break; case 4:IO1SET=LED3;IO1CLR=LED4;break; case 5:IO1SET=LED4;IO1CLR=LED5;break; case 6:IO1SET=LED5;IO1CLR=LED6;break; case 7:IO1SET=LED6;IO1CLR=LED7;break; case 8:IO1SET=LED7;num1=0;break; } }
while((IO0PIN&KEY2)==0);//等待按键释放 }
if((IO0PIN&KEY3)==0) {
Delay(1);
if((IO0PIN&KEY3)==0) {
abd();
switch(num) {
case 1:if(SEC==0)SEC=59;else SEC--;break; case 2:if(MIN==0)MIN=59;else MIN--;break;
case 3:if(HOUR==0)HOUR=23;else HOUR--;break; case 4:if(DOW==1)DOW=7;else DOW--;break; case 5:if(DOM==1)DOM=31;else DOM--;break;
case 6:if(MONTH==1)MONTH=12;else MONTH--;break; case 7:if(YEAR==0)YEAR=4095;else YEAR--;break; case 8:break; }
switch(num1) {
case 1:if(ALSEC==0)ALSEC=59;else ALSEC--;break; case 2:if(ALMIN==0)ALMIN=59;else ALMIN--;break;
case 3:if(ALHOUR==0)ALHOUR=23;else ALHOUR--;break; case 4:if(ALDOW==1)ALDOW=7;else ALDOW--;break; case 5:if(ALDOM==1)ALDOM=31;else ALDOM--;break;
case 6:if(ALMON==1)ALMON=12;else ALMON--;break;
case 7:if(ALYEAR==0)ALYEAR=4095;else ALYEAR--;break; case 8:break; }
if(num1!=0) SendTimeRtc1(); else SendTimeRtc();
//while((IO0PIN&KEY3)==0);//等待按键释放 } }
if((IO0PIN&KEY4)==0) {
Delay(2);
if((IO0PIN & KEY4)==0) {
abd();
switch(num) {
case 1:if(SEC==59)SEC=0;else SEC++;break; case 2:if(MIN==59)MIN=0;else MIN++;break;
case 3:if(HOUR==23)HOUR=0;else HOUR++;break; case 4:if(DOW==7)DOW=1;else DOW++;break; case 5:if(DOM==31)DOM=1;else DOM++;break;
case 6:if(MONTH==12)MONTH=1;else MONTH++;break; case 7:if(YEAR==4095)YEAR=0;else YEAR++;break; case 8:break; }
switch(num1) {
case 1:if(ALSEC==59)ALSEC=0;else ALSEC++;break; case 2:if(ALMIN==59)ALMIN=0;else ALMIN++;break;
case 3:if(ALHOUR==23)ALHOUR=0;else ALHOUR++;break; case 4:if(ALDOW==7)ALDOW=1;else ALDOW++;break; case 5:if(ALDOM==31)ALDOM=1;else ALDOM++;break; case 6:if(ALMON==12)ALMON=1;else ALMON++;break; case 7:if(ALYEAR==4095)ALYEAR=0;else ALYEAR++;break; case 8:break; }
if(num1!=0) SendTimeRtc1(); else SendTimeRtc();
// while((IO0PIN & KEY4)==0);//等待按键释放 } } }
return (0); }
因篇幅问题不能全部显示,请点此查看更多更全内容