读源码写作,尊重原创;

本博文根据蓝牙4.0, 协议族版本是1.3.2
本博文分两大块。一部分是时间管理,另一部分是软件定时器管理。


OSAL的时钟实现在OSAL_CockBLE.c与OSAL_Clock.h两个文件中。OSAL支持完整的UTC(世界统一时间),以2000年1月1日00:00:00为时间起点,可以精确到年、月、日、时、分、秒的时间值。

背景知识

// number of seconds since 0 hrs, 0 minutes, 0 seconds, on the 1st of January 2000 UTC存储自2000年1月1日开始的**秒数**,uint32可存储大约135年的数据,就是到2135年左右。
typedef uint32 UTCTime;
UTCTime OSAL_timeSeconds = 0;//定义一个存储秒数的变量// To be used with
typedef struct
{uint8 seconds;  // 0-59uint8 minutes;  // 0-59uint8 hour;     // 0-23uint8 day;      // 0-30uint8 month;    // 0-11uint16 year;    // 2000+
} UTCTimeStruct;//这是将UTCTime转化为UTCTimeStruct的结构//几个宏定义,判断闰年
#define IsLeapYear(yr)  (!((yr) % 400) || (((yr) % 100) && !((yr) % 4)))
//每年多少天
#define YearLength(yr)  (IsLeapYear(yr) ? 366 : 365)
// (MAXCALCTICKS * 5) + (max remainder) must be <= (uint16 max),
// so: (13105 * 5) + 7 <= 65535这是防止微妙转化为毫秒是uint16溢出
#define MAXCALCTICKS  ((uint16)(13105))#define BEGYEAR         2000     // UTC started at 00:00:00 January 1, 2000   UTC时间是自2000年1月1日开始#define DAY             86400UL  // 24 hours * 60 minutes * 60 seconds一天的秒数//全局变量
static uint16 previousLLTimerTick = 0;   //存储的是上次调用osalTimeUpdate获得的625us反转次数,也即上次调用的节拍数量
static uint16 remUsTicks = 0;   //存储的是每次更新us转ms *5/8的余数
static uint16 timeMSec = 0;     //存储的是ms转s   的余数

osalTimeUpdata

OSAL里面所有时间的根源都来自于硬件层一个定时器的更新,它是625us自动反转重新计时的定时器,只要计算前后两次获得它反转的次数想减那么就可以计算出之间花费了多少时间。
这个定时器由osalTimeUpdata调用。

void osalTimeUpdate( void )
{uint16 tmp;  //记录这次获得625us逝去的次数uint16 ticks625us;   //记录前后两次625us逝去的次数uint16 elapsedMSec = 0;  //记录前后两次625 us逝去的毫秒数// Get the free-running count of 625us timer tickstmp = ll_McuPrecisionCount();   //计算当前过去了多少个625us次数if ( tmp != previousLLTimerTick ){// Calculate the elapsed ticks of the free-running timer.ticks625us = tmp - previousLLTimerTick;// Store the LL Timer tick count for the next time through this function.存储这次过去的625us的次数previousLLTimerTick = tmp;/* It is necessary to loop to convert the usecs to msecs in increments so as* not to overflow the 16-bit variables.*/while ( ticks625us > MAXCALCTICKS ) //主要为了数据过大转换为ms是溢出了。MAXCALCTICKS >13105就会溢出从零开始,对时间来说是不行的{ticks625us -= MAXCALCTICKS;elapsedMSec += MAXCALCTICKS * 5 / 8;  //计算逝去的毫秒;整除,MAXCALCTICKS =13105个节拍转换为ms是8190msremUsTicks += MAXCALCTICKS * 5 % 8;   //计算当前循环的余数,余数为5,时间0.625ms;5除8 =0.625}}// update converted number with remaining ticks from loop and the// accumulated remainder from loop把余数加进去,组合成剩余的滴答数*5;这里限制了必须使得((ticks625us *5) + 7 < = 65535)tmp = (ticks625us * 5) + remUsTicks;// Convert the 625 us ticks into milliseconds and a remainderelapsedMSec += tmp / 8;   //将剩余的滴答数转化成msremUsTicks = tmp % 8;      //将余数留到下次使用// Update OSAL Clock and Timers更新时钟和软件定时器if ( elapsedMSec )//{osalClockUpdate( elapsedMSec );//更新整个系统的时间,UTCTime以秒为单位记录  osalTimerUpdate( elapsedMSec );//传入过去流逝的时间值,进一步更新每个软件定时器,减去一定的时间  }}
}

