这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 软件与操作系统 » rtthread闹钟模块代码分析

共1条 1/1 1 跳转至

rtthread闹钟模块代码分析

工程师
2025-02-05 22:13:45     打赏

        确切的说,闹钟模块应该属于rtc功能的附加功能,他主要是基于rtc计时,外加rtc提供的超时中断来实现闹钟功能,属于纯应用逻辑。

源码分析

源码路径

components\drivers\rtc\alarm.c

闹钟核心处理线程

// 将struct tm当天的时间转化成s
rt_inline rt_uint32_t alarm_mkdaysec(struct tm *time)
{
    rt_uint32_t sec;

    sec = time->tm_sec;
    sec += time->tm_min * 60;
    sec += time->tm_hour * 3600;

    return (sec);
}

// 设置闹钟,需要注意的是,设置闹钟所使用的结构体变成了 struct rt_alarm
static rt_err_t alarm_set(struct rt_alarm *alarm)
{
    rt_device_t device;
    struct rt_rtc_wkalarm wkalarm;
    rt_err_t ret;

    device = rt_device_find("rtc");

    if (device == RT_NULL)
    {
        return (RT_ERROR);
    }

    if (alarm->flag & RT_ALARM_STATE_START)
        wkalarm.enable = RT_TRUE;
    else
        wkalarm.enable = RT_FALSE;

    wkalarm.tm_sec = alarm->wktime.tm_sec;
    wkalarm.tm_min = alarm->wktime.tm_min;
    wkalarm.tm_hour = alarm->wktime.tm_hour;
    wkalarm.tm_mday = alarm->wktime.tm_mday;
    wkalarm.tm_mon = alarm->wktime.tm_mon;
    wkalarm.tm_year = alarm->wktime.tm_year;

    ret = rt_device_control(device, RT_DEVICE_CTRL_RTC_SET_ALARM, &wkalarm);
    if ((ret == RT_EOK) && wkalarm.enable)
    {
        ret = rt_device_control(device, RT_DEVICE_CTRL_RTC_GET_ALARM, &wkalarm);
        if (ret == RT_EOK)
        {
            // 如果使能了,则更新当前闹钟的参数,因为部分芯片的RTC闹钟部分最小时间单位并不是s,所以需要将秒默认设为0
            alarm->wktime.tm_sec = wkalarm.tm_sec;
            alarm->wktime.tm_min = wkalarm.tm_min;
            alarm->wktime.tm_hour = wkalarm.tm_hour;
            alarm->wktime.tm_mday = wkalarm.tm_mday;
            alarm->wktime.tm_mon = wkalarm.tm_mon;
            alarm->wktime.tm_year = wkalarm.tm_year;
        }
    }

    return (ret);
}

