理解rtt-pm之看门狗与rtc

本文深入探讨了STM32单片机中的RTC(实时时钟)模块,包括电源管理、时钟源、日历功能、自动唤醒和闹钟设置。同时介绍了看门狗功能,分为独立看门狗IWDG和窗口看门狗WWDG,以及它们在电源管理和系统监控中的应用。最后,展示了如何在项目中初始化和使用看门狗进行低功耗管理。


主要是为pm管理打下基础。平台还是F407.

rtc

这个项目中早就用过了。只是没有了解过内部的运行原理。

电源

在VDD电源关闭时,可通过VBAT保持上电。

时钟的得到

一般使用LSE,32768Hz的外部晶振。异步预分频器时钟 fck_APRE=frtc/(PREDIV_A+1),同步预分频器时钟fck_spre=frtc/(prediv_s+1) ;一般的寄存器设置为 prediv_a=127,prediv_s=255.这样得到时钟为:32768/((127+1)(255+1))=32768/(128*256)=1Hz

日历的得到

F4的rtc->dr中可以直接得到年,月,日,星期(F1,就不行,只可得到秒)

自动唤醒

这便是与PM相关的功能。

闹钟

当 RTC 运行的时间跟预设的闹钟时间相同的时候,相应的标志位 ALRAF(在 RTC_ISR 寄存器中)和 ALRBF 会置 1。可以作为备忘提醒功能。
如果使能了闹钟输出(由 RTC_CR 的 OSEL[0:1]位控制),则 ALRAF 和 ALRBF 会连接到闹钟输出引脚 RTC_ALARM,RTC_ALARM 最终连接到 RTC 的外部引脚 RTC_AF1(即 PC13),输出的极性由 RTC_CR 寄存器的 POL 位配置,可以是高电平或者低电平。

时间戳

为了记录外部紧急事件触发的时间。时间戳复用功能 (RTC_TS) 可映射到 RTC_AF1 或 RTC_AF2,当发生外部的入侵事件时,即发生时间戳事件时, RTC_ISR 寄存器中的时间戳标志位 (TSF) 将置 1,日历会保存到时间戳寄存器( RTC_TSSSR、RTC_TSTR 和RTC_TSDR)中。

入侵检测

RTC 自带两个入侵检测引脚 RTC_AF1(PC13)和 RTC_AF2。这两个输入既可配置为边沿检测,也可配置为带过滤的电平检测。当检测到外部事件时,可复位备份寄存器,共80个字节,可用于记录用户数据。备份寄存器不会在系统复位或电源复位时复位,也不会在器件从待机模
式唤醒时复位。

看门狗

电源

可以VDD电压域供电,在停止模式和待机模式下仍能工作。

IWDG 独立看门狗

由独立的LSI提供时钟。不受主时钟影响。一般为40khz.

WWDG 窗口看门狗

只能在上窗口和下窗口之间才可以喂狗。也就是说计数器值在一个范围内才可以喂狗。这样可以在独立看门狗定义的闹钟下,再定义一个窗口时间。

从brick看pm使用思路

初始化看门狗及任务

在platform-lowpower.c中

/*
 * @brief	   硬件驱动初始化
 * @param[in]   none
 * @return 	    none
 */
static void bsp_init(void)
{    
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  
    tty.init(9600);
    SystemCoreClockUpdate();
    RCC_APB2PeriphClockLPModeCmd(RCC_APB2Periph_SYSCFG,ENABLE);
    rtc_init();
    rtc_wakeup_config(SYS_TICK_INTERVAL);               //设置rtc的唤醒时间  10ms
    wdog_conf(MAX_DOG_FEED_TIME);                       //初始化看门狗  10s
    pm_init(&pm_adapter);                               //初始化低功耗管理器
    pm_enable();                                        //启用低功耗管理,只是一个启动标志位
    
    //
    //低功耗模式下,为了能够正常接收数据,配置串口1 RX为唤醒中断
    //
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource10);    
    exti_conf(EXTI_Line10, EXTI_Trigger_Falling, ENABLE); 
    nvic_conf(EXTI15_10_IRQn, 5, 1);
    
}system_init("bsp", bsp_init); 
/*
 * @brief	   功耗管理任务
 */
static void pm_task(void)
{
    pm_process();
}task_register("pm", pm_task, 0); //可以看到轮询间隔为0,也就是说一直是监控的。
/*
 * @brief	   喂狗任务
 */
