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

被折叠的 条评论
为什么被折叠?