// 闹钟唤醒处理
static void alarm_wakeup(struct rt_alarm *alarm, struct tm *now)
{
    rt_uint32_t sec_alarm, sec_now;
    rt_bool_t wakeup = RT_FALSE;
    time_t timestamp;

    // 获取当前时间和闹钟时间(当天对应时间)
    sec_alarm = alarm_mkdaysec(&alarm->wktime);
    sec_now = alarm_mkdaysec(now);

    if (alarm->flag & RT_ALARM_STATE_START)
    {
        switch (alarm->flag & 0xFF00)
        {
        case RT_ALARM_ONESHOT:
        {    // 如果是单次闹钟且当前时间超过了闹钟所要求的时间,则关闭该闹钟并置需要闹铃标记
        
            // 转换成1970年至今的秒级的时间
            sec_alarm = timegm(&alarm->wktime);
            sec_now = timegm(now);
            if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm))
            {
                // 关闭闹钟
                alarm->flag &= ~RT_ALARM_STATE_START;
                alarm_set(alarm);
                // 置闹铃标记
                wakeup = RT_TRUE;
            }
        }
        break;
        case RT_ALARM_SECOND:
        {    // 如果是每秒都响的闹钟,则更新闹钟时间信息并置闹铃标记
            alarm->wktime.tm_hour = now->tm_hour;
            alarm->wktime.tm_min = now->tm_min;
            alarm->wktime.tm_sec = now->tm_sec + 1;
            if (alarm->wktime.tm_sec > 59)
            {
                alarm->wktime.tm_sec = 0;
                alarm->wktime.tm_min = alarm->wktime.tm_min + 1;
                if (alarm->wktime.tm_min > 59)
                {
                    alarm->wktime.tm_min = 0;
                    alarm->wktime.tm_hour = alarm->wktime.tm_hour + 1;
                    if (alarm->wktime.tm_hour > 23)
                    {
                        alarm->wktime.tm_hour = 0;
                    }
                }
            }
            wakeup = RT_TRUE;
        }
        break;
        case RT_ALARM_MINUTE:
        { // 如果是每分钟响一次的闹钟,则更新闹钟时间并置闹铃标记
            alarm->wktime.tm_hour = now->tm_hour;
            if (alarm->wktime.tm_sec == now->tm_sec)
            {
                alarm->wktime.tm_min = now->tm_min + 1;
                if (alarm->wktime.tm_min > 59)
                {
                    alarm->wktime.tm_min = 0;
                    alarm->wktime.tm_hour = alarm->wktime.tm_hour + 1;
                    if (alarm->wktime.tm_hour > 23)
                    {
                        alarm->wktime.tm_hour = 0;
                    }
                }
                wakeup = RT_TRUE;
            }
        }
        break;
        case RT_ALARM_HOUR:
        { // 如果是每小时响一次的闹钟,则更新闹钟时间并置闹铃标记
            if ((alarm->wktime.tm_min == now->tm_min) &&
                (alarm->wktime.tm_sec == now->tm_sec))
            {
                alarm->wktime.tm_hour = now->tm_hour + 1;
                if (alarm->wktime.tm_hour > 23)
                {
                    alarm->wktime.tm_hour = 0;
                }
                wakeup = RT_TRUE;
            }
        }
        break;
        case RT_ALARM_DAILY:
        {// 如果是每天响的闹钟,则直接置闹铃标记(因为闹钟处理其实都是按天来操作的,超过一天,wktime不需要更新)
            if (((sec_now - sec_alarm) <= RT_ALARM_DELAY) && (sec_now >= sec_alarm))
                wakeup = RT_TRUE;
        }
        break;
        case RT_ALARM_WEEKLY:
        {    // 如果是每周响的闹钟,则直接置闹铃标记
            if (alarm->wktime.tm_wday == now->tm_wday)
            {
                sec_alarm += alarm->wktime.tm_wday * 24 * 3600;
                sec_now += now->tm_wday * 24 * 3600;

                if (sec_now == sec_alarm)
                    wakeup = RT_TRUE;
            }
        }
        break;
        case RT_ALARM_MONTHLY:
        {    // 如果是每月响的闹钟,则直接置闹铃标记
            if (alarm->wktime.tm_mday == now->tm_mday)
            {
                if ((sec_now - sec_alarm) <= RT_ALARM_DELAY)
                    wakeup = RT_TRUE;
            }
        }
        break;
        case RT_ALARM_YAERLY:
        {    // 如果是每年响的闹钟,则直接置闹铃标记
            if ((alarm->wktime.tm_mday == now->tm_mday) && \
                    (alarm->wktime.tm_mon == now->tm_mon))
            {
                if ((sec_now - sec_alarm) <= RT_ALARM_DELAY)
                    wakeup = RT_TRUE;
            }
        }
        break;
        }

        if ((wakeup == RT_TRUE) && (alarm->callback != RT_NULL))
        {    // 如果闹铃标记置位且闹钟的回调函数被注册,则调用闹钟处理回调函数
            timestamp = (time_t)0;
            get_timestamp(×tamp);
            alarm->callback(alarm, timestamp);
        }
    }
}

