前列腺炎需要做什么检查| 电音是什么意思| 诺氟沙星胶囊治什么病| 沉淀是什么意思| 今年62岁属什么生肖| 夏天吃什么食物| 什么是飘窗| 彩虹为什么有七种颜色| 什么是热性水果| 肚子胀痛什么原因| 整体认读音节有什么| 龙抄手是什么| 眉毛脱落是什么原因造成的| 猫的五行属什么| 梦见被狗追是什么意思| bcc是什么意思| 加拿大的国宝是什么动物| 儿童脾胃不好吃什么调理脾胃| 看甲状腺挂什么科| 炒菜勾芡用什么淀粉| 人黄是什么| alk是什么意思| 斑是什么原因造成的| tf口红是什么牌子| se什么意思| 微米是什么单位| squirrel是什么意思| 体检前一天要注意什么| 切除阑尾对身体有什么影响| 湖南湖北以什么湖为界| 点茶是什么意思| 池塘里有什么| 什么叫强迫症| 麦芯粉是什么面粉| 看喉咙挂什么科| 肝火旺是什么症状| 乙肝表面抗体是什么意思| 吸土是什么意思| 本科一批和本科二批有什么区别| 小孩包皮挂什么科| 人类免疫缺陷病毒是什么| 胃穿孔是什么原因引起的| 九月份有什么节日| 榴莲为什么是苦的| 党委副书记是什么级别| 活动无耐力与什么有关| 脸上肉跳动是什么原因| 大雄宝殿是什么意思| 什么的帽子| 吃什么水果好| 瓦是什么的单位| 炊饼是什么| 不拘是什么意思| 风雨雷电代表什么生肖| 戴笠什么军衔| rainbow什么意思| 甲状腺低密度结节是什么意思| 黑色水笔是什么笔| 什么是肛瘘| 砖红色是什么颜色| 谷氨酰转移酶高是什么原因| spss是什么| 降噪是什么意思| 股骨径是指胎儿什么| 什么发型好看| 尿隐血挂什么科| 铁蛋白高吃什么药能降下来| 腮腺炎不能吃什么| 什么是腺样体面容| 北京大栅栏有什么好玩的| 虎视眈眈是什么意思| 乳头痛什么问题| 什么病不能吃松花粉| 萌是什么意思| 一什么石子| 女人阴虚火旺吃什么药| 低血糖吃什么好的最快| 骨质增生挂什么科| 为什么喝咖啡会心慌| 直系亲属为什么不能输血| 变异性哮喘吃什么药| 刘备字什么| 足底血查什么| 尿道炎是什么原因引起的| 婶婶是什么意思| 一天两包烟会导致什么后果| 肠梗阻是什么原因引起的| 杏仁有什么功效和作用| 格列本脲和格列美脲有什么区别| 咳嗽想吐是什么原因| 鱼缸底部铺什么好| 六味地黄丸什么时候吃| 排暖期出血是什么原因| 豆腐干炒什么好吃| 6月17日什么星座| 1994属什么| 身体缺钾是什么原因造成的| 纳氏囊肿是什么意思| 封闭抗体是什么意思| 自我为中心是什么意思| 阴囊湿疹用什么药| 高血脂是什么意思| 神经内科主要看什么| 2018年属什么生肖| 什么 姿势 最深| 什么水晶招财旺事业| 射不出来是什么原因| 阿修罗是什么意思| 古代男子成年叫什么| 氯雷他定片是治什么的| 过继是什么意思| 1970年五行属什么| 经由是什么意思| bell什么意思| 我宣你是什么意思| 小猫什么时候驱虫| 浑身解数是什么意思| 2023年五行属什么| 身体起水泡是什么病症| 夜盲症缺什么维生素| 猫奴是什么意思| 女人梦见好多蛇是什么预兆| 右侧后背疼是什么原因| 母猪上树是什么生肖| 警察两杠三星是什么级别| aquascutum是什么牌子| 9月28号什么星座| o型血和o型血生的孩子是什么血型| 交尾是什么意思| 什么秒必争| 什么牌空调好用又省电| 性激素六项什么时候查| 烟花三月是什么意思| 拔牙后注意什么| 老年人喝什么奶粉好| 白无常叫什么名字| 天道好轮回什么意思| 三大产能营养素是什么| 党内的最高处分是什么| 转氨酶高吃什么药好| 胃反流吃什么药好| ideal是什么意思| 下葬有什么讲究或忌讳| 咽喉炎吃什么药管用| 人为什么有两个鼻孔| 6.14什么星座| 家人们是什么意思| 小孩智力发育迟缓挂什么科| 肝郁气滞有什么症状| 夜宵吃什么不会胖| 刘海是什么意思| 狗嚎叫有什么预兆| 松花粉是什么| 95年五行属什么| 927是什么意思| 受害者是什么意思| 什么是冰种翡翠| 男性内分泌失调有什么症状| 什么假什么威| cd是什么牌子| 牛皮和牛皮革有什么区别| 肌肉代偿是什么意思| 嘴唇暗红色是什么原因| 开火车什么意思| 九月三号是什么星座| 嘴唇正常颜色是什么样| 靠谱什么意思| 钦字五行属什么| 吃金针菇有什么好处| 什么的腊梅| 闭关是什么意思| 支气管炎是什么症状| 气喘吁吁什么意思| 梦到生女儿是什么意思| 肾阳虚的表现是什么| 肠息肉有什么症状| 双向转诊是什么意思| 什么网卡好| 梦见下大雪是什么预兆| 虾仁炒什么| ct检查是什么意思| 20年是什么婚| 顶臀径是指什么| 肌肉紧张是什么症状| 茄子吃了有什么好处| 类风湿性关节炎吃什么药| 手链断了是什么预兆| 霉菌性炎症用什么药效果最好| 阿戈美拉汀片是什么药| 什么水什么山| 尘字五行属什么| 孤辰寡宿是什么意思| 十月是什么月| 阿玛尼属于什么档次| 牙痛上火吃什么药| 靖国神社是什么地方| 资讯是什么意思| mac是什么意思啊| 丁是什么意思| 什么七什么八| 应届毕业生是什么意思| t什么意思| 菊花像什么比喻句| 鱼子酱是什么鱼| 违反禁令标志指示是什么意思| 莫名其妙什么意思| 胆结石不能吃什么东西| 咳嗽痰多用什么药| 循序渐进是什么意思| 包干价是什么意思| 榴莲为什么贵| 隐翅虫是什么样子| 什么是绩效工资| 儿童肚子疼挂什么科| 熊猫是什么科| 腕管综合征挂什么科| 迎刃而解是什么意思| 炭疽病用什么药最好| 德国纳粹是什么意思| 肛门松弛是什么原因| 什么是月子病| 蚂蚁为什么要搬家| 肛门痒挂什么科检查| 做梦梦见猪是什么意思| 比基尼是什么意思| 牛肉和什么菜包饺子好吃| 男人爱出汗是什么原因| 曼妥思是什么糖| 夹腿什么意思| 就诊是什么意思| 图灵是什么意思| 陶和瓷有什么区别| 魄力是什么意思| 上师是什么意思| 三分三是什么药| 肛门痒挂什么科| 沉鱼落雁什么意思| 转氨酶高吃什么食物好| 慢性肾炎吃什么药| 为什么打雷| 为什么会宫寒| 腮腺炎是什么症状| 激素六项挂什么科| 献血之后吃什么比较好| 尿频什么原因| 逍遥丸配什么治失眠| 上面一个山下面一个今读什么| 子宫息肉有什么危害| 苦瓜有什么功效和作用| 外耳道炎用什么药| 支抗钉是什么| 蓝朋友什么意思| 身体缺糖有什么症状| 新型冠状病毒有什么症状| 儿童矮小挂什么科| 变形虫是什么生物| 黄茶适合什么人喝| 地动山摇是什么生肖| 俄罗斯是什么人种| 滴水观音叶子发黄是什么原因| 反胃吃什么可以缓解| 支付宝账号是什么| 什么原因造成低血糖| 嘴唇发紫是什么病| 百度
这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 ? 论坛首页 ? 嵌入式开发 ? 软件与操作系统 ? rtthread cputime模块解析

