/*

	《STM8S105K4实验板+DS3231模块+HT1621模式 = 实时时钟实验》

程序较为完整地实现了时间显示、时间设置功能,并充分考虑了节电性能,
使之比较适合使用电池供电来长时间运行,具备一定的实用性。但程序仍然
是实验性的,因为使用了DS3231的SQW脚来输出1Hz信号唤醒MCU,所以程序
没用设计闹钟功能。

注:
1、除STM8官方库文件外,部分基础代码源于实验板例程;按键代码源于网
络;软件IIC驱动基于原子例程修改;DS3231代码、电源管理代码、全部主
程序逻辑为原创。
2、代码中有些源代码文件项目中并未使用,为方便以后使用并未中删除。

bg4uvr	最后版本完成于2020.3.5
*/

#include "stm8s.h"
#include "timer.h"
#include "ht1621.h"
#include "delay.h"
#include "myiic.h"
#include "ds3231.h"
#include "key.h"
#include "button.h"
#include "power.h"

bool last_halfsec;				//半秒信号
uint8_t sysMode;				//系统工作模式
uint8_t timeChanged;			//设置时是否改变了时钟
uint8_t year,month,date,hour,min;	//HEX格式时间(设置时间时使用)
uint8_t buf[4];					//显示缓存
ds3231_info ds3231;             //DS3231结构体

Button_t Button1;               //注册按键1
Button_t Button2;               //注册按键2

//按键1单击
void Btn1_Dowm_CallBack(void *btn)
{
	ModeTimeOutCnt=0;			//模式超时计数器

	switch(sysMode)
	{
		//普通模式,循环切换6种显示状态
	case 0:					//普通模式
	case 1:					//秒
	case 2:					//年
	case 3:					//月。日
	case 4:					//星期
		sysMode++;
		break;
	case 5:					//温度
		sysMode=0;
		break;

		//设置模式
	case 11:				//设置年份的世纪位
		if(year<100)    year+=100;
		else year-=100;
		break;
	case 12:				//设置年份的10年位
		if(year%100 < 90) year+=10;
		else year-=90;
		break;
	case 13:				//设置年份的个位
		if(year%10 < 9) year++;
		else year-=9;
		break;
	case 14:				//设置月的十位
		if(month<10) month=10;
		else month=1;
		break;
	case 15:				//设置月的个位
		month++;
		if(month>=13)    month=10;
		else if(month==10)   month=1;
		break;
	case 16:				//设置日的十位
		date+=10;
		if(date>max_date(year,month))
		{
			if(date/10!=0)
			{
				date=date/10*10;
				if(date>max_date(year,month))
					date=1;

			}
			else
				date-=10;
		}
		break;
	case 17:				//设置日的个位
		date++;
		if(date%10==0)
		{
			date-=10;
			if(date==0) date=1;
		}
		if(date>max_date(year,month))
		{
			date=date/10*10;
		}
		break;
	case 18:				//设置小时10位
		hour+=10;
		if(hour>23)
		{
			if(hour%10!=0)  hour=20;
			else    hour=0;
		}
		timeChanged=1;
		break;
	case 19:				//设置小时个位
		hour++;
		if(hour>23) hour=hour/10*10;
		else if(hour%10==0) hour-=10;
		timeChanged=1;
		break;
	case 20:				//设置分钟10位
		if(min<50) min+=10;
		else    min%=10;
		timeChanged=1;
		break;
	case 21:				//设置分钟个位
		min++;
		if(min>60)  min/=10;
		else if(min%10==0)  min-=10;
		timeChanged=1;
		break;

		//设置完毕,待启动模式
	case 50:
		ds3231.sec=0;
		ds3231.min=hex2bcd(min);
		ds3231.hour=hex2bcd(hour);
		DS3231_Write(0,3,(uint8_t*)&ds3231);    //写入时分秒数据
		sysMode=0;			//回到普通模式
		break;
	}
}


//按键2单击
void Btn2_Dowm_CallBack(void *btn)
{
	ModeTimeOutCnt=0;			//模式超时计数器

	//普通显示模式,如果非主页面,切换回主页面
	if(sysMode > 0 && sysMode < 10)
		sysMode = 0;

	//设置模式,变更为一下项设置
	else if(sysMode >= 10 && sysMode <50)
	{
		if(++sysMode > 21)		//设置模式供有21-11+1种,到第21种返回第11种
			sysMode = 11;
	}
}