static void alarm_update(rt_uint32_t event)
{
    struct rt_alarm *alm_prev = RT_NULL, *alm_next = RT_NULL;
    struct rt_alarm *alarm;
    rt_int32_t sec_now, sec_alarm, sec_tmp;
    rt_int32_t sec_next = 24 * 3600, sec_prev = 0;
    time_t timestamp = (time_t)0;
    struct tm now;
    rt_list_t *next;

    rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
    if (!rt_list_isempty(&_container.head))
    {
        // 获取struct tm格式的时钟信息
        get_timestamp(×tamp);
        gmtime_r(×tamp, &now);

        // 依次查询闹钟列表并处理闹钟任务
        for (next = _container.head.next; next != &_container.head; next = next->next)
        {
            alarm = rt_list_entry(next, struct rt_alarm, list);
            alarm_wakeup(alarm, &now);
        }

        // 获取当天已经经过的秒数
        get_timestamp(×tamp);
        gmtime_r(×tamp, &now);
        sec_now = alarm_mkdaysec(&now);

        // 找到离当前时间最近的两个使能的闹钟(一个是今天会执行的,一个是明天会执行的)
        for (next = _container.head.next; next != &_container.head; next = next->next)
        {
            alarm = rt_list_entry(next, struct rt_alarm, list);
            sec_alarm = alarm_mkdaysec(&alarm->wktime);
            if (alarm->flag & RT_ALARM_STATE_START)
            {
                sec_tmp = sec_alarm - sec_now;
                if (sec_tmp > 0)
                {
                    if (sec_tmp < sec_next)
                    {
                        sec_next = sec_tmp;
                        alm_next = alarm;
                    }
                }
                else
                {
                    if (sec_tmp < sec_prev)
                    {
                        sec_prev = sec_tmp;
                        alm_prev = alarm;
                    }
                }
            }
        }

        // 如果当天还有一个未使用的闹钟,则将该闹钟的时间设置到RTC模块中去
        if (sec_next < 24 * 3600)
        {
            if (alarm_set(alm_next) == RT_EOK)
                _container.current = alm_next;
        }
        else if (sec_prev < 0)
        {
            // 如果当天没有启用的闹钟了,则启用后一天的第一个闹钟
            if (alarm_set(alm_prev) == RT_EOK)
                _container.current = alm_prev;
        }
        else
        {
            if (_container.current != RT_NULL)
            {    // 如果都找不到,则启用目前指定启用的闹钟并清空当前指定的闹钟
                alarm_set(_container.current);
                if (!(_container.current->flag & RT_ALARM_STATE_START))
                    _container.current = RT_NULL;
            }
        }
    }
    rt_mutex_release(&_container.mutex);
}

static void rt_alarmsvc_thread_init(void *param)
{
    rt_uint32_t recv;

    _container.current = RT_NULL; // 设置当前闹钟为控

    while (1)
    {
        if (rt_event_recv(&_container.event, 0xFFFF,
                          RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                          RT_WAITING_FOREVER, &recv) == RT_EOK)
        {
            alarm_update(recv); // 收到消息后跳转至闹钟处理函数
        }
    }
}

// 注册alarm线程并运行线程
int rt_alarm_system_init(void)
{
    rt_thread_t tid;

    rt_list_init(&_container.head); // 初始化闹钟列表
    rt_event_init(&_container.event, "alarmsvc", RT_IPC_FLAG_FIFO); // 初始化用于闹钟事件处理的事件集
    rt_mutex_init(&_container.mutex, "alarmsvc", RT_IPC_FLAG_PRIO); // 初始化闹钟列表操作的互斥表

    tid = rt_thread_create("alarmsvc",
                           rt_alarmsvc_thread_init, RT_NULL,
                           2048, 10, 5);
    if (tid != RT_NULL)
        rt_thread_startup(tid);

    return 0;
}

INIT_PREV_EXPORT(rt_alarm_system_init);

注册闹钟入口

rt_alarm_t rt_alarm_create(rt_alarm_callback_t callback, struct rt_alarm_setup *setup)
{
    struct rt_alarm *alarm;

    if (setup == RT_NULL)
        return (RT_NULL);

    alarm = rt_malloc(sizeof(struct rt_alarm));
    if (alarm == RT_NULL)
        return (RT_NULL);

    rt_list_init(&alarm->list);

    alarm->wktime = setup->wktime;
    alarm->flag = setup->flag & 0xFF00;
    alarm->callback = callback;
    rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
    rt_list_insert_after(&_container.head, &alarm->list);
    rt_mutex_release(&_container.mutex);

    return (alarm);
}

      可以发现,注册函数本质上就是往闹钟列表内添加一个新的闹钟。

闹钟删除入口

