STM32 RTC实时时钟(二)

发布时间:2024-02-27  

上次实验完成了对实时时钟的基本功能——计时的实验,这次在计时的基础上对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)

44efa15d4874d6051738d65fe5a8b78f_wKgZomS7iJuAAOKwAAAvX0kDxLA561.jpg

位 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闹钟实验测试成功。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>