个人觉得上面对MAXCALCTICKS处理有点问题,详情可以看下面OSAL时钟优化

osalClockUpdate( elapsedMSec );

用于更新系统时钟时间,保存到一个全局变量OSAL_timeSeconds中。参数是毫秒数据,

static void osalClockUpdate( uint16 elapsedMSec )
{// Add elapsed milliseconds to the saved millisecond portion of timetimeMSec += elapsedMSec;// Roll up milliseconds to the number of secondsif ( timeMSec >= 1000 ){OSAL_timeSeconds += timeMSec / 1000;timeMSec = timeMSec % 1000;   //存储ms转s余数}
}

时间管理的其他API

void osal_setClock函数是更新系统时间,以UTCTime为参数,秒级别

void osal_setClock( UTCTime newTime )
{OSAL_timeSeconds = newTime;
}

osal_getClock函数用于获得当前的系统时钟时间

UTCTime osal_getClock( void )
{return ( OSAL_timeSeconds );
}

osal_ConvertUTCTime函数是将时间从UTCTime转化成UTCTimeStruct

void osal_ConvertUTCTime( UTCTimeStruct *tm, UTCTime secTime )
{// calculate the time less than a day - hours, minutes, seconds计算时间不足一天{uint32 day = secTime % DAY;//不足一天的秒数tm->seconds = day % 60UL;  //秒tm->minutes = (day % 3600UL) / 60UL;  //分tm->hour = day / 3600UL;   //小时}// Fill in the calendar - day, month, year填充日,月,年{uint16 numDays = secTime / DAY;//总的天数tm->year = BEGYEAR;while ( numDays >= YearLength( tm->year ) ){numDays -= YearLength( tm->year );//根据YearLength判定是否是闰年?返回366/365tm->year++;}tm->month = 0;while ( numDays >= monthLength( IsLeapYear( tm->year ), tm->month ) )//这里判定月份天数的算法比较独特{numDays -= monthLength( IsLeapYear( tm->year ), tm->month );tm->month++;}tm->day = numDays;}
}
//下面是对月份判断的代码
static uint8 monthLength( uint8 lpyr, uint8 mon )
{uint8 days = 31;if ( mon == 1 ) // feb{days = ( 28 + lpyr );}else{if ( mon > 6 ) // aug-dec{mon--;}if ( mon & 1 ){days = 30;}}return ( days );
}

月份的计算小算法

osal_ConvertUTCSecs 函数
//下面这里是将UTCStruct时间转化为uint32的秒数

UTCTime osal_ConvertUTCSecs( UTCTimeStruct *tm )
{uint32 seconds;/* Seconds for the partial day */seconds = (((tm->hour * 60UL) + tm->minutes) * 60UL) + tm->seconds;//计算不足一天的秒数/* Account for previous complete days计算当前月已过去的天数 ,比如20,就等于20天*/{/* Start with complete days in current month计算已当前月过去的月份 */uint16 days = tm->day;/* Next, complete months in current year计算当年已过去的月,比如month = 2,三月,过了两月了,执行两次循环 */{int8 month = tm->month;while ( --month >= 0 ){days += monthLength( IsLeapYear( tm->year ), month );}}/* Next, complete years before current year计算自2000年到当前多了多少年 */{uint16 year = tm->year;while ( --year >= BEGYEAR ){days += YearLength( year );}}/* Add total seconds before partial day */seconds += (days * DAY);}return ( seconds );
}

