首先是手表首页的UI设计
首页代码设计如下:
#include "stm32f10x.h" // Device header
#include "MyRTC.h"
#include "OLED.h"
#include "Key.h"
void Peripheral_Init(void)
{
MyRTC_Init();
}
//设置显示首页时钟代码
void Show_Clock_UI(void)
{
MyRTC_ReadTime();//读取实时时钟
OLED_Printf(0,0,OLED_6X8,"%d-%d-%d",MyRTC_Time[0],MyRTC_Time[1],MyRTC_Time[2]);//设置年月日
OLED_Printf(16,16,OLED_12X24,"%02d-%02d-%02d",MyRTC_Time[3],MyRTC_Time[4],MyRTC_Time[5]);//设置时分秒
OLED_ShowString(0,48,"菜单",OLED_8X16);//设置菜单,注意这里的8X16相当于汉字符的16X16
OLED_ShowString(96,48,"设置",OLED_8X16);
}
//选项设置
int First_Page_Clock()
{
uint8_t ClFlag=1;
uint8_t KeyNum;
while(1)
{
KeyNum=Key_GetNum();
if(KeyNum==1)//上一项
{
ClFlag—;
if(ClFlag<=0){ClFlag=2;}
}
else if(KeyNum==2)//下一项
{
ClFlag++;
if(ClFlag>=3){ClFlag=1;}
}
else if(KeyNum==3)//确认
{
OLED_Clear();
OLED_Update();
return ClFlag;
}
switch (ClFlag)//选择菜单还是设置
{
case 1:
Show_Clock_UI();
OLED_ReverseArea(0,48,32,16);
OLED_Update();
break;
case 2:
Show_Clock_UI();
OLED_ReverseArea(96,48,32,16);
OLED_Update();
break;
}
}
}
主函数代码如下:
int main(void)
{
uint8_t ClFlag1;
OLED_Init();
OLED_Clear();
Timer_Init();
Peripheral_Init();
Key_Init();
LED_Init();
while (1)
{
ClFlag1=First_Page_Clock();
if(ClFlag1==1) //进入菜单页面
{
}
else if(ClFlag1==2) //进入设置页面
{
}
}
}
void TIM2_IRQHandler(void) //每毫秒进一次中断
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Key_Tick(); //20毫秒读一次键数
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
这里要注意的是原函数代码没有12X24的像素点,也没有“菜单设置”这四个汉字,需要自己配置,配置流程如下:
首先是像素点的配置:

进入这个取模软件,然后将
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~
这段代码复制粘贴到方框里,然后点开设置按照

这样来设置,就是删了两个括号。




然后按照上面图片来操作,就是先到OLED_Data.C添加格式,然后到.h去声明一下,然后到OLED.C添加这个参数,最后就是定义。
配置汉字操作如下:


