3801
- 收藏
- 点赞
- 分享
- 举报
STM32实现万年历
实现的功能:
1、日历功能。
2、数字和模拟时钟功能。
要点分析:
1、STM32自带了RTC时钟计数器,从0开始计数到232。每一个计数代表秒计数,每六十个计数代表分计数,以此类推。24(小时)*60(分钟)*60(秒钟)=86400代表一天的计数时间。假设当前计数为count,count/86400得到计数的天数,根据这个得到年月日。Count%86400得到时分秒。
2、一些根据1中得到的年月日时分秒,进行计算的程序有:阳历转阴历,闰年判断,节气判断,星期几计算,当前月有多少天等等。
3、模拟时钟的绘制:时钟指针运动算法、屏幕重绘方法、RTC消息、画笔/画刷等。指针运动算法和屏幕重绘方法是本程序主要难点所在。(以下参照百度文库之模拟时钟)
不论何种指针,每次转动均以π/30弧度(一秒的角度)为基本单位,且都以表盘中心为转动圆心。计算指针端点(x, y)的公式如下:
x =圆心x坐标 + 指针长度 * cos (指针方向角)
y =圆心y坐标 + 指针长度 * sin (指针方向角)
注意,指针长度是指自圆心至指针一个端点的长度(是整个指针的一部分),由于指针可能跨越圆心,因此一个指针需要计算两个端点。
由于屏幕的重绘1秒钟一次,如果采用全屏删除式重绘则闪烁十分明显,显示效果不佳。本程序采用非删除式重绘,假定指针将要移动一格,则先采用背景色(这里是白色)重绘原来指针以删除原来位置的指针,再采用指针的颜色在当前位置绘制指针(如果指针没有动,则直接绘制指针,此句在程序中被我删除,具体原因,为数据截断导致一些误差)。
另外,秒表为RTC一秒钟定时计数。
程序分析:
uCGUI+uCOS,一共三个任务:主处理任务、触摸屏任务、秒更新任务。
void App_UCGUI_TaskCreate (void)
{
CPU_INT08U os_err;
os_err = os_err;
Clock_SEM=OSSemCreate(1); //建立秒更新中断的信号量
//硬件平台初始化
BSP_Init();
//主处理任务---------------------------------------------------------
os_err = OSTaskCreateExt(AppTaskUserIF,
(void *)0,
(OS_STK *)&AppTaskUserIFStk[APP_TASK_USER_IF_STK_SIZE-1],
APP_TASK_USER_IF_PRIO,
APP_TASK_USER_IF_PRIO,
(OS_STK *)&AppTaskUserIFStk[0],
APP_TASK_USER_IF_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
//触摸屏任务---------------------------------------------------------
os_err = OSTaskCreateExt(AppTaskKbd,
(void *)0,
(OS_STK *)&AppTaskKbdStk[APP_TASK_KBD_STK_SIZE-1],
APP_TASK_KBD_PRIO,
APP_TASK_KBD_PRIO,
(OS_STK *)&AppTaskKbdStk[0],
APP_TASK_KBD_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
//秒更新任务
os_err = OSTaskCreateExt(Clock_Updata,
(void *)0,
(OS_STK *)&Clock_Updata_Stk[Clock_Updata_STK_SIZE-1],
Clock_Updata_PRIO,
Clock_Updata_PRIO,
(OS_STK *)&Clock_Updata_Stk[0],
Clock_Updata_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
}
万年历中的时间用的是STM32自带的RTC实时时钟。
1、主处理任务:界面背景初始化,并根据当前时间,画出图1的数据。
static void AppTaskUserIF (void *p_arg)
{
(void)p_arg;
INT8U err;
//界面初始化
GUI_Init(); //ucgui 初始化
_ExecCalibration(); /* 触摸屏校准 */
GUI_SetBkColor(GUI_WHITE); //设置背景色
GUI_SetColor(GUI_GRAY); //设置前景色
GUI_Clear(); //清屏
Lcd_show_bmp(0, 0,"/RTC.bmp"); //显示万年历背景
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispStringAt("一",15,47); //显示星期一
GUI_DispStringAt("二",44,47); //显示星期二
GUI_DispStringAt("三",73,47); //显示星期三
GUI_DispStringAt("四",102,47); //显示星期四
GUI_DispStringAt("五",131,47); //显示星期五
GUI_SetColor(GUI_RED); //用红字显示周末
GUI_DispStringAt("六",160,47); //显示星期六
GUI_DispStringAt("日",189,47); //显示星期日
to_tm(RTC_GetCounter(), &s_time); //根据RTC时钟得到万年历时间的初值,注意,这个值是根据用户查询万年历变化
GUI_SetFont(&GUI_Font16_1 ); //设置英文字体
GUI_DispDecAt(s_time.tm_year,4,13,4);//显示万年历的年份
GUI_SetFont(&GUI_FontHZ_SimSun_16); //设置中文字体
GUI_DispString("年"); //显示年
GUI_SetFont(&GUI_Font16_1 ); //设置英文字体
GUI_DispDec(s_time.tm_mon,2); //显示万年历的月份
GUI_SetFont(&GUI_FontHZ_SimSun_16); //设置中文字体
GUI_DispString("月"); //显示月
GUI_SetFont(&GUI_Font16_1 ); //设置英文字体
GUI_DispDec(s_time.tm_mday,2); //显示万年历的日子
GUI_SetFont(&GUI_FontHZ_SimSun_16);//设置中文字体
GUI_DispString("日"); //显示日
//画模拟时钟界面
u16 index,x,y;
GUI_SetPenSize(1);
GUI_SetColor(GUI_RED);
GUI_DrawCircle(264,170, 45); //画时钟最外层的圆,
for( index = 0; index < 60; index++ )//画时钟的刻度
{
x = -(40* cos(( index * 6 ) * DEG2RAD)) + 264;
y = -(40* sin(( index * 6 ) * DEG2RAD)) + 170;
if( index % 5 == 0 )
{
GUI_SetPenSize(4); //指示为小时的刻度用粗点画
}
else
{
GUI_SetPenSize(2); //其它刻度用西点画
}
GUI_DrawPoint( x, y );
}
while(1)
{
RTC_Show(RTC_GetCounter()); //主任务执行程序,接下来分析
OSTimeDlyHMSM(0,0,0,10);
}
}
//主任务执行程序
void RTC_Show(uint32_t TimeVar)
{
u8 str[15]; // 字符串暂存
u8 i,k;
static flag=0;
#if (GUI_SUPPORT_CURSOR|GUI_SUPPORT_TOUCH)
GUI_CURSOR_Show(); //鼠标显示
#endif
WM_SetCreateFlags(WM_CF_MEMDEV); /* Automatically use memory devices on all windows */
if(flag==0) //初始化按键,只执行一次。
{
//建立按键F1-F5
_ahButton[0] =BUTTON_Create(5, 0, 32,13, GUI_KEY_F1 , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[1]=BUTTON_Create(5, 29, 32,13, GUI_KEY_F2 , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[2]=BUTTON_Create(53, 0, 16,13, GUI_KEY_F3 , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[3]=BUTTON_Create(53,29,16,13, GUI_KEY_F4 , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[4]=BUTTON_Create(85, 0, 16,13, GUI_KEY_F5, WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[5]=BUTTON_Create(85,29, 16,13, GUI_KEY_F6, WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
//按键字体设置
BUTTON_SetFont(_ahButton[0],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[1],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[2],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[3],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[4],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[5],&GUI_Font6x8);//GUI_Font16_ASCII
//按键背景色设置
BUTTON_SetBkColor(_ahButton[0],0,GUI_DARKMAGENTA); //按键背景颜色
BUTTON_SetBkColor(_ahButton[1],0,GUI_DARKMAGENTA);
BUTTON_SetBkColor(_ahButton[2],0,GUI_DARKMAGENTA);
BUTTON_SetBkColor(_ahButton[3],0,GUI_DARKMAGENTA);
BUTTON_SetBkColor(_ahButton[4],0,GUI_DARKMAGENTA);
BUTTON_SetBkColor(_ahButton[5],0,GUI_DARKMAGENTA);
//按键前景色设置
BUTTON_SetTextColor(_ahButton[0],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[1],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[2],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[3],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[4],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[5],0,GUI_WHITE);
//按键显示字符
BUTTON_SetText(_ahButton[0], "+");
BUTTON_SetText(_ahButton[1], "-");
BUTTON_SetText(_ahButton[2], "+");
BUTTON_SetText(_ahButton[3], "-");
BUTTON_SetText(_ahButton[4], "+");
BUTTON_SetText(_ahButton[5], "-");
flag=1;
}
u8 key = GUI_GetKey(); //实时获得触摸按键的值
if(key==40) //年数增加按钮
{
s_time.tm_year++; //F1
if(s_time.tm_year>2099) //超过范围处理
s_time.tm_year=1970;
}
else if(key==41) //年数减小按钮
{
s_time.tm_year--; //F2
if(s_time.tm_year<1900) //超过范围处理
s_time.tm_year=2099;
}
else if(key==42) //月数增加按钮
{
s_time.tm_mon++; //F3
if(s_time.tm_mon>12) //超过范围处理
{
s_time.tm_mon=1;
}
}
else if(key==43) //月数减小按钮
{
s_time.tm_mon--; //F4
if(s_time.tm_mon<0) //超过范围处理
{
s_time.tm_mon=12;
}
}
else if(key==44) //日数增加按钮
{
s_time.tm_mday++; //F5
if(s_time.tm_mday>getDays(s_time.tm_year,s_time.tm_mon)) //超过范围处理
{
s_time.tm_mday=1;
}
}
else if(key==45) //日数减小按钮
{
s_time.tm_mday--; //F6
if(s_time.tm_mday<0) //超过范围处理
{
s_time.tm_mday=getDays(s_time.tm_year,s_time.tm_mon);
}
}
GUI_SetFont(&GUI_Font16_1 );
GUI_DispDecAt(s_time.tm_year,4,13,4); //显示万年历的年数
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispString("年"); //显示年
GUI_SetFont(&GUI_Font16_1 );
GUI_DispDec(s_time.tm_mon,2); //显示万年历的月数
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispString("月"); //显示月
GUI_SetFont(&GUI_Font16_1 );
GUI_DispDec(s_time.tm_mday,2); //显示万年历的日数
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispString("日"); //显示日
GUI_DispString(" ");
GetChinaCalendarStr((u16)systmtime.tm_year,(u8)systmtime.tm_mon,(u8)systmtime.tm_mday,str); //阳历转阴历
//见下面子程序1
GUI_DispString(str); //显示阴历
GUI_SetColor(GUI_RED); //字体颜色
GUI_DispString("竹天笑万年历");
k=getWeekDay(s_time.tm_year,s_time.tm_mon,1); //得到某年某月的第一天的星期数 //见下面子程序2
GUI_GotoXY(18,69); .//位置设定
GUI_SetFont(&GUI_Font6x8);
//万年历日子显示
for(i=1;i<=40;i++) //第一行最少显示一个,第六行最多显示二个,i的上限只需要大于37即可
{
if(igetDays(s_time.tm_year,s_time.tm_mon)+k) //本月1号前和最后一天后的格子显示清零
{ //见子程序3
GUI_DispString(" "); //显示空格,用于清除之前数据
}
else
{
if((i+1)%7==0||i%7==0) //如果为星期六和星期天,字体设置为红色
{
GUI_SetColor(GUI_RED);
}
else //否则为黑色
{
GUI_SetColor(GUI_BLACK);
}
if(i==s_time.tm_mday+k) //如果该天为所选日期,设置背景为黄色
{
GUI_SetBkColor(GUI_YELLOW);
}
GUI_DispDecSpace(i-k,2); //显示日子
GUI_SetBkColor(GUI_WHITE); //恢复之前背景色
}
GUI_GotoXY(GUI_GetDispPosX()+17,GUI_GetDispPosY()); //光标移动到下一个格子
if(i%7==0)
{
GUI_GotoXY(18,GUI_GetDispPosY()+27); //7天换行显示
}
}
GUI_GotoXY(12,79); //光标移动到下一行首行
GUI_SetFont(&GUI_FontHZ_SimSun_11);
//显示为初几,若为节气则显示节气
for(i=1;i<=40;i++)
{
if(igetDays(s_time.tm_year,s_time.tm_mon)+k) //本月1号前和最后一天后的格子显示清零
{
GUI_DispString(" "); //显示两个中文空格,占两个中文字符
}
else
{
if(GetJieQiDay((u16)s_time.tm_year, (u8)s_time.tm_mon, i-k, str)==1) //如果为节气
{ //见子程序4
GUI_SetColor(GUI_MAGENTA); //设置字体为橙色
}
else
{
GUI_SetColor(GUI_BLUE); //正常显示蓝色
}
if(i==s_time.tm_mday+k) //如果该天为所选日期,设置背景为黄色
{
GUI_SetBkColor(GUI_YELLOW);
}
GUI_DispString(str); //显示阴历号
GUI_SetBkColor(GUI_WHITE); //恢复背景色
}
GUI_GotoXY(GUI_GetDispPosX()+7,GUI_GetDispPosY());
if(i%7==0)
{
GUI_GotoXY(12,GUI_GetDispPosY()+27);
}
}
WM_ExecIdle(); //刷新屏幕
#defineDEG2RAD (3.1415926f / 180)
GUI_POINT m_Hour[2],m_Sec[2],m_Min[2]; //时分秒两个端点
static GUI_POINT m_OldHour[2],m_OldMin[2],m_OldSec[2]; //时分秒之前的两个端点
m_Hour[0].x=-20*cos((systmtime.tm_hour*30+90)*DEG2RAD)+264; //时钟指针端点计算,每一时旋转30
m_Hour[0].y=-20*sin((systmtime.tm_hour*30+90)*DEG2RAD)+170; //度,逆时针旋转,当前时钟*30得旋
m_Hour[1].x=-2*cos((systmtime.tm_hour*30+270)*DEG2RAD)+264; //转的度数, 时钟另一端加上180度
m_Hour[1].y=-2*sin((systmtime.tm_hour*30+270)*DEG2RAD)+170; //将极坐标形式转换成直角坐标
GUI_SetColor(GUI_WHITE); //重绘上一次时钟指针覆盖的背景
GUI_DrawLine(m_OldHour[0].x,m_OldHour[0].y, m_OldHour[1].x,m_OldHour[1].y);
GUI_SetColor(GUI_RED); //画新的时钟指针
GUI_DrawLine(m_Hour[0].x,m_Hour[0].y, m_Hour[1].x,m_Hour[1].y);
m_Min[0].x=-30*cos((systmtime.tm_min*6+90)*DEG2RAD)+264; //分钟指针端点计算,每一分旋转6
m_Min[0].y=-30*sin((systmtime.tm_min*6+90)*DEG2RAD)+170; //度,逆时针旋转,当前分钟*6得旋
m_Min[1].x=-4*cos((systmtime.tm_min*6+270)*DEG2RAD)+264; //转的度数, 时钟另一端加上180度
m_Min[1].y=-4*sin((systmtime.tm_min*6+270)*DEG2RAD)+170; //将极坐标形式转换成直角坐标
GUI_SetColor(GUI_WHITE); //重绘上一次分钟指针覆盖的背景
GUI_DrawLine(m_OldMin[0].x,m_OldMin[0].y, m_OldMin[1].x,m_OldMin[1].y);
GUI_SetColor(GUI_BLUE); //画新的分钟指针
GUI_DrawLine(m_Min[0].x,m_Min[0].y, m_Min[1].x,m_Min[1].y);
m_Sec[0].x=-35*cos((systmtime.tm_sec*6+90)*DEG2RAD)+264; //分钟指针端点计算,每一秒旋转6
m_Sec[0].y=-35*sin((systmtime.tm_sec*6+90)*DEG2RAD)+170; //度,逆时针旋转,当前秒钟*6得旋
m_Sec[1].x=-8*cos((systmtime.tm_sec*6+270)*DEG2RAD)+264; //转的度数, 时钟另一端加上180度
m_Sec[1].y=-8*sin((systmtime.tm_sec*6+270)*DEG2RAD)+170; //将极坐标形式转换成直角坐标
GUI_SetColor(GUI_WHITE);
GUI_DrawLine(m_OldSec[0].x,m_OldSec[0].y, m_OldSec[1].x,m_OldSec[1].y);
GUI_SetColor(GUI_BLACK);
GUI_DrawLine(m_Sec[0].x,m_Sec[0].y, m_Sec[1].x,m_Sec[1].y);
for(i=0;i<2;i++)
{
m_OldHour=m_Hour; //保存当前时分秒指针
m_OldMin=m_Min;
m_OldSec=m_Sec;
}
/* 输出公历时间 */
GUI_SetFont(&GUI_Font16_1 );
GUI_DispDecAt(systmtime.tm_year, 240, 60,4); //显示当前年
GUI_DispString("-");
GUI_DispDec(systmtime.tm_mon,2); //显示当前月
GUI_DispString("-");
GUI_DispDec(systmtime.tm_mday,2); //显示当前日
GUI_DispDecAt(systmtime.tm_hour,240,76,2); //显示当前时
GUI_DispString(":");
GUI_DispDec(systmtime.tm_min,2); //显示当前分
GUI_DispString(":");
GUI_DispDec(systmtime.tm_sec,2); //显示当前秒
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispStringAt("星期",240,92); //显示当前星期
GUI_DispString(WEEK_STR[systmtime.tm_wday]);
}
子程序1
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称:GetChinaCalendarStr
// 功能描述:输入公历日期得到农历字符串
// 如:GetChinaCalendarStr(2007,02,06,str) 返回str="丙戌年腊月十九"
// 输 入: year 公历年
// month 公历月
// day 公历日
// str 储存农历日期字符串地址 15Byte
// 输 出: 无
/////////////////////////////////////////////////////////////////////////////////////////////////////////
void GetChinaCalendarStr(u16 year,u8 month,u8 day,u8 *str)
{
u8 NLyear[4];
u8 SEyear;
StrCopy(&str[0],(u8 *)"甲子年正月初一",15);
if(GetChinaCalendar(year,month,day,(u8 *)NLyear)==0) return; //GetChinaCalendar见子程序5,转化为阴历
GetSkyEarth(NLyear[0]*100+NLyear[1],&SEyear);
StrCopy(&str[0],(u8 *) sky[SEyear%10],2); // 甲
StrCopy(&str[2],(u8 *)earth[SEyear%12],2); // 子
if(NLyear[2]==1) StrCopy(&str[6],(u8 *)"正",2);
else StrCopy(&str[6],(u8 *)monthcode[NLyear[2]-1],2);
if(NLyear[3]>10) StrCopy(&str[10],(u8 *)nongliday[NLyear[3]/10],2);
else StrCopy(&str[10],(u8 *)"初",2);
StrCopy(&str[12],(u8 *)monthcode[(NLyear[3]-1)%10],2);
}
子程序2
u8 getWeekDay(u16 y, u8 m, u8 d) //得到指定年月日的星期数
{
if (m == 1) m = 13;
if (m == 2) m = 14;
u8 week = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7 + 1;
return week;
}
子程序3
/*
*判断是否闰年
*参数: y 整型, 接收年份值
*返回值: 整型, 只为0或1, 0代表假, 1代表真
*/
u8 isRunNian(u16 y)
{
return (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) ? 1 : 0;
}
/*
*计算某个月的天数
*参数: y 整型,接收年份值; m 整型,接收月份值;
*返回值: 整型, 是0, 28, 29, 30, 31之间的一个数
*注意: 返回值为0,表示你调用该函数时传递了不正确的年份值或月份值.
*/
u8 getDays(u16 y, u8 m)
{
u8 days = 0;
switch(m)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
days = 31; break;
case 4:
case 6:
case 9:
case 11:
days = 30; break;
case 2:
days = isRunNian(y) ? 29 : 28; break;
default:;
}
return days;
}
子程序4
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称:GetJieQiDay
// 功能描述:输入公历日期得到24节气字符串
// 是否为节气
// 输 入: year 公历年
// month 公历月
// day 公历日
// str 储存24节气字符串地址 5Byte
// 输 出: 1 成功
// 0 失败
/////////////////////////////////////////////////////////////////////////////////////////////////////////
u8 GetJieQiDay(u16 year,u8 month,u8 day,u8 *str)
{
u8 JQdate,JQ;
u8 NLyear[4];
StrCopy(&str[0],(u8 *)"初一",5);
if(GetJieQi(year,month,day,&JQdate)==0) return 0;
JQ = (month-1) *2 ; //获得节气顺序标号(0~23
if(day >= 15) JQ++; //判断是否是上半月
if(day==JQdate) //今天正是一个节气日
{
StrCopy(str,(u8 *)JieQiStr[JQ],5);
return 1;
} //今天不是一个节气日
else
{
GetChinaCalendar(year,month,day,(u8 *)NLyear);
if(NLyear[3]>10) StrCopy(&str[0],(u8 *)nongliday[NLyear[3]/10],2);
else StrCopy(&str[0],(u8 *)"初",2);
StrCopy(&str[2],(u8 *)monthcode[(NLyear[3]-1)%10],2);
return 0;
}
}
子程序5
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称:GetChinaCalendar
//功能描述:公农历转换(只允许1901-2099年)
// 输 入: year 公历年
// month 公历月
// day 公历日
// p 储存农历日期地址
// 输 出: 1 成功
// 0 失败
/////////////////////////////////////////////////////////////////////////////////////////////////////////
u8 GetChinaCalendar(u16 year,u8 month,u8 day,u8 *p)
{
u8 temp1,temp2,temp3,month_p,yearH,yearL;
u8 flag_y;
unsigned short temp4,table_addr;
yearH=year/100; yearL=year%100;//年份的高低两个字节
if((yearH!=19)&&(yearH!=20))return(0);//日期不在19xx ~ 20xx 范围内,则退出
// 定位数据表地址
if(yearH==20) table_addr=(yearL+100-1)*3;
else table_addr=(yearL-1)*3;
// 取当年春节所在的公历月份
temp1=year_code[table_addr+2]&0x60;
temp1>>=5;
// 取当年春节所在的公历日
temp2=year_code[table_addr+2]&31;
// 计算当年春年离当年元旦的天数,春节只会在公历1月或2月
if(temp1==1) temp3=temp2-1;
else temp3=temp2+31-1;
// 计算公历日离当年元旦的天数
if (month<10) temp4=day_code1[month-1]+day-1;
else temp4=day_code2[month-10]+day-1;
// 如果公历月大于2月并且该年的2月为闰月,天数加1
if ((month>2)&&(yearL%4==0)) temp4++;
// 判断公历日在春节前还是春节后
if (temp4>=temp3)
{
temp4-=temp3;
month=1;
month_p=1;
flag_y=0;
if(GetMoonDay(month_p,table_addr)==0) temp1=29; //小月29天
else temp1=30; //大小30天
// 从数据表中取该年的闰月月份,如为0则该年无闰月
temp2=year_code[table_addr]/16;
while(temp4>=temp1)
{
temp4-=temp1;
month_p++;
if(month==temp2)
{
flag_y=~flag_y;
if(flag_y==0)month++;
}
else month++;
if(GetMoonDay(month_p,table_addr)==0) temp1=29;
else temp1=30;
}
day=temp4+1;
}
// 公历日在春节前使用下面代码进行运算
else
{
temp3-=temp4;
if (yearL==0)
{
yearL=100-1;
yearH=19;
}
else yearL--;
table_addr-=3;
month=12;
temp2=year_code[table_addr]/16;
if (temp2==0) month_p=12;
else month_p=13;
flag_y=0;
if(GetMoonDay(month_p,table_addr)==0) temp1=29;
else temp1=30;
while(temp3>temp1)
{
temp3-=temp1;
month_p--;
if(flag_y==0) month--;
if(month==temp2) flag_y=~flag_y;
if(GetMoonDay(month_p,table_addr)==0) temp1=29;
else temp1=30;
}
day=temp1-temp3+1;
}
*p++=yearH;
*p++=yearL;
*p++=month;
*p=day;
return(1);
}
2、触摸屏任务:处理触摸屏按键任务,更改要查询的年月日。
static void AppTaskKbd (void *p_arg)
{
(void)p_arg;
TP_Init(); //触摸初始化
while(1)
{
OSTimeDlyHMSM(0,0,0,10);
GUI_TOUCH_Exec(); //触摸执行程序,详细请阅读ucgui教程
}
}
3、秒更新任务:秒更新任务中一直等待一个OS_EVENT(信号量),RTC闹钟一秒钟中断一次,闹钟中断程序中发送信号量,秒更新任务接到OS_EVENT(信号量)便执行一次,更新当前的时间。(如果没有不用uCOS中的信号量的话,直接设置一个全局变量也可,在中断函数中将其置1,秒更新任务中判断其为1执行,然后将其置零,等待下一次为1。
void Clock_Updata(void *p_arg)
{
INT8U err;
(void)p_arg;
while(1)
{
OSSemPend(Clock_SEM,0,&err); //等待 时钟更新信号量
RTC_Get(RTC_GetCounter());
}
}
void RTC_Get(uint32_t TimeVar)
{
//根据RTC时钟得到时钟的初值,注意,这个值是根据系统自动变化
to_tm(TimeVar, &systmtime); //使用野火RTC例程中的date.c
}
//时间结构体
struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
};
//RTC计数转化为年月日时分秒,储存在systmtime
void to_tm(u32 tim, struct rtc_time * tm)
{
register u32 i;
register long hms, day;
day = tim / SECDAY; //#define SECDAY 86400L 得到天数
hms = tim % SECDAY; //余下的为时分秒
/* Hours, minutes, seconds are easy */
tm->tm_hour = hms / 3600; //时
tm->tm_min = (hms % 3600) / 60; //分
tm->tm_sec = (hms % 3600) % 60; //秒
/* Number of years in days */ /*算出当前年份,起始的计数年份为1970年,总天数减去从1970开始递增的每年天数,当天数小于365/366时,得到当前的年份*/
for (i = STARTOFTIME; day >= days_in_year(i); i++) //#define STARTOFTIME 1970
{
day -= days_in_year(i); //#define days_in_year(a) (leapyear(a) ? 366 : 365)
} .//#define leapyear(year) ((year) % 4 == 0)
tm->tm_year = i;
/* Number of months in days left */ /*计算当前的月份*/
if (leapyear(tm->tm_year)) //如果为闰年,二月为29天
{
days_in_month(FEBRUARY) = 29; //#define FEBRUARY 2
}
for (i = 1; day >= days_in_month(i); i++) //剩下的总天数减去从1月开始递增的每月天数,当天数小于当前月天数时,得到当前的月份。
{ //static int month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
day -= days_in_month(i); //#define days_in_month(a) (month_days[(a) - 1])
}
days_in_month(FEBRUARY) = 28; //还原二月的天数为28天
tm->tm_mon = i;
/* Days are what is left over (+1) from all that. *//*计算当前日期*/
tm->tm_mday = day + 1; //剩下的天数加一为日。
/*
* Determine the day of week
*/
GregorianDay(tm);
}
/*得到星期几*/
void GregorianDay(struct rtc_time * tm)
{
int leapsToDate;
int lastYear;
int day;
int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
lastYear=tm->tm_year-1;
/*计算从公元元年到计数的前一年之中一共经历了多少个闰年*/
leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
/*如若计数的这一年为闰年,且计数的月份在2月之后,则日数加1,否则不加1*/
if((tm->tm_year%4==0) &&((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && (tm->tm_mon>2)) {
day=1;
} else {
day=0;
}
day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday; /*计算从公元元年元旦到计数日期一共有多少天*/
tm->tm_wday=day%7; //得到今天为星期几
}
附件:附件:UU_Clock_TOUCH.rar
1、日历功能。
2、数字和模拟时钟功能。
要点分析:
1、STM32自带了RTC时钟计数器,从0开始计数到232。每一个计数代表秒计数,每六十个计数代表分计数,以此类推。24(小时)*60(分钟)*60(秒钟)=86400代表一天的计数时间。假设当前计数为count,count/86400得到计数的天数,根据这个得到年月日。Count%86400得到时分秒。
2、一些根据1中得到的年月日时分秒,进行计算的程序有:阳历转阴历,闰年判断,节气判断,星期几计算,当前月有多少天等等。
3、模拟时钟的绘制:时钟指针运动算法、屏幕重绘方法、RTC消息、画笔/画刷等。指针运动算法和屏幕重绘方法是本程序主要难点所在。(以下参照百度文库之模拟时钟)
不论何种指针,每次转动均以π/30弧度(一秒的角度)为基本单位,且都以表盘中心为转动圆心。计算指针端点(x, y)的公式如下:
x =圆心x坐标 + 指针长度 * cos (指针方向角)
y =圆心y坐标 + 指针长度 * sin (指针方向角)
注意,指针长度是指自圆心至指针一个端点的长度(是整个指针的一部分),由于指针可能跨越圆心,因此一个指针需要计算两个端点。
由于屏幕的重绘1秒钟一次,如果采用全屏删除式重绘则闪烁十分明显,显示效果不佳。本程序采用非删除式重绘,假定指针将要移动一格,则先采用背景色(这里是白色)重绘原来指针以删除原来位置的指针,再采用指针的颜色在当前位置绘制指针(如果指针没有动,则直接绘制指针,此句在程序中被我删除,具体原因,为数据截断导致一些误差)。
另外,秒表为RTC一秒钟定时计数。
程序分析:
uCGUI+uCOS,一共三个任务:主处理任务、触摸屏任务、秒更新任务。
void App_UCGUI_TaskCreate (void)
{
CPU_INT08U os_err;
os_err = os_err;
Clock_SEM=OSSemCreate(1); //建立秒更新中断的信号量
//硬件平台初始化
BSP_Init();
//主处理任务---------------------------------------------------------
os_err = OSTaskCreateExt(AppTaskUserIF,
(void *)0,
(OS_STK *)&AppTaskUserIFStk[APP_TASK_USER_IF_STK_SIZE-1],
APP_TASK_USER_IF_PRIO,
APP_TASK_USER_IF_PRIO,
(OS_STK *)&AppTaskUserIFStk[0],
APP_TASK_USER_IF_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
//触摸屏任务---------------------------------------------------------
os_err = OSTaskCreateExt(AppTaskKbd,
(void *)0,
(OS_STK *)&AppTaskKbdStk[APP_TASK_KBD_STK_SIZE-1],
APP_TASK_KBD_PRIO,
APP_TASK_KBD_PRIO,
(OS_STK *)&AppTaskKbdStk[0],
APP_TASK_KBD_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
//秒更新任务
os_err = OSTaskCreateExt(Clock_Updata,
(void *)0,
(OS_STK *)&Clock_Updata_Stk[Clock_Updata_STK_SIZE-1],
Clock_Updata_PRIO,
Clock_Updata_PRIO,
(OS_STK *)&Clock_Updata_Stk[0],
Clock_Updata_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
}
万年历中的时间用的是STM32自带的RTC实时时钟。
1、主处理任务:界面背景初始化,并根据当前时间,画出图1的数据。
static void AppTaskUserIF (void *p_arg)
{
(void)p_arg;
INT8U err;
//界面初始化
GUI_Init(); //ucgui 初始化
_ExecCalibration(); /* 触摸屏校准 */
GUI_SetBkColor(GUI_WHITE); //设置背景色
GUI_SetColor(GUI_GRAY); //设置前景色
GUI_Clear(); //清屏
Lcd_show_bmp(0, 0,"/RTC.bmp"); //显示万年历背景
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispStringAt("一",15,47); //显示星期一
GUI_DispStringAt("二",44,47); //显示星期二
GUI_DispStringAt("三",73,47); //显示星期三
GUI_DispStringAt("四",102,47); //显示星期四
GUI_DispStringAt("五",131,47); //显示星期五
GUI_SetColor(GUI_RED); //用红字显示周末
GUI_DispStringAt("六",160,47); //显示星期六
GUI_DispStringAt("日",189,47); //显示星期日
to_tm(RTC_GetCounter(), &s_time); //根据RTC时钟得到万年历时间的初值,注意,这个值是根据用户查询万年历变化
GUI_SetFont(&GUI_Font16_1 ); //设置英文字体
GUI_DispDecAt(s_time.tm_year,4,13,4);//显示万年历的年份
GUI_SetFont(&GUI_FontHZ_SimSun_16); //设置中文字体
GUI_DispString("年"); //显示年
GUI_SetFont(&GUI_Font16_1 ); //设置英文字体
GUI_DispDec(s_time.tm_mon,2); //显示万年历的月份
GUI_SetFont(&GUI_FontHZ_SimSun_16); //设置中文字体
GUI_DispString("月"); //显示月
GUI_SetFont(&GUI_Font16_1 ); //设置英文字体
GUI_DispDec(s_time.tm_mday,2); //显示万年历的日子
GUI_SetFont(&GUI_FontHZ_SimSun_16);//设置中文字体
GUI_DispString("日"); //显示日
//画模拟时钟界面
u16 index,x,y;
GUI_SetPenSize(1);
GUI_SetColor(GUI_RED);
GUI_DrawCircle(264,170, 45); //画时钟最外层的圆,
for( index = 0; index < 60; index++ )//画时钟的刻度
{
x = -(40* cos(( index * 6 ) * DEG2RAD)) + 264;
y = -(40* sin(( index * 6 ) * DEG2RAD)) + 170;
if( index % 5 == 0 )
{
GUI_SetPenSize(4); //指示为小时的刻度用粗点画
}
else
{
GUI_SetPenSize(2); //其它刻度用西点画
}
GUI_DrawPoint( x, y );
}
while(1)
{
RTC_Show(RTC_GetCounter()); //主任务执行程序,接下来分析
OSTimeDlyHMSM(0,0,0,10);
}
}
//主任务执行程序
void RTC_Show(uint32_t TimeVar)
{
u8 str[15]; // 字符串暂存
u8 i,k;
static flag=0;
#if (GUI_SUPPORT_CURSOR|GUI_SUPPORT_TOUCH)
GUI_CURSOR_Show(); //鼠标显示
#endif
WM_SetCreateFlags(WM_CF_MEMDEV); /* Automatically use memory devices on all windows */
if(flag==0) //初始化按键,只执行一次。
{
//建立按键F1-F5
_ahButton[0] =BUTTON_Create(5, 0, 32,13, GUI_KEY_F1 , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[1]=BUTTON_Create(5, 29, 32,13, GUI_KEY_F2 , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[2]=BUTTON_Create(53, 0, 16,13, GUI_KEY_F3 , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[3]=BUTTON_Create(53,29,16,13, GUI_KEY_F4 , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[4]=BUTTON_Create(85, 0, 16,13, GUI_KEY_F5, WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
_ahButton[5]=BUTTON_Create(85,29, 16,13, GUI_KEY_F6, WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV);
//按键字体设置
BUTTON_SetFont(_ahButton[0],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[1],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[2],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[3],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[4],&GUI_Font6x8);//GUI_Font16_ASCII
BUTTON_SetFont(_ahButton[5],&GUI_Font6x8);//GUI_Font16_ASCII
//按键背景色设置
BUTTON_SetBkColor(_ahButton[0],0,GUI_DARKMAGENTA); //按键背景颜色
BUTTON_SetBkColor(_ahButton[1],0,GUI_DARKMAGENTA);
BUTTON_SetBkColor(_ahButton[2],0,GUI_DARKMAGENTA);
BUTTON_SetBkColor(_ahButton[3],0,GUI_DARKMAGENTA);
BUTTON_SetBkColor(_ahButton[4],0,GUI_DARKMAGENTA);
BUTTON_SetBkColor(_ahButton[5],0,GUI_DARKMAGENTA);
//按键前景色设置
BUTTON_SetTextColor(_ahButton[0],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[1],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[2],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[3],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[4],0,GUI_WHITE);
BUTTON_SetTextColor(_ahButton[5],0,GUI_WHITE);
//按键显示字符
BUTTON_SetText(_ahButton[0], "+");
BUTTON_SetText(_ahButton[1], "-");
BUTTON_SetText(_ahButton[2], "+");
BUTTON_SetText(_ahButton[3], "-");
BUTTON_SetText(_ahButton[4], "+");
BUTTON_SetText(_ahButton[5], "-");
flag=1;
}
u8 key = GUI_GetKey(); //实时获得触摸按键的值
if(key==40) //年数增加按钮
{
s_time.tm_year++; //F1
if(s_time.tm_year>2099) //超过范围处理
s_time.tm_year=1970;
}
else if(key==41) //年数减小按钮
{
s_time.tm_year--; //F2
if(s_time.tm_year<1900) //超过范围处理
s_time.tm_year=2099;
}
else if(key==42) //月数增加按钮
{
s_time.tm_mon++; //F3
if(s_time.tm_mon>12) //超过范围处理
{
s_time.tm_mon=1;
}
}
else if(key==43) //月数减小按钮
{
s_time.tm_mon--; //F4
if(s_time.tm_mon<0) //超过范围处理
{
s_time.tm_mon=12;
}
}
else if(key==44) //日数增加按钮
{
s_time.tm_mday++; //F5
if(s_time.tm_mday>getDays(s_time.tm_year,s_time.tm_mon)) //超过范围处理
{
s_time.tm_mday=1;
}
}
else if(key==45) //日数减小按钮
{
s_time.tm_mday--; //F6
if(s_time.tm_mday<0) //超过范围处理
{
s_time.tm_mday=getDays(s_time.tm_year,s_time.tm_mon);
}
}
GUI_SetFont(&GUI_Font16_1 );
GUI_DispDecAt(s_time.tm_year,4,13,4); //显示万年历的年数
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispString("年"); //显示年
GUI_SetFont(&GUI_Font16_1 );
GUI_DispDec(s_time.tm_mon,2); //显示万年历的月数
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispString("月"); //显示月
GUI_SetFont(&GUI_Font16_1 );
GUI_DispDec(s_time.tm_mday,2); //显示万年历的日数
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispString("日"); //显示日
GUI_DispString(" ");
GetChinaCalendarStr((u16)systmtime.tm_year,(u8)systmtime.tm_mon,(u8)systmtime.tm_mday,str); //阳历转阴历
//见下面子程序1
GUI_DispString(str); //显示阴历
GUI_SetColor(GUI_RED); //字体颜色
GUI_DispString("竹天笑万年历");
k=getWeekDay(s_time.tm_year,s_time.tm_mon,1); //得到某年某月的第一天的星期数 //见下面子程序2
GUI_GotoXY(18,69); .//位置设定
GUI_SetFont(&GUI_Font6x8);
//万年历日子显示
for(i=1;i<=40;i++) //第一行最少显示一个,第六行最多显示二个,i的上限只需要大于37即可
{
if(i
{ //见子程序3
GUI_DispString(" "); //显示空格,用于清除之前数据
}
else
{
if((i+1)%7==0||i%7==0) //如果为星期六和星期天,字体设置为红色
{
GUI_SetColor(GUI_RED);
}
else //否则为黑色
{
GUI_SetColor(GUI_BLACK);
}
if(i==s_time.tm_mday+k) //如果该天为所选日期,设置背景为黄色
{
GUI_SetBkColor(GUI_YELLOW);
}
GUI_DispDecSpace(i-k,2); //显示日子
GUI_SetBkColor(GUI_WHITE); //恢复之前背景色
}
GUI_GotoXY(GUI_GetDispPosX()+17,GUI_GetDispPosY()); //光标移动到下一个格子
if(i%7==0)
{
GUI_GotoXY(18,GUI_GetDispPosY()+27); //7天换行显示
}
}
GUI_GotoXY(12,79); //光标移动到下一行首行
GUI_SetFont(&GUI_FontHZ_SimSun_11);
//显示为初几,若为节气则显示节气
for(i=1;i<=40;i++)
{
if(i
{
GUI_DispString(" "); //显示两个中文空格,占两个中文字符
}
else
{
if(GetJieQiDay((u16)s_time.tm_year, (u8)s_time.tm_mon, i-k, str)==1) //如果为节气
{ //见子程序4
GUI_SetColor(GUI_MAGENTA); //设置字体为橙色
}
else
{
GUI_SetColor(GUI_BLUE); //正常显示蓝色
}
if(i==s_time.tm_mday+k) //如果该天为所选日期,设置背景为黄色
{
GUI_SetBkColor(GUI_YELLOW);
}
GUI_DispString(str); //显示阴历号
GUI_SetBkColor(GUI_WHITE); //恢复背景色
}
GUI_GotoXY(GUI_GetDispPosX()+7,GUI_GetDispPosY());
if(i%7==0)
{
GUI_GotoXY(12,GUI_GetDispPosY()+27);
}
}
WM_ExecIdle(); //刷新屏幕
#defineDEG2RAD (3.1415926f / 180)
GUI_POINT m_Hour[2],m_Sec[2],m_Min[2]; //时分秒两个端点
static GUI_POINT m_OldHour[2],m_OldMin[2],m_OldSec[2]; //时分秒之前的两个端点
m_Hour[0].x=-20*cos((systmtime.tm_hour*30+90)*DEG2RAD)+264; //时钟指针端点计算,每一时旋转30
m_Hour[0].y=-20*sin((systmtime.tm_hour*30+90)*DEG2RAD)+170; //度,逆时针旋转,当前时钟*30得旋
m_Hour[1].x=-2*cos((systmtime.tm_hour*30+270)*DEG2RAD)+264; //转的度数, 时钟另一端加上180度
m_Hour[1].y=-2*sin((systmtime.tm_hour*30+270)*DEG2RAD)+170; //将极坐标形式转换成直角坐标
GUI_SetColor(GUI_WHITE); //重绘上一次时钟指针覆盖的背景
GUI_DrawLine(m_OldHour[0].x,m_OldHour[0].y, m_OldHour[1].x,m_OldHour[1].y);
GUI_SetColor(GUI_RED); //画新的时钟指针
GUI_DrawLine(m_Hour[0].x,m_Hour[0].y, m_Hour[1].x,m_Hour[1].y);
m_Min[0].x=-30*cos((systmtime.tm_min*6+90)*DEG2RAD)+264; //分钟指针端点计算,每一分旋转6
m_Min[0].y=-30*sin((systmtime.tm_min*6+90)*DEG2RAD)+170; //度,逆时针旋转,当前分钟*6得旋
m_Min[1].x=-4*cos((systmtime.tm_min*6+270)*DEG2RAD)+264; //转的度数, 时钟另一端加上180度
m_Min[1].y=-4*sin((systmtime.tm_min*6+270)*DEG2RAD)+170; //将极坐标形式转换成直角坐标
GUI_SetColor(GUI_WHITE); //重绘上一次分钟指针覆盖的背景
GUI_DrawLine(m_OldMin[0].x,m_OldMin[0].y, m_OldMin[1].x,m_OldMin[1].y);
GUI_SetColor(GUI_BLUE); //画新的分钟指针
GUI_DrawLine(m_Min[0].x,m_Min[0].y, m_Min[1].x,m_Min[1].y);
m_Sec[0].x=-35*cos((systmtime.tm_sec*6+90)*DEG2RAD)+264; //分钟指针端点计算,每一秒旋转6
m_Sec[0].y=-35*sin((systmtime.tm_sec*6+90)*DEG2RAD)+170; //度,逆时针旋转,当前秒钟*6得旋
m_Sec[1].x=-8*cos((systmtime.tm_sec*6+270)*DEG2RAD)+264; //转的度数, 时钟另一端加上180度
m_Sec[1].y=-8*sin((systmtime.tm_sec*6+270)*DEG2RAD)+170; //将极坐标形式转换成直角坐标
GUI_SetColor(GUI_WHITE);
GUI_DrawLine(m_OldSec[0].x,m_OldSec[0].y, m_OldSec[1].x,m_OldSec[1].y);
GUI_SetColor(GUI_BLACK);
GUI_DrawLine(m_Sec[0].x,m_Sec[0].y, m_Sec[1].x,m_Sec[1].y);
for(i=0;i<2;i++)
{
m_OldHour=m_Hour; //保存当前时分秒指针
m_OldMin=m_Min;
m_OldSec=m_Sec;
}
/* 输出公历时间 */
GUI_SetFont(&GUI_Font16_1 );
GUI_DispDecAt(systmtime.tm_year, 240, 60,4); //显示当前年
GUI_DispString("-");
GUI_DispDec(systmtime.tm_mon,2); //显示当前月
GUI_DispString("-");
GUI_DispDec(systmtime.tm_mday,2); //显示当前日
GUI_DispDecAt(systmtime.tm_hour,240,76,2); //显示当前时
GUI_DispString(":");
GUI_DispDec(systmtime.tm_min,2); //显示当前分
GUI_DispString(":");
GUI_DispDec(systmtime.tm_sec,2); //显示当前秒
GUI_SetFont(&GUI_FontHZ_SimSun_16);
GUI_DispStringAt("星期",240,92); //显示当前星期
GUI_DispString(WEEK_STR[systmtime.tm_wday]);
}
子程序1
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称:GetChinaCalendarStr
// 功能描述:输入公历日期得到农历字符串
// 如:GetChinaCalendarStr(2007,02,06,str) 返回str="丙戌年腊月十九"
// 输 入: year 公历年
// month 公历月
// day 公历日
// str 储存农历日期字符串地址 15Byte
// 输 出: 无
/////////////////////////////////////////////////////////////////////////////////////////////////////////
void GetChinaCalendarStr(u16 year,u8 month,u8 day,u8 *str)
{
u8 NLyear[4];
u8 SEyear;
StrCopy(&str[0],(u8 *)"甲子年正月初一",15);
if(GetChinaCalendar(year,month,day,(u8 *)NLyear)==0) return; //GetChinaCalendar见子程序5,转化为阴历
GetSkyEarth(NLyear[0]*100+NLyear[1],&SEyear);
StrCopy(&str[0],(u8 *) sky[SEyear%10],2); // 甲
StrCopy(&str[2],(u8 *)earth[SEyear%12],2); // 子
if(NLyear[2]==1) StrCopy(&str[6],(u8 *)"正",2);
else StrCopy(&str[6],(u8 *)monthcode[NLyear[2]-1],2);
if(NLyear[3]>10) StrCopy(&str[10],(u8 *)nongliday[NLyear[3]/10],2);
else StrCopy(&str[10],(u8 *)"初",2);
StrCopy(&str[12],(u8 *)monthcode[(NLyear[3]-1)%10],2);
}
子程序2
u8 getWeekDay(u16 y, u8 m, u8 d) //得到指定年月日的星期数
{
if (m == 1) m = 13;
if (m == 2) m = 14;
u8 week = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7 + 1;
return week;
}
子程序3
/*
*判断是否闰年
*参数: y 整型, 接收年份值
*返回值: 整型, 只为0或1, 0代表假, 1代表真
*/
u8 isRunNian(u16 y)
{
return (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) ? 1 : 0;
}
/*
*计算某个月的天数
*参数: y 整型,接收年份值; m 整型,接收月份值;
*返回值: 整型, 是0, 28, 29, 30, 31之间的一个数
*注意: 返回值为0,表示你调用该函数时传递了不正确的年份值或月份值.
*/
u8 getDays(u16 y, u8 m)
{
u8 days = 0;
switch(m)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
days = 31; break;
case 4:
case 6:
case 9:
case 11:
days = 30; break;
case 2:
days = isRunNian(y) ? 29 : 28; break;
default:;
}
return days;
}
子程序4
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称:GetJieQiDay
// 功能描述:输入公历日期得到24节气字符串
// 是否为节气
// 输 入: year 公历年
// month 公历月
// day 公历日
// str 储存24节气字符串地址 5Byte
// 输 出: 1 成功
// 0 失败
/////////////////////////////////////////////////////////////////////////////////////////////////////////
u8 GetJieQiDay(u16 year,u8 month,u8 day,u8 *str)
{
u8 JQdate,JQ;
u8 NLyear[4];
StrCopy(&str[0],(u8 *)"初一",5);
if(GetJieQi(year,month,day,&JQdate)==0) return 0;
JQ = (month-1) *2 ; //获得节气顺序标号(0~23
if(day >= 15) JQ++; //判断是否是上半月
if(day==JQdate) //今天正是一个节气日
{
StrCopy(str,(u8 *)JieQiStr[JQ],5);
return 1;
} //今天不是一个节气日
else
{
GetChinaCalendar(year,month,day,(u8 *)NLyear);
if(NLyear[3]>10) StrCopy(&str[0],(u8 *)nongliday[NLyear[3]/10],2);
else StrCopy(&str[0],(u8 *)"初",2);
StrCopy(&str[2],(u8 *)monthcode[(NLyear[3]-1)%10],2);
return 0;
}
}
子程序5
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数名称:GetChinaCalendar
//功能描述:公农历转换(只允许1901-2099年)
// 输 入: year 公历年
// month 公历月
// day 公历日
// p 储存农历日期地址
// 输 出: 1 成功
// 0 失败
/////////////////////////////////////////////////////////////////////////////////////////////////////////
u8 GetChinaCalendar(u16 year,u8 month,u8 day,u8 *p)
{
u8 temp1,temp2,temp3,month_p,yearH,yearL;
u8 flag_y;
unsigned short temp4,table_addr;
yearH=year/100; yearL=year%100;//年份的高低两个字节
if((yearH!=19)&&(yearH!=20))return(0);//日期不在19xx ~ 20xx 范围内,则退出
// 定位数据表地址
if(yearH==20) table_addr=(yearL+100-1)*3;
else table_addr=(yearL-1)*3;
// 取当年春节所在的公历月份
temp1=year_code[table_addr+2]&0x60;
temp1>>=5;
// 取当年春节所在的公历日
temp2=year_code[table_addr+2]&31;
// 计算当年春年离当年元旦的天数,春节只会在公历1月或2月
if(temp1==1) temp3=temp2-1;
else temp3=temp2+31-1;
// 计算公历日离当年元旦的天数
if (month<10) temp4=day_code1[month-1]+day-1;
else temp4=day_code2[month-10]+day-1;
// 如果公历月大于2月并且该年的2月为闰月,天数加1
if ((month>2)&&(yearL%4==0)) temp4++;
// 判断公历日在春节前还是春节后
if (temp4>=temp3)
{
temp4-=temp3;
month=1;
month_p=1;
flag_y=0;
if(GetMoonDay(month_p,table_addr)==0) temp1=29; //小月29天
else temp1=30; //大小30天
// 从数据表中取该年的闰月月份,如为0则该年无闰月
temp2=year_code[table_addr]/16;
while(temp4>=temp1)
{
temp4-=temp1;
month_p++;
if(month==temp2)
{
flag_y=~flag_y;
if(flag_y==0)month++;
}
else month++;
if(GetMoonDay(month_p,table_addr)==0) temp1=29;
else temp1=30;
}
day=temp4+1;
}
// 公历日在春节前使用下面代码进行运算
else
{
temp3-=temp4;
if (yearL==0)
{
yearL=100-1;
yearH=19;
}
else yearL--;
table_addr-=3;
month=12;
temp2=year_code[table_addr]/16;
if (temp2==0) month_p=12;
else month_p=13;
flag_y=0;
if(GetMoonDay(month_p,table_addr)==0) temp1=29;
else temp1=30;
while(temp3>temp1)
{
temp3-=temp1;
month_p--;
if(flag_y==0) month--;
if(month==temp2) flag_y=~flag_y;
if(GetMoonDay(month_p,table_addr)==0) temp1=29;
else temp1=30;
}
day=temp1-temp3+1;
}
*p++=yearH;
*p++=yearL;
*p++=month;
*p=day;
return(1);
}
2、触摸屏任务:处理触摸屏按键任务,更改要查询的年月日。
static void AppTaskKbd (void *p_arg)
{
(void)p_arg;
TP_Init(); //触摸初始化
while(1)
{
OSTimeDlyHMSM(0,0,0,10);
GUI_TOUCH_Exec(); //触摸执行程序,详细请阅读ucgui教程
}
}
3、秒更新任务:秒更新任务中一直等待一个OS_EVENT(信号量),RTC闹钟一秒钟中断一次,闹钟中断程序中发送信号量,秒更新任务接到OS_EVENT(信号量)便执行一次,更新当前的时间。(如果没有不用uCOS中的信号量的话,直接设置一个全局变量也可,在中断函数中将其置1,秒更新任务中判断其为1执行,然后将其置零,等待下一次为1。
void Clock_Updata(void *p_arg)
{
INT8U err;
(void)p_arg;
while(1)
{
OSSemPend(Clock_SEM,0,&err); //等待 时钟更新信号量
RTC_Get(RTC_GetCounter());
}
}
void RTC_Get(uint32_t TimeVar)
{
//根据RTC时钟得到时钟的初值,注意,这个值是根据系统自动变化
to_tm(TimeVar, &systmtime); //使用野火RTC例程中的date.c
}
//时间结构体
struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
};
//RTC计数转化为年月日时分秒,储存在systmtime
void to_tm(u32 tim, struct rtc_time * tm)
{
register u32 i;
register long hms, day;
day = tim / SECDAY; //#define SECDAY 86400L 得到天数
hms = tim % SECDAY; //余下的为时分秒
/* Hours, minutes, seconds are easy */
tm->tm_hour = hms / 3600; //时
tm->tm_min = (hms % 3600) / 60; //分
tm->tm_sec = (hms % 3600) % 60; //秒
/* Number of years in days */ /*算出当前年份,起始的计数年份为1970年,总天数减去从1970开始递增的每年天数,当天数小于365/366时,得到当前的年份*/
for (i = STARTOFTIME; day >= days_in_year(i); i++) //#define STARTOFTIME 1970
{
day -= days_in_year(i); //#define days_in_year(a) (leapyear(a) ? 366 : 365)
} .//#define leapyear(year) ((year) % 4 == 0)
tm->tm_year = i;
/* Number of months in days left */ /*计算当前的月份*/
if (leapyear(tm->tm_year)) //如果为闰年,二月为29天
{
days_in_month(FEBRUARY) = 29; //#define FEBRUARY 2
}
for (i = 1; day >= days_in_month(i); i++) //剩下的总天数减去从1月开始递增的每月天数,当天数小于当前月天数时,得到当前的月份。
{ //static int month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
day -= days_in_month(i); //#define days_in_month(a) (month_days[(a) - 1])
}
days_in_month(FEBRUARY) = 28; //还原二月的天数为28天
tm->tm_mon = i;
/* Days are what is left over (+1) from all that. *//*计算当前日期*/
tm->tm_mday = day + 1; //剩下的天数加一为日。
/*
* Determine the day of week
*/
GregorianDay(tm);
}
/*得到星期几*/
void GregorianDay(struct rtc_time * tm)
{
int leapsToDate;
int lastYear;
int day;
int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
lastYear=tm->tm_year-1;
/*计算从公元元年到计数的前一年之中一共经历了多少个闰年*/
leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
/*如若计数的这一年为闰年,且计数的月份在2月之后,则日数加1,否则不加1*/
if((tm->tm_year%4==0) &&((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && (tm->tm_mon>2)) {
day=1;
} else {
day=0;
}
day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday; /*计算从公元元年元旦到计数日期一共有多少天*/
tm->tm_wday=day%7; //得到今天为星期几
}
附件:附件:UU_Clock_TOUCH.rar
文件: UU_Clock_TOUCH.rar
下载
我来回答
回答0个
时间排序
认可量排序
暂无数据
或将文件直接拖到这里
悬赏:
E币
网盘
* 网盘链接:
* 提取码:
悬赏:
E币
Markdown 语法
- 加粗**内容**
- 斜体*内容*
- 删除线~~内容~~
- 引用> 引用内容
- 代码`代码`
- 代码块```编程语言↵代码```
- 链接[链接标题](url)
- 无序列表- 内容
- 有序列表1. 内容
- 缩进内容
- 图片![alt](url)
相关问答
-
2008-08-24 12:38:49
-
2015-05-27 11:40:02
-
2013-12-10 20:07:17
-
2014-04-08 23:35:47
-
2013-08-25 12:41:23
-
2016-10-17 23:41:01
-
2013-08-27 13:35:24
-
02013-08-24 22:15:18
-
2013-08-26 23:30:31
-
2013-11-18 11:08:00
-
2013-11-23 08:55:19
-
2013-08-24 22:48:10
-
2018-11-14 13:52:21
-
2018-10-25 09:40:45
-
2013-08-25 00:35:00
-
2013-11-20 20:52:00
-
2013-11-20 13:51:20
-
2013-08-24 22:21:18
-
2013-08-26 23:28:54
无更多相似问答 去提问
点击登录
-- 积分
-- E币
提问
—
收益
—
被采纳
—
我要提问
切换马甲
上一页
下一页
悬赏问答
-
5Hi3516CV610 如何使用SD卡升级固件
-
5cat /dev/logmpp 报错 <3>[ vi] [func]:vi_send_frame_node [line]:99 [info]:vi pic queue is full!
-
50如何获取vpss chn的图像修改后发送至vo
-
5FPGA通过Bt1120传YUV422数据过来,vi接收不到数据——3516dv500
-
50SS928 运行PQtools 拼接 推到设备里有一半画面会异常
-
53536AV100的sample_vdec输出到CVBS显示
-
10海思板子mpp怎么在vi阶段改变视频数据尺寸
-
10HI3559AV100 多摄像头同步模式
-
9海思ss928单路摄像头vio中加入opencv处理并显示
-
10EB-RV1126-BC-191板子运行自己编码的程序
举报反馈
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明
提醒
你的问题还没有最佳答案,是否结题,结题后将扣除20%的悬赏金
取消
确认
提醒
你的问题还没有最佳答案,是否结题,结题后将根据回答情况扣除相应悬赏金(1回答=1E币)
取消
确认