蓝桥杯嵌入式设计与开发项目模拟赛题““电子定时器”的程序设计与调试”

~QQ:3020889729

~小蔡

蓝桥杯嵌入式设计与开发项目模拟赛题““电子定时器”的程序设计与调试”

(我也才备赛,可能会有写的或者说得不太对的地方。如果有错误的地方或者有什么好的建议,大家可以评论一下
——集思广益,共同进步。)

该题功能要求:
通过按键设置定时时间,启动定时器后,开始倒计时;计时过程中,可以暂停、取消定时器。在定时时间内,按要求输出 
PWM 信号和控制 LED 指示灯。

题目系统框图

电子钟系统框图

设计任务以及要求简要说明

1.LCD显示

LCD 显示存储位置、定时时间和当前状态。停止状态显示为: Standby;设置时间时:Setting;运行时:Running;暂
停时:Pause——并且系统预留 5 个存储位置用于存储常用的定时时间。

LCD显示参考界面

2.电子定时器设定——按键部分

使用 4 个按键,B1、B2、B3 和 B4——超过 0.8 秒为长按判定。
①按键 B1 为存储位置切换键——每按一次,存储位置依次切换——1,2,3,4,5。
②按键 B2 为时间位置(时、分、秒)切换键和存储键——短按切换,长按退出设置存储当前设定时间到当前存储位置。
③按键 B3 为时、分、秒(按键 B2 确定当前位置)数字增加键——短按加一,长按快速增加——超出范围则从头循环。
④按键 B4 为定时器启动键——第一次短按下运行启动,再短按下暂停,长按就退出——回到Standy。

3.电子定时器设定——PWM输出

定时器运行时,PA6 口输出 PWM 信号—— 信号频率为 1KHz,占空比为 80%——定时器停止或暂停时,停止输入 PWM 
信号。

如图

B2 按键:切换参数选项。
B3 按键:增加当前参数项,增值为5——最高到95。
B4 按键:减小当前参数项,减值为5——最低到5。

4.电子定时器设定—— LED控制

定时器运行时——LED 灯(LD1)以 0.5 秒的频率闪烁。
定时器停止或暂停时,LED灯灭。

5.电子定时器设定——定时时间存储

设定(B2)好的定时时间存储在 EEPROM 中。
掉电重启后,显示存储位置 1 的定时时间。

程序流程图·设计

电子定时器流程图

解读:

在题目要求下,将必要的基本配置配置好。
(主要是:按键的初始化配置,LED灯的初始化配置,24C02的初始化配置,定时器的初始化配置。)

LCD显示部分的思路:

主要工作界面上的设计思路:
然后首先处理的就是24c02中存储的值的读取和显示。
然后判断按键进入不同的工作界面——运行、设置或切换存储界面。
定时开始前后刷新问题可能在写的时候要注意一下——尽可能使用同一行的显示——如果可以就避免经常使用刷新吧。

PWM部分的思路:

没有运行时:我们就不输出,但是在程序一开始可以直接打开IO这些的使能。
在运行时,我们将通道打开,输出指定PWM波。

LED部分的思路

在运行时时,LED灯按要求闪烁,指示当前运行情况。
不运行时,我们就关闭LED灯。

状态图·设计

电子定时器状态图

解读:

由LCD主持工作界面的显示,由按键控制存储界面切换、运行界面以及定时时间的设定。整个过程程序始终运行——当运行时,需要打开PWM通道输出和LED灯的闪烁;当没在运行时,要及时关闭相关操作。
(LED在这个事件过程中属于指定指示项,要对应拼接在指定的那一个触发函数中——进行嵌套。)

主要代码分析

1.通用初始化部分

ps:我写的代码是按模块完成的——尽量使得主函数.c部分更简单。

①按键初始化:

ps:按键初始化很简单,主要是对后边按键的读取处理,才算重点。(每一套题中,按键基础配置.c部分基本都是一样的)

(.c部分)

#include "key.h"
void KEY_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

}

(.h部分)

#ifndef _KEY_H
#define _KEY_H

#include "stm32f10x.h"
#include "lcd.h"

#define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define KEY2 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define KEY3 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define KEY4 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)


void KEY_Init(void);
//按键扫描
u8 key_scan(void);

void key_read(void);
//按键状态处理函数
//按键读取操作,返回指定状态

void key_cunchu_adress(void);//功能:改变存储位置/读取某一位置的存储的时间数据
void key_data_on_time_order(void);//功能:调整定时时间——操作映射值
void time_work(void);
#endif

②LED初始化

ps:此处的灯由于LCD共用引脚,所以呢,需要在每次使用LCD后,都要及时将LED全部拉高,不然会导致LED灯全部打开。

(.c部分)(每一套题中,按键基础配置.c部分基本都是一样的)

#include "led.h"

void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOD, &GPIO_InitStructure);//锁存器的引脚初始化
	
	GPIO_InitStructure.GPIO_Pin = 0XFF00;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//8个LED灯引脚的初始化
	
	GPIOD-> ODR |= (1<<2);//锁存器使能引脚控制
	GPIOC-> ODR = 0XFFFF;//初始使能引脚拉高,灯灭
	GPIOD-> ODR &=~(1<<2);

}

(.h部分)

#ifndef _LED_H
#define _LED_H

#include "stm32f10x.h"
//LED宏定义——引自GPIO.h
#define LED1                 ((uint16_t)0x0100)  /*!< Pin 8 selected */
#define LED2                 ((uint16_t)0x0200)  /*!< Pin 9 selected */
#define LED3                ((uint16_t)0x0400)  /*!< Pin 10 selected */
#define LED4                ((uint16_t)0x0800)  /*!< Pin 11 selected */
#define LED5                ((uint16_t)0x1000)  /*!< Pin 12 selected */
#define LED6                ((uint16_t)0x2000)  /*!< Pin 13 selected */
#define LED7                ((uint16_t)0x4000)  /*!< Pin 14 selected */
#define LED8                ((uint16_t)0x8000)  /*!< Pin 15 selected */



void LED_Init(void);//LED初始化

