一、IWDG简介
- IWDG有什么作用?
二、IWDG工作原理
三、IWDG框图
四、IWDG寄存器
- 键寄存器(IWDG_KR)
- 预分频器寄存器 (IWDG_PR)
- 重装载寄存器(IWDG_RLR)
- 状态寄存器(IWDG_SR)
- 寄存器配置操作步骤
五、IWDG溢出时间计算
- IWDG溢出时间计算公式(HAL库)
- 寄存器设置分频系数的方法
- IWDG溢出时间计算公式(寄存器)
- IWDG最短最长超时时间
六、IWDG配置步骤
七、编程实战:验证不及时喂狗,系统将复位重启
八、总结
一、IWDG简介
IWDG(Independent Watchdog,独立看门狗)的基本概念:
-
IWDG的全称: IWDG全称为Independent Watchdog,它是一种用于监控系统运行的硬件设备。
-
IWDG的本质: IWDG是一个递减的计数器,如果在一段时间内没有被重新加载(喂狗),则会产生系统复位信号。它是为了防止系统由于某些原因(例如程序错误或系统死锁)导致无响应。
-
IWDG的时钟: IWDG的时钟通常由独立的RC(Resistor-Capacitor)振荡器提供。这使得IWDG能够在待机(Standby)和停止(Stop)模式下运行,即使主系统时钟停止,IWDG仍然能够正常工作。
-
复位触发条件: IWDG的计数器递减,当计数器的值减至0x000时,会产生复位。这种设计保证了即使系统出现问题,也会在一定时间内进行复位。
-
喂狗: 喂狗是指在IWDG计数器减至0之前,通过重新加载计数器的值来防止复位。通过定期喂狗,可以确保系统正常运行,否则IWDG会在计数器减至0时触发复位。
IWDG通常是嵌入式系统中的一个重要功能,用于提高系统的稳定性和可靠性。在实际应用中,程序员需要根据系统的实际需求配置IWDG,并在合适的位置定期喂狗,以确保系统不会因为某些异常情况而一直停滞。
STM32F10xxx闪存编程手册
IWDG有什么作用?
IWDG的作用的关键点:
-
检测异常: IWDG主要用于检测系统中的异常情况,包括但不限于外界电磁干扰、硬件异常或软件异常。当系统出现无法处理的异常时,可能导致程序失控、死循环等问题,此时IWDG可以通过触发复位来使系统重新启动,从而恢复正常运行。
-
电磁干扰: 在一些嵌入式系统中,特别是一些对抗电磁干扰要求较高的应用场景,IWDG可以起到一种保护作用。外界的电磁干扰有可能导致系统产生错误,而IWDG则能够在检测到异常时及时复位系统,以保障系统的稳定性。
-
硬件异常: 除了电磁干扰,IWDG还能够监测到一些硬件异常,比如电源波动、时钟故障等。这些硬件异常可能导致系统运行不正常,而IWDG可以在检测到这些异常时采取措施。
-
最后手段: IWDG通常被看作是异常处理的最后手段。当其他的异常处理机制都无法正常工作或者不可靠时,IWDG可以作为一个保险机制,确保在极端情况下系统可以重新启动。
-
应用场景: IWDG适用于对系统稳定性要求较高、对时间精度要求较低的场合。在一些嵌入式产品中,特别是一些安全关键系统或长时间运行的系统中,使用IWDG可以提高系统的可靠性。
-
依赖性: 尽管IWDG是一个有力的异常处理手段,但设计时应避免对它的过度依赖。最好的做法是在系统设计和软件编程中尽量避免异常的发生,使得IWDG只在极端情况下起到最后的保护作用。
二、IWDG工作原理
IWDG的工作原理:
-
时钟源 (Src CLK): IWDG的工作始于一个时钟源,该时钟源通常是一个独立的RC振荡器。这个时钟源提供了IWDG的时基。
-
预分频器 (PSC): 时钟源进入预分频器,预分频器用于将时钟源的频率进行分频。预分频因子决定了IWDG的时钟频率,进而影响计数器的递减速度。
-
IWDG时钟 (IWDG CLK): 经过预分频后的时钟被用作IWDG的时钟,这个时钟将用于递减计数器。
-
递减计数器 (CNT): 递减计数器是IWDG的核心组件。它以IWDG时钟的速率递减,当计数器减至零时,触发相应的操作,通常是产生复位信号。
-
重装载寄存器 (RELOAD): 在IWDG工作期间,可以通过将重装载寄存器的值写入CNT寄存器,重置计数器。这就是“喂狗”的概念。如果在计数器递减至零之前及时进行重装载,系统就不会因为IWDG的复位而重新启动。
-
复位: 当计数器递减至零时,IWDG触发相应的操作,通常是系统复位。这样可以确保系统在长时间没有喂狗的情况下仍能重新启动,从而防止系统因为外界干扰或软硬件错误而陷入死循环或无法响应的状态。
整个过程通过控制时钟源、预分频、递减计数器和重装载寄存器,实现了对系统的监控和复位功能,确保系统在异常情况下能够恢复到正常工作状态。
三、IWDG框图
IWDG的框图和各个组成部分的功能描述的一些关键点:
-
低速内部(LSI) RC振荡器: IWDG的时钟源是LSI(低速内部)RC振荡器。在启用IWDG后,LSI时钟会自动开启。需要注意的是,LSI的时钟频率可能不够精确,因此在计算时需要注意。
-
预分频器寄存器 (IWDG_PR): 8位预分频器用于对LSI时钟进行预分频,从而得到IWDG的时钟。通过调整预分频因子,可以调整IWDG的工作频率,影响计数器的递减速度。
-
状态寄存器 (IWDG_SR): 状态寄存器用于监测IWDG的状态,其中包括计数器的运行状态和复位状态。
-
递减计数器 (IWDG_CNT): 12位递减计数器是IWDG的核心部分。它以预分频后的时钟为基准递减,当计数器减至零时,触发相应的操作。
-
密钥寄存器 (IWDG_KR): 密钥寄存器用于对IWDG进行操作的授权。写入指定值后,才能对IWDG进行启动、重装载等操作。
-
重载寄存器 (IWDG_RLR): 重载寄存器用于设置IWDG的重装载值,即重新加载计数器的值。通过定期对该寄存器进行写入,可以避免IWDG触发复位。
-
看门狗功能供电: IWDG的功能是由 V D D V_{DD} VDD电压域供电的,这意味着即使在停止模式和待机模式下,IWDG仍然能够正常工作。
整个框图清晰地展示了IWDG的各个组成部分,并说明了它们之间的关系和作用。这样的硬件设计使得IWDG能够在异常情况下提供可靠的系统监控和复位功能。
四、IWDG寄存器
键寄存器(IWDG_KR)
键寄存器可以看作是独立看门狗的控制寄存器
在键寄存器(IWDG_KR)中写入 0xCCCC,开始启用独立看门狗;此时计数器开始从其复位值 0xFFF 递减计数。当计数器计数到末尾 0x000 时,会产生一个复位信号(IWDG_RESET)。无论何时,只要键寄存器 IWDG_KR 中被写入 0xAAAA,IWDG_RLR 中的值就会被重新加载到计数器中从而避免产生看门狗复位。
IWDG_PR 和 IWDG_RLR 寄存器具有写保护功能。要修改这两个寄存器的值,必须先向IWDG_KR 寄存器中写入 0x5555。将其他值写入这个寄存器将会打乱操作顺序,寄存器将重新被保护。重装载操作(即写入 0xAAAA)也会启动写保护功能。
(若选择了硬件看门狗则不受此命令字限制)
预分频器寄存器 (IWDG_PR)
该寄存器用来设置看门狗时钟(LSI)的分频系数,最低为 4,最高位 256,该寄存器是一个 32 位的寄存器,但是我们只用了最低 3 位,其他都是保留位。
预分频器寄存器(IWDG_PR)是独立看门狗(IWDG)中的一个寄存器,用于设置预分频因子,从而调整IWDG的时钟频率。IWDG的时钟频率直接影响计数器的递减速度。
预分频器寄存器 (IWDG_PR) 用于设置 IWDG 的预分频因子,从而确定 IWDG 的时钟频率。这个预分频因子决定了递减计数器的速度,进而影响看门狗的定时周期。IWDG_PR 寄存器是一个 3 位的寄存器,可以设置的预分频因子如下:
Bit 2:0 PR[2:0]: Prescaler divider
000: f(LSI) / 4
001: f(LSI) / 8
010: f(LSI) / 16
011: f(LSI) / 32
100: f(LSI) / 64
101: f(LSI) / 128
110: f(LSI) / 256
111: f(LSI) / 256
- 000: IWDG时钟 = LSI / 4
- 001: IWDG时钟 = LSI / 8
- 010: IWDG时钟 = LSI / 16
- 011: IWDG时钟 = LSI / 32
- 100: IWDG时钟 = LSI / 64
- 101: IWDG时钟 = LSI / 128
- 110: IWDG时钟 = LSI / 256
- 111: IWDG时钟 = LSI / 256
这里的f(LSI)
表示低速内部(LSI)RC振荡器的频率。通过设置PR位,可以选择不同的预分频因子,从而得到不同的IWDG时钟频率。
例如,如果设置PR位为010
,则预分频因子为16,IWDG的时钟频率将是LSI时钟频率的1/16。这会使得计数器的递减速度减缓,增加喂狗的时间间隔。
在使用IWDG时,你可以根据实际需求选择适当的预分频因子,以平衡系统对看门狗的监测精度和系统响应速度的要求。
通过选择适当的预分频因子,可以调整 IWDG 的时钟频率,以满足特定的定时需求。更小的预分频因子将导致更快的计数器递减速度,而更大的预分频因子将导致更慢的计数器递减速度。在实际应用中,需要根据具体的系统需求和预期的看门狗定时周期来选择合适的预分频因子。
重装载寄存器(IWDG_RLR)
该寄存器用来保存重装载到计数器中的值。该寄存器也是一个 32 位寄存器,只有低 12 位是有效的。
重装载寄存器(IWDG_RLR)是 IWDG 的一个重要寄存器,用于设置 IWDG 的重载值。这个值决定了递减计数器的初始值,也就是看门狗的计数周期。当递减计数器递减到零时,会触发相应的操作,通常是产生复位信号。
IWDG_RLR 是一个 12 位的寄存器,可以设置的范围是 0 到 0xFFF(4095)。这个数值表示递减计数器的初始值。在 IWDG 启动后,计数器开始递减,当递减计数器减至零时,会产生复位。为了防止复位发生,需要定期“喂狗”(重新加载计数器)。
重装载寄存器的使用步骤:
- 初始化 IWDG_RLR,设置递减计数器的初始值。这通常在系统初始化的时候进行。
IWDG_RLR = 0x0FFF; // 设置重载值,决定计数周期
- 启动 IWDG。一旦 IWDG 启动,递减计数器开始递减。
IWDG_KR = 0xAAAA; // 启动 IWDG
- 定期“喂狗”,重新加载计数器。这通常在程序的主循环中或者其他适当的位置进行。
IWDG_KR = 0xAAAA; // 喂狗,重新加载计数器
通过调整重装载寄存器的值,可以实现对看门狗定时周期的控制,满足系统的监控和复位需求。
状态寄存器(IWDG_SR)
状态寄存器 (IWDG_SR) 是 IWDG(Independent Watchdog,独立看门狗)的一个寄存器,用于监控 IWDG 的运行状态。IWDG_SR 寄存器的位域描述了 IWDG 的状态信息。以下是 IWDG_SR 寄存器的主要位域:
-
Bit 0 - RVU: 重装载值更新
- 0: 重装载寄存器的值没有被更新。
- 1: 重装载寄存器的值已经被更新。
-
Bit 1 - PVU: 预分频器值更新
- 0: 预分频器寄存器的值没有被更新。
- 1: 预分频器寄存器的值已经被更新。
-
Bit 2 - WVU: 窗口寄存器值更新
- 0: 窗口寄存器的值没有被更新。
- 1: 窗口寄存器的值已经被更新。
-
Bit 3 - KEY: 密钥值
- 0: 密钥寄存器的值没有被更新。
- 1: 密钥寄存器的值已经被更新。
使用状态寄存器的步骤:
-
检查 RVU、PVU、WVU、KEY 位: 在对 IWDG 进行任何配置之前,可以检查这些位,确保相关寄存器的值已经被更新。
-
配置重载值(RLR)、预分频器值(PR)和窗口寄存器值(WINR): 根据系统需求配置相应的寄存器。
-
更新密钥寄存器(KR): 将 IWDG_KR 寄存器写入 0x5555,然后写入 0xAAAA,以更新相关寄存器的值。
-
检查状态寄存器位: 可以通过检查 RVU、PVU、WVU、KEY 位来确认更新是否成功。
总体而言,状态寄存器用于提供关于 IWDG 配置的更新状态信息,确保相关寄存器的值在使用前已经被更新。
寄存器配置操作步骤
配置STM32上独立看门狗(IWDG)的寄存器的操作。下面是对每个步骤的详细解释:
-
使能IWDG:
通过在键寄存器(IWDG_KR)中写入0xCCCC
,可以使能IWDG。这个操作告诉IWDG开始工作,启动递减计数器。 -
使能寄存器访问:
通过在键寄存器(IWDG_KR)中写入0x5555
,可以使能对IWDG相关寄存器的访问。这个步骤通常在修改IWDG配置之前进行,确保能够对寄存器进行写操作。 -
配置预分频器:
通过将预分频器寄存器(IWDG_PR)的值设置为0到7之间的数值,可以配置IWDG的预分频器。预分频器用于将IWDG的时钟源进行分频,以设置递减计数器的计数速度。 -
写入重载寄存器:
通过对重载寄存器(IWDG_RLR)进行写操作,设置递减计数器的初始值。这个值决定了IWDG的计数周期。 -
等待寄存器更新:
在写入寄存器配置后,等待寄存器更新。可以通过检查状态寄存器(IWDG_SR)的值,确认相关寄存器的更新状态。通常等待IWDG_SR等于0x0000 0000。 -
刷新计数器:
最后,通过在键寄存器(IWDG_KR)中写入0xAAAA
来刷新计数器。这个操作相当于“喂狗”,重新加载计数器的值,以防止IWDG产生复位信号。
这些步骤确保了IWDG的正确配置和运行,以提供对系统的监控和保护。
五、IWDG溢出时间计算
IWDG溢出时间计算公式(HAL库)
在HAL库中,IWDG(Independent Watchdog,独立看门狗)的溢出时间 T o u t T_{out} Tout 可以通过以下公式计算:
T o u t = p s c × r l r f IWDG T_{out} = \frac{psc \times rlr}{f_{\text{IWDG}}} Tout=fIWDGpsc×rlr
其中:
- T o u t T_{out} Tout 是看门狗的溢出时间。
- f IWDG f_{\text{IWDG}} fIWDG 是看门狗的时钟源频率。
- p s c psc psc 是看门狗的预分频系数。
- r l r rlr rlr 是看门狗的重装载值。
这个公式描述了看门狗计数器的计数时间,即在重新加载计数器之前的时间。通常, T o u t T_{out} Tout 可以用来估算系统的监视时间,确保系统在这个时间内喂狗,防止看门狗产生复位信号。
推导IWDG溢出时间的公式
推导IWDG溢出时间的公式涉及时钟源频率、预分频系数和重装载值等参数。以下是一个简化的推导过程:
-
IWDG时钟源频率 f IWDG f_{\text{IWDG}} fIWDG:
在HAL库中,IWDG的时钟源通常是LSI(低速内部振荡器)。该频率通常在芯片手册中给出,以 H z Hz Hz为单位。假设为 f IWDG f_{\text{IWDG}} fIWDG。 -
IWDG预分频系数 p s c psc psc:
预分频系数 p s c psc psc 用于将时钟源的频率进行分频,以降低计数器的计数速度。 p s c psc psc 的值可以设置为0到7之间的整数。 -
IWDG重装载值 r l r rlr rlr:
重装载值 r l r rlr rlr 决定了IWDG计数器的初始值,即溢出时间的起点。 r l r rlr rlr 也是可以根据系统需求设置的值。 -
IWDG计数器的溢出时间 T out T_{\text{out}} Tout:
IWDG的计数器溢出时间可以通过以下公式计算:
T out = p s c × r l r f IWDG T_{\text{out}} = \frac{psc \times rlr}{f_{\text{IWDG}}} Tout=fIWDGpsc×rlr
推导的步骤:
-
计算分频后的时钟频率:
通过时钟频率 f IWDG f_{\text{IWDG}} fIWDG 和看门狗预分频系数 p s c psc psc 的除法得到分频后的时钟频率 f IWDG p s c \frac{f_{\text{IWDG}}}{psc} pscfIWDG。这个值表示IWDG实际工作时的时钟频率,即一秒钟内IWDG可以进行的计数次数。 -
计算一次计数需要的时间:
取上一步计算的分频后时钟频率的倒数,即 1 f IWDG p s c \frac{1}{\frac{f_{\text{IWDG}}}{psc}} pscfIWDG1,这个值表示IWDG进行一次计数所需的时间。 -
计算溢出时间:
将上一步得到的一次计数时间乘以看门狗重装载值 r l r rlr rlr,即 p s c f IWDG × r l r \frac{psc}{f_{\text{IWDG}}} \times rlr fIWDGpsc×rlr,得到的结果就是看门狗的溢出时间。
这个公式的推导基于时钟源的分频、重装载值和时钟频率的关系。确保 T out T_{\text{out}} Tout 不超过IWDG的设定溢出时间,以免系统因未及时喂狗而触发复位。
寄存器设置分频系数的方法
在STM32系列的IWDG中,预分频系数 p s c psc psc 与寄存器 I W D G _ P R IWDG\_PR IWDG_PR 的设置关系为:
p s c = 4 × 2 p r e r psc = 4 \times 2^{prer} psc=4×2prer
其中:
- p r c r prcr prcr 是 I W D G _ P R IWDG\_PR IWDG_PR 寄存器的设置值。
这个公式表达了预分频系数 p s c psc psc 与 I W D G _ P R IWDG\_PR IWDG_PR 寄存器的值之间的关系。通过调整 I W D G _ P R IWDG\_PR IWDG_PR 的值,可以间接地影响 p s c psc psc,从而调整IWDG的时钟频率。在设置IWDG时,需要根据具体的应用和需求选择合适的 I W D G _ P R IWDG\_PR IWDG_PR 的值,以满足对看门狗的计数速度的要求。
IWDG溢出时间计算公式(寄存器)
在使用寄存器配置IWDG时,计算溢出时间的公式可以表示为:
T o u t = ( 4 × 2 p r e r ) × r l r f IWDG T_{out} = \frac{(4 \times 2^{prer}) \times rlr}{f_{\text{IWDG}}} Tout=fIWDG(4×2prer)×rlr
其中:
- T o u t T_{out} Tout 是IWDG的溢出时间。
- f IWDG f_{\text{IWDG}} fIWDG 是IWDG的时钟源频率,通常是LSI的频率。
- p r e r prer prer 是 I W D G _ P R IWDG\_PR IWDG_PR 寄存器的设置值,决定了预分频系数。
- r l r rlr rlr 是IWDG的重装载值,决定了计数器的初始值。
这个公式描述了IWDG计数器的溢出时间,即在重新加载计数器之前的时间。确保 T o u t T_{out} Tout 不超过IWDG的设定溢出时间,以免系统因未及时喂狗而触发复位。
IWDG最短最长超时时间
在STM32系列中,IWDG的时钟源通常是LSI(低速内部振荡器),其频率为40kHz和32kHz。根据这个时钟源频率,我们可以计算IWDG的最短和最长超时时间。
-
最短超时时间:
由于IWDG是一个12位的递减计数器,最短超时时间为计数器值为1时的时间。因此,最短超时时间 $ T_{\text{min}}$ 可以通过以下公式计算:
T min = 1 LSI频率 T_{\text{min}} = \frac{1}{\text{LSI频率}} Tmin=LSI频率1 -
最长超时时间:
最长超时时间取决于IWDG的重装载值 r l r rlr rlr 的设置。假设 r l r rlr rlr 设置为最大值(即4095),最长超时时间 T max T_{\text{max}} Tmax 可以通过以下公式计算:
T max = 4095 × 4 × 2 p r e r LSI频率 T_{\text{max}} = \frac{4095 \times 4 \times 2^{prer}}{\text{LSI频率}} Tmax=LSI频率4095×4×2prer
其中, p r e r prer prer 是 I W D G _ P R IWDG\_PR IWDG_PR 寄存器的设置值,决定了预分频系数。确保 T max T_{\text{max}} Tmax 不超过IWDG的设定溢出时间,以免系统因未及时喂狗而触发复位。
IWDG最短最长超时时间(F1)
IWDG最短最长超时时间(F4/F7/H7)
六、IWDG配置步骤
HAL库相关函数介绍
HAL_IWDG_Init
函数和 HAL_IWDG_Refresh
函数是HAL库中用于初始化和刷新IWDG的两个函数。
HAL_IWDG_Init
函数:
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
- 输入参数:
IWDG_HandleTypeDef
结构体,包含IWDG的基地址和初始化参数。 - 功能: 使能IWDG,设置预分频系数和重装载值等。
- 具体操作:
- 使用给定的
IWDG_InitTypeDef
结构体中的参数,配置IWDG的预分频系数和重装载值。 - 启用IWDG时钟。
- 使用配置好的参数初始化IWDG。
- 使用给定的
HAL_IWDG_Refresh
函数:
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg)
- 输入参数:
IWDG_HandleTypeDef
结构体,包含IWDG的基地址。 - 功能: 将重装载寄存器的值重载到计数器中,喂狗。
- 具体操作:
- 通过IWDG的键寄存器 (
IWDG_KR
) 将重装载值加载到递减计数器。 - 此函数用于定期刷新IWDG,以防止计数器溢出导致系统复位。
- 通过IWDG的键寄存器 (
IWDG_HandleTypeDef
结构体包含IWDG的基地址和初始化参数,而 IWDG_InitTypeDef
结构体包含预分频系数和重装载值等参数。这两个函数配合使用,能够实现对IWDG的初始化和刷新操作。
stm32f1xx_hal.c
#include "stm32f1xx_hal.h"
#ifdef HAL_IWDG_MODULE_ENABLED
#define HAL_IWDG_DEFAULT_TIMEOUT ((6UL * 256UL * 1000UL) / LSI_VALUE)
HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
{
uint32_t tickstart;
/* Check the IWDG handle allocation */
if (hiwdg == NULL)
{
return HAL_ERROR;
}
/* Check the parameters */
assert_param(IS_IWDG_ALL_INSTANCE(hiwdg->Instance));
assert_param(IS_IWDG_PRESCALER(hiwdg->Init.Prescaler));
assert_param(IS_IWDG_RELOAD(hiwdg->Init.Reload));
/* Enable IWDG. LSI is turned on automatically */
__HAL_IWDG_START(hiwdg);
/* Enable write access to IWDG_PR and IWDG_RLR registers by writing
0x5555 in KR */
IWDG_ENABLE_WRITE_ACCESS(hiwdg);
/* Write to IWDG registers the Prescaler & Reload values to work with */
hiwdg->Instance->PR = hiwdg->Init.Prescaler;
hiwdg->Instance->RLR = hiwdg->Init.Reload;
/* Check pending flag, if previous update not done, return timeout */
tickstart = HAL_GetTick();
/* Wait for register to be updated */
while (hiwdg->Instance->SR != 0x00u)
{
if ((HAL_GetTick() - tickstart) > HAL_IWDG_DEFAULT_TIMEOUT)
{
return HAL_TIMEOUT;
}
}
/* Reload IWDG counter with value defined in the reload register */
__HAL_IWDG_RELOAD_COUNTER(hiwdg);
/* Return function status */
return HAL_OK;
}
HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg)
{
/* Reload IWDG counter with value defined in the reload register */
__HAL_IWDG_RELOAD_COUNTER(hiwdg);
/* Return function status */
return HAL_OK;
}
#endif /* HAL_IWDG_MODULE_ENABLED */
stm32f1xx_hal.h
#ifndef __STM32F1xx_HAL_DEF
#define __STM32F1xx_HAL_DEF
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx.h"
#include "Legacy/stm32_hal_legacy.h"
#include <stddef.h>
/* Exported types ------------------------------------------------------------*/
/**
* @brief HAL Status structures definition
*/
typedef enum
{
HAL_OK = 0x00U,
HAL_ERROR = 0x01U,
HAL_BUSY = 0x02U,
HAL_TIMEOUT = 0x03U
} HAL_StatusTypeDef;
/**
* @brief HAL Lock structures definition
*/
typedef enum
{
HAL_UNLOCKED = 0x00U,
HAL_LOCKED = 0x01U
} HAL_LockTypeDef;
/* Exported macro ------------------------------------------------------------*/
#define HAL_MAX_DELAY 0xFFFFFFFFU
#define HAL_IS_BIT_SET(REG, BIT) (((REG) & (BIT)) != 0U)
#define HAL_IS_BIT_CLR(REG, BIT) (((REG) & (BIT)) == 0U)
#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \
do{ \
(__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \
(__DMA_HANDLE__).Parent = (__HANDLE__); \
} while(0U)
#define UNUSED(X) (void)X /* To avoid gcc/g++ warnings */
#define __HAL_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = 0U)
#if (USE_RTOS == 1U)
/* Reserved for future use */
#error "USE_RTOS should be 0 in the current HAL release"
#else
#define __HAL_LOCK(__HANDLE__) \
do{ \
if((__HANDLE__)->Lock == HAL_LOCKED) \
{ \
return HAL_BUSY; \
} \
else \
{ \
(__HANDLE__)->Lock = HAL_LOCKED; \
} \
}while (0U)
#define __HAL_UNLOCK(__HANDLE__) \
do{ \
(__HANDLE__)->Lock = HAL_UNLOCKED; \
}while (0U)
#endif /* USE_RTOS */
#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */
#ifndef __weak
#define __weak __attribute__((weak))
#endif
#ifndef __packed
#define __packed __attribute__((packed))
#endif
#elif defined ( __GNUC__ ) && !defined (__CC_ARM) /* GNU Compiler */
#ifndef __weak
#define __weak __attribute__((weak))
#endif /* __weak */
#ifndef __packed
#define __packed __attribute__((__packed__))
#endif /* __packed */
#endif /* __GNUC__ */
/* Macro to get variable aligned on 4-bytes, for __ICCARM__ the directive "#pragma data_alignment=4" must be used instead */
#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */
#ifndef __ALIGN_BEGIN
#define __ALIGN_BEGIN
#endif
#ifndef __ALIGN_END
#define __ALIGN_END __attribute__ ((aligned (4)))
#endif
#elif defined ( __GNUC__ ) && !defined (__CC_ARM) /* GNU Compiler */
#ifndef __ALIGN_END
#define __ALIGN_END __attribute__ ((aligned (4)))
#endif /* __ALIGN_END */
#ifndef __ALIGN_BEGIN
#define __ALIGN_BEGIN
#endif /* __ALIGN_BEGIN */
#else
#ifndef __ALIGN_END
#define __ALIGN_END
#endif /* __ALIGN_END */
#ifndef __ALIGN_BEGIN
#if defined (__CC_ARM) /* ARM Compiler V5*/
#define __ALIGN_BEGIN __align(4)
#elif defined (__ICCARM__) /* IAR Compiler */
#define __ALIGN_BEGIN
#endif /* __CC_ARM */
#endif /* __ALIGN_BEGIN */
#endif /* __GNUC__ */
/**
* @brief __RAM_FUNC definition
*/
#if defined ( __CC_ARM ) || (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
#define __RAM_FUNC
#elif defined ( __ICCARM__ )
/* ICCARM Compiler
---------------
RAM functions are defined using a specific toolchain keyword "__ramfunc".
*/
#define __RAM_FUNC __ramfunc
#elif defined ( __GNUC__ )
/* GNU Compiler
------------
RAM functions are defined using a specific toolchain attribute
"__attribute__((section(".RamFunc")))".
*/
#define __RAM_FUNC __attribute__((section(".RamFunc")))
#endif
/**
* @brief __NOINLINE definition
*/
#if defined ( __CC_ARM ) || (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) || defined ( __GNUC__ )
/* ARM V4/V5 and V6 & GNU Compiler
-------------------------------
*/
#define __NOINLINE __attribute__ ( (noinline) )
#elif defined ( __ICCARM__ )
/* ICCARM Compiler
---------------
*/
#define __NOINLINE _Pragma("optimize = no_inline")
#endif
#ifdef __cplusplus
}
#endif
#endif /* ___STM32F1xx_HAL_DEF */
七、编程实战:验证不及时喂狗,系统将复位重启
wdg.c
#include "./BSP/WDG/wdg.h"
IWDG_HandleTypeDef g_iwdg_handle; // 定义IWDG的句柄
/* IWDG初始化函数 */
void iwdg_init(uint8_t prer, uint16_t rlr)
{
g_iwdg_handle.Instance = IWDG; // 设置IWDG句柄的寄存器基地址为IWDG
g_iwdg_handle.Init.Prescaler = prer; // 设置IWDG的预分频系数
g_iwdg_handle.Init.Reload = rlr; // 设置IWDG的重装载值
HAL_IWDG_Init(&g_iwdg_handle); // 使用HAL库初始化IWDG
}
/* 喂狗函数 */
void iwdg_feed(void)
{
HAL_IWDG_Refresh(&g_iwdg_handle); // 使用HAL库刷新IWDG,防止看门狗复位系统
}
wdg.h
#ifndef __WDG_H
#define __WDG_H
#include "./SYSTEM/sys/sys.h"
void iwdg_init(uint8_t prer, uint16_t rlr);
void iwdg_feed(void);
#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/WDG/wdg.h"
int main(void)
{
HAL_Init(); // 初始化HAL库
sys_stm32_clock_init(RCC_PLL_MUL9); // 设置时钟为72MHz
delay_init(72); // 延时初始化
usart_init(115200); // 串口初始化为115200
printf("您还没喂狗,请及时喂狗!!!\r\n"); // 打印提示信息
iwdg_init(IWDG_PRESCALER_32, 1250); // 预分频系数为32,重装载值为1250,溢出时间约为1s
while (1)
{
delay_ms(1000); // 延时1秒
iwdg_feed(); // 喂狗,刷新看门狗计数器
printf("已经喂狗\r\n"); // 打印喂狗提示信息
}
}
IWDG的重装载值(Reload Value)的计算方式与溢出时间直接相关。溢出时间(Timeout)的计算公式如下:
Tout = PSC × RLR fIWDG \text{Tout} = \frac{\text{PSC} \times \text{RLR}}{\text{fIWDG}} Tout=fIWDGPSC×RLR
其中:
- Tout \text{Tout} Tout 是看门狗溢出时间。
- PSC \text{PSC} PSC 是预分频系数。
- RLR \text{RLR} RLR 是重装载值。
- fIWDG \text{fIWDG} fIWDG 是看门狗的时钟源频率。
在你的代码中,预分频系数( PSC \text{PSC} PSC)被设置为32,这是一个固定的值。时钟源频率( fIWDG \text{fIWDG} fIWDG)为40kHz。
如果要计算重装载值( RLR \text{RLR} RLR),可以根据预期的溢出时间来确定。在你的情况下,你希望溢出时间约为1秒。因此,可以通过重排上述公式解出 RLR \text{RLR} RLR:
RLR = Tout × fIWDG PSC \text{RLR} = \frac{\text{Tout} \times \text{fIWDG}}{\text{PSC}} RLR=PSCTout×fIWDG
将数值代入,得到:
RLR = 1 × 40000 32 = 1250 \text{RLR} = \frac{1 \times 40000}{32} = 1250 RLR=321×40000=1250
所以,重装载值(
RLR
\text{RLR}
RLR)为1250。
八、总结