28、嵌入式Linux的看门狗与电源管理

嵌入式Linux的看门狗与电源管理

1. 看门狗的添加

在嵌入式设备中,看门狗是常见需求。若关键服务停止工作,通常需采取行动,一般是重置系统。多数嵌入式片上系统(SoC)有硬件看门狗,可通过 /dev/watchdog 设备节点访问。看门狗在启动时会设置超时时间,之后必须在该时间段内重置,否则会触发看门狗,导致系统重启。与看门狗驱动的接口在内核源码的 Documentation/watchdog 中描述,驱动代码位于 drivers/watchdog

若有两个或更多关键服务需由看门狗保护,就会出现问题。 systemd 有个实用功能,可在多个服务间分配看门狗。

systemd 可配置为期望服务定期发送保活调用,若未收到则采取行动,从而为每个服务创建软件看门狗。要实现这一点,需在守护进程中添加代码以发送保活消息。代码需检查 WATCHDOG_USEC 环境变量是否为非零值,然后在该时间内(建议为看门狗超时时间的一半)调用 sd_notify(false, "WATCHDOG=1") systemd 源码中有相关示例。

要在服务单元中启用看门狗,可在 [Service] 部分添加如下内容:

WatchdogSec=30s 
Restart=on-watchdog 
StartLimitInterval=5min 
StartLimitBurst=4 
StartLimitAction=reboot-force 

在这个示例中,服务期望每30秒收到一次保活消息。若未收到,服务将重启;若在五分钟内重启次数超过四次, systemd 将强制立即重启。 systemd.service(5) 手册页中有这些设置的完整描述。

这种看门狗可照顾单个服务,但如果 systemd 本身故障、内核崩溃或硬件锁定,该怎么办?在这些情况下,需告知 systemd 使用看门狗驱动:只需在 /etc/systemd/system.conf 中添加 RuntimeWatchdogSec=NN systemd 会在该时间段内重置看门狗,因此若 systemd 因某种原因故障,系统将重置。

2. 对嵌入式Linux的影响

systemd 在嵌入式Linux中有很多实用功能,包括本文未提及的,如使用切片进行资源控制(在 systemd.slice(5) systemd.resource-control(5) 手册页中有描述)、设备管理( udev(7) )和系统日志功能( journald(5) )。

不过,要考虑其大小:即使仅构建核心组件 systemd udevd journald 的最小版本,包括共享库,也接近10 MiB的存储空间。

还需记住, systemd 的开发与内核紧密相关,因此它无法在比 systemd 发布时间早一两年以上的内核上运行。

3. 不同初始化程序的选择

每个Linux设备都需要某种初始化程序。若设计的系统在启动时只需启动少量守护进程,且之后状态相对稳定,那么 BusyBox init 就能满足需求。若使用Buildroot作为构建系统,它通常是不错的选择。

另一方面,若系统在启动或运行时服务间存在复杂依赖关系,且有足够存储空间,那么 systemd 是最佳选择。即便没有这些复杂性, systemd 在处理看门狗、远程日志等方面也有实用功能,值得认真考虑。

同时,System V init依然存在。它易于理解,且针对每个重要组件都已有初始化脚本。它仍是Yocto项目的Poky发行版的默认初始化程序。

在减少启动时间方面,对于类似工作负载, systemd 比System V init更快。然而,若追求极快启动,没有什么能比得上带有最少启动脚本的简单 BusyBox init

4. 电源管理的重要性

对于使用电池供电的设备,电源管理至关重要。任何能降低功耗的措施都能延长电池寿命。即使是使用市电供电的设备,降低功耗也有助于减少散热需求和能源成本。本文将介绍电源管理的四个原则:
- 不必匆忙时就不要着急
- 不要羞于闲置
- 关闭不用的设备
- 无事可做时就休眠

用更专业的术语来说,这些原则意味着电源管理系统应努力降低CPU时钟频率;在闲置期间,应选择最深的睡眠状态;通过关闭未使用的外设来减轻负载;并且应能将整个系统置于挂起状态。