rt_err_t rt_alarm_delete(rt_alarm_t alarm)
{
    rt_err_t ret = RT_EOK;

    if (alarm == RT_NULL)
        return -RT_ERROR;
    rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
    // 设置关闭闹钟标记
    alarm->flag &= ~RT_ALARM_STATE_START;
    if (_container.current == alarm)
    { // 如果要删除的闹钟是当前在使用的闹钟,则关闭闹钟
        ret = alarm_set(alarm);
        _container.current = RT_NULL;
        alarm_update(0);
    }
    // 移除闹钟并释放资源
    rt_list_remove(&alarm->list);
    rt_free(alarm);

    rt_mutex_release(&_container.mutex);

    return (ret);
}

闹钟停用入口

rt_err_t rt_alarm_stop(rt_alarm_t alarm)
{
    rt_err_t ret = RT_EOK;

    if (alarm == RT_NULL)
        return (RT_ERROR);
    rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
    if (!(alarm->flag & RT_ALARM_STATE_START))
        goto _exit;
    /* stop alarm */
    alarm->flag &= ~RT_ALARM_STATE_START;

    if (_container.current == alarm)
    {
        ret = alarm_set(alarm);
        _container.current = RT_NULL;
    }

    if (ret == RT_EOK)
        alarm_update(0);

_exit:
    rt_mutex_release(&_container.mutex);

    return (ret);
}

      停用入口就很简单了,把标记置为非启用,若当前在正在使用,则关闭该闹钟,另外需要重新检查一下闹钟是能列表,以防异常。

闹钟启用入口

static int days_of_year_month(int tm_year, int tm_mon)
{
    int ret, year;

    year = tm_year + 1900;
    if (tm_mon == 1)
    { // 如果是2月,若是闰年,则返回29天,若是普通年,则返回28天
        ret = 28 + ((!(year % 4) && (year % 100)) || !(year % 400));
    }
    else if (((tm_mon <= 6) && (tm_mon % 2 == 0)) || ((tm_mon > 6) && (tm_mon % 2 == 1)))
    {    // 大月返回31天
        ret = 31;
    }
    else
    {  // 其他月份返回30天
        ret = 30;
    }

    return (ret);
}

// 判断日期是否有效
static rt_bool_t is_valid_date(struct tm *date)
{
    // struct tm时间要求时间在1900年至2037年之间,因此不在这个范围内的时间都是无效的
    if ((date->tm_year < 0) || (date->tm_year > RT_RTC_YEARS_MAX))
    {
        return (RT_FALSE);
    }

    // 月份参数需要保证在12个月以内
    if ((date->tm_mon < 0) || (date->tm_mon > 11))
    {
        return (RT_FALSE);
    }

    // 天的参数需要保证符合要求每月天数要求
    if ((date->tm_mday < 1) || \
            (date->tm_mday > days_of_year_month(date->tm_year, date->tm_mon)))
    {
        return (RT_FALSE);
    }

    return (RT_TRUE);
}