//下面这个函数根据上面osalTimeUpdate函数更新系统时间osal_systemClock,更新软件定时器里面定时值。OSAL的定时器的相关代码实现OSAL_Timers.c与OSAL_Timers.h

osalTimerUpdate( elapsedMSec );

函数功能是用过去的毫秒值去更新软件定时器的值,减去时间;

软件定时器相关的背景知识

typedef union {uint32 time32;uint16 time16[2];uint8 time8[4];
} osalTime_t;typedef struct
{void   *next;osalTime_t timeout;uint16 event_flag;uint8  task_id;uint32 reloadTimeout;
} osalTimerRec_t;
//上面是一个软件定时器的结构体,OSAL的定时器主要为任务提供定时服务,所以定时器的结构需要包括任务的ID(task_id)以及它承载的事件ID(event_flag),以便告诉OSAL让哪个任务做什么事情;除此之外定时器还需要记录的定时时间(timeout)以及超时后需要重载的定时值(reloadTimeout)。
//一个用来记录系统上电的运行时间
static uint32 osal_systemClock;
osalTimerRec_t *timerHead;  //定义一个头指针,指向软件定时器链表

例程讲解

void osalTimerUpdate( uint32 updateTime )
{halIntState_t intState;osalTimerRec_t *srchTimer;  //遍历软件定时器链表osalTimerRec_t *prevTimer;osalTime_t timeUnion;timeUnion.time32 = updateTime;//共用体,下面可以看到这个共用体是为了可以字节操作timeout而设置的。这是共用体的好处吧HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.// Update the system timeosal_systemClock += updateTime;//将逝去的时间更新到系统时钟,以毫秒级别HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.// Look for open timer slotif ( timerHead != NULL ){// Add it to the end of the timer listsrchTimer = timerHead;//指向定时器链表头prevTimer = (void *)NULL;//初始化为空// Look for open timer slotwhile ( srchTimer ){osalTimerRec_t *freeTimer = NULL;//记录待释放的软件定时器HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.// To minimize time in this critical section, avoid 32-bit math为了避免32位匹配,计算最小时间if ((timeUnion.time16[1] == 0) && (timeUnion.time8[1] == 0)){// If upper 24 bits are zero, check lower 8 bits for roll over逝去的时间只有 1 byteif (srchTimer->timeout.time8[0] >= timeUnion.time8[0]){// 8-bit math减去逝去的时间srchTimer->timeout.time8[0] -= timeUnion.time8[0];}else{// 32-bit mathif (srchTimer->timeout.time32 > timeUnion.time32){srchTimer->timeout.time32 -= timeUnion.time32;}else{srchTimer->timeout.time32 = 0;}}}else{// 32-bit mathif (srchTimer->timeout.time32 > timeUnion.time32){srchTimer->timeout.time32 -= timeUnion.time32;//减去逝去的时间}else{srchTimer->timeout.time32 = 0;//定时时间到}}// Check for reloading对于自动重载的软件定时器处理if ( (srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0) &&(srchTimer->reloadTimeout) && (srchTimer->event_flag) ){// Notify the task of a timeout时间到,设定事件osal_set_event( srchTimer->task_id, srchTimer->event_flag );// Reload the timer timeout value重新设定时间srchTimer->timeout.time32 = srchTimer->reloadTimeout;}// When timeout or delete (event_flag == 0)对于只定时一次的软件定时器处理和要删除的软件定时器处理if ( ((srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0)) ||(srchTimer->event_flag == 0) ){// Take out of listif ( prevTimer == NULL ){timerHead = srchTimer->next;}else{prevTimer->next = srchTimer->next;}// Setup to free memoryfreeTimer = srchTimer;//记录当前待释放的定时器// NextsrchTimer = srchTimer->next;}else{// Get next如果没有软件定时器到时,则遍历下一个软件定时器prevTimer = srchTimer;srchTimer = srchTimer->next;}HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.if ( freeTimer ){if ( (freeTimer->timeout.time16[0] == 0) && (freeTimer->timeout.time16[1] == 0) ){osal_set_event( freeTimer->task_id, freeTimer->event_flag );}osal_mem_free( freeTimer );//释放软件定时器}}}
}

