STM32RTC外设加入番茄钟算法,效果非常棒

一般我们常用RTC外设,去显示一些日期,实时时间,当然如果用的是ESP32那就更简单了,连接到比如心知天气的API后,这直接可以获取到最新的时间。今天呢,我尝试用STM32去做这个时钟项目,突发奇想加入番茄工作法在我的单片机中(后续暂且叫做番茄钟)。我是做了USBHub和STM32的拼板,应用场景在桌面,插上电脑后,单片机的供电问题也就得到了解决,同时呢,使用了VBAT电源,所以时钟断电后依然在运行。接下来是展示环节,上图

实时时钟+闹钟

番茄钟

USBHUB(合体)


好好好  废话不多说,直接上代码

下面的是番茄钟的执行逻辑

void work1(void)
{
		photo_flag =1;
    if (Flag_Change == 3) {
        OLED_ShowString(1, 1, "Pomodoro Timer");
        OLED_ShowString(2, 1, "Working:");
        OLED_ShowNum(2, 10, work_min, 2);
        OLED_ShowString(3, 1, "Resting:");
        OLED_ShowNum(3, 10, rest_min, 2);
        OLED_ShowString(4, 1, "Cycle:");
        OLED_ShowNum(4, 10, cycle_count, 2);

        last_state = 0;
        setting_mode = 1;
        return;
    }

if (setting_mode) {  
    // 处于设定模式,按键调整参数
    switch (KeyNum) {
		case 1:  
            // 按键一:进入计时模式
            setting_mode = 0;
            Pomodoro_State = 1;
            Pomodoro_StartTime = RTC_GetCounter();
            RTC_SetAlarm(Pomodoro_StartTime + work_min * 60);
            OLED_Clear();
            break;
        case 2:  
            // 按键二:增加选中的参数
            switch (selected_param) {
                case 0: work_min = (work_min < 60) ? (work_min + 1) : 60; break;
                case 1: rest_min = (rest_min < 30) ? (rest_min + 1) : 30; break;
                case 2: cycle_count = (cycle_count < 99) ? (cycle_count + 1) : 99; break;
            }
					work2();
            break;

        case 3:  
            // 按键三:减少选中的参数
            switch (selected_param) {
                case 0: work_min = (work_min > 1) ? (work_min - 1) : 1; break;
                case 1: rest_min = (rest_min > 1) ? (rest_min - 1) : 1; break;
                case 2: cycle_count = (cycle_count > 1) ? (cycle_count - 1) : 1; break;
            }
					work2();
            break;
        case 4:  
            // 切换正在调整的参数 (0 -> 1 -> 2 -> 0)
            selected_param = (selected_param + 1) % 3;  
			work2();
            break;
        default:KeyNum=4;break;
    }
        return;
    }

    // 运行中状态
    if (Pomodoro_State != last_state) {
        last_state = Pomodoro_State;
		if(Pomodoro_State == 1)
		{
		    OLED_ShowString(2, 1, " State: Working  "); // 清除旧状态	
		}
		else 
		{
        	OLED_ShowString(2, 1, " State: Resting  "); 
		}
		
    }

    // 按键处理逻辑
if (KeyNum == 1) {  
    if (Pomodoro_State == 0) {  
        // **初次启动**
        Pomodoro_State = 1; // 进入工作状态
        cycle_counter = 0;   // 从第 0 轮开始
        Pomodoro_StartTime = RTC_GetCounter();
        RTC_SetAlarm(Pomodoro_StartTime + work_min * 60);
    } else {  
        // **手动停止**
        Pomodoro_State = 0;  
        RTC_SetAlarm(0xFFFFFFFF);
    }
} 
	else if (KeyNum == 4) { 
        // 返回设定界面
        setting_mode = 1;
        Pomodoro_State = 0;
        OLED_Clear();
        Msta = 0;
		photo_flag = 0;
	OLED_ShowString(3, 1, "Alarm:XX:XX:XX");
        return;
    }

// **计时逻辑**
if (Pomodoro_State != 0) {
    uint32_t elapsed_time = RTC_GetCounter() - Pomodoro_StartTime;  
    uint32_t remain_time = 0;  

    if (Pomodoro_State == 1) {  
        // **工作时间倒计时**
        remain_time = work_min * 60 - elapsed_time;  
        if (remain_time == 0) {  
            // **进入休息模式**
            Pomodoro_State = 2;  
            Pomodoro_StartTime = RTC_GetCounter();
            RTC_SetAlarm(Pomodoro_StartTime + rest_min * 60);
			// **LED 闪烁 10 次**
            for (int i = 0; i < 10; i++) {
                LED_TURN();
                Delay_ms(500); // 控制闪烁速度,200ms 只是示例
            }
        }
    } else if (Pomodoro_State == 2) {  
        // **休息时间倒计时**
        remain_time = rest_min * 60 - elapsed_time;  
        if (remain_time == 0) {  
            cycle_counter++;  // **完成一次工作+休息**
            if (cycle_counter < cycle_count) {  
                // **继续下一轮**
                Pomodoro_State = 1;  
                Pomodoro_StartTime = RTC_GetCounter();
                RTC_SetAlarm(Pomodoro_StartTime + work_min * 60);
            } else {  
                // **全部循环完成,停止番茄钟**
                Pomodoro_State = 0;  
                RTC_SetAlarm(0xFFFFFFFF);
            }
        }
    }

    // **显示倒计时**
    OLED_ShowNum(3, 6, remain_time / 60, 2);
    OLED_ShowString(3, 8, ":");
    OLED_ShowNum(3, 10, remain_time % 60, 2);
}
}