// 设置闹钟参数
static rt_err_t alarm_setup(rt_alarm_t alarm, struct tm *wktime)
{
    rt_err_t ret = -RT_ERROR;
    time_t timestamp = (time_t)0;
    struct tm *setup, now;

    setup = &alarm->wktime;
    *setup = *wktime; // 这个操作没看明白,结构体能够直接赋值?
    
    // 获取struct tm格式的当前时间
    get_timestamp(×tamp);
    gmtime_r(×tamp, &now);

    // 如果设置的时间无效,则使用系统获取的时间
    if ((setup->tm_sec > 59) || (setup->tm_sec < 0))
        setup->tm_sec = now.tm_sec;
    if ((setup->tm_min > 59) || (setup->tm_min < 0))
        setup->tm_min = now.tm_min;
    if ((setup->tm_hour > 23) || (setup->tm_hour < 0))
        setup->tm_hour = now.tm_hour;

    switch (alarm->flag & 0xFF00)
    {
    case RT_ALARM_SECOND:
    {    // 如果是按秒响的闹钟,则直接设置下次闹铃时间为当前时间的下一秒
        alarm->wktime.tm_hour = now.tm_hour;
        alarm->wktime.tm_min = now.tm_min;
        alarm->wktime.tm_sec = now.tm_sec + 1;
        if (alarm->wktime.tm_sec > 59)
        {
            alarm->wktime.tm_sec = 0;
            alarm->wktime.tm_min = alarm->wktime.tm_min + 1;
            if (alarm->wktime.tm_min > 59)
            {
                alarm->wktime.tm_min = 0;
                alarm->wktime.tm_hour = alarm->wktime.tm_hour + 1;
                if (alarm->wktime.tm_hour > 23)
                {
                    alarm->wktime.tm_hour = 0;
                }
            }
        }
    }
    break;
    case RT_ALARM_MINUTE:
    {    // 如果是按照分响的闹钟,则设置下次闹铃时间为下一分钟
        alarm->wktime.tm_hour = now.tm_hour;
        alarm->wktime.tm_min = now.tm_min + 1;
        if (alarm->wktime.tm_min > 59)
        {
            alarm->wktime.tm_min = 0;
            alarm->wktime.tm_hour = alarm->wktime.tm_hour + 1;
            if (alarm->wktime.tm_hour > 23)
            {
                alarm->wktime.tm_hour = 0;
            }
        }
    }
    break;
    case RT_ALARM_HOUR:
    {    // 如果是按照小时响的闹钟,则设置下一次响铃时间为下一小时
        alarm->wktime.tm_hour = now.tm_hour + 1;
        if (alarm->wktime.tm_hour > 23)
        {
            alarm->wktime.tm_hour = 0;
        }
    }
    break;
    case RT_ALARM_DAILY:
    {    // 如果是按天响的闹钟,则不需要做任何处理
        /* do nothing but needed */
    }
    break;
    case RT_ALARM_ONESHOT:
    {    // 如果是单次响的闹钟,则仅仅需要确保设置下的闹钟有效即可
        if (setup->tm_year == RT_ALARM_TM_NOW)
            setup->tm_year = now.tm_year;
        if (setup->tm_mon == RT_ALARM_TM_NOW)
            setup->tm_mon = now.tm_mon;
        if (setup->tm_mday == RT_ALARM_TM_NOW)
            setup->tm_mday = now.tm_mday;

        if (!is_valid_date(setup))
            goto _exit;
    }
    break;
    case RT_ALARM_WEEKLY:
    {    // 如果是按周响的闹钟,则仅仅需要保证闹钟周参数符合一周七天即可
        if ((setup->tm_wday < 0) || (setup->tm_wday > 6))
            setup->tm_wday = now.tm_wday;
    }
    break;
    case RT_ALARM_MONTHLY:
    {  // 如果是按月响的闹钟,则仅仅需要保证闹钟的月份参数符合标准即可
       // 但存在一个问题,每个月的天数不是不一样嘛?为何这里没做区别判断?
        if ((setup->tm_mday < 1) || (setup->tm_mday > 31))
            setup->tm_mday = now.tm_mday;
    }
    break;
    case RT_ALARM_YAERLY:
    {  // 如果是按年的闹钟,则需要保证所有参数都是OK的
        if ((setup->tm_mon < 0) || (setup->tm_mon > 11))
            setup->tm_mon = now.tm_mon;

        if (setup->tm_mon == 1)
        {
            // 如果是2月,则需要保证天数在29日内
            if ((setup->tm_mday < 1) || (setup->tm_mday > 29))
                setup->tm_mday = now.tm_mday;
        }
        else if (((setup->tm_mon <= 6) && (setup->tm_mon % 2 == 0)) || \
                 ((setup->tm_mon > 6) && (setup->tm_mon % 2 == 1)))
        {
            // 如果是 1,3,5 ,7,8,10,12月,则天数需要保证在31天内
            if ((setup->tm_mday < 1) || (setup->tm_mday > 31))
                setup->tm_mday = now.tm_mday;
        }
        else
        {
            // 非前面两种情况,需保证每月天数在30天内
            if ((setup->tm_mday < 1) || (setup->tm_mday > 30))
                setup->tm_mday = now.tm_mday;
        }
    }
    break;
    default:
    {
        goto _exit;
    }
    }

    if ((setup->tm_hour == 23) && (setup->tm_min == 59) && (setup->tm_sec == 59))
    {
        // 如果闹钟时间是在23:59:59,则设置闹铃时间在58s,估计是为了防止59分处理不过来才这么操作
        setup->tm_sec = 60 - RT_ALARM_DELAY;
    }
    
    // 设置闹钟已初始化标记
    alarm->flag |= RT_ALARM_STATE_INITED;
    ret = RT_EOK;

_exit:

    return (ret);
}


