[其他] 【HC32L196PCTA测评】9.综合实验XTL+RTC+LCD实现低功耗电子时钟

[复制链接]
 楼主| yuyy1989 发表于 2023-8-11 13:20 | 显示全部楼层 |阅读模式
#申请原创# @21小跑堂  
9.综合实验XTL+RTC+LCD实现低功耗电子时钟
结合之前的各种测试,利用开发板上的资源实现一个低功耗电子时钟,使用开发板上的LCD显示24小时制时间,利用RTC的闹钟功能实现闹钟,LED闪烁模拟闹钟响起,按键按下后LED闪烁停止,长按按键设置时间,再次长按设置闹钟,双击切换设置的数字位,再次长按退出设置
使用XTL外部低速晶振作为系统时钟源

  1. void Xtl_init()
  2. {
  3.     Sysctrl_XTLDriverCfg(SysctrlXtlAmp3, SysctrlXtalDriver3);
  4.     Sysctrl_SetXTLStableTime(SysctrlXtlStableCycle4096);
  5.     Sysctrl_ClkSourceEnable(SysctrlClkXTL,TRUE);
  6.     Sysctrl_SysClkSwitch(SysctrlClkXTL);
  7.     Flash_WaitCycle(FlashWaitCycle0);
  8. }
LCD和LED初始化方法和LCD显示数字方法省略,和之前测试用的相同,初始化按键的IO,开启按键下降沿中断
  1. void key_init(void)
  2. {
  3.     stc_gpio_cfg_t stcGpioCfg;
  4.     Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
  5.     stcGpioCfg.enDir = GpioDirIn;
  6.     stcGpioCfg.enDrv = GpioDrvL;
  7.     stcGpioCfg.enPu = GpioPuDisable;
  8.     stcGpioCfg.enPd = GpioPdDisable;
  9.     Gpio_Init(STK_USER_PORT, STK_USER_PIN, &stcGpioCfg);
  10.     Gpio_EnableIrq(STK_USER_PORT, STK_USER_PIN, GpioIrqFalling);
  11.     EnableNvic(PORTA_IRQn, IrqLevel2, TRUE);
  12. }
按键中断处理,下降沿中断触发后开启LPTimer计时
  1. void PortA_IRQHandler(void)
  2. {
  3.     if(TRUE == Gpio_GetIrqStatus(STK_USER_PORT, STK_USER_PIN))
  4.     {         
  5.         Gpio_ClearIrq(STK_USER_PORT, STK_USER_PIN);         
  6.         if(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE)
  7.         {
  8.             Lptim_Cmd(M0P_LPTIMER0, TRUE);
  9.             clock_key_clickcount += 1;
  10.             if(clock_key_clickcount == 2)
  11.             {
  12.                 key_double_clicked();//双击
  13.                 clock_key_clickcount = 0;
  14.             }
  15.         }  
  16.     }
  17. }
初始化LPTimer,设定触发时间为0.0625秒
  1. void lptimer_init()
  2. {
  3.     stc_lptim_cfg_t    stcLptCfg;
  4.     DDL_ZERO_STRUCT(stcLptCfg);
  5.     ///< 使能LPTIM0 外设时钟
  6.     Sysctrl_ClkSourceEnable(SysctrlClkXTL,TRUE);
  7.     Sysctrl_SetPeripheralGate(SysctrlPeripheralLpTim0, TRUE);
  8.     stcLptCfg.enPrs    = LptimPrsDiv256;
  9.     stcLptCfg.enGate   = LptimGateLow;
  10.     stcLptCfg.enGatep  = LptimGatePLow;
  11.     stcLptCfg.enTcksel = LptimXtl;
  12.     stcLptCfg.enTogen  = LptimTogEnLow;
  13.     stcLptCfg.enCt     = LptimTimerFun;         
  14.     stcLptCfg.enMd     = LptimMode2;            //工作模式为模式2:自动重载16位计数器/定时器
  15.     stcLptCfg.u16Arr   = 0x10000 - 8;          //预装载寄存器值
  16.     Lptim_Init(M0P_LPTIMER0, &stcLptCfg);

  17.     Lptim_ClrItStatus(M0P_LPTIMER0);   //清除中断标志位
  18.     Lptim_ConfIt(M0P_LPTIMER0, TRUE);  //允许LPTIMER中断
  19.     EnableNvic(LPTIM_0_1_IRQn, IrqLevel3, TRUE);
  20. }