void LED_Control(u16 LEDx,u8 state);//LED控制

void LED_Control_liangmie(void);//LED用于检测BUG——这里可以用来控制闪烁

void LED_SWITCH(void);//LED开关——也就是运行时启动闪烁,不运行时灯关


#endif

③PWM初始化:

ps:初始化部分都是固定的,需要注意的是后期的入口参数控制频率等,这里这次使用了固定频率(1000Hz)——所以
我就仅仅设置一个状态入口——控制PWM输出与否就好。

(.c部分)
(我就简单解释下,PWM初始化部分的含义)

#include "pwm.h"
//频率1000hz,占空比80%
void TIM3_PWM_Init(u8 state)
{
	GPIO_InitTypeDef GPIO_InitStructure;//IO口初始化结构体
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//定时器初始化配置结构体
	TIM_OCInitTypeDef TIM_OCInitStructure;//输出通道初始化结构体
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//时钟使能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;//输出引脚配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//下拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//
	
	TIM_TimeBaseInitStructure.TIM_Period = 999;//1ms一周期/1000hz
	TIM_TimeBaseInitStructure.TIM_Prescaler = 71;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = 0x0;//不分割
	TIM_TimeBaseInitStructure.TIM_CounterMode = 0x0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
		
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//通道一打开,模式二
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//有效电平低
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//通道使能
	TIM_OCInitStructure.TIM_Pulse = 999*80/100;//占空比80%
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);//通道一初始化

	if(state)
	{
		TIM_Cmd(TIM3,ENABLE);//定时器使能
	}
	else
	{
		TIM_Cmd(TIM3,DISABLE);//定时器关闭使能
	}

}

(.h部分)

#ifndef _PWM_H
#define _PWM_H

#include "stm32f10x.h"

void TIM3_PWM_Init(u8 state);//频率1000hz,占空比80%

void PWM_Switch(void);//PWM开关


#endif

④24C02初始化

ps:在蓝桥杯比赛的开发板中,存储数据需要用到**i2c**总线下的24c02,所以需要使用**i2c**对**24c02**进行控制。

(.c部分)

#include"24c02.h"
//写入24c02的指定地址的指定数据的命令
void _24c02_Write(u8 address,u8 data)
{
	I2CStart();//i2c开始
	I2CSendByte(0xa0);//发送写入命令
	I2CWaitAck();//等待任务完成
	I2CSendByte(address);//发送地址
	I2CWaitAck();
	I2CSendByte(data);//发送数据
	I2CWaitAck();
	I2CStop();//i2c结束
}
//读取24c02的指定地址的数据的命令
u8 _24c02_Read(u8 address)
{
	u8 temp;
	I2CStart();
	I2CSendByte(0xa0);//发送写入命令
	I2CWaitAck();
	I2CSendByte(address);//写入地址
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0xa1);//发送读取指令
	I2CWaitAck();
	temp = I2CReceiveByte();//读取指定数据
	I2CWaitAck();
	I2CStop();
	return temp;//返回读取值

}

(.h部分)

#ifndef _24C02_H
#define _24C02_H

#include "stm32f10x.h"
#include "i2c.h"

void _24c02_Write(u8 adress,u8 data);

u8 _24c02_Read(u8 adress);

#endif

2.主要功能函数部分

1.按键功能部分:
(功能放在对应的.c里边)
(我这部分写得比较仔细——有的部分是逻辑必然,但是却不一定是需要的标志位。这部分主要是在按键状态那里有体现。但是我想的是,将一个动作下所有状态进行一次类似遍历的方式处理下,这样会更容易理解吧。)


//800ms检测长按
u8 KEY2_on_state=0;//key2按下标志,只有按下才开始长按判断计时
u8 KEY2_CHECK=0;
u8 KEY3_on_state=0;//key3按下标志,只有按下才开始长按判断计时
u8 KEY3_CHECK=0;
u8 KEY4_on_state=0;//key4按下标志,只有按下才开始长按判断计时
u8 KEY4_CHECK=0;

u16 KEY_LONG_DELAY=800;

//短按扫描
u8 key_flag=0;//按键扫描标志
u16 key_short_delay=5;//按键扫描周期

u8 key_flag_down=0;//按键按下标志

//按键扫描
u8 key_scan(void)
{
	static u8 key_back=1;//按键弹起标志
	if(key_back&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))//单次按键
	{
		if(key_flag)//允许扫描时
		{
			key_short_delay=5;key_flag=0;//扫描标志初始化
			key_back=0;//按键按下
			if(KEY1==0) {key_flag_down=1;return 1;}//按下标志一下
			if(KEY2==0) {key_flag_down=1;KEY2_on_state=1;return 2;}//按下同时产生长按判断
			if(KEY3==0) {key_flag_down=1;KEY3_on_state=1;return 3;}
			if(KEY4==0) {key_flag_down=1;KEY4_on_state=1;return 4;}
		}
	}else if(key_back==0&&KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)
						{	//当没有按键按下(松开)时,停止长按判断,并执行反弹确认
							key_back=1;//反弹确认,可再次按下
							//key_flag_down=0;//没有按下
							KEY2_on_state=0;//长按标志清除
							KEY3_on_state=0;
							KEY4_on_state=0;
							KEY_LONG_DELAY=800;//长按判断时长——0.8s
						}

	if(KEY2_CHECK)//长按KEY2标志
	{
		if(KEY2==0) {KEY2_CHECK=1;return 2;}
		//满足长按后,继续返回有效值——不再执行像上边单按一样需要弹起再按下才有新输出
		else {KEY2_CHECK=0;KEY_LONG_DELAY=800;}//重新计数
	}
	
	if(KEY3_CHECK)//长按KEY3标志
	{
		if(KEY3==0) {KEY3_CHECK=1;return 3;}
		else {KEY3_CHECK=0;KEY_LONG_DELAY=800;}
	}
	if(KEY4_CHECK)//长按KEY4标志
	{
		if(KEY4==0) {KEY4_CHECK=1;return 4;}
		else {KEY4_CHECK=0;KEY_LONG_DELAY=800;}
	}
	
	return 0;
}


