之前花了几百元买的电子时钟坏了,就用闲置的板子做了一个。

功能是显示/调整日期,时间,多个闹钟,倒计时。倒计时使用实体脉冲旋钮控制。

基本软件架构是STM32F407+UCOSIII+STEMWIN+RTC。

效果如下:

这个是定时器,依靠旋钮调节定时时间

1.RTC

参照原子历程初始化,并且定义一个新的日期时间结构这是为了一次性把相关消息发送到UI,注意要使能后备寄存器。用于保存闹铃数据。这样关机后再次开机依然能保持原来的闹铃。

/** * @brief  RTC Date&Time structure definition  */
typedef struct
{RTC_TimeTypeDef Time;RTC_DateTypeDef Date;
}RTC_DTTypeDef;

时间显示是需要每秒进行更新,所以需要一个周期性的秒中断

void xRTC_Set_WakeUp(u32 wksel,u16 cnt)
{ EXTI_InitTypeDef   EXTI_InitStructure;NVIC_InitTypeDef   NVIC_InitStructure;RTC_WakeUpCmd(DISABLE);//关闭WAKE UPRTC_WakeUpClockConfig(wksel);//唤醒时钟选择RTC_SetWakeUpCounter(cnt);//设置WAKE UP自动重装载寄存器RTC_ClearITPendingBit(RTC_IT_WUT); //清除RTC WAKE UP的标志EXTI_ClearITPendingBit(EXTI_Line22);//清除LINE22上的中断标志位 RTC_ITConfig(RTC_IT_WUT,ENABLE);//开启WAKE UP 定时器中断RTC_WakeUpCmd( ENABLE);//开启WAKE UP 定时器 EXTI_InitStructure.EXTI_Line = EXTI_Line22;//LINE22 (RTC Wakeup)EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE22EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道NVIC_Init(&NVIC_InitStructure);
}//RTC WAKE UP中断服务函数
RTC_DTTypeDef RTC_DT;
static WM_MESSAGE    RTC_Msg = {0};
void RTC_WKUP_IRQHandler(void)
{    OSIntEnter();if(RTC_GetFlagStatus(RTC_FLAG_WUTF)==SET)//WK_UP中断?{ RTC_ClearFlag(RTC_FLAG_WUTF);    //清除中断标志LED1=!LED1; RTC_GetTime(RTC_Format_BIN,&RTC_DT.Time);RTC_GetDate(RTC_Format_BIN, &RTC_DT.Date);RTC_Msg.MsgId=UPDATA_MSG;RTC_Msg.Data.p=&RTC_DT;WM_SendMessage(WM_HBKWIN, &RTC_Msg);}   EXTI_ClearITPendingBit(EXTI_Line22);//清除中断线22(RTC Wakeup event)的中断标志     OSIntExit();
}

2.脉冲旋钮

旋钮旋转时发出两路正交的脉冲信号,使用两个GPIO,配置其中一个为外部中断模式

/*
初始化为输入脚,中断脚
*/
void xRotaryKey_Init(void)
{GPIO_InitTypeDef   GPIO_InitStructure;NVIC_InitTypeDef   NVIC_InitStructure;EXTI_InitTypeDef   EXTI_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOF,ENABLE);  //使能GPIOB/F时钟GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10|GPIO_Pin_11;   GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN;  //输入模式GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;  //浮空,具体看外部硬件GPIO_Init(GPIOB,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin= GPIO_Pin_6;   GPIO_Init(GPIOF,&GPIO_InitStructure);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource10);/* 配置EXTI_Line10 */EXTI_InitStructure.EXTI_Line = EXTI_Line10 ;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中断线使能EXTI_Init(&EXTI_InitStructure);//配置NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//外部中断10,11NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//抢占优先级0NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道NVIC_Init(&NVIC_InitStructure);//配置
}

中断处理旋钮脉冲

