STM32 RTC实时时钟(二)

电子说

1.3w人已加入

描述

上次实验完成了对实时时钟的基本功能——计时的实验,这次在计时的基础上对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)进行编程或更新,必须执行类似的步骤:

  1. 将 RTC_CR 寄存器中的 ALRAE 或 ALRBE 位清零以禁止闹钟 A 或闹钟 B。
  2. 轮询 RTC_ISR 寄存器中的 ALRAWF 或 ALRBWF 位,直到其中一个置 1,以确保闹钟寄存器可以访问。大约需要 2 个 RTCCLK 时钟周期(由于时钟同步)。
  3. 编程闹钟 A 或闹钟 B 寄存器(RTC_ALRMASSR/RTC_ALRMAR 或 RTC_ALRMBSSR/RTC_ALRMBR)。
  4. 将 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闹钟实验测试成功。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分