您的位置:首页 > 编程语言 > C语言/C++

[嵌入式开发模块]系统时间SYSTEMTIME 轻量实现

2017-08-02 23:30 295 查看
美丽的暑假被叫回来干活。要我改个3年前的嵌入式程序,马上项目要用。一看程序,亲娘滴,全部业务代码全部写在main.c文件中,大段大段的复制黏贴微改,任务间通信全用的轮询标志位╮(╯▽╰)╭。有强迫症的我用一周把整个程序重构了一遍,各模块分到不同的文件中,各种封装,同步的方式全改成异步。最后一算,扣掉操作系统的代码,代码量(CODE+DATA)减少了一半,这还是实现了许多原来没实现的功能后的,比原来更精简了操作系统的部分就不算它了。七天重构了个前人写了两个多月的程序,差点没把我累死。

因为之前做毕设的时候没时间好好封装系统时间模块,这次就顺带封装了一下,只实现了最基本的功能,一般的使用是够了。

由于嵌入式开发中没有提供系统时间,需要自己实现(不知道其他嵌入式操作系统有没有,反正uCOS是没有,它只是个内核),所以自己简单封装了个SYSTEMTIME模块。需要的自取。下面贴出代码:

/*
*********************************************************************************************************
*
*
*                                     Systemtime SUPPORT PACKAGE
*                                              uCos-II
*                                        uCos-II系统时间支持包
*
* File : time_uCos.h
* By   : Lin Shijun
* Date : 2018/03/29
* Version: V1.2
* Note : 1. If you think struct system_time too big, you can modify all field,except for mYear and mMSecond,
*           to INT8U.
*        2. you should call Systemtime_Tick_Signal() every 1ms in your app or operate system.
*              for example,make your OS tick once per ms and modify your app_hooks.c file:
*
*                 ……
*                 #include "time_uCos.h"
*                 ……
*                 void  App_TimeTickHook (void)
*                  {
*                   #if OS_CRITICAL_METHOD == 3
*                      OS_CPU_SR  cpu_sr = 0;
*                   #endif
*                      ……
*                      OS_ENTER_CRITICAL();
*                      Systemtime_Tick_Signal();
*                      OS_EXIT_CRITICAL();
*                   }
*                 ……
*
*        3. this version is for banked memory model.If your are using other memory model,
*            some modification may need.
*
* History: 2017/05/04 v1.0 the original version of the systemtime module.
*          2017/10/16 v1.1 modify some pragma statement to gain more efficiency.
*          2018/03/29 v1.2 some modification to #pragma
*********************************************************************************************************
*/

#ifndef TIME_UCOS_H
#define TIME_UCOS_H

/*
*********************************************************************************************************
*                                       INCLUDES
*********************************************************************************************************
*/
#include <os_cpu.h>
//typedef unsigned short INT16U ;
//typedef unsigned char INT8U  ;
//typedef unsigned char BOOLEAN ;
/*
*********************************************************************************************************
*                                      CONFIGURE
*********************************************************************************************************
*/

// 启动时初始化的时间设定
#define  SYSTEM_DEFAULT_YEAR     2018
#define  SYSTEM_DEFAULT_MONTH    4
#define  SYSTEM_DEFAULT_DAY      1
#define  SYSTEM_DEFAULT_HOUR     0
#define  SYSTEM_DEFAULT_MINUTE   0
#define  SYSTEM_DEFAULT_SECOND   0
#define  SYSTEM_DEFAULT_MSECOND  0

// 是否使用对应事件
#define  SYSTEM_EVENT_ONMSECONDINC_EN FALSE
#define  SYSTEM_EVENT_ONSECONDINC_EN  FALSE
#define  SYSTEM_EVENT_ONMINUTEINC_EN  FALSE
#define  SYSTEM_EVENT_ONHOURINC_EN    FALSE
#define  SYSTEM_EVENT_ONDAYINC_EN     TRUE
#define  SYSTEM_EVENT_ONMONTHINC_EN   FALSE
#define  SYSTEM_EVENT_ONYEARINC_EN    FALSE