共2条 1/1 1 跳转至

rtthread cputime模块解析

高工
2025-08-04 22:58:27     打赏
百度 (余潞)(责编:邱越、黄子娟)

      在分析硬件定时器框架时,我们发现硬件定时器基于的是mcu提供的systick定时器, 会存在影响系统调度速度以及精度的问题。而为了解决这个问题,rtt引入了cputime的模块,cputime 通过对接单独的硬件定时器来实现,提高了精度,不过作为代价是,在每个 bsp 中都需要具体实现 cputime 的对接工作。

cputime模块解析

源码路径

RTT_PATH\components\drivers\cputime\
                                     cputime.c    // cputime框架代码
                                     cputimer.c   // cputime对应用暴露的接口
                                     cputime_cortexm.c //cortex m核的cputime实现代码
                                     cputime_riscv.c // riscv核的cputime实现代码

      由于源码中存在两个核的对接实现,因此暂时只看cortexm核部分。

对接驱动接口

cputime注册接口

struct rt_clock_cputime_ops
{
    // 获取剩余时间入口
    uint64_t (*cputime_getres)(void);
    // 获取当前时间入口
    uint64_t (*cputime_gettime)(void);
    // 设置硬件定时器超时时间,回调函数,以及回调函数的参数的入口
    int (*cputime_settimeout)(uint64_t tick, void (*timeout)(void *param), void *param);
};

