上次实验完成了对实时时钟的基本功能——计时的实验,这次在计时的基础上对RTC的可编程闹钟的功能进行测试。
RTC 单元提供两个可编程闹钟,即闹钟 A 和闹钟 B。
可通过将 RTC_CR 寄存器中的 ALRAE 和 ALRBE 位置 1 来使能可编程闹钟功能。如果日历亚秒、秒、分钟、小时、日期或日分别与闹钟寄存器RTC_ALRMASSR/RTC_ALRMAR 和RTC_ALRMBSSR/RTC_ALRMBR 中编程的值相匹配,则 ALRAF 和 ALRBF 标志会被置为1。可通过 RTC_ALRMAR 和 RTC_ALRMBR 寄存器的 MSKx 位以及 RTC_ALRMASSR 和RTC_ALRMBSSR 寄存器的 MASKSSx 位单独选择各日历字段。可通过 RTC_CR 寄存器中的 ALRAIE 和 ALRBIE 位使能闹钟中断。
闹钟 A 和闹钟 B(如果已通过 RTC_CR 寄存器中的位 OSEL[0:1] 使能)可连接到 RTC_ALARM输出。可通过 RTC_CR 寄存器的 POL 位配置 RTC_ALARM 极性。
要对可编程的闹钟(闹钟 A 或闹钟 B)进行编程或更新,必须执行类似的步骤:
将 RTC_CR 寄存器中的 ALRAE 或 ALRBE 位清零以禁止闹钟 A 或闹钟 B。
轮询 RTC_ISR 寄存器中的 ALRAWF 或 ALRBWF 位,直到其中一个置 1,以确保闹钟寄存器可以访问。大约需要 2 个 RTCCLK 时钟周期(由于时钟同步)。
编程闹钟 A 或闹钟 B 寄存器(RTC_ALRMASSR/RTC_ALRMAR 或 RTC_ALRMBSSR/RTC_ALRMBR)。
将 RTC_CR 寄存器中的 ALRAE 或 ALRBE 位置 1 以再次使能闹钟 A 或闹钟 B。
注意:约 2 个 RTCCLK 时钟周期(由于时钟同步)后,将执行对 RTC_CR 寄存器的更改。
写程序前先简单了解下闹钟相关的寄存器:
RTC闹钟A寄存器 (RTC_ALRMAR)
位 31 MSK4:闹钟 A 日期掩码 (Alarm A date mask)
0:如果日期/日匹配,则闹钟 A 置 1
1:在闹钟 A 比较中,日期/日无关
位 30 WDSEL:星期几选择 (Week day selection)
0:DU[3:0] 代表日期的个位
1:DU[3:0] 代表星期几。DT[1:0] 为无关位。
位 29:28 DT[1:0]:日期的十位(BCD 格式)
位 27:24 DU[3:0]:日期的个位或日(BCD 格式)
位 23 MSK3:闹钟 A 小时掩码 (Alarm A hours mask)
0:如果小时匹配,则闹钟 A 置 1
1:在闹钟 A 比较中,小时无关
位 22 PM :AM/PM 符号 (AM/PM notation)
0:AM 或 24 小时制
1:PM
位 21:20 HT[1:0]:小时的十位(BCD 格式)
位 19:16 HU[3:0]:小时的个位(BCD 格式)
位 15 MSK2:闹钟 A 分钟掩码 (Alarm A minutes mask)
0:如果分钟匹配,则闹钟 A 置 1
1:在闹钟 A 比较中,分钟无关
位 14:12 MNT[2:0]:分钟的十位(BCD 格式)
位 11:8 MNU[3:0]:分钟的个位(BCD 格式)
位 7 MSK1:闹钟 A 秒掩码 (Alarm A seconds mask)
0:如果秒匹配,则闹钟 A 置 1
1:在闹钟 A 比较中,秒无关
位 6:4 ST[2:0]:秒的十位(BCD 格式)
位 3:0 SU[3:0]:秒的个位(BCD 格式)
闹钟B寄存器RTC_ALRMBR与闹钟A寄存器RTC_ALRMAR的寄存器功能一样,这里不再进行介绍。
RTC闹钟配置函数和中断处理函数
void RTC_AlarmConfig(u8 Alarm_sel,u8 Alarm_set,u8 Alarm_day,u8 Alarm_hour,u8 Alarm_minute)
{
u32 prigroup = 0;
u32 priority = 0;
u32 temp = 0;
//1.解除保护
//写密钥:先写0xca,再写0x53
RTC- >WPR = 0XCA;
RTC- >WPR = 0X53;
switch(Alarm_set)
{
case SPECIAL_DAY:
temp |= Alarm_day< < 24; //设置具体星期
break;
case WORK_DAY:
case ALL_DAY:
temp |= (u32)(1< < 31);
break;
case DISABLE_DAY:
break;
}
temp |= 1< < 30; //用星期来匹配
temp |= ((Alarm_hour/10)< < 4 | (Alarm_hour%10))< < 16 | 1< < 23;
temp |= ((Alarm_minute/10)< < 4 | (Alarm_minute%10))< < 8 | 1< < 15;
temp |= 1< < 7;
//关闭闹钟,等待闹钟可写入
if(Alarm_sel == ALARM_A)
{
RTC- >CR &= ~(1< < 8);
while((RTC- >ISR & (1< < 0)) == 0){}
RTC- >ALRMAR = temp;
RTC- >CR |= 1< < 8; //开启闹钟A
//开闹钟A的中断
RTC- >CR |= 1< < 12;
//清中断标记
RTC- >ISR &= ~(1< < 8);
}
else if(Alarm_sel == ALARM_B)
{
RTC- >CR &= ~(1< < 9);
while((RTC- >ISR & (1< < 1)) == 0){}
RTC- >ALRMBR = temp;
RTC- >CR |= 1< < 9; //开启闹钟B
//开闹钟B的中断
RTC- >CR |= 1< < 13;
//清中断标记
RTC- >ISR &= ~(1< < 9);
}
//闹钟中断对应于外部中断线,因此要设置外部中断线(17)
EXTI- >IMR |= 1< < 17;
EXTI- >RTSR |= 1< < 17; //闹钟中断需设置为上升沿
//清中断标记
EXTI- >PR |= 1< < 17;
prigroup = NVIC_GetPriorityGrouping();
priority = NVIC_EncodePriority(prigroup,1,2);
NVIC_SetPriority(RTC_Alarm_IRQn,priority);
NVIC_EnableIRQ(RTC_Alarm_IRQn);
switch(Alarm_set)
{
case SPECIAL_DAY:
break;
case WORK_DAY:
temp = (RTC- >DR & 0x0000e000) >>13;
if(temp >5)
{
if(Alarm_sel == ALARM_A)
RTC- >CR &= ~(1< < 8);
else if(Alarm_sel == ALARM_B)
RTC- >CR &= ~(1< < 9);
}
else
{
if(Alarm_sel == ALARM_A)
RTC- >CR |= 1< < 8;
else if(Alarm_sel == ALARM_B)
RTC- >CR |= 1< < 9;
}
break;
case ALL_DAY:
break;
case DISABLE_DAY:
if(Alarm_sel == ALARM_A)
RTC- >CR &= ~(1< < 8);
else if(Alarm_sel == ALARM_B)
RTC- >CR &= ~(1< < 9);
break;
}
//2. 再次保护
RTC- >WPR = 0XFF; //写任意值,再次保护
}
//中断处理
void RTC_Alarm_IRQHandler()
{
EXTI- >PR |= 1< < 17;
//中断处理
if(RTC- >ISR & (1< < 8))
{
RTC- >ISR &= ~(1< < 8);
//闹钟A中断处理
LED_Toggle(DS1);
}
else if(RTC- >ISR & (1< < 9))
{
RTC- >ISR &= ~(1< < 9);
//闹钟B中断处理
}
}
由于闹钟的编写比较简单,这里就不多加描述了,接下来编写主函数进行测试。
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "stdio.h"
#include "RTC.h"
#include "led.h"
typedef struct
{
u8 shi;
u8 fen;
u8 miao;
u8 nian;
u8 yue;
u8 ri;
u8 xingqi;
}TIME_Typedef;
TIME_Typedef time = {1};
int main()
{
LED_Init();
Usart1_Init(115200);
RTC_Init(23,59,50,19,7,9,2);
RTC_AlarmConfig(ALARM_A,SPECIAL_DAY,3,0,0);
while(1)
{
time.shi = ((RTC- >TR & 0x300000) >>20)*10 + ((RTC- >TR & 0xf0000) >>16);
time.fen = ((RTC- >TR & 0x7000) >>12)*10 + ((RTC- >TR & 0xf00) >>8);
time.miao = ((RTC- >TR & 0x70) >>4)*10 + (RTC- >TR & 0xf);
printf("%d:%d:%drn",time.shi,time.fen,time.miao);
Delay_ms(1000);
}
}
初始时间设为23:59:50,闹钟时间设为0:0:0。运行程序,经过10秒钟后,闹钟进入中断,LED灯实现翻转,RTC闹钟实验测试成功。