/*
*********************************************************************************************************
*                                      DATATYPE 数据类型
*********************************************************************************************************
*/

typedef struct system_time{
INT16U wYear;            // 年
INT16U wMonth;           // 月
INT16U wDay;             // 日
INT16U wHour;            // 时
INT16U wMinute;          // 分
INT16U wSecond;          // 秒
INT16U wMSecond;         // 毫秒
} SYSTEM_TIME;

typedef void (* SYSTEM_TIME_EVENT)(const SYSTEM_TIME *time);
/*
*********************************************************************************************************
*                                      CONSTANT
*********************************************************************************************************
*/
// 定义空时间,比较赋值
extern const SYSTEM_TIME* const TimeNull;

/*
*********************************************************************************************************
*                                  FUNCTION PROTOTYPES  函数原型
*********************************************************************************************************
*/
/********* 注意: 以下函数都不需考虑任务冲突之类的事情 **********/
#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED
// 获取当前系统时间,用输入参数返回
void SystemTime_get(SYSTEM_TIME *rst);
#pragma pop

// 设置当前系统时间,如设置失败(一般是因为日期无效),则返回FALSE,如成功则返回TRUE
BOOLEAN SystemTime_set(const SYSTEM_TIME *val);
// 验证时间是否有效,有效则返回TRUE
BOOLEAN SystemTime_Valid(const SYSTEM_TIME *val);
// 赋值为NULL;
void SystemTimeSetNull(SYSTEM_TIME *lval);

// 计算某年是否是闰年
#define isLeapYear(Year) (Year % 4 == 0 && Year % 100 != 0 || Year % 400 == 0)

/*
*********************************************************************************************************
*                                        EVENT 事件
*********************************************************************************************************
*/

// 要注意,各事件都是由调用Systemtime_Tick_Signal()的函数内调用的,一般是中断服务程序中
// 事件中的传入参数为本地系统时间,只读
// 用法:声明个SYSTEM_TIME_EVENT类型的函数,然后赋值给对应事件
extern SYSTEM_TIME_EVENT SystemTime_onMSecondInc;
extern SYSTEM_TIME_EVENT SystemTime_onSecondInc;
extern SYSTEM_TIME_EVENT SystemTime_onMinuteInc;
extern SYSTEM_TIME_EVENT SystemTime_onHourInc;
extern SYSTEM_TIME_EVENT SystemTime_onDayInc;
extern SYSTEM_TIME_EVENT SystemTime_onMonthInc;
extern SYSTEM_TIME_EVENT SystemTime_onYearInc;

/*
*********************************************************************************************************
*                                  FUNCTION PROTOTYPES  函数原型
*********************************************************************************************************
*/
// 对外接口,需要被外部定时调用来
ffc3
通知系统时间增加1ms
// 调用时要保证执行过程中不会发生中断(比如禁止中断嵌套并在中断中调用)
#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED
void Systemtime_Tick_Signal(void);
#pragma pop
/*
*********************************************************************************************************
*                                     ERROR CHECK     错误检查
*********************************************************************************************************
*/
#if (SYSTEM_DEFAULT_YEAR < 0)
#error "year must bigger than 0!"
#endif
#if (SYSTEM_DEFAULT_MONTH <= 0 || SYSTEM_DEFAULT_MONTH >= 13)
#error "month must between 1 and 12!"
#endif
#if (SYSTEM_DEFAULT_DAY <= 0 || SYSTEM_DEFAULT_DAY >= 32)
#error "day must between 1 and 31!"
#endif
#if (SYSTEM_DEFAULT_HOUR < 0 || SYSTEM_DEFAULT_HOUR >= 24)
#error "hour must between 0 and 23!"
#endif
#if (SYSTEM_DEFAULT_MINUTE < 0 || SYSTEM_DEFAULT_MINUTE >= 60)
#error "minute must between 0 and 59!"
#endif
#if (SYSTEM_DEFAULT_SECOND < 0 || SYSTEM_DEFAULT_SECOND >= 60)
#error "second must between 0 and 59!"
#endif
#if (SYSTEM_DEFAULT_MSECOND < 0 || SYSTEM_DEFAULT_MSECOND >= 1000)
#error "millsecond must between 0 and 999!"
#endif