int clock_cpu_setops(const struct rt_clock_cputime_ops *ops)
{
    _cputime_ops = ops;
    if (ops)
    {
        RT_ASSERT(ops->cputime_getres  != RT_NULL);
        RT_ASSERT(ops->cputime_gettime != RT_NULL);
    }

    return 0;
}

// 很奇怪,cortexm核的处理和riscv核的处理,都没有注册cputime_settimeout
// 只能说,可能两份源码都不是最终的实现,但即使是参考的,也应该把这个实现写出来吧
const static struct rt_clock_cputime_ops _cortexm_ops =
{
    cortexm_cputime_getres,
    cortexm_cputime_gettime
};

int cortexm_cputime_init(void) // cortexm核的注册函数
{
#ifdef PKG_USING_PERF_COUNTER
    clock_cpu_setops(&_cortexm_ops);
#else
    /* check support bit */
    if ((DWT->CTRL & (1UL << DWT_CTRL_NOCYCCNT_Pos)) == 0)
    {
        /* enable trace*/
        CoreDebug->DEMCR |= (1UL << CoreDebug_DEMCR_TRCENA_Pos);

        /* whether cycle counter not enabled */
        if ((DWT->CTRL & (1UL << DWT_CTRL_CYCCNTENA_Pos)) == 0)
        {
            /* enable cycle counter */
            DWT->CTRL |= (1UL << DWT_CTRL_CYCCNTENA_Pos);
        }

        clock_cpu_setops(&_cortexm_ops);
    }
#endif /* PKG_USING_PERF_COUNTER */
    return 0;
}
INIT_BOARD_EXPORT(cortexm_cputime_init);

对接应用接口

注册函数

void rt_cputimer_init(rt_cputimer_t timer,
                      const char   *name,
                      void (*timeout)(void *parameter),
                      void       *parameter,
                      rt_uint64_t tick,
                      rt_uint8_t  flag)
{
    /* parameter check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(timeout != RT_NULL);
    RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

    /* set flag */
    timer->parent.flag = flag;

    /* set deactivated */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
    timer->timeout_func = timeout;
    timer->parameter    = parameter;
    timer->timeout_tick = tick + clock_cpu_gettime();
    timer->init_tick    = tick;

    rt_list_init(&(timer->row));
    rt_sem_init(&(timer->sem), "cputime", 0, RT_IPC_FLAG_PRIO);
}

     与之前的几个定时器不一样的是,cputimer并没有提供create函数,仅提供了init,也就意味着变量struct rt_cputimer只能外部定义好,再调用init函数完成参数初始化。