//按键2长按
void Btn2_Long_CallBack(void *btn)
{
	ModeTimeOutCnt=0;			//模式超时计数器

	//如果是普通模式,则进入设置模式
	if(sysMode==0)
	{
		sysMode=10;				//进入设置模式
		timeChanged=0;			//时钟已更改状态清零
		//读取DS3231时间数据到缓存
		DS3231_Read(0x00,7,(uint8_t*)&ds3231);
		//把时间转换为hex数值,便于后续设置时计算
		year=ds3231.month&0x80?bcd2hex(ds3231.year)+100:bcd2hex(ds3231.year);
		month=bcd2hex(ds3231.month&0x1f);
		date=bcd2hex(ds3231.date);
		hour=bcd2hex(ds3231.hour);
		min=bcd2hex(ds3231.min);
	}

	//如果是设置模式,则保存设置
	else if(sysMode >=11 && sysMode < 50)
	{
		//把已设置的hex格式数据,转换为bcd格式
		ds3231.date=hex2bcd(date);
		ds3231.month=year/100?hex2bcd(month)|0x80:hex2bcd(month);
		ds3231.year=hex2bcd(year%100);
		//根据日期计算星期
		ds3231.week=week(ds3231.year,ds3231.month,ds3231.date);
		//保存年月日和星期(4个字节)
		DS3231_Write(WEEK,4,(uint8_t*)&ds3231.week);

		//如果更改为时间数据,进入等待启动状态
		if(timeChanged) sysMode=50;
		//否则直接返回普通模式
		else    sysMode=0;
	}
}


//系统初始化
void Sys_init(void)
{
	ht1621Init();				//初始化LCD
	IIC_Init();					//初始化软件IIC
	KEY_Init();					//按键初始化
	DS3231_init();				//DS3231初始化
	power_init();				//休眠模式初始化

	//注册按键1
	Button_Create("Button1",&Button1,Read_KEY1_Level,KEY_ON);
	Button_Attach(&Button1,BUTTON_DOWM,Btn1_Dowm_CallBack);		//单击

	//注册按键2
	Button_Create("Button2",&Button2,Read_KEY2_Level,KEY_ON);
	Button_Attach(&Button2,BUTTON_DOWM,Btn2_Dowm_CallBack);		//单击
	Button_Attach(&Button2,BUTTON_LONG,Btn2_Long_CallBack);		//长按

	Timer4_Init();				//初始化定时器4
}

