STM32+EMWIN电子日历
之前花了幾百元買的電子時鐘壞了,就用閑置的板子做了一個。
功能是顯示/調(diào)整日期,時間,多個鬧鐘,倒計時。倒計時使用實體脈沖旋鈕控制。
基本軟件架構(gòu)是STM32F407+UCOSIII+STEMWIN+RTC。
效果如下:
這個是定時器,依靠旋鈕調(diào)節(jié)定時時間
?
1.RTC
參照原子歷程初始化,并且定義一個新的日期時間結(jié)構(gòu)這是為了一次性把相關消息發(fā)送到UI,注意要使能后備寄存器。用于保存鬧鈴數(shù)據(jù)。這樣關機后再次開機依然能保持原來的鬧鈴。
/** * @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; //上升沿觸發(fā) EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE22EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//搶占優(yōu)先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子優(yōu)先級NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中斷通道NVIC_Init(&NVIC_InitStructure); }//RTC WAKE UP中斷服務函數(shù) 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.脈沖旋鈕
旋鈕旋轉(zhuǎn)時發(fā)出兩路正交的脈沖信號,使用兩個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; //上升沿觸發(fā)EXTI_InitStructure.EXTI_LineCmd = ENABLE;//中斷線使能EXTI_Init(&EXTI_InitStructure);//配置NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//外部中斷10,11NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//搶占優(yōu)先級0NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子優(yōu)先級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/* 按鍵掃描,旋轉(zhuǎn)按鈕有按鍵 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中做背光調(diào)節(jié)功能,所以還要加上TIM的PWM功能
背光LED控制IO為PB15,對應定時器TIM12-CH2。
/* 說明:PWM初始化 pb15 ,TIM12-ch2 arr:自動重裝值 psc:時鐘預分頻數(shù) */ 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; //向上計數(shù)模式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脈沖寬度調(diào)制模式2TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //輸出極性:TIM輸出比較極性低TIM_OC2Init(TIM12, &TIM_OCInitStructure); //根據(jù)T指定的參數(shù)初始化外設TIM12 OC2TIM_OC2PreloadConfig(TIM12, TIM_OCPreload_Enable); //使能TIM12在CCR2上的預裝載寄存器TIM_ARRPreloadConfig(TIM12,ENABLE);//ARPE使能 TIM_Cmd(TIM12, ENABLE); //使能TIM }在LCD初始化函數(shù)末尾加上
xTIM12_PWM_Init(50-1,840-1);TIM_SetCompare2(TIM12,50-1);?這樣,通過庫函數(shù)TIM_SetCompare2(TIM12,num)可調(diào)節(jié)背光。num范圍為0~49。
4.任務分配
三個工作任務:顯示UI任務,觸摸任務,脈沖旋鈕掃描任務。顯示任務比較耗時,優(yōu)先級要低。
//RotaryKnob任務 //設置任務優(yōu)先級 #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任務 //設置任務優(yōu)先級 #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任務 //設置任務優(yōu)先級 #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);三個任務函數(shù)
//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處理主函數(shù)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; //總數(shù)unsigned char availableNum;//有效鬧鐘數(shù)目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);}} } //判斷鬧鐘是否響鈴了,返回響鈴持續(xù)時間(秒),=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界面?
主界面底部有三大按鈕:系統(tǒng)配置,時間設置,鬧鐘設置,分別呼出配置窗口,時間設置窗口,鬧鈴對話框。
主界面左半部分有三個文本小工具顯示時間、日期和星期。右半部分顯示定時器(倒計時)無邊框窗口。
代碼實在太多,不一一粘貼了。
源碼在下面請下載:
https://download.csdn.net/download/wangzibigan/10765408
總結(jié)
以上是生活随笔為你收集整理的STM32+EMWIN电子日历的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab的spwm仿真,SPWM交流
- 下一篇: 集丰照明|无主灯智能照明设计,从构想到实