Linux具备解决这些问题的功能。下面将逐一描述,并给出示例和建议,说明如何将它们应用于嵌入式系统,以实现电源的最佳利用。

5. 测量功耗

本文示例需使用真实硬件而非虚拟硬件,这意味着需要一个具备正常电源管理功能的BeagleBone Black。遗憾的是, meta-yocto-bsp 层附带的BeagleBone板级支持包(BSP)不包含电源管理集成电路(PMIC)所需的固件,因此需构建带有定制内核的镜像。具体步骤如下:
1. 获取Yocto Project Morty版本,并从代码存档中添加 meta-bbb-pm 层:

$ git clone -b morty git://git.yoctoproject.org/poky.git
$ cd poky
$ cp -a MELP/chapter_11/poky/meta-bbb-pm
$ source oe-init-build-env build-bbb
$ bitbake-layers add-layer ../meta-bbb-pm
  1. 编辑 conf/local.conf ,并在文件开头添加以下行:
MACHINE = "beaglebone"
PREFERRED_PROVIDER_virtual/kernel = "ti-linux-kernel"
IMAGE_INSTALL_append = " powertop dropbear rt-tests amx3-cm3 kernel-vmlinux"
  1. 构建镜像,例如 core-image-minimal
$ bitbake core-image-minimal
  1. 格式化微型SD卡,并使用MELP存档中的脚本将镜像复制到卡上:
$ MELP/format-sdcard.sh [your sdcard reader]
$ MELP/copy-yoctoproject-image-to-sdcard.sh \
beaglebone core-image-minimal
  1. 启动BeagleBone Black并检查电源管理是否正常工作:
# cat /sys/power/state
freeze standby mem disk

若看到所有四种状态,则一切正常;若只看到 freeze ,则电源管理子系统未正常工作,需返回并仔细检查前面的步骤。

测量功耗有两种方法:外部测量和内部测量。从系统外部进行外部测量时,只需用电流表测量电流,用电压表测量电压,然后将两者相乘得到功率。可以使用能给出读数的基本仪表,然后记录下来;也可以使用更复杂的、具备数据记录功能的仪表,以便能看到负载变化时功率的毫秒级变化。本文中,使用迷你USB端口为BeagleBone供电,并使用了一个售价几美元的廉价USB电源监视器。

另一种方法是使用Linux内置的监控系统。通过 sysfs 可获取大量信息。还有一个非常实用的程序 PowerTOP ,它能从多个来源收集信息并集中展示。 PowerTOP 是Yocto Project和Buildroot的软件包。在前面提供的Yocto Project配置中,已将 powertop 作为附加软件包包含在内。以下是 PowerTOP 在BeagleBone Black上运行的示例,从截图中可看到系统处于安静状态,CPU使用率仅为0.4%。

6. 时钟频率的缩放

跑一千米比走一千米消耗更多能量。类似地,以较低频率运行CPU或许能节省能量。CPU执行代码时的功耗是静态组件和动态组件之和,静态组件由门泄漏电流等因素引起,动态组件由门的开关引起:
[P_{cpu} = P_{static} + P_{dyn}]
动态功率组件取决于被切换的逻辑门的总电容、时钟频率和电压的平方:
[P_{dyn} = CfV^2]
由此可见,仅改变频率本身不会节省任何功率,因为执行给定子程序所需的CPU周期数是固定的。若将频率降低一半,完成计算所需的时间将加倍,但动态功率组件消耗的总功率不变。实际上,降低频率可能会增加功率预算,因为CPU进入空闲状态所需的时间更长。因此,在这种情况下,最好使用尽可能高的频率,以便CPU能快速返回空闲状态,这称为“争分夺秒进入空闲状态”。

降低频率还有另一个动机:为了将封装温度控制在范围内,可能需要以较低频率运行。但这不是本文的重点。