/************************************/
/******    实际状态参数表    ********/
/************************************/

u8 data_exchange_flag=0;//存储切换标志
u8 data_cunchu_flag=0;//当前数据将存储到当前存储位置的标志——长按状态下
u8 time_set_flag=0;//时间设置标志
u8 time_set_ups_flag=0;//时间快速上调单位的标志——长按状态下
u8 time_set_Oneup_flag=0;//时间上调1个单位的标志
u8 time_end_flag=1;//定时结束标志——长按状态下
u8 time_start_flag=0;//定时开始标志
u8 time_wait_flag=0;//定时暂停标志

u8 time_set_exchange_flag=0;//时分秒切换标志

u8 time_run_setpage_flag=0;//判定此时是不是由设置界面直接启动定时器的标志


/******************状态设置函数********************/
//
//状态参数表



/************************************/
//按键状态处理函数
//按键读取操作,返回指定状态
void key_read(void)
{
	static u8 time_open_off_flag=0;//定时器开始暂停的转换标志
	u8 key_return=0;//按键值读取位
	key_return=key_scan();
	
	if(time_end_flag)//定时结束时
		time_open_off_flag=0;//定时器不再转换开始——定时器状态为暂停,对应停止位
	switch(key_return)
	{
		case 1://按键1
		{		
		//仅仅在停止界面(存储界面)时有效
			if(time_end_flag&&time_set_flag==0&&time_start_flag==0&&time_wait_flag==0){
				
			//time_end_flag=1;//此时为定时停止状态
			data_exchange_flag=1;//存储位置具体在后边处理,这里仅仅标记允许存储位置切换
				time_set_exchange_flag=0;//时分秒切换标志——初始化参数项切换标志
			data_cunchu_flag=0;//不标志数据存储——当前按键下仅仅是移动存储位置
		//每按一次,存储位置依次以 1、2、3、4、5 循环切
		//换,切换后定时时间设定为当前位置存储的时间。
				}
		}break;
		case 2:
		{
			if(KEY2_CHECK&&time_set_flag&&time_end_flag==0&&time_start_flag==0&&time_wait_flag==0)//长按2——//设置界面才反应
			{
				time_end_flag=1;//此时为定时停止状态
				data_cunchu_flag=1;//存储标志——允许存储
				time_set_flag=0;//存储数据——退出设置界面
				time_set_exchange_flag=0;//时分秒切换标志——初始化
				
				time_run_setpage_flag=0;//判定此时不会由设置界面直接启动定时器
			}
			if((KEY2_CHECK==0)&&time_start_flag==0&&time_wait_flag==0)//短按——进入设置界面——停止界面有效
			{
				time_run_setpage_flag=1;//判定此时可以由设置界面直接启动定时器——即未储存直接定时
				
				time_end_flag=0;//此时定时停止状态关闭(改为其它状态)
				data_cunchu_flag=0;//只是设置并不会存储
				data_exchange_flag=0;//存储切换标志
				time_set_exchange_flag=1;//时分秒切换标志
				time_set_flag=1;
				//时间设置标志:后边处理,第一次按下进入设置界面的秒设置
				//然后置零该标志,下一次按下,移动到分设置……
			}
		}break;
		case 3:
		{
			if(KEY3_CHECK&&time_set_flag&&time_end_flag==0&&time_start_flag==0&&time_wait_flag==0)
			//长按2——//设置界面才反应
			{
				time_run_setpage_flag=1;//保持可直接运行标志
				time_set_exchange_flag=0;//时分秒切换标志——在按下增加值的按键时,不可切换
				data_exchange_flag=0;//存储切换标志——置零,不可切换
				data_cunchu_flag=0;//不存储数据
				time_set_flag=1;//时间设置标志
				time_set_ups_flag=1;//快速增加
				time_set_Oneup_flag=0;//关闭单次增加
			}
			if((KEY3_CHECK==0)&&time_set_flag&&time_end_flag==0&&time_start_flag==0&&time_wait_flag==0)
			//短按2——//设置界面才反应
			{
				time_run_setpage_flag=1;//保持可直接运行标志
				time_set_exchange_flag=0;//时分秒切换标志——在按下增加值的按键时,不可切换
				data_exchange_flag=0;//存储切换标志——置零,不可切换
				data_cunchu_flag=0;//不存储数据
				time_set_flag=1;//时间设置标志
				time_set_ups_flag=0;//快速增加——关闭
				//快速增加不开启,即打断按键计时,不能继续块加
				time_set_Oneup_flag=1;//按一下增加一次
			}
			
		}break;
		case 4:
		{
			if(KEY4_CHECK)//长按2
			{
				time_set_exchange_flag=0;//时分秒切换关闭
				data_exchange_flag=0;//存储切换标志
				data_cunchu_flag=0;
				time_set_flag=0;//时间设置标志
				time_open_off_flag=0;//转换标志也要清零
				time_end_flag=1;//定时器结束运行//此时为定时停止状态
				time_start_flag=0;//定时结束,必然不再有开始和暂停
				time_wait_flag=0;//
			}
			else
			{
				if(time_open_off_flag==0)//第一次按下是暂停——之后按下翻转状态
				{
					time_set_exchange_flag=0;//时分秒切换关闭
					time_open_off_flag=1;//定时器正在运行——允许暂停
					data_exchange_flag=0;//存储切换标志——关闭
					data_cunchu_flag=0;//存储标志——不存储时间
					time_set_flag=0;//时间设置标志
					time_end_flag=0;//此时为定时运行状态——不是停止状态
					time_start_flag=1;//开始状态,暂停状态关闭
					time_wait_flag=0;}
				else
				{
					time_open_off_flag=0;//定时器已暂停——允许启动
					data_exchange_flag=0;//存储切换标志——关闭
					data_cunchu_flag=0;//存储标志——不存储时间
					time_set_flag=0;//时间设置标志
					time_end_flag=0;//此时为定时暂停状态——不是停止状态
					time_start_flag=0;//暂停状态,启动状态关闭
					time_wait_flag=1;//暂停标志位
				}//再按一下是暂停,开始状态关闭
			}
			
		}break;
		default:
			break;
	}
	
}