#endif


/*
*********************************************************************************************************
*
*
*                                    Systemtime SUPPORT PACKAGE
*                                           uCos-II
*                                     uCos-II系统时间支持包
*
* File : time_uCos.c
* By   : Lin Shijun
* Date : 2018/03/29
* Version: V1.2
* Note : 1. If you think struct system_time too big, you can modify all field,except for mYear and mMSecond,
*           to INT8U.
*        2. you should call Systemtime_Tick_Signal() every 1ms in your app or operate system.
*              for example,make your OS tick once per ms and modify your app_hooks.c file:
*
*                 ……
*                 #include "time_uCos.h"
*                 ……
*                 void  App_TimeTickHook (void)
*                  {
*                   #if OS_CRITICAL_METHOD == 3
*                      OS_CPU_SR  cpu_sr = 0;
*                   #endif
*                      ……
*                      OS_ENTER_CRITICAL();
*                      Systemtime_Tick_Signal();
*                      OS_EXIT_CRITICAL();
*                   }
*                 ……
*
*        3. this version is for banked memory model.If your are using other memory model,
*            some modification may need.
*
* History: 2017/05/04 v1.0 the original version of the systemtime module.
*          2017/10/16 v1.1 modify some pragma statement to gain more efficiency.
*          2018/03/29 v1.2 some modification to #pragma
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                       INCLUDES
*********************************************************************************************************
*/

#include <stddef.h>
#include <string.h>
#include "time_uCos.h"

/*
*********************************************************************************************************
*                                      EVENT VARIABLE
*********************************************************************************************************
*/
#if (SYSTEM_EVENT_ONMSECONDINC_EN == TRUE)
SYSTEM_TIME_EVENT SystemTime_onMSecondInc = NULL;
#endif
#if (SYSTEM_EVENT_ONSECONDINC_EN == TRUE)
SYSTEM_TIME_EVENT SystemTime_onSecondInc = NULL;
#endif
#if (SYSTEM_EVENT_ONMINUTEINC_EN == TRUE)
SYSTEM_TIME_EVENT SystemTime_onMinuteInc = NULL;
#endif
#if (SYSTEM_EVENT_ONHOURINC_EN   == TRUE)
SYSTEM_TIME_EVENT SystemTime_onHourInc   = NULL;
#endif
#if (SYSTEM_EVENT_ONDAYINC_EN    == TRUE)
SYSTEM_TIME_EVENT SystemTime_onDayInc    = NULL;
#endif
#if (SYSTEM_EVENT_ONMONTHINC_EN  == TRUE)
SYSTEM_TIME_EVENT SystemTime_onMonthInc  = NULL;
#endif
#if (SYSTEM_EVENT_ONYEARINC_EN   == TRUE)
SYSTEM_TIME_EVENT SystemTime_onYearInc   = NULL;
#endif
/*
*********************************************************************************************************
*                                   LOCAL FUNCTION PROTOTYPE
*********************************************************************************************************
*/

// 内部使用,对本地系统时间各种单位的自增运算
#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED
static void _SystemTime_incMSecond(void);
static void _SystemTime_incSecond(void);
#pragma pop
static void _SystemTime_incMinute(void);
static void _SystemTime_incHour(void);
static void _SystemTime_incDay(void);
static void _SystemTime_incMonth(void);
static void _SystemTime_incYear(void);