由图片可以知道,先设置你想要的汉字大小,然后填入的你需要的汉字,再复制粘贴到OLED.Data库就行了,注意设置页面要改成C51的。
最后因为江科大的Key代码是阻塞式操作,就是按下按键函数会一直卡在while循环里,这就会导致一直按下按键,手表首页UI时间就会卡着不动,所以解决这个方法就可以把读取按键改成,每20ms读取一次按键值,然后按键值变成非0数后又变回0,就是按下了那个按键,计时就可以交给中断,设置一个函数,定义一个初始值为20的Count变量,然后每调用一次函数就减一次Count,减到0就读取按键值,然后将这个函数放到每1ms进一次中断的中断函数里,这样就实现了每20ms读一次按键值的操作。
代码如下:
uint8_t Key_GetNum(void)
{
uint8_t Temp;
if(Key_Num)
{
Temp=Key_Num;
Key_Num=0;
return Temp;
}
else
{
return 0;
}
}
uint8_t Key_GetState()
{
uint8_t KeyNum = 0;//定义变量,默认键码值为0
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == 0)//读PA4输入寄存器的状态,如果为0,则代表按键1按下
{
KeyNum = 1;//置键码为1
}
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4) == 0)//读PA6输入寄存器的状态,如果为0,则代表按键2按下
{
KeyNum = 2;//置键码为2
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)//读PB1输入寄存器的状态,如果为0,则代表按键2按下
{
KeyNum = 3;//置键码为3
}
return KeyNum;//返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
void Key_Tick(void)//每1ms调用一次,20ms读取一次键值
{
static uint8_t CurrentState = 0;//当前状态值(静态变量,保持值)
static uint8_t PreState = 0;//以前状态值(静态变量,保持值)
static uint8_t Count = 0;//计数器(静态变量,保持值)
Count++;
if(Count >= 20)
{
Count = 0;
PreState = CurrentState;
CurrentState = Key_GetState();
if(PreState != 0 && CurrentState == 0)//检测按键松开(松开时返回键值)
{
Key_Num = PreState;
}
}
}
主函数调用中断代码如下:
void TIM2_IRQHandler(void) //每毫秒进一次中断
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Key_Tick(); //20毫秒读一次键数
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
接下来是进入设置页面和更改日期时间的代码:
进入设置页面代码:
//进入设置界面代码
void show_SettingPage_UI(void)
{
OLED_ShowImage(0,0,16,16,Return);
OLED_ShowString(0,16,"日期设置时间",OLED_8X16);
}
uint16_t Setting_Page()
{
uint8_t SetTime_SelectFlag=0;//及时清零,不然会直接跳转页面
uint8_t SetFlag=1;
while(1)
{
OLED_ShowImage(0,0,16,16,Return);
OLED_ShowString(0,16,"设置日期时间",OLED_8X16);
KeyNum=Key_GetNum();
if(KeyNum==1)//上一项
{
SetFlag—;
if(SetFlag<=0){SetFlag=2;}
}
else if(KeyNum==2)//下一项
{
SetFlag++;
if(SetFlag>=3){SetFlag=1;}
}
else if(KeyNum==3)//确认
{
OLED_Clear();
OLED_Update();
SetTime_SelectFlag=SetFlag;//选择返回还是进入时间设置页面
}
if(SetTime_SelectFlag==1){return 0;}//跳出Setting_Page()函数,回到main函数while底部,重新运行
else if(SetTime_SelectFlag==2){SetTime(); SetTime_SelectFlag=0;}//进入时间设置页面,返回后清零标志
switch (SetFlag)//选择返回或者设置日期时间
{
case 1:
show_SettingPage_UI();
OLED_ReverseArea(0,0,16,16);
OLED_Update();
break;
case 2:
show_SettingPage_UI();
OLED_ReverseArea(0,16,96,16);
OLED_Update();
break;
}
}
}
进入设置日期时间代码:
#include "stm32f10x.h" // Device header
#include "MyRTC.h"
#include "OLED.h"
#include "Key.h"
//显示时间第一页(年月日)
void ShowTime_FirstPage(void)
{
OLED_ShowImage(0,0,16,16,Return);
OLED_Printf(0,16,OLED_8X16,"年:%4d",MyRTC_Time[0]);
OLED_Printf(0,32,OLED_8X16,"月:%2d",MyRTC_Time[1]);
OLED_Printf(0,48,OLED_8X16,"日:%2d",MyRTC_Time[2]);
}
//显示时间第二页(时分秒)
void ShowTime_SecondPage(void)
{
OLED_Printf(0,0,OLED_8X16,"时:%2d",MyRTC_Time[3]);
OLED_Printf(0,16,OLED_8X16,"分:%2d",MyRTC_Time[4]);
OLED_Printf(0,32,OLED_8X16,"秒:%2d",MyRTC_Time[5]);
}
void Chance_RTCTime(uint8_t i,uint16_t Flag) //Flag=1;加1,Flag=0;减1
{
if(Flag==1){ MyRTC_Time[i]++;}
else if(Flag==0){ MyRTC_Time[i]—;}
MyRCT_SetTime(); //将更改的值刷新到硬件中去
}
//【修复】去掉 extern KeyNum,改为各函数内部使用局部变量,避免跨文件全局变量依赖
//extern uint8_t KeyNum; //声明KeyNum在别处有定义,直接调用
//【修复】SetYear 加了 while(1) 循环,持续读取按键直到按确认键(Key3)退出
//原来的代码只执行一次就返回,外层 SetTime() 会反复调用导致死循环
void SetYear(void) //改变年函数
{
while(1)
{
uint8_t KeyNum=Key_GetNum(); //【修复】用局部变量替代 extern 全局变量
if(KeyNum==1)//加1
{
Chance_RTCTime(0,1);
}
else if(KeyNum==2)//减1
{
Chance_RTCTime(0,0);
}
else if(KeyNum==3){return;} //确认并退出
ShowTime_FirstPage();
OLED_ReverseArea(24,16,32,16); //显示更改的数值
OLED_Update();
}
}
//【修复】SetMonth 加了 while(1) 循环,同上
void SetMonth(void) //改变月函数
{
while(1)
{
uint8_t KeyNum=Key_GetNum();
if(KeyNum==1)//加1
{
Chance_RTCTime(1,1);
if(MyRTC_Time[1]>=13){MyRTC_Time[1]=1; MyRCT_SetTime();}
}
else if(KeyNum==2)//减1
{
Chance_RTCTime(1,0);
if(MyRTC_Time[1]<=0){MyRTC_Time[1]=12; MyRCT_SetTime();}
}
else if(KeyNum==3){return;} //确认并退出
ShowTime_FirstPage();
OLED_ReverseArea(24,32,16,16); //显示更改的数值
OLED_Update();
}
}
//【修复】SetDay 加了 while(1) 循环,同上
void SetDay(void) //改变日函数
{
while(1)
{
uint8_t KeyNum=Key_GetNum();
if(KeyNum==1)//加1
{
Chance_RTCTime(2,1);
if(MyRTC_Time[2]>=32){MyRTC_Time[2]=1; MyRCT_SetTime();}
}
else if(KeyNum==2)//减1
{
Chance_RTCTime(2,0);
if(MyRTC_Time[2]<=0){MyRTC_Time[2]=31; MyRCT_SetTime();}
}
else if(KeyNum==3){return;} //确认并退出
ShowTime_FirstPage();
OLED_ReverseArea(24,48,16,16); //显示更改的数值
OLED_Update();
}
}
//【修复】SetHour 加了 while(1) 循环;边界值 >=25 改为 >=24(小时是0~23,24时应绕回0)
void SetHour(void) //改变时函数
{
while(1)
{
uint8_t KeyNum=Key_GetNum();
if(KeyNum==1)//加1
{
Chance_RTCTime(3,1);
if(MyRTC_Time[3]>=24){MyRTC_Time[3]=0; MyRCT_SetTime();} //【修复】>=25→>=24,绕回到0而非1
}
else if(KeyNum==2)//减1
{
Chance_RTCTime(3,0);
if(MyRTC_Time[3]<0){MyRTC_Time[3]=23; MyRCT_SetTime();}
}
else if(KeyNum==3){return;} //确认并退出
ShowTime_SecondPage();
OLED_ReverseArea(24,0,16,16); //显示更改的数值
OLED_Update();
}
}
//【修复】SetMinute 加了 while(1) 循环;边界值 >=60 绕回到0而非1
void SetMinute(void) //改变分函数
{
while(1)
{
uint8_t KeyNum=Key_GetNum();
if(KeyNum==1)//加1
{
Chance_RTCTime(4,1);
if(MyRTC_Time[4]>=60){MyRTC_Time[4]=0; MyRCT_SetTime();} //【修复】绕回到0而非1
}
else if(KeyNum==2)//减1
{
Chance_RTCTime(4,0);
if(MyRTC_Time[4]<0){MyRTC_Time[4]=59; MyRCT_SetTime();} //【修复】<=0→<0,配合 int16_t 类型
}
else if(KeyNum==3){return;} //确认并退出
ShowTime_SecondPage();
OLED_ReverseArea(24,16,16,16); //显示更改的数值
OLED_Update();
}
}
//【修复】SetSecond 加了 while(1) 循环;边界值 >=60 绕回到0;减1改用 <0 判断
void SetSecond(void) //改变秒函数
{
while(1)
{
uint8_t KeyNum=Key_GetNum();
if(KeyNum==1)//加1
{
Chance_RTCTime(5,1);
if(MyRTC_Time[5]>=60){MyRTC_Time[5]=0; MyRCT_SetTime();} //【修复】绕回到0而非1
}
else if(KeyNum==2)//减1
{
Chance_RTCTime(5,0);
if(MyRTC_Time[5]<0){MyRTC_Time[5]=59; MyRCT_SetTime();} //【修复】<=0→<0,配合 int16_t 类型
}
else if(KeyNum==3){return;} //确认并退出
ShowTime_SecondPage();
OLED_ReverseArea(24,32,16,16); //显示更改的数值
OLED_Update();
}
}
uint8_t SetTimeFlag=1; //选项设置
uint8_t SetTime_Flag_Temp; //确定选项
//【修复】每次进入 SetTime 重置状态变量,避免上次退出时的残留值导致显示错乱
//【修复】每个 setter 调用后 SetTime_Flag_Temp 清零,防止外层 while 反复进入死循环
uint16_t SetTime(void) //设置时间,第一页是年月日,第二页是时分秒
{
SetTimeFlag=1; //【修复】每次进入重置,避免残留上次的选项值
SetTime_Flag_Temp=0; //【修复】每次进入清零,避免直接跳转
while(1)
{
uint8_t KeyNum=Key_GetNum(); //【修复】用局部变量替代 extern 全局变量
if(KeyNum==1)//上一项
{
SetTimeFlag—;
if(SetTimeFlag<=0){SetTimeFlag=7;}
}
else if(KeyNum==2)//下一项
{
SetTimeFlag++;
if(SetTimeFlag>=8){SetTimeFlag=1;}
}
else if(KeyNum==3)//确认更改时间
{
OLED_Clear();
OLED_Update();
SetTime_Flag_Temp=SetTimeFlag;
}
//【修复】每个 setter 调用后将 SetTime_Flag_Temp 清零,防止死循环
if(SetTime_Flag_Temp==1){return 0;} //跳出 SetTime()函数,回到Setting_Page()函数
if(SetTime_Flag_Temp==2){SetYear(); SetTime_Flag_Temp=0; OLED_Clear();}//年
if(SetTime_Flag_Temp==3){SetMonth(); SetTime_Flag_Temp=0; OLED_Clear();}//月
if(SetTime_Flag_Temp==4){SetDay(); SetTime_Flag_Temp=0; OLED_Clear();}//日
if(SetTime_Flag_Temp==5){SetHour(); SetTime_Flag_Temp=0; OLED_Clear();}//时
if(SetTime_Flag_Temp==6){SetMinute(); SetTime_Flag_Temp=0; OLED_Clear();}//分
if(SetTime_Flag_Temp==7){SetSecond(); SetTime_Flag_Temp=0; OLED_Clear();}//秒
switch (SetTimeFlag)//显示年月日时分秒选项
{
case 1: //显示返回
OLED_Clear();
ShowTime_FirstPage();
OLED_ReverseArea(0,0,16,16);
OLED_Update();
break;
case 2: //显示年
OLED_Clear();
ShowTime_FirstPage();
OLED_ReverseArea(0,16,16,16);
OLED_Update();
break;
case 3: //显示月
OLED_Clear();
ShowTime_FirstPage();
OLED_ReverseArea(0,32,16,16);
OLED_Update();
break;
case 4: //显示日
OLED_Clear();
ShowTime_FirstPage();
OLED_ReverseArea(0,48,16,16);
OLED_Update();
break;
case 5: //显示时
OLED_Clear();
ShowTime_SecondPage(); //第二页头
OLED_ReverseArea(0,0,16,16);
OLED_Update();
break;
case 6: //显示分
OLED_Clear();
ShowTime_SecondPage();
OLED_ReverseArea(0,16,16,16);
OLED_Update();
break;
case 7: //显示秒
OLED_Clear();
ShowTime_SecondPage();
OLED_ReverseArea(0,32,16,16);
OLED_Update();
break;
}
}
}
其实这里的代码底层逻辑几乎一样,先是设置一个页面函数,然后选择按键选择高亮部分,如果选择返回就是return 0;,那么就会跳出这个页面函数,回到上一个页面函数
例如:
void SetYear(void) //改变年函数
{
while(1)
{
uint8_t KeyNum=Key_GetNum();
if(KeyNum==1)//加1
{
Chance_RTCTime(0,1);
}
else if(KeyNum==2)//减1
{
Chance_RTCTime(0,0);
}
else if(KeyNum==3){return 0;} //确认并退出
ShowTime_FirstPage();
OLED_ReverseArea(24,16,32,16); //显示更改的数值
OLED_Update();
}
}
这个函数就是一个高亮年日期,并且能改变年数组的页面函数,return0,后它就会退出SetYear(void)这函数,回到,SetTime这个页面函数。不过这个return中的0并没有意义,只是借助return跳出这个函数,所以按理来说你写return也可以运行
网硕互联帮助中心




评论前必须登录!
注册