/************************************/
//按键状态识别具体操作函数

//按键状态识别              1
/*当前函数主要引用的参数——来源lcd.h*/

extern u8 data_xuhao;//1~5->对应存储位置

//功能:改变存储位置/读取某一位置的存储的时间数据
void key_cunchu_adress(void)
{
	if(data_exchange_flag&&time_end_flag)
	{
		data_exchange_flag=0;//每次切换后,将标志位置0
		data_xuhao++;//进入下一个存储位置
	}
	if(data_xuhao==6)
		data_xuhao=1;//返回第一个存储位置
}

//按键状态识别              2

/*当前函数主要引用的参数——来源lcd.h*/

extern int data_on_time_yinshe[4];//映射数据存储位置——0-2时分秒
//暂存时间值的地方——引自lcd.h
extern int data_time[5][3];//1~5->对应存储位置

u8 up_lianxu_falg=0;//连续增加的状态
u8 up_lianxu_delay=3;//连续增加的周期


u8 linshi_hh_val=0;//当前显示时间值的存储位
u8 linshi_mm_val=0;
u8 linshi_ss_val=0;


u32 time_runnig=5;//略微设一点值,除去启动时,导致的界面替换
u8 time_running_flag=0;//定时器运行标志
u8 time_run_ok_flag=0;//定时器运行完成标志


//功能:调整定时时间——操作映射值

//时间数据处理部分
void key_data_on_time_order(void)
{
	static int key_on_time_set_shunxu=4;//设置顺序——3-秒,2-分,1-时,这样的设置顺序
	
	static int val_shuhao=0;//存储位置——序号
	
	static int midle_val_hh=0;//当前位置的hour值
	static int midle_val_mm=0;//当前位置的min值
	static int midle_val_ss=0;//当前位置的second值
	
	if(time_set_flag==0)//退出设置界面时
		key_on_time_set_shunxu=4;//初始化设置顺序
	
	if(time_set_flag&&time_set_exchange_flag)//在设置界面时/存储位置切换时进入
		//该部分放在LCD显示前,LCD中会置零time_set_exchange_flag
		//所以这里无需置零
	{
		val_shuhao=data_xuhao;//得到当前存储序号(位置)
		midle_val_hh=data_time[data_xuhao-1][0];
		//暂存配置值,将当前位置的时间预先存下来,等确定后边的时间数据需要存储再改变
		//否则,后边就直接将该值又赋给当前位置的data_time中
		midle_val_mm=data_time[data_xuhao-1][1];
		midle_val_ss=data_time[data_xuhao-1][2];
		
		
		key_on_time_set_shunxu--;//顺序值减一,满足调用设置——1-时位,2-分位,3-秒位
		if(key_on_time_set_shunxu==0)//如果到零了,回到3-秒位,而不是4
			key_on_time_set_shunxu=3;
	}
	switch(key_on_time_set_shunxu)
	{
		case 1:
			//时设置
			if(time_set_Oneup_flag&&time_set_ups_flag==0)//单按条件下,当前值直接加一就好
			{time_set_Oneup_flag=0;data_time[data_xuhao-1][0]++;}
			
			if(time_set_Oneup_flag==0&&time_set_ups_flag)
			//连续按键,就要进入其中——同时置零快加标志,反复操作,直到不再有快加标志出现
			{
				time_set_ups_flag=0;//快加标志清零
				if(up_lianxu_falg)//快速加,每3ms加一次
					{
						up_lianxu_falg=0;//取消标记
						up_lianxu_delay=3;
						data_time[data_xuhao-1][0]++;//暂存值加一
					}
			}
			if(data_time[data_xuhao-1][0]==24)//小时位置数字归零,其余位置值不变
				data_time[data_xuhao-1][0]=0;
			break;
			
		case 2://分设置
			if(time_set_Oneup_flag&&time_set_ups_flag==0)//单按条件下,当前值直接加一就好
			{data_time[data_xuhao-1][1]++;time_set_Oneup_flag=0;}
			
			if(time_set_Oneup_flag==0&&time_set_ups_flag)
			//连续按键,就要进入其中——同时置零快加标志,反复操作,直到不再有快加标志出现
			{
				time_set_ups_flag=0;//快加标志清零
				if(up_lianxu_falg)//快速加,每20ms加一次
					{
						up_lianxu_falg=0;//取消标记
						up_lianxu_delay=3;
						data_time[data_xuhao-1][1]++;//暂存值加一
					}
			}
			if(data_time[data_xuhao-1][1]==60)//分归零,其余位置值不变
				data_time[data_xuhao-1][1]=0;
			break;
		case 3://秒设置
			
			if(time_set_flag&&time_set_Oneup_flag&&time_set_ups_flag==0)//单按条件下,当前值直接加一就好
			{data_time[data_xuhao-1][2]++;time_set_Oneup_flag=0;}
			
			if(time_set_flag&&time_set_Oneup_flag==0&&time_set_ups_flag)
			//连续按键,就要进入其中——同时置零快加标志,反复操作,直到不再有快加标志出现
			{
				time_set_ups_flag=0;//快加标志清零
				if(up_lianxu_falg)//快速加,每20ms加一次
					{
						up_lianxu_falg=0;//取消标记
						up_lianxu_delay=3;
						data_time[data_xuhao-1][2]++;//暂存值加一
					}
			}
			if(data_time[data_xuhao-1][2]==60)//秒归零,其余位置值不变
				data_time[data_xuhao-1][2]=0;
			break;
		default:
			break;
	
	}
	
	linshi_hh_val=data_time[data_xuhao-1][0];
	//暂存时间设置值,这里的数据为定时器在设置界面留下的不保存的时间数据
	//以备在设置界面未保存就启动定时的操作
	linshi_mm_val=data_time[data_xuhao-1][1];
	linshi_ss_val=data_time[data_xuhao-1][2];
	
	//功能:映射值存储到实际存储位置
	if(time_end_flag&&data_cunchu_flag==0&&val_shuhao==data_xuhao)
	//当存储位置和当前留下的序号一致,位置确定无误后,设置结束,但不需要存储时
	//将之前的值覆盖当前设置的值
	//也就是简单点——保持原来存储位置本身的值
	{
		data_time[data_xuhao-1][0]=midle_val_hh;//本来的小时数据
		data_time[data_xuhao-1][1]=midle_val_mm;
		data_time[data_xuhao-1][2]=midle_val_ss;
	}
	
}