上面这个函数是对软件定时器链表的所有定时器时间进行更新。

OSAL维护着一个定时器全局链表timerHead,每当调用osal_start_timerEx或者osal_start_reload_timer实际再调用osalAddTimer,来创建一个新的软件Timer。

osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint32 timeout )
{osalTimerRec_t *newTimer;osalTimerRec_t *srchTimer;// Look for an existing timer firstnewTimer = osalFindTimer( task_id, event_flag );查找是否已存在这个任务对应事件的的定时器;if ( newTimer ){// Timer is found - update it.如果存在更新超时时间newTimer->timeout.time32 = timeout;return ( newTimer );}else{// New Timer若没有,只要创建新的软件定时器newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) );if ( newTimer ){// Fill in new timernewTimer->task_id = task_id;newTimer->event_flag = event_flag;newTimer->timeout.time32 = timeout;newTimer->next = (void *)NULL;newTimer->reloadTimeout = 0;// Does the timer list already existif ( timerHead == NULL ){// Start task list如果软件定时器为空,那么指向第一个软件定时器timerHead = newTimer;}else{// Add it to the end of the timer list不然加到线性表末尾srchTimer = timerHead;// Stop at the last recordwhile ( srchTimer->next )srchTimer = srchTimer->next;// Add to the listsrchTimer->next = newTimer;}return ( newTimer );}else{return ( (osalTimerRec_t *)NULL );}}
}

OSAL之时间管理,软件定时器链表管理相关推荐

  1. ubuntu20.1 查看apt仓库_进销存管理软件仓库如何管理划分?

    货多是每一个卖场商家的一个特点也是一个痛点,俗话说"货卖齐全,货卖堆山".商家的货越多管理商品需要的精力就越多,怎么管理好商品成为了每一个商家的重大难题. 进销存管理软件仓库如何管 ...

  2. 客户服务支持管理软件—工单管理

    客户服务支持管理软件-工单管理 (1)邮件工单管理 根据设定的规则,自动将收到的邮件转为工单. 可对每个业务单元配置发件人名称.ID.邮件模板等. 设置过滤规则,过滤垃圾邮件或不相关邮件,防止其进入服 ...

  3. 在线软件-管理软件 收银管理软件 连锁管理软件 餐饮管理软件 足浴管理软件 会员管理软件...

    以企业管理需求为基础,以IT技术为支撑,为企业提供数据信息的综合管理办法.  管理软件 随着科学技术的发展,越来越多的管理软件出现在公众的面前,供企业使用. 领峰管理软件专业提供:管理软件lfengs ...

  4. 生产图纸的权限管理,彩虹EDM图纸管理软件:权限管理功能

    生产图纸的权限管理,彩虹EDM虹图纸管理软件: 权限管理功能 所有用户通过彩虹客户端账号密码登录系统,系统根据公司不同岗位职能,授予相应的功能模块以及图纸和技术资料操作权限. 可选择授予浏览.修改.新 ...

  5. 安卓手机管理软件_日程管理软件哪个好?

    无论是工作还是生活,都需要好好地进行日程安排规划,将自己的日程安排计划管理得井井有条,滋滋有味.尤其是临近过年,每天要忙碌的事情很多,如果有一款免费且好用的日程管理软件,来辅助我们的日常生活和工作,那 ...

  6. 四维轻云地理空间数据在线管理软件能够在线管理哪些数据?

    四维轻云是一款地理空间数据在线管理软件,支持各类地理空间数据的在线管理.浏览及分享,用户可不受时间地点限制,随时随地查看各类地理空间数据.软件还具有项目管理.场景搭建.素材库等功能模块,支持在线协作管 ...

  7. android+计划管理软件,日程计划管理软件下载-日程计划管理app安卓版v1.10-电玩之家...

    日程计划管理是一款好用的日常生活进程管理应用软件,日程计划管理拥有多种代办事项提醒模式,用户可以自行进行选择使用.日程计划管理支持日程表管理功能,协助用户高效管理办公事项,日程计划管理支持云端备份功能 ...

  8. 洗浴管理软件如何高效管理员工?

    现在什么最贵?人才最贵?高精端管理人才更贵.对于洗浴行业来说更是如此,能有一个可以高效管理员工的领班,基本上成功的一半.而这样的人才可以与不可求,因而很多洗浴店都钟情于洗浴管理软件,用它来实现员工的高 ...

  9. php手机上日程管理软件,手机日程管理软件哪款好用?5款日程管理软件推荐

    手机日程管理软件哪个好?如果你有拖延症,如果你忘性大,那么你的手机里一定要有一款手机日程管理软件! <高效Todo>基于管理学大师史蒂夫.柯维和余世维所倡导的第四代时间管理理论设计.把事情 ...