因此,若要节省功率,必须能够改变CPU核心的工作电压。但对于任何给定电压,都有一个最大频率,超过该频率,门的开关将变得不可靠。更高的频率需要更高的电压,因此两者需要一起调整。许多SoC实现了这样的功能,称为动态电压和频率缩放(DVFS)。制造商计算出核心频率和电压的最佳组合,每个组合称为操作性能点(OPP)。高级配置和电源接口(ACPI)规范将它们称为P状态,其中P0是频率最高的OPP。尽管OPP是频率和电压的组合,但通常仅通过频率组件来引用它们。

7. CPUFreq驱动

Linux有一个名为 CPUFreq 的组件,用于管理OPP之间的转换。它是每个SoC软件包板级支持的一部分。 CPUFreq drivers/cpufreq/ 中的驱动程序和一组实现切换策略的调节器组成,驱动程序用于实现从一个OPP到另一个OPP的转换。每个CPU通过 /sys/devices/system/cpu/cpuN/cpufreq 目录进行控制,其中N是CPU编号。在该目录中,有许多文件,其中最有趣的如下:
- cpuinfo_cur_freq cpuinfo_max_freq cpuinfo_min_freq :该CPU的当前频率以及最大和最小频率,以KHz为单位。
- cpuinfo_transition_latency :从一个OPP切换到另一个OPP所需的时间,以纳秒为单位。若值未知,则设置为 -1。
- scaling_available_frequencies :该CPU可用的OPP频率列表。
- scaling_available_governors :该CPU可用的调节器列表,具体如下:
- powersave :始终选择最低频率。
- performance :始终选择最高频率。
- ondemand :根据CPU利用率改变频率。若CPU空闲时间少于20%,则将频率设置为最高;若空闲时间超过30%,则将频率降低5%。
- conservative :与 ondemand 类似,但以5%的步长切换到更高频率,而不是立即切换到最高频率。
- userspace :频率由用户空间程序设置。
- scaling_governor :当前使用的 CPUFreq 调节器。
- scaling_max_freq scaling_min_freq :调节器可用的频率范围,以KHz为单位。
- scaling_setspeed :当调节器为 userspace 时,可通过写入该文件手动设置频率。

调节器设置改变OPP的策略,可在 scaling_min_freq scaling_max_freq 的限制范围内设置频率。 ondemand 调节器用于决定何时改变OPP的参数可通过 /sys/devices/system/cpu/cpufreq/ondemand/ 查看和修改。 ondemand conservative 调节器都会考虑改变频率和电压所需的努力,该参数在 cpuinfo_transition_latency 中。这些计算适用于具有正常调度策略的线程;若线程按实时调度,则两者都会立即选择最高OPP,以便线程能满足其调度期限。

userspace 调节器允许用户空间守护进程执行选择OPP的逻辑,例如 cpudyn powernowd ,但它们都更适用于基于x86的笔记本电脑,而非嵌入式设备。

8. 使用CPUFreq

以BeagleBone Black为例,其OPP编码在设备树中。以下是 am33xx.dtsi 的摘录:

cpu0_opp_table: opp-table {
  compatible = "operating-points-v2-ti-cpu";
  syscon = <&scm_conf>;
  opp50@300000000 {
    opp-hz = /bits/ 64 <300000000>;
    opp-microvolt = <950000 931000 969000>;
    opp-supported-hw = <0x06 0x0010>;
    opp-suspend;
  };
  opp100@600000000 {
    opp-hz = /bits/ 64 <600000000>;
    opp-microvolt = <1100000 1078000 1122000>;
    opp-supported-hw = <0x06 0x0040>;
  };
  opp120@720000000 {
    opp-hz = /bits/ 64 <720000000>;
    opp-microvolt = <1200000 1176000 1224000>;
    opp-supported-hw = <0x06 0x0080>;
  };
  oppturbo@800000000 {
    opp-hz = /bits/ 64 <800000000>;
    opp-microvolt = <1260000 1234800 1285200>;
    opp-supported-hw = <0x06 0x0100>;
  };
  oppnitro@1000000000 {
    opp-hz = /bits/ 64 <1000000000>;
    opp-microvolt = <1325000 1298500 1351500>;
    opp-supported-hw = <0x04 0x0200>;
  };
};