u8 look_string[20];

void time_work(void)
{
	static u8 first_in=1;//判断是否是一次新的定时,1——为新定时
	
	if(time_set_flag)//在设置界面时,就先预存短期未存储值为计时数。
			time_runnig=(linshi_hh_val*3600+linshi_mm_val*60+linshi_ss_val)*1000;//该值用以在滴答中断中计时——1下1ms
	//先存未保存的调整值,如果直接计时,就直接用
	//如果要用存储值,就在后边调储存数据data_time来计算实际运行时间
	
	if(time_run_setpage_flag)//在设置界面直接进入定时运行的情况判定
	{
		if(time_end_flag)first_in=1;
		//每回到停止界面就初始化标志位
		if(time_start_flag&&time_running_flag==0)//是一次新的运行,而不是暂停后又开启
		{
			time_running_flag=1;//运行标志
			if(first_in)//将新定时标志置0
				{
					first_in=0;
				}
			
		}
		if(time_wait_flag)//暂停模式下
		{
			time_running_flag=0;//置零运行标志
		}
		if(time_run_ok_flag)//一次完整的定时运行完成
		{
			time_end_flag=1;//回到停止界面——所以标志置1
			time_start_flag=0;//停止态,启动标志关闭——置0
			time_wait_flag=0;//停止态,暂停标志也关闭
			time_run_ok_flag=0;//进入运行完成判断了——所以完成标志置0
			time_run_setpage_flag=0;//停止界面自然没有在设置界面中了——所以置0
			first_in=1;//再次进入,就是一次新的运行
		}
	}
	else//由停止界面进入定时模式,也就是开始界面进入的定时
	{
		if(time_end_flag)first_in=1;//回到停止界面,都是新的定时
		
		if(time_start_flag&&time_running_flag==0)//开始运行,是一次新的运行——之前运行标志为0
		{
			time_running_flag=1;//运行标志
			if(first_in)//停止界面进入——计时值为存储值
		{
			first_in=0;//当前本次定时之后的操作不再属于新运行
			time_runnig=(data_time[data_xuhao-1][0]*3600+data_time[data_xuhao-1][1]*60+data_time[data_xuhao-1][2])*1000;
			//获取当前存储的定时值
		}
	
		}
		if(time_wait_flag)//暂停情况
		{
			time_running_flag=0;
		}
		if(time_run_ok_flag)//运行完成情况
		{
			time_end_flag=1;//结束标志——开始界面的标志
			time_start_flag=0;
			time_wait_flag=0;
			time_run_ok_flag=0;
			time_run_setpage_flag=0;
			first_in=1;//下一次运行为新运行
		}
	}
}

2.LED功能部分

extern u8 time_start_flag;//定时器启动标志

//LED控制函数,1点亮
void LED_Control(u16 LEDx,u8 state)
{
	if(state){
		GPIOD-> ODR |= (1<<2);
		GPIO_ResetBits(GPIOC,LEDx);
		GPIOD-> ODR &=~(1<<2);}
	else{
		GPIOD-> ODR |= (1<<2);
		GPIO_SetBits(GPIOC,LEDx);
		GPIOD-> ODR &=~(1<<2);}
}

//检查BUG用——但这里直接使用来实现灯亮灭(闪烁)
void LED_Control_liangmie(void)
{
		static u8 led_flag_shan=0;
	
		led_flag_shan=!led_flag_shan;//状态翻转
		if(led_flag_shan)
		{	
			GPIOD-> ODR |= (1<<2);
			GPIOC-> ODR |=0xff00;
			GPIOD-> ODR &=~(1<<2);
			GPIOD-> ODR |= (1<<2);
			GPIO_ResetBits(GPIOC,LED4);
			GPIOD-> ODR &=~(1<<2);
		}
		else
		{
			GPIOD-> ODR |= (1<<2);
			GPIOC-> ODR |=0xff00;
			GPIOD-> ODR &=~(1<<2);
		}
}

u16 led_delay=500;//指示灯闪烁延时
u8 led_fflag=0;//亮灭状态切换标志——也就是闪烁标志
//将被引用到滴答中断中使用

void LED_SWITCH(void)//LED指示灯开关
{
	if(time_start_flag)
	{
		if(led_fflag)//切换标志产生
		{
			led_fflag=0;//置零标志,等待下一次标志来到
			led_delay=500;//初始化闪烁周期
			LED_Control_liangmie();//闪烁
		}
	}
	else
	{
		led_delay=500;//延时归零
		GPIOC-> ODR = 0XFFFF;//熄灯
		GPIOD-> ODR |= (1<<2);
		GPIOD-> ODR &=~(1<<2);
	}
	
	
}

3.PWM功能部分

extern u8 time_start_flag;//定时器启动标志

//PWM使能开关
void PWM_Switch(void)
{
	if(time_start_flag)//启动时打开
	TIM3_PWM_Init(1);
	else//不启动时关闭
	TIM3_PWM_Init(0);
}

3.中断部分

1.滴答中断部分

ps:这个在stm32f10x_it.c中,我也是直接将一些延时和标志位引用过来,实现延时或者等待一定时长的周期性动作。
//在stm32f10x_it.c中添加引用的.h文件
#include "key.h"
#include "lcd.h"

/***********************滴答中断的修改**************************/

/*******参数引用部分********/
//800ms检测长按——以及状态
extern u8 KEY2_on_state;//key2按下标志,只有按下才开始长按判断计时
extern u8 KEY2_CHECK;//确定当前键长按
extern u8 KEY3_on_state;//key3按下标志,只有按下才开始长按判断计时
extern u8 KEY3_CHECK;//确定当前键长按
extern u8 KEY4_on_state;//key4按下标志,只有按下才开始长按判断计时
extern u8 KEY4_CHECK;//确定当前键长按