在LPTimer中断中查询按键状态,当检测到按键松开时计时停止,通过计时长短判断是点击操作还是长按操作,按键松开后并过了双击检测时间后关闭LPTimer
  1. void LpTim0_IRQHandler(void)
  2. {
  3.     if (TRUE == Lptim_GetItStatus(M0P_LPTIMER0))
  4.     {
  5.         Lptim_ClrItStatus(M0P_LPTIMER0);//清除LPTimer的中断标志位
  6.         if(clock_lptimer_counter < 0xFF)
  7.             clock_lptimer_counter += 1;
  8.         if(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE)
  9.         {
  10.             if(clock_key_keepdowncount < 0xFF)
  11.                 clock_key_keepdowncount += 1;
  12.             if(clock_key_keepdowncount == 16)
  13.             {
  14.                 clock_key_clickcount = 0;
  15.                 key_long_pressed();//长按
  16.             }
  17.         }
  18.         else
  19.         {
  20.             if(clock_lptimer_counter > 8)
  21.             {
  22.                 if(clock_key_keepdowncount<16 && clock_key_clickcount == 1)
  23.                 {
  24.                     key_single_clicked();//单击
  25.                 }
  26.                 clock_key_clickcount = 0;
  27.                 clock_lptimer_counter = 0;
  28.                 Lptim_Cmd(M0P_LPTIMER0, FALSE);
  29.             }
  30.             clock_key_keepdowncount = 0;
  31.         }
  32.     }
  33. }
按键的单机双击长按处理
单击处理:闹钟响起时单击关闭响起状态,未进入设置模式时控制闹钟的开启与关闭,用第三个小数点显示闹钟开启状态,设置模式时增加数字

  1. void key_single_clicked()
  2. {
  3.     uint8_t temp,tempmax;
  4.     if(clock_alarmed)
  5.     {
  6.         Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,FALSE);
  7.         clock_alarmed = 0;
  8.     }
  9.     else if(clock_settingmode == 0)
  10.     {
  11.         clock_alarmon = 1-clock_alarmon;
  12.         Rtc_AlmIeCmd(clock_alarmon);
  13.         lcdshowdot(3,clock_alarmon);
  14.     }
  15.     else
  16.     {
  17.         if(clock_settingtpos == 0)
  18.         {
  19.             temp = clock_hour&0xF0;
  20.             if((clock_hour&0x0F)>3)
  21.                 tempmax = 0x10;
  22.             else
  23.                 tempmax = 0x20;
  24.             if(temp < tempmax)
  25.                 temp += 0x10;
  26.             else
  27.                 temp = 0x00;
  28.             clock_hour = temp|(clock_hour&0x0F);
  29.         }
  30.         else if(clock_settingtpos == 1)
  31.         {
  32.             temp = clock_hour&0x0F;
  33.             if(temp<9)
  34.                 temp += 1;
  35.             else
  36.                 temp = 0;
  37.             if((clock_hour&0xF0)==0x20&&temp>3)
  38.                 clock_hour = temp;
  39.             else
  40.                 clock_hour = temp|(clock_hour&0xF0);
  41.         }
  42.         else if(clock_settingtpos == 2)
  43.         {
  44.             temp = clock_minute&0xF0;
  45.             if(temp<0x50)
  46.                 temp += 0x10;
  47.             else
  48.                 temp = 0x00;
  49.             clock_minute = temp|(clock_minute&0x0F);
  50.         }
  51.         else
  52.         {
  53.             temp = clock_minute&0x0F;
  54.             if(temp<9)
  55.                 temp += 1;
  56.             else
  57.                 temp = 0;
  58.             clock_minute = temp|(clock_minute&0xF0);
  59.         }
  60.         if(clock_settingmode == 1)
  61.             clock_time_cahnged = 1;
  62.         clock_settinshownum = 0;
  63.         show_setting_time();
  64.     }
  65. }
双击处理:向后移动设置位
  1. void key_double_clicked()
  2. {
  3.     if(clock_settingtpos < 3)
  4.         clock_settingtpos += 1;
  5.     else
  6.         clock_settingtpos = 0;
  7. }