可通过查看可用频率来确认运行时使用的OPP:

# cd /sys/devices/system/cpu/cpu0/cpufreq
# cat scaling_available_frequencies
300000 600000 720000 800000 1000000 

通过选择用户空间调节器,可通过写入 scaling_setspeed 来设置频率,从而测量每个OPP的功耗。这些测量并不十分准确,不必过于当真。

首先,在系统空闲时,结果为70mA @ 4.6V = 320 mW,这与频率无关,因为这是该特定系统功耗的静态组件。

现在,通过运行如下计算密集型负载,可了解每个OPP的最大功耗:

# dd if=/dev/urandom of=/dev/null bs=1

结果如下表所示,其中“Delta power”是相对于空闲系统的额外功耗:
| OPP | Freq, KHz | Power, mW | Delta power, mW |
| — | — | — | — |
| OPP50 | 300,000 | 370 | 50 |
| OPP100 | 600,000 | 505 | 185 |
| OPP120 | 720,000 | 600 | 280 |
| Turbo | 800,000 | 640 | 320 |
| Nitro | 1,000,000 | 780 | 460 |

这些测量显示了不同OPP下的最大功耗,但这并非公平测试,因为CPU以100%的利用率运行,因此在更高频率下执行的指令更多。若保持负载恒定但改变频率,可得到以下结果:
| OPP | Freq, KHz | CPU utilization, % | Power, mW |
| — | — | — | — |
| OPP50 | 300,000 | 94 | 320 |
| OPP100 | 600,000 | 48 | 345 |
| OPP120 | 720,000 | 40 | 370 |
| Turbo | 800,000 | 34 | 370 |
| Nitro | 1,000,000 | 28 | 370 |

这表明在最低频率下有明显的节能效果,约为15%。使用 PowerTOP 可查看在每个OPP上花费的时间百分比。通常情况下, ondemand 调节器是最佳选择。要选择特定的调节器,可通过配置内核设置默认调节器,例如 CPU_FREQ_DEFAULT_GOV_ONDEMAND ,也可使用启动脚本来在启动时更改调节器。在 MELP/chapter_11/sysvinit-ondemand.sh 中有一个来自Ubuntu 14.04的System V(SysV)初始化脚本示例。若需了解更多关于 CPU-freq 驱动的信息,可查看内核源码中 Documentation/cpu-freq 目录。

9. 选择最佳空闲状态

前面关注的是CPU忙碌时的功耗,接下来将探讨CPU空闲时如何节省功率。

当处理器没有更多工作要做时,会执行暂停指令并进入空闲状态。空闲时,CPU功耗降低。当发生硬件中断等事件时,CPU会退出空闲状态。大多数CPU有多个空闲状态,功耗各不相同。通常,功耗和退出状态所需的延迟(即时间长度)之间存在权衡。在ACPI规范中,这些状态称为C状态。

在更深的C状态下,更多电路会关闭,但会丢失一些状态,因此恢复到正常操作所需的时间更长。例如,在某些C状态下,CPU缓存可能会断电,因此当CPU再次运行时,可能需要从主内存重新加载一些信息,这代价高昂,因此只有在CPU可能长时间处于该状态时才值得这么做。不同系统的状态数量不同,每个状态从睡眠恢复到完全活动都需要一定时间。

选择正确空闲状态的关键是要大致了解CPU将静止多长时间。预测未来总是很困难,但有一些因素可以提供帮助。一是当前的CPU负载:若当前负载较高,短期内可能仍会如此,那么深度睡眠就没有好处。即使负载较低,也值得查看是否有即将到期的定时器事件。若没有负载且没有定时器,那么选择更深的空闲状态是合理的。