rt_err_t rt_alarm_start(rt_alarm_t alarm)
{
    rt_int32_t sec_now, sec_old, sec_new;
    rt_err_t ret = RT_EOK;
    time_t timestamp = (time_t)0;
    struct tm now;

    // 检查输入信息是否符合标准
    if (alarm == RT_NULL)
        return (RT_ERROR);
    rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);

    if (!(alarm->flag & RT_ALARM_STATE_START))
    {   // 只有当前闹钟是停用状态才启用
        // 更新alarm参数
        if (alarm_setup(alarm, &alarm->wktime) != RT_EOK)
        {
            ret = -RT_ERROR;
            goto _exit;
        }

        // 获取系统时间并转换成struct tm格式
        get_timestamp(×tamp);
        gmtime_r(×tamp, &now);

        // 启用闹钟
        alarm->flag |= RT_ALARM_STATE_START;

        if (_container.current == RT_NULL)
        { // 如果当前没有使用的闹钟,则直接启用闹钟
            ret = alarm_set(alarm);
        }
        else
        {   // 如果当前有启用的闹钟,则判断当前闹钟是否是下一个需要启用的闹钟,如果是,则启用闹钟,如果不是,则启用原先的闹钟
            sec_now = alarm_mkdaysec(&now);
            sec_old = alarm_mkdaysec(&_container.current->wktime);
            sec_new = alarm_mkdaysec(&alarm->wktime);

            if ((sec_new < sec_old) && (sec_new > sec_now))
            { // 如果新的闹钟比目前启用的闹钟更早响铃,则启用新闹钟
                ret = alarm_set(alarm);
            }
            else if ((sec_new > sec_now) && (sec_old < sec_now))
            { // 如果新的闹钟比当前时间大,且老的闹钟已经响铃过,则启用新闹钟
                ret = alarm_set(alarm);
            }
            else if ((sec_new < sec_old) && (sec_old < sec_now))
            { // 如果新的闹钟比老的闹钟时间小,且老的闹钟已经响过了,则启用新的闹钟
                ret = alarm_set(alarm);
            }
            else
            {
                ret = RT_EOK;
                goto _exit;
            }
        }

        if (ret == RT_EOK)
        {    // 将目前启用的闹钟置为当前使能的闹钟
            _container.current = alarm;
        }
    }

_exit:
    rt_mutex_release(&_container.mutex);

    return (ret);
}

闹钟控制入口

rt_err_t rt_alarm_control(rt_alarm_t alarm, int cmd, void *arg)
{
    rt_err_t ret = -RT_ERROR;

    RT_ASSERT(alarm != RT_NULL);

    rt_mutex_take(&_container.mutex, RT_WAITING_FOREVER);
    switch (cmd)
    {
    case RT_ALARM_CTRL_MODIFY:
    {
        struct rt_alarm_setup *setup;

        RT_ASSERT(arg != RT_NULL);
        setup = arg;
        rt_alarm_stop(alarm);
        alarm->flag = setup->flag & 0xFF00;
        alarm->wktime = setup->wktime;
        ret = alarm_setup(alarm, &alarm->wktime);
    }
    break;
    }

    rt_mutex_release(&_container.mutex);

    return (ret);
}

      闹钟控制入口,其实就是更新闹钟的参数信息。

总结

     通过对源码的分析,我们大致了解了alarm模块的使用逻辑,首先,启用alarm模块,系统会默认起一个用于处理alarm事件的任务。其次,可以发现,alarm模块其实依赖于RTC模块提供的闹铃中断任务。最后,系统通过调用闹钟对外提供的接口来实现闹钟的一系列操作。

        而个人认为目前版本的闹钟代码实现思路有些混乱,典型的点为:

        1. _container.current为何需要单独拉出来,而且从代码上看,这单独拉出来的操作并没有带来什么好处,反而造成了代码维护逻辑混乱

        2. alarm_setup中的每月有多少天的处理,貌似没那么完美,设置不考虑是否是闰年,反而后面校验是判断是否是闰年






关键词: rtthread     闹钟     模块     代码     分析     alar    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]