长按处理:在时间设置-闹钟设置-退出设置间切换,用第1和第2小数点显示当前设置的是时间还是闹钟
  1. void key_long_pressed()
  2. {
  3.     stc_rtc_time_t readtime;
  4.     stc_rtc_alarmtime_t alarmtime;
  5.     if(clock_settingmode == 0)
  6.     {
  7.         Rtc_ReadDateTime(&readtime);
  8.         clock_hour = readtime.u8Hour;
  9.         clock_minute = readtime.u8Minute;
  10.         clock_settingmode = 1;
  11.         clock_settingtpos = 0;
  12.         clock_time_cahnged = 0;
  13.         lcdshowdot(0,1);
  14.         lcdshowdot(1,1);
  15.     }
  16.     else if(clock_settingmode == 1)
  17.     {
  18.         if(clock_settingmode > 0)
  19.         {
  20.             Rtc_ReadDateTime(&readtime);
  21.             readtime.u8Hour = clock_hour;
  22.             readtime.u8Minute = clock_minute;
  23.             readtime.u8Second = 0x00;
  24.             Rtc_SetTime(&readtime);
  25.             clock_showseconddot = 1;
  26.         }
  27.         Rtc_GetAlarmTime(&alarmtime);
  28.         clock_hour = alarmtime.RtcAlarmHour;
  29.         clock_minute = alarmtime.RtcAlarmMinute;
  30.         clock_settingmode = 2;
  31.         clock_settingtpos = 0;
  32.         lcdshowdot(0,0);
  33.         lcdshowdot(2,1);
  34.     }
  35.     else
  36.     {
  37.         alarmtime.RtcAlarmHour = clock_hour;
  38.         alarmtime.RtcAlarmMinute = clock_minute;
  39.         alarmtime.RtcAlarmSec = 0x00;
  40.         alarmtime.RtcAlarmWeek = 0x7F;
  41.         Rtc_SetAlarmTime(&alarmtime);
  42.         clock_settingmode = 0;
  43.         lcdshowdot(0,0);
  44.         lcdshowdot(2,0);
  45.     }
  46. }
设置RTC中断周期0.5秒,用于控制中间那两个点的闪烁表示秒和设置时间时对应位位的闪动
  1. void rtc_init()
  2. {
  3.     stc_rtc_initstruct_t RtcInitStruct;
  4.     stc_rtc_alarmtime_t RtcAlmStruct;
  5.     DDL_ZERO_STRUCT(RtcInitStruct);                      //变量初始值置零
  6.     DDL_ZERO_STRUCT(RtcAlmStruct);
  7.     Sysctrl_SetPeripheralGate(SysctrlPeripheralRtc,TRUE);//RTC模块时钟打开
  8.     RtcInitStruct.rtcAmpm = RtcPm;                       //24小时制
  9.     RtcInitStruct.rtcClksrc = RtcClkXtl;                 //外部32768晶振
  10.     RtcInitStruct.rtcPrdsel.rtcPrdsel = RtcPrdx;         //周期中断类型PRDX
  11.     RtcInitStruct.rtcPrdsel.rtcPrdx = 0u;                //周期中断时间间隔 0.5秒
  12.     RtcInitStruct.rtcTime.u8Second = 0x00;               //配置RTC时间
  13.     RtcInitStruct.rtcTime.u8Minute = 0x00;
  14.     RtcInitStruct.rtcTime.u8Hour   = 0x09;
  15.     RtcInitStruct.rtcTime.u8Day    = 0x11;
  16.     RtcInitStruct.rtcTime.u8DayOfWeek = 0x05;
  17.     RtcInitStruct.rtcTime.u8Month  = 0x08;
  18.     RtcInitStruct.rtcTime.u8Year   = 0x23;
  19.     RtcInitStruct.rtcCompen = RtcCompenEnable;           // 使能时钟误差补偿
  20.     RtcInitStruct.rtcCompValue = 0;                      //补偿值  根据实际情况进行补偿
  21.     Rtc_Init(&RtcInitStruct);
  22.    
  23.     RtcAlmStruct.RtcAlarmSec = 0x00;
  24.     RtcAlmStruct.RtcAlarmMinute = 0x00;
  25.     RtcAlmStruct.RtcAlarmHour = 0x08;
  26.     RtcAlmStruct.RtcAlarmWeek = 0x7f;                    //从周一到周日   
  27.     Rtc_SetAlarmTime(&RtcAlmStruct);                     //配置闹铃时间   
  28.     Rtc_AlmIeCmd(FALSE);                                 //禁止闹钟中断
  29.    
  30.     EnableNvic(RTC_IRQn, IrqLevel3, TRUE);               //使能RTC中断向量
  31.     Rtc_Cmd(TRUE);                                       //使能RTC开始计数
  32. }