extern u16 KEY_LONG_DELAY;//长按判断周期

//短按扫描-状态
extern u8 key_flag;//按键扫描标志
extern u16 key_short_delay;//按键扫描周期


//key状态实际操作部分参数引用
extern u8 up_lianxu_delay;//连续按键——读取周期
extern u8 up_lianxu_falg;//连续按键——读取标志

/***************************/
//定时变量参数引用

extern u32 time_runnig;//当前运行时间
extern u8 time_running_flag;//运行标志
extern u8 time_run_ok_flag;//运行完成标志

extern u16 led_delay;//闪烁周期
extern u8 led_fflag;//闪烁标志
/***************************************/

void SysTick_Handler(void)
{
	TimingDelay--;
	if(--key_short_delay==0)//按键扫描
		key_flag=1;
	if(--up_lianxu_delay==0)//按键长按后连续读取扫描
		up_lianxu_falg=1;
	if(time_running_flag)//正在运行的标志
	{
		time_runnig--;//用这个显示定时效果——定时时长
	}
	if(time_runnig==0)//运行标志为0,则说明运行完成
	{
		time_run_ok_flag=1;//完成标志
	}
	if(--led_delay==0)//LED闪烁标志操作
		led_fflag=1;//开启LED闪烁变化
	key_long();//长按计时,返回状态值
	
}

//           1
//功能:对长按的判定
void key_long(void)
{
	
	if(KEY2_on_state&&(--KEY_LONG_DELAY==0))
	//满足长按所有条件——该键按下,且持续800ms未弹起,执行判决
	{
		KEY2_CHECK=1;//当前长按确定
	}
	if(KEY3_on_state&&(--KEY_LONG_DELAY==0))
	{
		KEY3_CHECK=1;//当前长按确定
	}
	if(KEY4_on_state&&(--KEY_LONG_DELAY==0))
	{
		KEY4_CHECK=1;//当前长按确定
	}
}

4.LCD显示部分

ps:我把显示部分写在lcd.c中的,所以写完之后需要该函数添加声明到lcd.h中。

/**********************************自己定义的状态参数引用***************************************/

extern u8 time_end_flag;//定时结束标志——长按状态下,LCD状态改变——Standby
extern u8 time_set_flag;//时间设置标志,LCD高亮显示变化——Setting
extern u8 time_start_flag;//定时开始标志——LCD状态改变——Running
extern u8 time_wait_flag;//定时暂停标志——LCD状态改变——Pause
extern u8 time_set_exchange_flag;////时分秒切换标志

/**************************************LCD模块所定义参数**************************************/
u8 data_xuhao=1;//1~5->对应存储位置data_time中的序列值:0~4
int data_time[5][3]={{20,10,20},{13,0,10},{15,0,25},{11,0,15},{8,0,30}};//存储定时数据,[3]=0~3对应时分秒

extern u32 time_runnig;//定时时长

u8 lcd_string[6][20];//存储需要输出的格式——状态,时间,存储位置

/*********************************************************************************************/


void LCD_SHOW_SET_TIME_S(void)
{
	static int set_falg=4;//设置显示标志位
	
	if(time_end_flag)//开始界面——停止(存储)界面
	{
		time_runnig=(data_time[data_xuhao-1][0]*3600+data_time[data_xuhao-1][1]*60+data_time[data_xuhao-1][2])*1000;
		//当处在定时器开始界面时,当前定时时长要初始化为当前存储的定时总时长

		set_falg=4;//设置显示标志位置
		sprintf((char*)lcd_string[0]," NUM: %d   ",data_xuhao);//当前存储位置
		
		sprintf((char*)lcd_string[1],"      %d: %d :%d             ",data_time[data_xuhao-1][0],data_time[data_xuhao-1][1],data_time[data_xuhao-1][2]);//当前定时值
		sprintf((char*)lcd_string[2],"       Standby    ");//当前状态
		
		LCD_SetTextColor(White);//文字颜色
		LCD_DisplayStringLine(Line1,lcd_string[0]);//显示存储位置在2行
		LCD_DisplayStringLine(Line5,lcd_string[1]);//显示时间在5行
		LCD_DisplayStringLine(Line8,lcd_string[2]);//显示状态在6行
		
	}
	//************设置状态下,执行映射数据显示**********************
	if(time_set_flag)//设置界面
	{
		
		if(time_set_exchange_flag==1)//满足设置位置(当前设置项)的切换
		{	
			time_set_exchange_flag=0;
			set_falg--;//设置项序号——3对应秒,2对应分,1对应时
			if(set_falg==0) //不允许存在0,而是回到3位置
			{
				set_falg=3;
			}
			LCD_ClearLine(Line5);//清除一下显示
		}//按下设置,减一,换一个高显示
		switch(set_falg)
		//设置高显的选择——我这里是做标志,实在要高显可以用字符显示函数,我的博客里有,有需要可以看下
		{
			case 3://秒高显示
				sprintf((char*)lcd_string[3],"              __     ");
				LCD_SetTextColor(Green);
				LCD_DisplayStringLine(Line6,lcd_string[3]);//高显秒
				break;
			case 2://分高显
				sprintf((char*)lcd_string[4],"          __         ");
				LCD_SetTextColor(Green);
				LCD_DisplayStringLine(Line6,lcd_string[4]);//高显分
				break;
			case 1://时高显
				sprintf((char*)lcd_string[5],"      __              ");
				LCD_SetTextColor(Green);
				LCD_DisplayStringLine(Line6,lcd_string[5]);//高显时
				break;
			default:
				break;
		}
		sprintf((char*)lcd_string[1],"      %d: %d :%d               ",data_time[data_xuhao-1][0],data_time[data_xuhao-1][1],data_time[data_xuhao-1][2]);//当前时间显示内容——实时设置值
		sprintf((char*)lcd_string[0]," NUM: %d   ",data_xuhao);//存储序号
		sprintf((char*)lcd_string[2],"       Setting    ");//当前状态
		
		LCD_SetTextColor(White);
		LCD_DisplayStringLine(Line5,lcd_string[1]);//显示时间在4行
		LCD_DisplayStringLine(Line1,lcd_string[0]);//显示存储位置在2行
		LCD_DisplayStringLine(Line8,lcd_string[2]);//显示状态在6行
		
	}

	if(time_start_flag)//************开始状态下,执行映射数据显示**********************
	{

		set_falg=4;//设置显示标志位置零,不开启
		//在设置界面以外的部分,设置显示位标志要设置满
		//以方便下一次的进入
		LCD_ClearLine(Line6);
		
		sprintf((char*)lcd_string[0]," NUM: %d   ",data_xuhao);
		sprintf((char*)lcd_string[1],"      %d: %d :%d               ",time_runnig/3600000,time_runnig%3600000/60000,time_runnig%3600000%60000/1000);
		sprintf((char*)lcd_string[2],"       Running    ");//显示剩余计时值

		LCD_SetTextColor(White);
		LCD_DisplayStringLine(Line1,lcd_string[0]);//显示存储位置在2行
		LCD_DisplayStringLine(Line5,lcd_string[1]);//显示时间在4行
		LCD_DisplayStringLine(Line8,lcd_string[2]);//显示状态在6行
		
	}
	
	if(time_wait_flag)//暂停状态下,也是执行映射数据显示
	{

		set_falg=4;//设置显示标志位置零,不开启
		//在设置界面以外的部分,设置显示位标志要设置满
		//以方便下一次的进入
		LCD_ClearLine(Line6);
		sprintf((char*)lcd_string[0]," NUM: %d   ",data_xuhao);
		sprintf((char*)lcd_string[1],"      %d: %d :%d                ",time_runnig/3600000,time_runnig%3600000/60000,time_runnig%3600000%60000/1000);
		sprintf((char*)lcd_string[2],"        Pause    ");//显示剩余计时值

		LCD_SetTextColor(White);
		LCD_DisplayStringLine(Line1,lcd_string[0]);//显示存储位置在2行
		LCD_DisplayStringLine(Line5,lcd_string[1]);//显示时间在4行
		LCD_DisplayStringLine(Line8,lcd_string[2]);//显示状态在6行
		
	}
}