/*
外部中断,处理旋钮脉冲
*/
static  WM_MESSAGE    keyMsg = {0};
void EXTI15_10_IRQHandler(void)
{OSIntEnter();if( RESET != EXTI_GetITStatus(EXTI_Line10) ){EXTI_ClearITPendingBit(EXTI_Line10);//清除LINE10上的中断标志位  if(PBin(11)==PBin(10))//通过两个管脚电平判断方向{keyMsg.Data.v=1;}else{keyMsg.Data.v=-1;}keyMsg.MsgId=UPDATA_KNOB_MSG;WM_SendMessage(WM_HBKWIN, &keyMsg);}OSIntExit();
}

旋钮本身自带按键,做按键单击双击长按处理

#define SINGLE_CLICK    1
#define DOUBLE_CLICK    2
#define LONG_PRESS      3
#define NULL_CLICK      0/*
按键扫描,旋转按钮有按键
20ms间隔检测
return: =SINGLE_CLICK单击 =DOUBLE_CLICK双击 =LONG_PRESS长按 =NULL_CLICK无
*/
#define  LONGPRESS_TIMES 35 //长按时间 50 -> 1S
#define _key_scan_PIN  PFin(6)
u8 xRotaryKey_Poll(void)
{static u8 step=0,times=0;switch(step){case 0:if ( _key_scan_PIN == 1 ){ step=1; }break;case 1://按下if ( _key_scan_PIN == 0 ){ step=2;   } break;case 2://times++;if( _key_scan_PIN == 0 ){       if( times > LONGPRESS_TIMES ){ times=step=0;return LONG_PRESS; }//长按  } else //松开{if(times<10) { step=3;}    //进入双击判断, times不清零else{ times=0;step=1; return SINGLE_CLICK; }  // 为单击1  时间为210ms}break;case 3: //已松开times++;if ( _key_scan_PIN == 0 ) { step=4; // } else //松开超时{  if(times>13){step=1;times=0; return SINGLE_CLICK; } //   为单击2  270ms时间}break;case 4://已按下times++;if ( _key_scan_PIN == 0 ) {       if(times>LONGPRESS_TIMES+20){ times=step=0;return LONG_PRESS; }//长按  }else{step=1;if(times<50){ times=0;return DOUBLE_CLICK; } //双击成功}                break;}return NULL_CLICK;
}

3.背光PWM

因为要在UI中做背光调节功能,所以还要加上TIM的PWM功能

背光LED控制IO为PB15,对应定时器TIM12-CH2。

/*
说明:PWM初始化 pb15 ,TIM12-ch2
arr:自动重装值
psc:时钟预分频数
*/
void xTIM12_PWM_Init(u32 arr,u32 psc)
{                              GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM12,ENABLE);      //TIM12时钟使能    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);     //使能PORTB时钟    GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_TIM12); //GPIOB15复用为定时器12GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;           //GPIOB15GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;    //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉GPIO_Init(GPIOB,&GPIO_InitStructure);              //初始化PB15TIM_TimeBaseStructure.TIM_Prescaler=psc;  //定时器分频TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseStructure.TIM_Period=arr;   //自动重装载值TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM12,&TIM_TimeBaseStructure);//初始化定时器12//初始化TIM12 Channel2 PWM模式     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低TIM_OC2Init(TIM12, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM12 OC2TIM_OC2PreloadConfig(TIM12, TIM_OCPreload_Enable);  //使能TIM12在CCR2上的预装载寄存器TIM_ARRPreloadConfig(TIM12,ENABLE);//ARPE使能 TIM_Cmd(TIM12, ENABLE);  //使能TIM
}

在LCD初始化函数末尾加上

    xTIM12_PWM_Init(50-1,840-1);TIM_SetCompare2(TIM12,50-1);

这样,通过库函数TIM_SetCompare2(TIM12,num)可调节背光。num范围为0~49。

4.任务分配

三个工作任务:显示UI任务,触摸任务,脉冲旋钮扫描任务。显示任务比较耗时,优先级要低。

//RotaryKnob任务
//设置任务优先级
#define KNOB_TASK_PRIO                 4
//任务堆栈大小
#define KNOB_STK_SIZE                512
//任务控制块
OS_TCB KNOBTaskTCB;
//任务堆栈
CPU_STK KNOB_TASK_STK[KNOB_STK_SIZE];
//led0任务
void RotaryKnob_task(void *p_arg);//TOUCH任务
//设置任务优先级
#define TOUCH_TASK_PRIO                5
//任务堆栈大小
#define TOUCH_STK_SIZE                128
//任务控制块
OS_TCB TouchTaskTCB;
//任务堆栈
CPU_STK TOUCH_TASK_STK[TOUCH_STK_SIZE];
//touch任务
void touch_task(void *p_arg);//EMWINDEMO任务
//设置任务优先级
#define EMWINDEMO_TASK_PRIO            6
//任务堆栈大小
#define EMWINDEMO_STK_SIZE            1024
//任务控制块
OS_TCB EmwindemoTaskTCB;
//任务堆栈
CPU_STK EMWINDEMO_TASK_STK[EMWINDEMO_STK_SIZE];
//emwindemo_task任务
void emwindemo_task(void *p_arg);

三个任务函数

//EMWINDEMO任务
void emwindemo_task(void *p_arg)
{//更换皮肤BUTTON_SetDefaultSkin(BUTTON_SKIN_FLEX); CHECKBOX_SetDefaultSkin(CHECKBOX_SKIN_FLEX);DROPDOWN_SetDefaultSkin(DROPDOWN_SKIN_FLEX);FRAMEWIN_SetDefaultSkin(FRAMEWIN_SKIN_FLEX);HEADER_SetDefaultSkin(HEADER_SKIN_FLEX);MENU_SetDefaultSkin(MENU_SKIN_FLEX);MULTIPAGE_SetDefaultSkin(MULTIPAGE_SKIN_FLEX);PROGBAR_SetDefaultSkin(PROGBAR_SKIN_FLEX);RADIO_SetDefaultSkin(RADIO_SKIN_FLEX);SCROLLBAR_SetDefaultSkin(SCROLLBAR_SKIN_FLEX);SLIDER_SetDefaultSkin(SLIDER_SKIN_FLEX);SPINBOX_SetDefaultSkin(SPINBOX_SKIN_FLEX); xRTC_Init();                 //初始化RTCxRTC_Set_WakeUp(RTC_WakeUpClock_CK_SPRE_16bits,0);//配置WAKE UP中断,1秒钟中断一次MainTask();//UI处理主函数while(1){ }
}//触摸任务
void touch_task(void *p_arg)
{OS_ERR err;while(1){GUI_TOUCH_Exec();    OSTimeDlyHMSM(0,0,0,5,OS_OPT_TIME_PERIODIC,&err);//频率5ms}
}//旋钮任务
void RotaryKnob_task(void *p_arg)
{OS_ERR err;u8 keyValue=0;WM_MESSAGE    Msg = {0,0,0,0};while(1){keyValue = xRotaryKey_Poll();if( SINGLE_CLICK == keyValue ){//printf("\r\n singkey \r\n"  );Msg.MsgId=UPDATA_KEY_MSG;Msg.Data.v=!Msg.Data.v;WM_SendMessage(WM_HBKWIN, &Msg);}elseif( DOUBLE_CLICK == keyValue ){//printf("\r\n doublekey \r\n"  );}OSTimeDlyHMSM(0,0,0,20,OS_OPT_TIME_PERIODIC,&err);//延时20ms}
}

5.闹钟

硬件闹钟只有一两个,这里使用软件自定义的闹钟组。

#define ALARM_NUM_MAX 5//闹钟
#pragma pack (1)
typedef struct
{unsigned  char en;         //是否启用,最低位表示,1启用。高4位表示type, 类型:0单次, 1工作日, 2每天unsigned  char hour;        //时针unsigned  char minute;    //分针unsigned  char duration;    //铃声时长(秒)//unsigned char type;        //类型:0单次, 1工作日, 2每天
}
xAlarm_TypeDef;
#pragma pack () //闹钟组
typedef struct
{unsigned char   num; //总数unsigned char   availableNum;//有效闹钟数目union {xAlarm_TypeDef   _alarm[ALARM_NUM_MAX];//最大5个闹钟u32     _alarmx[ALARM_NUM_MAX] ;}               alarm;unsigned char   remainingTime;//运行状况:各闹钟剩余响铃时间中,取最大者
}
xAlarms_TypeDef;

闹钟保存在后备寄存器里

/*
存储闹铃组到后备寄存器
*/
void StoreAlarmsToBkp(const xAlarms_TypeDef* pAl)
{char i;RTC_WriteBackupRegister(RTC_BKP_DR2,pAl->num);   RTC_WriteBackupRegister(RTC_BKP_DR3,pAl->availableNum);for(i=0;i<pAl->num;i++){RTC_WriteBackupRegister(RTC_BKP_DR4+i,pAl->alarm._alarmx[i] );}
}
/*
从后备寄存器加载到闹铃组
*/
void LoadAlarmsFromBkp(xAlarms_TypeDef* pAl)
{char i;if(RTC_ReadBackupRegister(RTC_BKP_DR0)==RTCBKP_KEYVALUE)       {pAl->num= RTC_ReadBackupRegister(RTC_BKP_DR2);if(pAl->num==0)return;pAl->availableNum= RTC_ReadBackupRegister(RTC_BKP_DR3);for(i=0;i<pAl->num;i++){pAl->alarm._alarmx[i] = RTC_ReadBackupRegister(RTC_BKP_DR4+i);}}
}
//判断闹钟是否响铃了,返回响铃持续时间(秒),=0代表没有
unsigned  char CheakAlarm( xAlarms_TypeDef* pAl,const RTC_TimeTypeDef *pTime,unsigned  char weekday)
{unsigned  char dur=0,maxdur=0;unsigned  char num = pAl->num;for(;num!=0;num--){if( pAl->alarm._alarm[num-1].en&0x01 &&pAl->alarm._alarm[num-1].minute == pTime->RTC_Minutes   &&pAl->alarm._alarm[num-1].hour == pTime->RTC_Hours       )//判断{switch( pAl->alarm._alarm[num-1].en>>4 ){case 0://单次dur = pAl->alarm._alarm[num-1].duration;(pAl->alarm._alarm[num-1].en) -=1;//&=0xfe  //更改闹铃,使失效pAl->availableNum--;StoreAlarmsToBkp(pAl);//储存到备份寄存器break;case 1://工作日if(weekday<=5)dur = pAl->alarm._alarm[num-1].duration;break;case 2://每天wdur = pAl->alarm._alarm[num-1].duration;break;}if( maxdur < dur) maxdur =dur; //取最大的}}if( maxdur > pAl->remainingTime )pAl->remainingTime = maxdur;//剩余响铃时间return maxdur;
}

6.UI界面

主界面底部有三大按钮:系统配置,时间设置,闹钟设置,分别呼出配置窗口,时间设置窗口,闹铃对话框。

主界面左半部分有三个文本小工具显示时间、日期和星期。右半部分显示定时器(倒计时)无边框窗口。

代码实在太多,不一一粘贴了。

源码在下面请下载:

https://download.csdn.net/download/wangzibigan/10765408

STM32+EMWIN电子日历相关推荐

  1. 基于STM32的电子时钟设计

    基于STM32的电子时钟设计 1.设计目的 2.设计具体要求 3.proteus仿真图 4.程序代码 1.设计目的 (1)掌握使用KEIL MDK5进行软件开发的具体流程: (2)掌握硬件电路的设计方 ...

  2. 电子日历插件(阳历转阴历工具...)

    现在网络上大部分农历算法的都很少,都是百年农历算法(截止到2020年).为此从某云上找到开源大佬[Jea杨(JJonline@JJonline.Cn)]的农历算法.实际上计算阳历转农历没有直接的转换算 ...

  3. HTML简单电子日历的设计与实现

    ** 效果图: ** 源码: 文字版: 简单电子日历

  4. 19、基于STM32的电子打铃器

    19.基于STM32的电子打铃器 引言 1 系统概述 1.1 设计任务 1.2 设计要求 2 方案设计与论证 2.1 芯片选择方案 2.2 系统概述: 2.3 设计要求: 2.4 系统总体设计思路 2 ...

  5. c语言课程设计 日历显示,c语言课程设计_电子日历.doc

    c语言课程设计_电子日历 第 PAGE \* Arabic \* MERGEFORMAT 14 页第 PAGE \* Arabic \* MERGEFORMAT 14 页 课 程 设 计( 论 文 ) ...

  6. 2021-10-22 KiKi定义电子日历类

    勇敢牛牛不怕困难 牛客网 编程初学者入门训练 BC139 题目:KiKi定义电子日历类 附: 思路: 先定义3个整形变量按照题目要求的格式年.月.日输入进去,再按照题目要求的格式日\月\年输出出来即可 ...

  7. 时钟晶振在电子日历上的作用!

    电子日历在生活中日益普遍,该设计主要利用时钟芯片.表晶32.768KHZ,石英晶体谐振器.温度传感器DS18B20 和液晶屏LCD1602 构建了一个可测温式万年历装置,电路结构简单,具有语音报时功能 ...

  8. MSP430G2553 launchpad 口袋板 口袋实验平台 数字电子日历时钟设计 秒表 闹钟 整点报时 万年历 电子时钟

    题10 数字电子日历/时钟设计 设计一个基于MSP430的电子日历和时钟. 基本要求 (1)可通过按键在日历和时间之间切换显示: (2)可由按键调整日期和时间 (3)可整点报时('铆.嘟"声 ...

  9. 电子日历的设计与实现

    电子日历的设计与实现 功能要求: 设计一款简单的电子日历,要求实现显示当天所在月份的全部日期,并且可以通过点击按钮控件切换月份. 实现效果图: 一.界面设计 1.使用<div>标签划分区域 ...

最新文章

  1. 在CentOS 6.9 x86_64的nginx 1.12.2上开启echo-nginx-module模块实录
  2. 2019-7-29 考试总结
  3. 后台服务系统之编写服务消费方实现
  4. gmod的css模块放哪,gmod模式怎么更换?gmod模块安装步骤教程
  5. TCPDUMP/LIBPCAP 3-PCAP's MAN手册(1)
  6. Mint-UI 报错提示缺少“raf.js / vue-lazyload / vue-popup” - 解决办法
  7. 大数据之-Hadoop3.x_MapReduce工作流程---大数据之hadoop3.x工作笔记0109
  8. 外部数据源的使用汇总二
  9. 安装IBM Data Studio Client
  10. Timesten安装
  11. 计算机的acpi高级电源选项配置,ACPI电源管理模式详解
  12. excel两列数据对比找不同_快速找出Excel表格中两列数据不同内容的3种方法!
  13. jquery根据HTML5视频播放实践,jQuery定制HTML 5视频播放器
  14. 日常开单送货VBA模块
  15. 使用wget报错-颁发的证书已经过期。
  16. KATEX简易语法:
  17. 两年内广州城中村全围闭 将施行“门禁”准则
  18. 低功耗电池摄像机、低功耗摄像机、低功耗WiFi摄像机
  19. ubuntu 下安装fetion
  20. ipad怎么和mac分屏_iPad如何进行分屏多任务操作【详细介绍】

热门文章

  1. Vue + Element-ui实现后台管理系统---项目搭建 + ⾸⻚布局实现
  2. Realme的Login接入过程记录
  3. 如何使用Python爬虫抓取数据?
  4. STM32F051 触摸按键功能
  5. ros::spin() 和 ros::spinOnce() 区别及用法
  6. Shiro认证-身份认证加密
  7. 如何让你的android模拟器连接上你电脑的网络
  8. shell 命令nohup不挂断式的命令
  9. 又是一个新问题,Field 'id' doesn't have a default value 最终完美解决
  10. 【光学】基于matlab迈克尔逊干涉仪动态仿真【含Matlab源码 2054期】