RTC中断处理,每次中断翻转两点的显示状态,并更新时间,判断是否在设置模式,如果在设置模式同时翻转对应位的显示状态
  1. void show_setting_time()
  2. {
  3.     clock_settinshownum = 1 - clock_settinshownum;
  4.     lcdshow09num((clock_settingtpos==0&&clock_settinshownum==0)?0xFF:((clock_hour>>4)&0x0F),0);
  5.     lcdshow09num((clock_settingtpos==1&&clock_settinshownum==0)?0xFF:clock_hour&0x0F,1);
  6.     lcdshow09num((clock_settingtpos==2&&clock_settinshownum==0)?0xFF:((clock_minute>>4)&0x0F),2);
  7.     lcdshow09num((clock_settingtpos==3&&clock_settinshownum==0)?0xFF:clock_minute&0x0F,3);
  8. }
  9. void show_time()
  10. {
  11.     stc_rtc_time_t readtime;
  12.     clock_showseconddot = 1 - clock_showseconddot;
  13.     if(clock_settingmode == 0)
  14.     {
  15.         Rtc_ReadDateTime(&readtime);
  16.         lcdshow09num(((readtime.u8Hour>>4)&0x0F),0);
  17.         lcdshow09num(readtime.u8Hour&0x0F,1);
  18.         lcdshow09num(((readtime.u8Minute>>4)&0x0F),2);
  19.         lcdshow09num(readtime.u8Minute&0x0F,3);
  20.         lcdshowdot(1,clock_showseconddot);
  21.     }
  22.     else
  23.     {
  24.         show_setting_time();
  25.     }
  26. }

  27. void Rtc_IRQHandler(void)
  28. {
  29.     if(Rtc_GetPridItStatus() == TRUE)
  30.     {
  31.         Rtc_ClearPrdfItStatus();             //清除中断标志位
  32.         if(clock_alarmed)
  33.             Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,!Gpio_ReadOutputIO(STK_LED_PORT, STK_LED_PIN));
  34.         show_time();
  35.     }
  36.     if (Rtc_GetAlmfItStatus() == TRUE) //闹铃中断
  37.     {
  38.         Rtc_ClearAlmfItStatus();       //清中断标志位
  39.         if(clock_alarmon > 0)
  40.             clock_alarmed = 1;
  41.     }
  42. }
主函数
  1. int32_t main(void)
  2. {
  3.     Xtl_init();
  4.     Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
  5.     Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
  6.     led_init();
  7.     key_init();
  8.     lptimer_init();
  9.     lcd_io_init();
  10.     lcd_init();
  11.     rtc_init();
  12.     Lcd_ClearDisp();
  13.     lcdshow09num(0,3);
  14.     lcdshow09num(0,2);
  15.     lcdshow09num(0,1);
  16.     lcdshow09num(0,0);
  17.     lcdshowdot(1,1);
  18.     Lpm_GotoDeepSleep(TRUE);
  19.     while(1)
  20.     {
  21.         ;
  22.     }
  23. }
运行效果:
正常时钟显示
WeChat_20230811104604 00_00_00-00_00_30.gif
开启关闭闹钟
WeChat_20230811104614 00_00_00-00_00_30.gif
设定时间
WeChat_20230811104622 00_00_00-00_00_30.gif
设定闹钟
WeChat_20230811104631 00_00_00-00_00_30.gif
闹钟响起

WeChat_20230811104637 00_00_00-00_00_30.gif
llljh 发表于 2023-8-11 23:07 | 显示全部楼层
支持原创
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

161

主题

814

帖子

10

粉丝
快速回复 返回顶部 返回列表