这个是番茄钟的显示

void work2(void)
{
	    OLED_ShowString(1, 1, "Pomodoro Timer");
        OLED_ShowString(2, 1, "Working:");
        OLED_ShowNum(2, 10, work_min, 2);
        OLED_ShowString(3, 1, "Resting:");
        OLED_ShowNum(3, 10, rest_min, 2);
        OLED_ShowString(4, 1, "Cycle:");
        OLED_ShowNum(4, 10, cycle_count, 2);
	    OLED_ShowNum(2, 10, work_min, 2);
        OLED_ShowNum(3, 10, rest_min, 2);
        OLED_ShowNum(4, 10, cycle_count, 2);
}

一整个main函数程序。实时时钟的外设参考的是江科大和另一位B站UP(感谢感谢抱拳!!!)

#include "stm32f10x.h"                  // Device header
#include "Delay.h"						//延时模块,主函数中未使用
#include "OLED.h"						//OLED显示模块
#include "MyRTC.h"						//RTC模块
#include "Key.h"						//按键模块
#include "Buzzer.h"						//蜂鸣器模块
#include <stdio.h>
#include <string.h>
/*
	功能:	简易时钟 可以通过按键调整日期时间和设定闹钟,通过OLED显示日期时间等相关信息
			番茄钟   可以使用番茄工作法高效学习
	
	原理:	RTC
	
	接线:	OLED显示屏:SCK接PB8,SDA接PB9
			有源蜂鸣器(高电平触发):I/O接PB14
			红色指示灯(高电平触发):I/O接PB14
			1~4号独立按键:分别接PB11,PB10,PB1,PB0

*/
void work0(void);
void work1(void);
void work2(void);
void work3(void);
uint16_t MyRTC_Time[] = {2025,3,24,13,47,50};		//定义全局的时间数组,数组内容分别为年、月、日、时、分、秒
uint32_t Alarm_CNT,Alarm_Time,Alarm_Time_Rest;	//闹钟相关变量,单位都是秒
uint8_t Hour,Min,Sec;							//用来调整闹钟时间的变量
uint8_t KeyNum;									//按键键码值
uint8_t Flag_Count;								//是否在计时标志,0为不在计时
uint8_t Flag_Change;							//按键调节闹钟/日期/时间,0为调节闹钟,1为调节日期,2为调节时间
//work工作标志位
u8 Msta=0;
uint8_t Pomodoro_State = 0;    // 0:停止 1:工作中 2:休息中
uint32_t Pomodoro_StartTime = 0;
uint8_t Pomodoro_Cycle = 0;    // 完成的番茄周期数
const uint16_t WORK_MIN = 25;  // 工作时间25分钟
const uint16_t REST_MIN = 5;   // 休息时间5分钟
uint8_t photo_flag = 0;
 uint8_t last_state = 0;
 uint8_t setting_mode = 1; // 是否处于设定模式,1 代表设置界面
 uint8_t work_min = 25;    // 工作时间(默认 25 分钟)
 uint8_t rest_min = 5;     // 休息时间(默认 5 分钟)
 uint8_t cycle_count = 0;  // 当前循环次数