static void wdog_task(void)
{
    IWDG_ReloadCounter();
}task_register("dog", wdog_task, 1000);//1s,会调一次喂狗操作

pm_init()实现了什么?

程序中的pm_init(&pm_adapter)
这个函数原型是pm.c里定义的

/**
 * @brief   初始化功耗管理器
 * @retval  adt - 适配器接口
 */
void pm_init(const pm_adapter_t *adt)
{
    pm_watch.adt = adt;//pm_watch是pm监视器,这个意思就是把pm_adapter指给了它
}

而pm_adapter,这个结构体指定了,系统最大休眠时长和进入休眠的函数指针。还是在platform-lowpower.c中,定义:

/*
 * @brief	   电源管理适配器
 */
static const pm_adapter_t pm_adapter = {
    MAX_DOG_FEED_TIME * 8 / 10,                           //确保能提前唤醒并喂狗
    system_sleep//这个便是进入休眠的函数
};

进入休眠

从pm_init()初始化后,还定义一个pm_task()的任务。推断,应该就是从这里进入休眠的。这个函数也是在pm.c中定义:

/**
 * @brief   功耗管理
 * @retval  none
 */
void pm_process(void)
{
    if (!pm_watch.enable || !system_is_idle())//如果没打开pm监控标志或设备都在忙,则直接跳出
        return;
    system_goto_sleep();//否则就会进判断函数,判断是否可以进休眠
}

这个函数其实不光定义了休眠还定义了唤醒,如下:

/*
 * @brief  系统进行休眠处理
 */
static void system_goto_sleep(void)
{
    const pm_item_t   *it;//这其是休眠设备定义,名称,设备是否允许休眠,还有一些休眠,唤醒通知 
    const pm_adapter_t *adt;
    unsigned int sleep_time;
    unsigned int tmp;
    
    adt = pm_watch.adt;
    
    sleep_time = adt->max_sleep_time;//最大休眠时长 8s
    
    //休眠处理
    for (it = &pm_tbl_start + 1; it < &pm_tbl_end; it++) {
        if (it->sleep_notify == NULL)
            continue;
        tmp = it->sleep_notify();          //休眠请求,并得到设备期待下次唤醒时间
        if (tmp && tmp < sleep_time)       //计算出所有设备中的最小允许休眠时间
            sleep_time = tmp;
    }
    
    adt->goto_sleep(sleep_time);//调用system_sleep()函数进入休眠,做得时钟后再唤醒
    
    //唤醒处理
    for (it = &pm_tbl_start + 1; it < &pm_tbl_end; it++) {
        if (it->wakeup_notify == NULL)
            continue;
        it->wakeup_notify();
    }
}

如何注册设备的休眠,唤醒函数

上面的休眠任务中的system_is_idle(),还是挺重要的,因为这个其实是实时轮询注册的pm设备,相当于调度。
通过pm.h里的pm_dev_register(name, idle, sleep_notify, wakeup_notify)
如开机时,3s不进休眠,在platfor-lowpower.c里则注册为:

/*
 * @brief	   默认开机或者串口有通信活动,3S内不允许进入低功耗
 */
static bool system_is_idle(void)
{
    return is_timeout(system_idle_time, 3000) && tty.rx_isempty() && tty.tx_isempty();
}pm_dev_register("sys", system_is_idle, NULL, NULL);

由于对应的sleep_notify, wakeup_notify,都为NULL,所以肯定是不会进入唤醒或休眠的,所以只会调度system_is_idle()这里注意:是pm.c里的函数,名字与这里的相同,但都以static声明的,所以是不同的。
这是pm.c里的system_is_idle定义:

/*
 * @brief  系统空闲检测
 */
static bool system_is_idle(void)
{
    const pm_item_t *it;
    for (it = &pm_tbl_start + 1; it < &pm_tbl_end; it++) {
        if (it->idle != NULL && !it->idle())
            return false;
    }
    return true;
}

假设只有这一个sys 的pm设备的话,并且不考虑串口状态的话,会一直查询platfor-lowpower.c里的system_is_idle,系统时间是否间隔了3000,间隔之后才会返回1,这样才会使pm.c里的system_is_idle返回1.这样才会使轮询pm设备任务执行system_goto_sleep();函数。

总结

看门狗只是起一保险作用,故障情况下会重启。pm真正使用到的还是rtc定时唤醒。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guangod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值