最新文章

  1. 声音大小与振幅的关系_原来声音的属性有这三种!
  2. jenkins+git+maven搭建项目自动化持续集成
  3. javascript 正则表达式提取数字使用
  4. [mybatis]缓存_二级缓存使用细节
  5. 五年了,你还在用junit4吗?
  6. 记个SwitchButton笔记
  7. mysql索引超出了数组接线_索引超出数组范围是什么意思
  8. 我从Web前端开发转到网页游戏开发
  9. 普通路由器连接光猫一体机的配置教程(以水星MW300R路由器,移动吉比特GS3202光猫一体机为例,可突破专供定制路由限制)
  10. ◆谷歌热榜|百度中文搜索风云榜|Top 50
  11. matlab二重定积分_怎么用matlab计算这个二重积分如何用matlab求二重积分
  12. Zemax自学--2(Zemax软件总览)
  13. TensorFlow学习笔记(4)——TensorFlow实现GloVe
  14. python商品销售情况数据分析_用python分析小红书销售情况
  15. 如何在VScode中实现markdown所见即所得的实时预览
  16. 浏览器广告屏蔽插件推荐(Chrome与Firefox)
  17. 魔坊APP项目-17-种植园,商城页面、服务端提供商品api,解决App打包编译以后的跨域限制、客户端获取商品列表并进行展示,集成Alipayplus模块完成支付
  18. nba2k18服务器暂时不可用,NBA2K18连不上服务器解决方法 连接不上服务器咋办_3DM单机...
  19. 难得!微软招测试开发了!
  20. Windows平台的x64dbg插件合集

热门文章

  1. 大数据改变小生活,政务大数据能为我们带来什么?
  2. MAXQ1850评估套件(EV kit)和面向MAXQ30的CrossWorks编译器的设计方案
  3. SIMPLIS仿真软件4-POP分析详解
  4. python中判断真子集的符号_使用运算符测试集合包含集合A是否为集合B的真子集的表达式可以写作_______。...
  5. 【美赛资料】自己整理的美国大学生数学建模竞赛资料
  6. linux系统调用:exit()与_exit()函数详解
  7. 让程序员崩溃的 60 个瞬间
  8. 2022年精密磨光棒行业市场下游应用规模前景预测及重点企业发展战略研究
  9. [DCDC](DC-DC 电感)纹波率r是难题?来我这里来学习
  10. Uncaught ReferenceError: jp2 is not defined,用jsonp抓取qq音乐总是说回调函数没有定义