cputime启动函数

int clock_cpu_settimeout(uint64_t tick, void (*timeout)(void *param), void *param)
{
    if (_cputime_ops)
        return _cputime_ops->cputime_settimeout(tick, timeout, param);

    rt_set_errno(ENOSYS);
    return 0;
}

static void _cputime_timeout_callback(void *parameter) // cputime超时回调函数
{
    struct rt_cputimer *timer;
    timer = (struct rt_cputimer *)parameter;
    rt_base_t level;
    level              = rt_hw_interrupt_disable();
    _cputimer_nowtimer = RT_NULL;
    rt_list_remove(&(timer->row));
    rt_hw_interrupt_enable(level);
    timer->timeout_func(timer->parameter); //调用超时回调处理

    if (&_cputimer_list != _cputimer_list.prev)  // 定时器列表非空,直接启用下一个定时器
    {
        struct rt_cputimer *t;
        t = rt_list_entry(_cputimer_list.next, struct rt_cputimer, row);
        clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
    }
    else
    {
        clock_cpu_settimeout(RT_NULL, RT_NULL, RT_NULL);
    }
}

static void _set_next_timeout()
{
    struct rt_cputimer *t;

    if (&_cputimer_list != _cputimer_list.prev) // 如果链表非空,则启动定时器
    {
        t = rt_list_entry((&_cputimer_list)->next, struct rt_cputimer, row);
        if (_cputimer_nowtimer != RT_NULL)   
        { // 如果当前有定时器任务在执行,则需要判断新插入的定时器的超时时间是否比当前任务短
            if (t != _cputimer_nowtimer && t->timeout_tick < _cputimer_nowtimer->timeout_tick)
            { // 若新定时器的超时时间比当前定时器的超时时间短,则更新定时器超时时间
                _cputimer_nowtimer = t;
                clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
            }
        }
        else
        {  // 如果当前没有定时器任务在执行,则直接启用定时器
            _cputimer_nowtimer = t;
            clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
        }
    }
    else // 非空,则禁用定时器
    {
        _cputimer_nowtimer = RT_NULL;
        clock_cpu_settimeout(RT_NULL, RT_NULL, RT_NULL);
    }
}

rt_err_t rt_cputimer_start(rt_cputimer_t timer)
{
    rt_list_t *timer_list;
    rt_base_t  level;

    /* parameter check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

    /* stop timer firstly */
    level = rt_hw_interrupt_disable();
    /* remove timer from list */

    rt_list_remove(&timer->row);  // 将当前定时器从启用的定时器列表中移除
    /* change status of timer */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

    timer_list = &_cputimer_list;

    for (; timer_list != _cputimer_list.prev; // 按照时间从小到大的顺序,将当前定时器插入定时器列表
         timer_list = timer_list->next)
    {
        struct rt_cputimer *t;
        rt_list_t *p = timer_list->next;

        t = rt_list_entry(p, struct rt_cputimer, row);

        if ((t->timeout_tick - timer->timeout_tick) == 0)
        {
            continue;
        }
        else if ((t->timeout_tick - timer->timeout_tick) < 0x7fffffffffffffff)
        {
            break;
        }
    }

    rt_list_insert_after(timer_list, &(timer->row));

    timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;

    _set_next_timeout();    // 设置下一次的定时器唤醒任务
    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    return RT_EOK;
}

      这里,个人认为可能存在一个问题,timerout的计时,在start函数中并没有被重新设置,也就意味着,如果上层init定时器参数后,但没有立马调用start函数,就会存在实际start时,定时器就已经超时的问题。

cputime配置函数