Linux中选择最佳空闲状态的部分是 CPUIdle 驱动,内核源码的 Documentation/cpuidle 目录中有关于它的大量信息。

10. 外设的断电管理

除了调整CPU的频率和选择合适的空闲状态,关闭未使用的外设也是电源管理的重要一环。许多嵌入式系统配备了各种各样的外设,如USB接口、SD卡插槽、蓝牙模块、Wi-Fi模块等。当这些外设不被使用时,让它们保持通电状态会白白消耗电能。

在Linux系统中,可以通过sysfs接口来控制外设的电源状态。每个外设通常在 /sys/bus 目录下有对应的设备节点,通过向这些节点写入特定的值,可以开启或关闭外设的电源。例如,对于某些USB设备,可以通过以下步骤进行电源管理:
1. 找到USB设备对应的sysfs节点,通常在 /sys/bus/usb/devices 目录下。
2. 进入该设备的目录,查找名为 power/control 的文件。
3. 若要关闭设备电源,向 power/control 文件写入 auto ;若要开启设备电源,写入 on

# 假设USB设备的sysfs节点为/sys/bus/usb/devices/1-1
# 关闭设备电源
echo auto > /sys/bus/usb/devices/1-1/power/control
# 开启设备电源
echo on > /sys/bus/usb/devices/1-1/power/control

对于其他类型的外设,如SD卡插槽、蓝牙模块等,也有类似的控制方法,只是对应的sysfs节点和控制文件可能不同。在实际应用中,需要根据具体的硬件和驱动来查找和使用相应的控制接口。

11. 系统休眠策略

当系统没有任何任务需要处理时,将整个系统置于休眠状态是最有效的节能方式。Linux支持多种休眠状态,不同的休眠状态在功耗和恢复时间上有所不同。

在ACPI规范中,常见的休眠状态有 freeze standby mem disk
- freeze :系统冻结,暂停所有用户空间进程,关闭部分设备,但内存保持通电,恢复速度较快。
- standby :除了冻结用户空间进程和关闭部分设备外,CPU也进入低功耗状态,恢复时间稍长。
- mem :将系统状态保存到内存中,关闭大部分设备,只有内存保持通电,恢复速度较快,功耗较低。
- disk :将系统状态保存到磁盘中,关闭所有设备,恢复时需要从磁盘重新加载系统状态,恢复时间较长,但功耗最低。

可以通过向 /sys/power/state 文件写入相应的状态名称来使系统进入休眠状态:

# 使系统进入freeze状态
echo freeze > /sys/power/state
# 使系统进入mem状态
echo mem > /sys/power/state

在选择休眠状态时,需要根据实际需求进行权衡。如果预计系统很快会被唤醒,如几分钟内,那么 freeze standby 状态可能更合适;如果系统长时间不需要运行,如几小时甚至几天,那么 mem disk 状态可以显著节省电能。

12. 电源管理的综合应用

在实际的嵌入式系统中,需要综合运用上述电源管理技术,根据系统的负载情况和使用场景动态调整电源策略。以下是一个简单的流程图,展示了如何根据CPU负载和定时器事件来选择合适的电源管理策略:

graph TD;
    A[系统启动] --> B{CPU负载高?};
    B -- 是 --> C[保持高频率运行];
    B -- 否 --> D{有即将到期的定时器事件?};
    D -- 是 --> E[选择较浅的空闲状态];
    D -- 否 --> F[选择较深的空闲状态];
    C --> G{长时间无负载?};
    G -- 是 --> H[关闭未使用的外设];
    G -- 否 --> C;
    E --> I{长时间无负载?};
    I -- 是 --> H;
    I -- 否 --> E;
    F --> J{长时间无负载?};
    J -- 是 --> H;
    J -- 否 --> F;
    H --> K{系统长时间空闲?};
    K -- 是 --> L[进入休眠状态];
    K -- 否 --> M[继续监测负载和定时器];
    L --> N{有唤醒事件?};
    N -- 是 --> O[恢复系统运行];
    N -- 否 --> L;
    O --> M;
    M --> B;

