#申请原创# @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)
{
;
}
}
运行效果:
正常时钟显示
开启关闭闹钟
设定时间
设定闹钟
闹钟响起
|