rt_err_t rt_cputimer_control(rt_cputimer_t timer, int cmd, void *arg)
{
    rt_base_t level;

    /* parameter check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

    level = rt_hw_interrupt_disable();
    switch (cmd)
    {
    case RT_TIMER_CTRL_GET_TIME:    // 获取超时时间设置
        *(rt_uint64_t *)arg = timer->init_tick;
        break;

    case RT_TIMER_CTRL_SET_TIME:    // 设置超时时间
        RT_ASSERT((*(rt_uint64_t *)arg) < 0x7fffffffffffffff);
        timer->init_tick    = *(rt_uint64_t *)arg;
        timer->timeout_tick = *(rt_uint64_t *)arg + clock_cpu_gettime();
        break;

    case RT_TIMER_CTRL_SET_ONESHOT:    //设置为单次触发模式
        timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
        break;

    case RT_TIMER_CTRL_SET_PERIODIC:    // 设置为周期触发模式
        timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
        break;

    case RT_TIMER_CTRL_GET_STATE:        // 获取当前运行状态
        if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
        {
            /*timer is start and run*/
            *(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED;
        }
        else
        {
            /*timer is stop*/
            *(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
        }
        break;

    case RT_TIMER_CTRL_GET_REMAIN_TIME:    // 获取当前设置的超时时间精确值
        *(rt_uint64_t *)arg = timer->timeout_tick;
        break;
    case RT_TIMER_CTRL_GET_FUNC:    // 获取超时回调函数
        arg = (void *)timer->timeout_func;
        break;

    case RT_TIMER_CTRL_SET_FUNC:    // 设置超时回调函数
        timer->timeout_func = (void (*)(void *))arg;
        break;

    case RT_TIMER_CTRL_GET_PARM:    // 获取定时器参数
        *(void **)arg = timer->parameter;
        break;

    case RT_TIMER_CTRL_SET_PARM:    // 设置定时器参数
        timer->parameter = arg;
        break;

    default:
        break;
    }
    rt_hw_interrupt_enable(level);

    return RT_EOK;
}

      这部分的入口,和前面几个定时器提供的接口,也是一致的,并没有什么不同。但是有个问题,cputime的执行函数中,并没有看到周期执行的处理,这个control函数设置周期执行或单次执行,其实并没有功能。

cputime停止函数

rt_err_t rt_cputimer_stop(rt_cputimer_t timer)
{
    rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    /* timer check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

    if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
    {
        rt_hw_interrupt_enable(level);
        return -RT_ERROR;
    }

    rt_list_remove(&timer->row);
    /* change status */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

    _set_next_timeout();
    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    return RT_EOK;
}

      停止函数的实现就很直白了,如果是启用状态,则从启用列表中移除定时器,并将定时器的可执行状态切换为非执行中,同时更新定时器的超时处理信息。

cputime删除函数

rt_err_t rt_cputimer_delete(rt_cputimer_t timer)
{
    rt_base_t level;

    /* parameter check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    rt_list_remove(&timer->row);
    /* stop timer */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    _set_next_timeout();

    return RT_EOK;
}

      删除函数的实现,有些看不明白,如果当前cputime任务并不在定时器列表中,那删除还需要再去执行一遍更新定时器的超时信息?

cputime分离函数

rt_err_t rt_cputimer_detach(rt_cputimer_t timer)
{
    rt_base_t level;

    /* parameter check */
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    rt_list_remove(&timer->row);
    /* stop timer */
    timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;

    _set_next_timeout();
    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    rt_sem_detach(&(timer->sem));

    return RT_EOK;
}

      同删除函数一样的问题,在没使用的时候也需要更新一次定时器超时处理信息,很奇怪。另外timer->sem这个信号量压根就没使用,为了一个没有使用的信号量写一个detach函数,很奇怪。

cputime休眠函数

static void _cputime_sleep_timeout(void *parameter)
{
    struct rt_semaphore *sem;
    sem = (struct rt_semaphore *)parameter;
    rt_sem_release(sem);    // 释放信号量
}