在这个流程图中,系统首先判断CPU负载是否高。如果负载高,则保持高频率运行;如果负载低,则进一步判断是否有即将到期的定时器事件,根据情况选择合适的空闲状态。当系统长时间无负载时,关闭未使用的外设。如果系统长时间空闲,则进入休眠状态。当有唤醒事件发生时,系统恢复运行,继续监测负载和定时器,循环执行上述过程。

13. 总结与建议

通过对嵌入式Linux系统的电源管理技术的介绍,我们了解到可以通过多种方式来降低系统的功耗,延长电池寿命或减少能源成本。以下是一些总结和建议:
- 选择合适的初始化程序 :根据系统的需求和特点,选择合适的初始化程序,如 BusyBox init systemd 或System V init。如果系统简单且对启动时间要求极高,可选择 BusyBox init ;如果系统复杂且需要更多的功能支持, systemd 是更好的选择。
- 合理调整CPU频率 :根据系统的负载情况,合理调整CPU的频率。在负载较低时,降低频率可以节省电能;在负载较高时,提高频率可以保证系统的性能。可以使用 CPUFreq 驱动和相应的调节器来实现频率的动态调整。
- 选择最佳空闲状态 :在CPU空闲时,选择合适的空闲状态,以降低功耗。 CPUIdle 驱动可以帮助我们根据系统的情况选择最佳的空闲状态。
- 关闭未使用的外设 :定期检查系统中未使用的外设,并将其电源关闭,以减少不必要的功耗。
- 适时进入休眠状态 :当系统长时间无任务时,将系统置于休眠状态,以最大程度地节省电能。

通过综合运用这些电源管理技术,可以使嵌入式Linux系统在性能和功耗之间取得良好的平衡,满足不同应用场景的需求。在实际开发中,需要根据具体的硬件平台和应用需求进行测试和优化,以达到最佳的电源管理效果。

内容概要:本文介绍了一套针对智能穿戴设备的跑步/骑行轨迹记录系统实战方案,旨在解决传统运动APP存在的定位漂移、数据断层和路径分析单一等问题。系统基于北斗+GPS双模定位、惯性测量单元(IMU)和海拔传感器,实现高精度轨迹采集,并通过卡尔曼滤波算法修正定位误差,在信号弱环境下利用惯性导航补位,确保轨迹连续性。系统支持跑步骑行两种场景的差异化功能,包括实时轨迹记录、多维度路径分析(如配速、坡度、能耗)、数据可视化(地图标注、曲线图、3D回放)、异常提醒及智能优化建议,并可通过蓝牙/Wi-Fi同步数据至手机APP,支持社交分享专业软件导出。技术架构涵盖硬件层、设备端手机端软件层以及云端数据存储,强调低功耗设计用户体验优化。经过实测验证,系统在定位精度、续航能力和场景识别准确率方面均达到预期指标,具备良好的实用性和扩展性。; 适合人群:具备一定嵌入式开发或移动应用开发经验,熟悉物联网、传感器融合数据可视化的技术人员,尤其是从事智能穿戴设备、运动健康类产品研发的工程师和产品经理;也适合高校相关专业学生作为项目实践参考。; 使用场景及目标:① 开发高精度运动轨迹记录功能,解决GPS漂移断点问题;② 实现跑步骑行场景下的差异化数据分析个性化反馈;③ 构建完整的“终端采集-手机展示-云端存储”系统闭环,支持社交互动商业拓展;④ 掌握低功耗优化、多源数据融合、动态功耗调节等关键技术在穿戴设备中的落地应用。; 阅读建议:此资源以真实项目为导向,不仅提供详细的技术实现路径,还包含硬件选型、测试验证商业扩展思路,建议读者结合自身开发环境,逐步实现各模块功能,重点关注定位优化算法、功耗控制策略跨平台数据同步机制的设计调优。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值