/*按键控制函数*/
void Key_Control(void)
{
	KeyNum = Key_GetNum();	//读取按键键码
	
	if(Flag_Change == 0&&photo_flag ==0)	//调节闹钟
	{
		if(KeyNum == 1)		//1号按键调整小时
		{
			Hour++;
			if(Hour > 60)
				Hour = 0;
		}
		else if(KeyNum == 2)	//2号按键调整分钟
		{
			Min++;
			if(Min > 60)
				Min = 0;
		}
		else if(KeyNum == 3)	//3号按键调整秒
		{
			Sec++;
			if(Sec > 60)
				Sec = 0;
		}

		else if(KeyNum == 4)	//4号按键
		{
			if(Buzzer_State()==0)		//若蜂鸣器没响
			{
				Alarm_Time = Hour*3600 + Min*60 + Sec;			//计算闹钟时长,单位是秒
				
				if(Alarm_Time > 0)
				{
					Alarm_CNT = RTC_GetCounter()+Alarm_Time-1;			//设定闹钟值,需要-1
					RTC_SetAlarm(Alarm_CNT);							//写入闹钟值到RTC的ALR寄存器
					Flag_Count = 1;
				}
				else	//若闹钟时长为0,则转到按键调节日期
				{
					Flag_Change = 1;	
				}
			}
			else	//若蜂鸣器响
			{
				Buzzer_OFF();		//关闭蜂鸣器
				OLED_ShowString(4,1,"          ");	//刷新oled第四行	
			}
		}
	}
	
	else if(Flag_Change == 1)		//调节日期
	{
		if(KeyNum == 1)			//1号按键调整年
		{
			MyRTC_Time[0]++;
			MyRTC_SetTime();
		}
		else if(KeyNum == 2)	//2号按键调整月
		{
			MyRTC_Time[1]++;
			MyRTC_SetTime();
		}
		else if(KeyNum == 3)	//3号按键调整日
		{
			MyRTC_Time[2]++;
			MyRTC_SetTime();
		}
		else if(KeyNum == 4)	//4号按键,改为调整时间
		{
			Flag_Change = 2;
		}			
	}
	
	else if(Flag_Change == 2)		//调节时间
	{
		if(KeyNum == 1)			//1号按键调整小时
		{
			MyRTC_Time[3]++;
			MyRTC_SetTime();
		}
		else if(KeyNum == 2)	//2号按键调整分钟
		{
			MyRTC_Time[4]++;
			MyRTC_SetTime();
		}
		else if(KeyNum == 3)	//3号按键调整秒
		{
			MyRTC_Time[5]++;
			MyRTC_SetTime();
		}
		else if(KeyNum == 4)	//4号按键
		{

			Flag_Change = 3;	//改为调整闹钟
		}			
	}
	else if(Flag_Change == 3)		//调节时间
	{
		if(KeyNum == 1)		
		{
			Msta = 1;
			OLED_Clear();
		}
		else if(KeyNum == 4)	//4号按键
		{
			Flag_Change = 0;	//改为调整闹钟
			OLED_ShowString(4,1,"              ");	//刷新oled第四行

		}	
	}

}

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	MyRTC_Init();		//RTC初始化
	Key_Init();			//按键初始化
	Buzzer_Init();		//蜂鸣器初始化
	OLED_ShowString(3, 1, "Alarm:XX:XX:XX");
	Msta=0;
	while (1)
	{		
		MyRTC_ReadTime();							//RTC读取时间,最新的时间存储到MyRTC_Time数组中
		Key_Control();								//调用按键控制函数
		switch(Msta)
		{
			case 0: work0();// 		
									break;
			case 1: work1();// 
									break;
			case 2: work2();// 
									break;
			case 3: work3();// 
									break;
			default: Msta=0;break;
		}//switch
	}
}
void work0(void)
{
	char buf1[30];
	OLED_ShowString(1, 1, "Date:");
	OLED_ShowString(2, 1, "Time:");
	OLED_ShowString(3, 1, "Alarm:");

	sprintf(buf1,"Date:%04d-%02d-%02d",MyRTC_Time[0],MyRTC_Time[1],MyRTC_Time[2]);
	OLED_ShowString(1,1,buf1);	
	sprintf(buf1,"Time:%02d:%02d:%02d",MyRTC_Time[3],MyRTC_Time[4],MyRTC_Time[5]);
	OLED_ShowString(2,1,buf1);
		if(Flag_Count)										//正在计时,则显示闹钟响起剩余时间
		{
			Alarm_Time_Rest = Alarm_CNT-RTC_GetCounter()+1;	//计算闹钟响起剩余时间
			if(Alarm_Time_Rest > Alarm_Time)				//防止溢出错误
				Alarm_Time_Rest = 0;
			
			OLED_ShowNum(3,7,Alarm_Time_Rest/3600,2);		//显示剩余小时
			OLED_ShowNum(3,10,(Alarm_Time_Rest%3600)/60,2);	//显示剩余分钟
			OLED_ShowNum(3,13,(Alarm_Time_Rest%3600)%60,2);	//显示剩余秒
			sprintf(buf1,"Alarm:%02d:%02d:%02d",(Alarm_Time_Rest/3600),(Alarm_Time_Rest%3600)/60,(Alarm_Time_Rest%3600)%60);
			OLED_ShowString(3, 1,buf1 );
			
			if(RTC_GetFlagStatus(RTC_FLAG_ALR) == 1)		//闹钟时间到,检查标志位为1
			{
				RTC_ClearFlag(RTC_FLAG_ALR);				//清除标志位
				Flag_Count = 0;Alarm_Time = 0;				//重置相关参数
				Hour = 0;Min = 0; Sec = 0;
				
				Buzzer_ON();								//打开蜂鸣器
				OLED_ShowString(4,1,"Time Out");
			}
			else											//闹钟时间未到
			{
				OLED_ShowString(4,1,"Counting");			//显示正在计时
			}
		}
		else												//不在计时,则显示需要设定的闹钟时间
		{
			OLED_ShowNum(3,7,Hour,2);
			OLED_ShowNum(3,10,Min,2);
			OLED_ShowNum(3,13,Sec,2);
		}
		
		if(Flag_Change == 1)		//显示“调节日期”
		{
			OLED_ShowString(4,1,"Change Date");
		}
		else if(Flag_Change == 2)	//显示“调节时间”
		{
			OLED_ShowString(4,1,"Change Time");
		}
		else if(Flag_Change == 3)	//显示“番茄时钟”
		{
			OLED_ShowString(4,1,"Pomodoro Time");
		}

}
	uint8_t selected_param = 0;  // 0: 工作时间, 1: 休息时间, 2: 循环次数