rt_err_t rt_cputime_sleep(rt_uint64_t tick)
{
    rt_base_t          level;
    struct rt_cputimer cputimer;

    if (!clock_cpu_issettimeout())
    {
        rt_int32_t ms = clock_cpu_millisecond(tick);
        return rt_thread_delay(rt_tick_from_millisecond(ms));
    }

    if (tick == 0)
    {
        return -RT_EINVAL;
    }

    rt_cputimer_init(&cputimer, "cputime_sleep", _cputime_sleep_timeout, &(cputimer.sem), tick,
                     RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER); //初始化cputime

    /* disable interrupt */
    level = rt_hw_interrupt_disable();

    rt_cputimer_start(&cputimer); /* reset the timeout of thread timer and start it */
    rt_hw_interrupt_enable(level);
    rt_sem_take_interruptible(&(cputimer.sem), RT_WAITING_FOREVER);    // 等待信号量释放

    rt_cputimer_detach(&cputimer);
    return RT_EOK;
}

// ns级别的延时
rt_err_t rt_cputime_ndelay(rt_uint64_t ns)
{
    uint64_t unit = clock_cpu_getres();
    return rt_cputime_sleep(ns * (1000UL * 1000) / unit);
}

// us级别的延时
rt_err_t rt_cputime_udelay(rt_uint64_t us)
{
    return rt_cputime_ndelay(us * 1000);
}

// ms级别的延时
rt_err_t rt_cputime_mdelay(rt_uint64_t ms)
{
    return rt_cputime_ndelay(ms * 1000000);
}

      从这里,我们可以发现,cputime延时本质上是利用信号量和cputime的精确定时功能,完成相对高精度的延时等待,可以实现rtthread_mdelay无法实现的us级别的延时设置。

总结

       首先,目前看提供的驱动层参考实现并不完美(缺乏最关键的超时回调处理,且其他代码也不确定是否真实有效)。此外,目前并未发现哪块板卡专门适配了该模块,若需使用,则需自行适配。

       其次,cputime仅仅实现了单次出发的模式,若想实现连续触发,并不能使用cputime模块。但不可否认的是,cputime能够实现与硬件定时器一致的高精度超时处理函数,并将处理效果等同于rt_thread_mdelay函数的实现。

      最后,目前版本的cputime,在使用时一定得注意,要么直接init后start,要么RT_TIMER_CTRL_SET_TIME后start,否则存在超时时间与所需超时时间不一致的问题,或者若有兴趣修复这个bug的话,可以直接修改cputime的源码,做到与其他几个定时器框架的实现思路一致。






关键词: rtthread     cputime     模块     解析    

专家
2025-08-04 08:20:40     打赏
2楼

感谢分享


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]
白细胞低要吃什么 什么人不能喝大麦茶 吃维生素b2有什么好处和副作用 tnt是什么意思 动员是什么意思
划扣是什么意思 月子里可以吃什么蔬菜 白细胞低是什么原因造成的 什么是回迁房 生是什么意思
hpv是什么意思 生茶和熟茶有什么区别 吃什么可以增加黄体酮 脐疝是什么 多多益善的益是什么意思
怀孕一个月肚子有什么变化 女性提高免疫力吃什么 navy是什么颜色 黑猫警长为什么只有5集 吃什么药死的快
得宝松是什么药hcv8jop0ns0r.cn 升白细胞的针剂叫什么hcv8jop8ns8r.cn 大伽是什么意思hcv8jop0ns2r.cn 喉咙干吃什么药hcv7jop7ns4r.cn 爱恨就在一瞬间是什么歌dayuxmw.com
戒备心是什么意思hcv9jop4ns7r.cn 经期不能吃什么hcv8jop6ns0r.cn 女人更年期有什么症状hcv8jop8ns8r.cn nibpdia过高是什么意思hcv9jop4ns8r.cn pab是什么意思hcv8jop2ns7r.cn
高血压挂什么科hcv9jop0ns0r.cn 指南针为什么不叫指北针hcv7jop6ns6r.cn 40岁属什么生肖hcv8jop6ns7r.cn 三头六臂指什么生肖naasee.com 主导是什么意思hcv9jop4ns5r.cn
大专跟本科有什么区别hcv9jop6ns5r.cn 万马奔腾是什么意思hcv8jop4ns5r.cn 经常拉肚子是什么原因hcv7jop7ns0r.cn 1月29日是什么星座hcv8jop6ns6r.cn 系统性红斑狼疮挂什么科hcv8jop6ns1r.cn
百度