/*
*********************************************************************************************************
*                                      LOCAL CONST
*********************************************************************************************************
*/
static const INT8U daytab[2][13] = {
{ 0,31,28,31,30,31,30,31,31,30,31,30,31 },
{ 0,31,29,31,30,31,30,31,31,30,31,30,31 }
};
static const SYSTEM_TIME time_Null = {
0,0,0,0,0,0,0
};
const SYSTEM_TIME* const TimeNull = &time_Null;

/*
*********************************************************************************************************
*                                      LOCAL VARIABLE
*********************************************************************************************************
*/
// 当前MCU的系统时间
static SYSTEM_TIME LocalTime ={
SYSTEM_DEFAULT_YEAR,
SYSTEM_DEFAULT_MONTH,
SYSTEM_DEFAULT_DAY,
SYSTEM_DEFAULT_HOUR,
SYSTEM_DEFAULT_MINUTE,
SYSTEM_DEFAULT_SECOND,
SYSTEM_DEFAULT_MSECOND
};
// 存储当前本地时间是否是闰年。用于加快计算,只在年份变更时重新计算
static INT8U leapYearNow = isLeapYear(SYSTEM_DEFAULT_YEAR);
// 重新计算现在是否是闰年
#define ReCalculateLeap()     leapYearNow = isLeapYear(LocalTime.wYear)

/*
*********************************************************************************************************
*                                     PUBLIC  FUNCTION
*********************************************************************************************************
*/
#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED
void SystemTime_get(SYSTEM_TIME *rst){
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
OS_CPU_SR  cpu_sr = 0;
#endif
OS_ENTER_CRITICAL();
*rst = LocalTime;
OS_EXIT_CRITICAL();
return;
}
#pragma pop

BOOLEAN SystemTime_set(const SYSTEM_TIME *val){
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
OS_CPU_SR  cpu_sr = 0;
#endif
if(SystemTime_Valid(val)){
OS_ENTER_CRITICAL();
LocalTime = *val;
OS_EXIT_CRITICAL();
return TRUE;
}
return FALSE;
}

BOOLEAN SystemTime_Valid(const SYSTEM_TIME *val){
INT8U leap;
if(val->wHour >= 24 || val->wMinute >= 60 || val->wSecond >= 60 || val->wMSecond >= 1000 ||
val->wMonth >= 13 || val->wMonth == 0 || val->wDay == 0)
return FALSE;
leap = isLeapYear(val->wYear);
if(val->wDay > daytab[leap][val->wMonth])
return FALSE;
return TRUE;
}
void SystemTimeSetNull(SYSTEM_TIME *lval){
memcpy(lval,TimeNull,sizeof(SYSTEM_TIME));
}

/*
************************************************************************************************************************
*                                      SIGNAL THAT IT'S TIME TO UPDATE THE SYSTEMTIME
*
* Description: This function is typically called by the ISR that occurs at the timer tick rate and is used to signal
*              the systemtime module to update.
*
* Arguments  : none
*
* Returns    :
************************************************************************************************************************
*/
#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED
void Systemtime_Tick_Signal(void)
{
_SystemTime_incMSecond();
}
#pragma pop
/*
*********************************************************************************************************
*                                      LOCAL FUNCTION
*********************************************************************************************************
*/
#pragma push
#pragma CODE_SEG __NEAR_SEG NON_BANKED
static void _SystemTime_incMSecond(void){
if(++LocalTime.wMSecond >= 1000){
LocalTime.wMSecond = 0;
_SystemTime_incSecond();
}
#if (SYSTEM_EVENT_ONMSECONDINC_EN == TRUE)
if(SystemTime_onMSecondInc != NULL)
SystemTime_onMSecondInc(&LocalTime);
#endif
}
static void _SystemTime_incSecond(void){
if(++LocalTime.wSecond >= 60){
LocalTime.wSecond = 0;
_SystemTime_incMinute();
}
#if (SYSTEM_EVENT_ONSECONDINC_EN == TRUE)
if(SystemTime_onSecondInc != NULL)
SystemTime_onSecondInc(&LocalTime);
#endif
}
#pragma pop