5.主函数体部分

(这次的主函数部分,我主要是想在延时部分偷个懒(实际并没能偷到/(ㄒoㄒ)/~~)。这次我就是在主函数钟直接进行数据存储和读取的函数体的设计,但是我还是觉得大家分模块的,有联系的写会好些——因为那样会更容易理清思路,哪个哪个在哪儿,它们怎么用的。)

#include "stm32f10x.h"
#include "lcd.h"
#include "24c02.h"
#include "led.h"
#include "key.h"
#include "pwm.h"
#include "stdio.h"

u32 TimingDelay = 0;

extern u8 data_time[5][3]; //存储位置的时间数据

extern u8 data_xuhao;//存储地址——序号
extern u8 data_cunchu_flag;//存储标志位

void _24c02_time_set_cunchu_now_page(void);//写入当前设置的存储位置的数据
void _24c02_time_set_cunchu_all_page(void);//存储当前所有数据
//读取端电存储的所有数据
void _24c02_Read_time_set_cunchu_all_page(void);//读取所有存储位置的数据

void Delay_Ms(u32 nTime);

//Main Body
int main(void)
{

	STM3210B_LCD_Init();
	
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	SysTick_Config(SystemCoreClock/1000);
	
	i2c_init();//IIC初始化
	TIM3_PWM_Init(0);//频率1000hz,占空比80%,开始不使能
	KEY_Init();//按键使能
	LED_Init();//LED初始化
	_24c02_Read_time_set_cunchu_all_page();//读取所有存储位置的值
	
	while(1)
	{	

		
		key_read();//按键状态获取
		
		LED_SWITCH();//LED指示灯动作
		
		PWM_Switch();//PWM指示动作
		
		key_cunchu_adress();//改变存储位置/读取某一位置的存储的时间数据
		
		key_data_on_time_order();//调整定时时间——操作映射值
		
		_24c02_time_set_cunchu_now_page();//写入当前正在设置的存储位置的存储数据 
		
		time_work();//定时器的定时工作部分
		
		LCD_SHOW_SET_TIME_S();//LCD显示
		if(data_cunchu_flag)//如果没有执行存储,那断电不存储——相当于,每一次存储都检查两遍是否存储完成
		//这里是由于我考虑的时候,担心存储异常又加了1次,当然,如果你们在思考的时候觉得这部分没必要
		//是完全可以剔除的
		{
			_24c02_time_set_cunchu_all_page();//存储/更新当前可存储的数据
		}
	}
	
}



//写入当前设置的存储数据              4
//要在映射数据存储之后执行该函数才有意义
void _24c02_time_set_cunchu_now_page(void)
{
	if(data_cunchu_flag)
	{
		data_cunchu_flag=0;
		switch(data_xuhao)
		{
			case 1:
			_24c02_Write(0x00,data_time[0][0]);//写入第一存储位置的数据
			Delay_Ms(5);
			_24c02_Write(0x01,data_time[0][1]);//
			Delay_Ms(5);
			_24c02_Write(0x02,data_time[0][2]);//
			Delay_Ms(5);
			break;
			case 2:
			_24c02_Write(0x03,data_time[1][0]);//写入第二存储位置的数据
			Delay_Ms(5);
			_24c02_Write(0x04,data_time[1][1]);//
			Delay_Ms(5);
			_24c02_Write(0x05,data_time[1][2]);//
			Delay_Ms(5);
			break;
			case 3:
			_24c02_Write(0x06,data_time[2][0]);//写入第三存储位置的数据
			Delay_Ms(5);
			_24c02_Write(0x07,data_time[2][1]);//
			Delay_Ms(5);
			_24c02_Write(0x08,data_time[2][2]);//
			Delay_Ms(5);
			break;
			case 4:
			_24c02_Write(0x09,data_time[3][0]);//写入第四存储位置的数据
			Delay_Ms(5);
			_24c02_Write(0x0A,data_time[3][1]);//
			Delay_Ms(5);
			_24c02_Write(0x0B,data_time[3][2]);//
			Delay_Ms(5);
			break;
			case 5:
			_24c02_Write(0x0C,data_time[4][0]);//写入第五存储位置的数据
			Delay_Ms(5);
			_24c02_Write(0x0D,data_time[4][1]);//
			Delay_Ms(5);
			_24c02_Write(0x0E,data_time[4][2]);//
			Delay_Ms(5);
			break;
			default:
				break;
		}
	
	}
}

