打印
[其他]

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

[复制链接]
694|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创# @21小跑堂  
9.综合实验XTL+RTC+LCD实现低功耗电子时钟
结合之前的各种测试,利用开发板上的资源实现一个低功耗电子时钟,使用开发板上的LCD显示24小时制时间,利用RTC的闹钟功能实现闹钟,LED闪烁模拟闹钟响起,按键按下后LED闪烁停止,长按按键设置时间,再次长按设置闹钟,双击切换设置的数字位,再次长按退出设置
使用XTL外部低速晶振作为系统时钟源

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

    Lptim_ClrItStatus(M0P_LPTIMER0);   //清除中断标志位
    Lptim_ConfIt(M0P_LPTIMER0, TRUE);  //允许LPTIMER中断
    EnableNvic(LPTIM_0_1_IRQn, IrqLevel3, TRUE);
}
在LPTimer中断中查询按键状态,当检测到按键松开时计时停止,通过计时长短判断是点击操作还是长按操作,按键松开后并过了双击检测时间后关闭LPTimer
void LpTim0_IRQHandler(void)
{
    if (TRUE == Lptim_GetItStatus(M0P_LPTIMER0))
    {
        Lptim_ClrItStatus(M0P_LPTIMER0);//清除LPTimer的中断标志位
        if(clock_lptimer_counter < 0xFF)
            clock_lptimer_counter += 1;
        if(Gpio_GetInputIO(STK_USER_PORT, STK_USER_PIN) == FALSE)
        {
            if(clock_key_keepdowncount < 0xFF)
                clock_key_keepdowncount += 1;
            if(clock_key_keepdowncount == 16)
            {
                clock_key_clickcount = 0;
                key_long_pressed();//长按
            }
        }
        else
        {
            if(clock_lptimer_counter > 8)
            {
                if(clock_key_keepdowncount<16 && clock_key_clickcount == 1)
                {
                    key_single_clicked();//单击
                }
                clock_key_clickcount = 0;
                clock_lptimer_counter = 0;
                Lptim_Cmd(M0P_LPTIMER0, FALSE);
            }
            clock_key_keepdowncount = 0;
        }
    }
}
按键的单机双击长按处理
单击处理:闹钟响起时单击关闭响起状态,未进入设置模式时控制闹钟的开启与关闭,用第三个小数点显示闹钟开启状态,设置模式时增加数字