// 变量定义
uint8_t cycle_counter = 0;  // 记录当前是第几轮循环
void work1(void)
{
		photo_flag =1;
    if (Flag_Change == 3) {
        OLED_ShowString(1, 1, "Pomodoro Timer");
        OLED_ShowString(2, 1, "Working:");
        OLED_ShowNum(2, 10, work_min, 2);
        OLED_ShowString(3, 1, "Resting:");
        OLED_ShowNum(3, 10, rest_min, 2);
        OLED_ShowString(4, 1, "Cycle:");
        OLED_ShowNum(4, 10, cycle_count, 2);

        last_state = 0;
        setting_mode = 1;
        return;
    }

if (setting_mode) {  
    // 处于设定模式,按键调整参数
    switch (KeyNum) {
		case 1:  
            // 按键一:进入计时模式
            setting_mode = 0;
            Pomodoro_State = 1;
            Pomodoro_StartTime = RTC_GetCounter();
            RTC_SetAlarm(Pomodoro_StartTime + work_min * 60);
            OLED_Clear();
            break;
        case 2:  
            // 按键二:增加选中的参数
            switch (selected_param) {
                case 0: work_min = (work_min < 60) ? (work_min + 1) : 60; break;
                case 1: rest_min = (rest_min < 30) ? (rest_min + 1) : 30; break;
                case 2: cycle_count = (cycle_count < 99) ? (cycle_count + 1) : 99; break;
            }
					work2();
            break;

        case 3:  
            // 按键三:减少选中的参数
            switch (selected_param) {
                case 0: work_min = (work_min > 1) ? (work_min - 1) : 1; break;
                case 1: rest_min = (rest_min > 1) ? (rest_min - 1) : 1; break;
                case 2: cycle_count = (cycle_count > 1) ? (cycle_count - 1) : 1; break;
            }
					work2();
            break;
        case 4:  
            // 切换正在调整的参数 (0 -> 1 -> 2 -> 0)
            selected_param = (selected_param + 1) % 3;  
			work2();
            break;
        default:KeyNum=4;break;
    }
        return;
    }

    // 运行中状态
    if (Pomodoro_State != last_state) {
        last_state = Pomodoro_State;
		if(Pomodoro_State == 1)
		{
		    OLED_ShowString(2, 1, " State: Working  "); // 清除旧状态	
		}
		else 
		{
        	OLED_ShowString(2, 1, " State: Resting  "); 
		}
		
    }

    // 按键处理逻辑
if (KeyNum == 1) {  
    if (Pomodoro_State == 0) {  
        // **初次启动**
        Pomodoro_State = 1; // 进入工作状态
        cycle_counter = 0;   // 从第 0 轮开始
        Pomodoro_StartTime = RTC_GetCounter();
        RTC_SetAlarm(Pomodoro_StartTime + work_min * 60);
    } else {  
        // **手动停止**
        Pomodoro_State = 0;  
        RTC_SetAlarm(0xFFFFFFFF);
    }
} 
	else if (KeyNum == 4) { 
        // 返回设定界面
        setting_mode = 1;
        Pomodoro_State = 0;
        OLED_Clear();
        Msta = 0;
		photo_flag = 0;
	OLED_ShowString(3, 1, "Alarm:XX:XX:XX");
        return;
    }

// **计时逻辑**
if (Pomodoro_State != 0) {
    uint32_t elapsed_time = RTC_GetCounter() - Pomodoro_StartTime;  
    uint32_t remain_time = 0;  

    if (Pomodoro_State == 1) {  
        // **工作时间倒计时**
        remain_time = work_min * 60 - elapsed_time;  
        if (remain_time == 0) {  
            // **进入休息模式**
            Pomodoro_State = 2;  
            Pomodoro_StartTime = RTC_GetCounter();
            RTC_SetAlarm(Pomodoro_StartTime + rest_min * 60);
			// **LED 闪烁 10 次**
            for (int i = 0; i < 10; i++) {
                LED_TURN();
                Delay_ms(500); // 控制闪烁速度,200ms 只是示例
            }
        }
    } else if (Pomodoro_State == 2) {  
        // **休息时间倒计时**
        remain_time = rest_min * 60 - elapsed_time;  
        if (remain_time == 0) {  
            cycle_counter++;  // **完成一次工作+休息**
            if (cycle_counter < cycle_count) {  
                // **继续下一轮**
                Pomodoro_State = 1;  
                Pomodoro_StartTime = RTC_GetCounter();
                RTC_SetAlarm(Pomodoro_StartTime + work_min * 60);
            } else {  
                // **全部循环完成,停止番茄钟**
                Pomodoro_State = 0;  
                RTC_SetAlarm(0xFFFFFFFF);
            }
        }
    }

    // **显示倒计时**
    OLED_ShowNum(3, 6, remain_time / 60, 2);
    OLED_ShowString(3, 8, ":");
    OLED_ShowNum(3, 10, remain_time % 60, 2);
}
}
void work2(void)
{
	    OLED_ShowString(1, 1, "Pomodoro Timer");
        OLED_ShowString(2, 1, "Working:");
        OLED_ShowNum(2, 10, work_min, 2);
        OLED_ShowString(3, 1, "Resting:");
        OLED_ShowNum(3, 10, rest_min, 2);
        OLED_ShowString(4, 1, "Cycle:");
        OLED_ShowNum(4, 10, cycle_count, 2);
	    OLED_ShowNum(2, 10, work_min, 2);
        OLED_ShowNum(3, 10, rest_min, 2);
        OLED_ShowNum(4, 10, cycle_count, 2);
}
void work3(void)
{
	
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值