//主程序
int main(void)
{
	Sys_init();					//初始化
	sysMode=0;					//系统模式初始化

	while(1)
	{
		//检查是否是被按键唤醒
		if(key_flag)
		{
			key_flag=FALSE;		//清空按键标志
			ModeTimeOutCnt=0;	//清零超时计数器
			TIM4_Cmd(ENABLE);	//启动定时器4
		}

		//如果定时器4是开启状态并且已超时
		if(TIM4->CR1&TIM4_CR1_CEN && ModeTimeOutCnt>500)
		{
			sysMode=0;			//恢复普通模式
			TIM4_Cmd(DISABLE);	//关闭定时器4
		}

		//普通模式
		if(sysMode == 0)
		{
			//只读取小时和分钟数据
			DS3231_Read(MIN,2,(uint8_t*)&ds3231.min);
			//显示小时和分钟
			if(ds3231.hour>>4==0)	//小时十位无效0消隐
				buf[0]=halfsec?17:16;
			else
				buf[0]=halfsec?ds3231.hour>>4|0x80:ds3231.hour>>4;
			buf[1]=ds3231.hour&0x0f;
			buf[2]=ds3231.min>>4;
			buf[3]=ds3231.min&0x0f;
			dispAll((uint8_t*)buf);
			//如果已超时,休眠
			if(ModeTimeOutCnt>500) 	power_halt();
		}
		//其他模式
		else
		{
			//每半秒才执行一次
			while(last_halfsec==halfsec)	wfi();	//如果未到半秒,MCU进入Wait模式,等待中断唤醒
			last_halfsec=halfsec;

			//读取全部7个字节的时间数据
			if(sysMode<=10)
			{
				DS3231_Read(0,7,(uint8_t*)&ds3231);
			}

			//根据模式不同,显示相对应的数据
			switch(sysMode)
			{

				//特殊显示模式
				//显示秒数
			case 1:
				buf[0]=17;				//显示冒号
				buf[1]=16;				//空白
				buf[2]=ds3231.sec>>4;	//秒十位
				buf[3]=ds3231.sec&0x0f;	//秒个位
				break;
				//显示年
			case 2:
				buf[0]=2;
				buf[1]=ds3231.month&0x80?1:0;	//月的最高位是世纪
				buf[2]=ds3231.year>>4;
				buf[3]=ds3231.year&0x0f;
				break;
				//显示月、日
			case 3:
				buf[0]=ds3231.month&0x10?1:16;
				buf[1]=ds3231.month&0x0f;
				buf[2]=ds3231.date>>4|0x80;
				buf[3]=ds3231.date&0x0f;
				break;
				//显示星期
			case 4:
				buf[0] = 16;
				buf[1] = 16;
				buf[2] = ds3231.week;
				buf[3] = 16;
				break;
				//显示温度
			case 5:
				DS3231_Read(TEMP_H,2,(uint8_t*)&ds3231.temp_h);	//读取温度
				buf[0] = ds3231.temp_h&0x80?18:16;				//判断是否显示负号
				buf[1] = (ds3231.temp_h&0x7f)/10;
				buf[2] = (ds3231.temp_h&0x7f)%10;
				buf[3] = ds3231.temp_l&0x80?5|0x80:0|0x80;
				break;

				//设置模式(以下模式,数据直接缓存中的,不从DS3231读取)
				//显示 "-SET"
			case 10:
				buf[0] = 18;
				buf[1] = 5;
				buf[2] = 0x0e;
				buf[3] = 7;
				break;
				//设置世纪位
			case 11:
				buf[0] = 2;
				buf[1] = halfsec ? year/100 : 16;
				buf[2] = year%100/10;
				buf[3] = year%10;
				break;
				//设置十年位
			case 12:
				buf[0] = 2;
				buf[1] = year/100?1:0;
				buf[2] = halfsec ? year%100/10 : 16;
				buf[3] = year%10;
				break;
				//设置年个位
			case 13:
				buf[0] = 2;
				buf[1] = year/100?1:0;
				buf[2] = year%100/10;
				buf[3] = halfsec ? year%10 : 16;
				break;
				//设置月十位
			case 14:
				buf[0] = halfsec ? month/10 : 16;
				buf[1] = month%10;
				buf[2] = date/10|0x80;
				buf[3] = date%10;
				break;
				//设置月个位
			case 15:
				buf[0] = month/10?1:16;
				buf[1] = halfsec ? month%10:16;
				buf[2] = date/10|0x80;
				buf[3] = date%10;
				break;
				//设置日十位
			case 16:
				buf[0] = month/10?1:16;
				buf[1] = month%10;
				buf[2] = halfsec ? date/10|0x80 : 16|0x80;
				buf[3] = date%10;
				break;
				//设置日个位
			case 17:
				buf[0] = month/10?1:16;
				buf[1] = month%10;
				buf[2] = date/10|0x80;
				buf[3] = halfsec ? date%10 : 16;
				break;

				//设置小时十位
			case 18:
				buf[0] = halfsec ? hour/10|0x80 : 17;
				buf[1] = hour%10;
				buf[2] = min/10;
				buf[3] = min%10;
				break;
				//设置小时个位
			case 19:
				buf[0] = hour/10|0x80;
				buf[1] = halfsec ? hour%10 : 16;
				buf[2] = min/10;
				buf[3] = min%10;
				break;
				//设置分钟十位
			case 20:
				buf[0] = hour/10|0x80;
				buf[1] = hour%10;
				buf[2] = halfsec ? min/10 : 16;
				buf[3] = min%10;
				break;
				//设置分钟个位
			case 21:
				buf[0] = hour/10|0x80;
				buf[1] = hour%10;
				buf[2] = min/10;
				buf[3] = halfsec ?min%10 : 16;
				break;

				//待启动模式
				//设置时钟后,时钟为停止状态,等待按KEY1键
			case 50:
				buf[0] = hour/10|0x80;
				buf[1] = hour%10;
				buf[2] = min/10;
				buf[3] = min%10;
				ModeTimeOutCnt=0;		//模式超时计数器清零,永不超时
				break;
			}
			dispAll((uint8_t*)buf);			//刷新LCD
		}
	}
}