void key_single_clicked()
{
    uint8_t temp,tempmax;
    if(clock_alarmed)
    {
        Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,FALSE);
        clock_alarmed = 0;
    }
    else if(clock_settingmode == 0)
    {
        clock_alarmon = 1-clock_alarmon;
        Rtc_AlmIeCmd(clock_alarmon);
        lcdshowdot(3,clock_alarmon);
    }
    else
    {
        if(clock_settingtpos == 0)
        {
            temp = clock_hour&0xF0;
            if((clock_hour&0x0F)>3)
                tempmax = 0x10;
            else
                tempmax = 0x20;
            if(temp < tempmax)
                temp += 0x10;
            else
                temp = 0x00;
            clock_hour = temp|(clock_hour&0x0F);
        }
        else if(clock_settingtpos == 1)
        {
            temp = clock_hour&0x0F;
            if(temp<9)
                temp += 1;
            else
                temp = 0;
            if((clock_hour&0xF0)==0x20&&temp>3)
                clock_hour = temp;
            else
                clock_hour = temp|(clock_hour&0xF0);
        }
        else if(clock_settingtpos == 2)
        {
            temp = clock_minute&0xF0;
            if(temp<0x50)
                temp += 0x10;
            else
                temp = 0x00;
            clock_minute = temp|(clock_minute&0x0F);
        }
        else
        {
            temp = clock_minute&0x0F;
            if(temp<9)
                temp += 1;
            else
                temp = 0;
            clock_minute = temp|(clock_minute&0xF0);
        }
        if(clock_settingmode == 1)
            clock_time_cahnged = 1;
        clock_settinshownum = 0;
        show_setting_time();
    }
}
双击处理:向后移动设置位
void key_double_clicked()
{
    if(clock_settingtpos < 3)
        clock_settingtpos += 1;
    else
        clock_settingtpos = 0;
}
长按处理:在时间设置-闹钟设置-退出设置间切换,用第1和第2小数点显示当前设置的是时间还是闹钟
void key_long_pressed()
{
    stc_rtc_time_t readtime;
    stc_rtc_alarmtime_t alarmtime;
    if(clock_settingmode == 0)
    {
        Rtc_ReadDateTime(&readtime);
        clock_hour = readtime.u8Hour;
        clock_minute = readtime.u8Minute;
        clock_settingmode = 1;
        clock_settingtpos = 0;
        clock_time_cahnged = 0;
        lcdshowdot(0,1);
        lcdshowdot(1,1);
    }
    else if(clock_settingmode == 1)
    {
        if(clock_settingmode > 0)
        {
            Rtc_ReadDateTime(&readtime);
            readtime.u8Hour = clock_hour;
            readtime.u8Minute = clock_minute;
            readtime.u8Second = 0x00;
            Rtc_SetTime(&readtime);
            clock_showseconddot = 1;
        }
        Rtc_GetAlarmTime(&alarmtime);
        clock_hour = alarmtime.RtcAlarmHour;
        clock_minute = alarmtime.RtcAlarmMinute;
        clock_settingmode = 2;
        clock_settingtpos = 0;
        lcdshowdot(0,0);
        lcdshowdot(2,1);
    }
    else
    {
        alarmtime.RtcAlarmHour = clock_hour;
        alarmtime.RtcAlarmMinute = clock_minute;
        alarmtime.RtcAlarmSec = 0x00;
        alarmtime.RtcAlarmWeek = 0x7F;
        Rtc_SetAlarmTime(&alarmtime);
        clock_settingmode = 0;
        lcdshowdot(0,0);
        lcdshowdot(2,0);
    }
}
设置RTC中断周期0.5秒,用于控制中间那两个点的闪烁表示秒和设置时间时对应位位的闪动
void rtc_init()
{
    stc_rtc_initstruct_t RtcInitStruct;
    stc_rtc_alarmtime_t RtcAlmStruct;
    DDL_ZERO_STRUCT(RtcInitStruct);                      //变量初始值置零
    DDL_ZERO_STRUCT(RtcAlmStruct);
    Sysctrl_SetPeripheralGate(SysctrlPeripheralRtc,TRUE);//RTC模块时钟打开
    RtcInitStruct.rtcAmpm = RtcPm;                       //24小时制
    RtcInitStruct.rtcClksrc = RtcClkXtl;                 //外部32768晶振
    RtcInitStruct.rtcPrdsel.rtcPrdsel = RtcPrdx;         //周期中断类型PRDX
    RtcInitStruct.rtcPrdsel.rtcPrdx = 0u;                //周期中断时间间隔 0.5秒
    RtcInitStruct.rtcTime.u8Second = 0x00;               //配置RTC时间
    RtcInitStruct.rtcTime.u8Minute = 0x00;
    RtcInitStruct.rtcTime.u8Hour   = 0x09;
    RtcInitStruct.rtcTime.u8Day    = 0x11;
    RtcInitStruct.rtcTime.u8DayOfWeek = 0x05;
    RtcInitStruct.rtcTime.u8Month  = 0x08;
    RtcInitStruct.rtcTime.u8Year   = 0x23;
    RtcInitStruct.rtcCompen = RtcCompenEnable;           // 使能时钟误差补偿
    RtcInitStruct.rtcCompValue = 0;                      //补偿值  根据实际情况进行补偿
    Rtc_Init(&RtcInitStruct);
   
    RtcAlmStruct.RtcAlarmSec = 0x00;
    RtcAlmStruct.RtcAlarmMinute = 0x00;
    RtcAlmStruct.RtcAlarmHour = 0x08;
    RtcAlmStruct.RtcAlarmWeek = 0x7f;                    //从周一到周日   
    Rtc_SetAlarmTime(&RtcAlmStruct);                     //配置闹铃时间   
    Rtc_AlmIeCmd(FALSE);                                 //禁止闹钟中断
   
    EnableNvic(RTC_IRQn, IrqLevel3, TRUE);               //使能RTC中断向量
    Rtc_Cmd(TRUE);                                       //使能RTC开始计数
}
RTC中断处理,每次中断翻转两点的显示状态,并更新时间,判断是否在设置模式,如果在设置模式同时翻转对应位的显示状态
void show_setting_time()
{
    clock_settinshownum = 1 - clock_settinshownum;
    lcdshow09num((clock_settingtpos==0&&clock_settinshownum==0)?0xFF:((clock_hour>>4)&0x0F),0);
    lcdshow09num((clock_settingtpos==1&&clock_settinshownum==0)?0xFF:clock_hour&0x0F,1);
    lcdshow09num((clock_settingtpos==2&&clock_settinshownum==0)?0xFF:((clock_minute>>4)&0x0F),2);
    lcdshow09num((clock_settingtpos==3&&clock_settinshownum==0)?0xFF:clock_minute&0x0F,3);
}
void show_time()
{
    stc_rtc_time_t readtime;
    clock_showseconddot = 1 - clock_showseconddot;
    if(clock_settingmode == 0)
    {
        Rtc_ReadDateTime(&readtime);
        lcdshow09num(((readtime.u8Hour>>4)&0x0F),0);
        lcdshow09num(readtime.u8Hour&0x0F,1);
        lcdshow09num(((readtime.u8Minute>>4)&0x0F),2);
        lcdshow09num(readtime.u8Minute&0x0F,3);
        lcdshowdot(1,clock_showseconddot);
    }
    else
    {
        show_setting_time();
    }
}

void Rtc_IRQHandler(void)
{
    if(Rtc_GetPridItStatus() == TRUE)
    {
        Rtc_ClearPrdfItStatus();             //清除中断标志位
        if(clock_alarmed)
            Gpio_WriteOutputIO(STK_LED_PORT, STK_LED_PIN,!Gpio_ReadOutputIO(STK_LED_PORT, STK_LED_PIN));
        show_time();
    }
    if (Rtc_GetAlmfItStatus() == TRUE) //闹铃中断
    {
        Rtc_ClearAlmfItStatus();       //清中断标志位
        if(clock_alarmon > 0)
            clock_alarmed = 1;
    }
}
主函数
int32_t main(void)
{
    Xtl_init();
    Sysctrl_SetHCLKDiv(SysctrlHclkDiv1);
    Sysctrl_SetPCLKDiv(SysctrlPclkDiv1);
    led_init();
    key_init();
    lptimer_init();
    lcd_io_init();
    lcd_init();
    rtc_init();
    Lcd_ClearDisp();
    lcdshow09num(0,3);
    lcdshow09num(0,2);
    lcdshow09num(0,1);
    lcdshow09num(0,0);
    lcdshowdot(1,1);
    Lpm_GotoDeepSleep(TRUE);
    while(1)
    {
        ;
    }
}
运行效果:
正常时钟显示

开启关闭闹钟

设定时间

设定闹钟

闹钟响起


使用特权

评论回复
沙发
llljh| | 2023-8-11 23:07 | 只看该作者
支持原创

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

146

主题

698

帖子

6

粉丝