static void _SystemTime_incMinute(void){
if(++LocalTime.wMinute >= 60){
LocalTime.wMinute = 0;
_SystemTime_incHour();
}
#if (SYSTEM_EVENT_ONMINUTEINC_EN == TRUE)
if(SystemTime_onMinuteInc != NULL)
SystemTime_onMinuteInc(&LocalTime);
#endif
}
static void _SystemTime_incHour(void){
if(++LocalTime.wHour >= 24){
LocalTime.wHour = 0;
_SystemTime_incDay();
}
#if (SYSTEM_EVENT_ONHOURINC_EN == TRUE)
if(SystemTime_onHourInc != NULL)
SystemTime_onHourInc(&LocalTime);
#endif
}
static void _SystemTime_incDay(void){
if(++LocalTime.wDay > daytab[leapYearNow][LocalTime.wMonth]){
LocalTime.wDay = 1;
_SystemTime_incMonth();
}
#if (SYSTEM_EVENT_ONDAYINC_EN == TRUE)
if(SystemTime_onDayInc != NULL)
SystemTime_onDayInc(&LocalTime);
#endif
}
static void _SystemTime_incMonth(void){
if(++LocalTime.wMonth > 12){
LocalTime.wMonth = 1;
_SystemTime_incYear();
}
#if (SYSTEM_EVENT_ONMONTHINC_EN == TRUE)
if(SystemTime_onMonthInc != NULL)
SystemTime_onMonthInc(&LocalTime);
#endif
}
static void _SystemTime_incYear(void){
++LocalTime.wYear;
// 重计算当前是否是闰年
ReCalculateLeap();
#if (SYSTEM_EVENT_ONYEARINC_EN == TRUE)
if(SystemTime_onYearInc != NULL)
SystemTime_onYearInc(&LocalTime);
#endif
}


目前版本只是最简单的实现,提供时间检查,系统时间递增,赋\取,而且递增还是必须1ms的,等以后有需要了再添加其他功能。

如嫌时间的结构体占的空间太大,其实可以把年以外的都改成INT8U。

要记得在适当的地方调用void Systemtime_Tick_Signal(void);以驱动系统时间模块。如在uC/OS-II中,可以把OSTick间隔设为1ms然后Hook在App_TimeTickHook()上(app_hook.c中),像这样修改:

#if OS_TIME_TICK_HOOK_EN > 0
void  App_TimeTickHook (void)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
OS_CPU_SR  cpu_sr = 0;
#endif
#if (uC_PROBE_OS_PLUGIN > 0) && (OS_PROBE_HOOKS_EN > 0)
OSProbe_TickHook();
#endif
OS_ENTER_CRITICAL();
// 通知系统时间模块,当前时间+1ms
Systemtime_Tick_Signal();
OS_EXIT_CRITICAL();
}


当然,该使能的地方要记得使能。具体的就不细讲了。

使用提供的事件可以很方便的监听系统时间的变化,比如在这个项目中我需要每天重新初始化数据包编号,于是我就可以使能系统时间模块的SystemTime_onDayInc事件(SYSTEM_TIME_EVENT_ONDAYINC_EN设为TRUE)。在初始化时挂载事件:

// 挂载天数增加事件
SystemTime_onDayInc = &onDayInc;


然后在发生事件时的回调函数中初始化包编号:

static void onDayInc(const SYSTEM_TIME *time){
PacketNum = 0;
}


然后调用接口获取及设置系统时间什么的都是C语言基本的东西,就不细讲了。

最后,既然读者有需要这个函数,那也有可能需要这个小工具。

【小工具】日期与一年中第几天的转换程序

OK就到这里吧,以后再慢慢改进。

更改历史:

2018/03/29 更新到v1.2
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息