//存储所有数据
void _24c02_time_set_cunchu_all_page(void)
{
		_24c02_Write(0x00,data_time[0][0]);//写入第一存储位置的数据
		Delay_Ms(5);
		_24c02_Write(0x01,data_time[0][1]);//
		Delay_Ms(5);
		_24c02_Write(0x02,data_time[0][2]);//
		Delay_Ms(5);

		_24c02_Write(0x03,data_time[1][0]);//写入第二存储位置的数据
		Delay_Ms(5);
		_24c02_Write(0x04,data_time[1][1]);//
		Delay_Ms(5);
		_24c02_Write(0x05,data_time[1][2]);//
		Delay_Ms(5);

		_24c02_Write(0x06,data_time[2][0]);//写入第三存储位置的数据
		Delay_Ms(5);
		_24c02_Write(0x07,data_time[2][1]);//
		Delay_Ms(5);
		_24c02_Write(0x08,data_time[2][2]);//
		Delay_Ms(5);

		_24c02_Write(0x09,data_time[3][0]);//写入第四存储位置的数据
		Delay_Ms(5);
		_24c02_Write(0x0A,data_time[3][1]);//
		Delay_Ms(5);
		_24c02_Write(0x0B,data_time[3][2]);//
		Delay_Ms(5);

		_24c02_Write(0x0C,data_time[4][0]);//写入第五存储位置的数据
		Delay_Ms(5);
		_24c02_Write(0x0D,data_time[4][1]);//
		Delay_Ms(5);
		_24c02_Write(0x0E,data_time[4][2]);//
		Delay_Ms(5);
	
}

//读取端电存储的所有数据
void _24c02_Read_time_set_cunchu_all_page(void)
{
	data_time[0][0]=_24c02_Read(0x00);
	Delay_Ms(5);
	data_time[0][1]=_24c02_Read(0x01);
	Delay_Ms(5);
	data_time[0][2]=_24c02_Read(0x02);
	Delay_Ms(5);
	
	data_time[1][0]=_24c02_Read(0x03);
	Delay_Ms(5);
	data_time[1][1]=_24c02_Read(0x04);
	Delay_Ms(5);
	data_time[1][2]=_24c02_Read(0x05);
	Delay_Ms(5);
	
	data_time[2][0]=_24c02_Read(0x06);
	Delay_Ms(5);
	data_time[2][1]=_24c02_Read(0x07);
	Delay_Ms(5);
	data_time[2][2]=_24c02_Read(0x08);
	Delay_Ms(5);
	
	data_time[3][0]=_24c02_Read(0x09);
	Delay_Ms(5);
	data_time[3][1]=_24c02_Read(0x0A);
	Delay_Ms(5);
	data_time[3][2]=_24c02_Read(0x0B);
	Delay_Ms(5);
	
	data_time[4][0]=_24c02_Read(0x0C);
	Delay_Ms(5);
	data_time[4][1]=_24c02_Read(0x0D);
	Delay_Ms(5);
	data_time[4][2]=_24c02_Read(0x0E);
	Delay_Ms(5);
}

//延时函数部分
void Delay_Ms(u32 nTime)
{
	TimingDelay = nTime;
	while(TimingDelay != 0);	
}

练习总结

1.模块总结

(对使用的模块的一个回顾)

  1. 按键

     初始化配置后,就是*按键扫描*配置,在基本功能函数写完——就要开始具体功能函数部分的设计。按键主体部分
     就是对应按键按下后,对相应的状态进行标记 (我是用状态来执行的,所以呢,大家如果对状态标志不是很
     喜欢的,可以直接对应把具体的功能动作写在该按键按下的部分。)。
     在标记完成后,执行相应的*定时器存储位置的切换、定时值的设置/存储、以及定时器的启动*——当然这些个函数
     也是可以写在任意部分的,只是呢,我习惯于把逻辑相关放在一 起, 由于要设置时和界面切换、以及运行会用
     到按键,所以就放在按键这里。
    
  2. LED

     LED部分除了初始化,就是对LED的控制函数的设计,实现LED闪烁——闪烁是按照定时开关实现的(LED_SWITCH)。
    
  3. PWM

     在初始化配置结束之后,主要就是对PWM输出的控制——是按照程序运行与否——关闭定时器/打开定时器实现的。
    
  4. 24C02:

     这个部分就没什么东西了,有的只是将一些具体存储的函数的设计和定义,比较简单,基本都是固定的,需要好好
     记住基本功能的配置——写入和读取指定位置的数据就好了。
    

2.逻辑回顾

(对一些逻辑思考的简单回顾)

从程序启动,显示第一存储位置的定时数据——接着受到按键的功能控制——进行设置或存储切换——甚至电子定时器的启动。
关于LED和PWM输出,就是简单的嵌入指定状态就好——使用标志位,实时引用状态来控制开关就好。
基于定时器启动,分俩钟情况:
①一种由停止界面开始的存储值为定时时长的定时;
②另外一种是,在设置界面临时设置定时值来启动定时。

结束

我知道,我才开始写blog,可能有很多地方做的不好,不过呢本意就是让自己学习到的东西和一些学习经验分享给
大家,同时也是换一种方式记录自己的经验和成长。
如果大家在阅读时发现任何问题,都可以评论或者其他方式联系我。
集思广益,共同进步。
发布了5 篇原创文章 · 获赞 5 · 访问量 704

猜你喜欢

转载自blog.csdn.net/weixin_44604887/article/details/104008313