【STM32H7教程】第36章 STM32H7的LPTIM低功耗定时器基础知识和HAL库API

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第36章       STM32H7的LPTIM低功耗定时器基础知识和HAL库API

本章节为大家讲解LPTIM1 – LPTIM5共计5个定时器的基础知识和对应的HAL库API。

36.1 初学者重要提示

36.2 低功耗定时器基础知识

36.3 低功耗定时器的HAL库用户

36.4 源文件stm32h7xx_hal_lptim.c

36.5 总结

36.1 初学者重要提示

  1. 使用LPTIM的好处是系统处于睡眠、停机状态依然可以正常工作(除了待机模式)。停机状态可以正常工作的关键是LSE,LSI时钟不会被关闭,同时也可以选择使用外部时钟源。
  2. 重点学习本章的2.9小节,对于LPTIM的认识起到至关重要的作用。

36.2 低功耗定时器基础知识

下面将低功耗定时器应用中要用到的基础知识做个介绍。

36.2.1 定时器的硬件框图

认识一个外设,最好的方式就是看它的框图,方便我们快速的了解定时器的基本功能,然后再看手册了解细节。

下面我们直接看最复杂的LPTIM1&LPTIM2框图:

通过这个框图,我们可以得到如下信息:

  •   lptim_pclk接口

主要用于LPTIM的寄存器提供时钟,寄存器操作需要高速时钟,不能像LPTIM的其它部分一样使用LSI,LSE等低速时钟。

  •   lptim_it接口

用于触发中断。

  • lptim_ker_ck接口

内核时钟,供lptim使用。lptim_ker_tim接入到CLKMUX双路选择器的一个输入端,另一个输入端是LPTIM_IN1或者LPTIM_IN2的输入。也就是说LPTIM的计数器可以选择lptim_ker_ck,也可以选择LPTIM_IN1或者LPTIM_IN。

注意:CLKMUX双路选择器对应的是CFGR寄存器的bit0:CKSEL,用于控制内核时钟选择由内部时钟源(APB时钟或LSE、LSI和HSI等任何其他内置振荡器)提供时钟。也可以选择由外部时钟源通过 LPTIM 外部 Input提供时钟。

Count mode对应的是CFGR寄存器的bit23:COUNTMODE计数模式位,用于选择 LPTIM 使用哪个时钟源来为计数器提供时钟。可以选择计数器在每个内部时钟脉冲后递增,或者在 LPTIM 外部 Input上的每个有效时钟脉冲后递增。

  •   lptim_wkup

用于将系统从睡眠或者停机模式唤醒。

  •   lptim_out和LPTIM_OUT

lptim_out表示计数器输出,用于内部触发。

LPTIM_OUT表示GPIO的输出通道,用于PWM。

  •   LPTIM_ETR

通过GPIO为LPTIM提供外部触发。

  •   lptim_ext_trigx

LPTIM的计数器既可以通过软件启动,也可以通过外部触发启动,有8种触发方式可供选择,以LPTIM1为例,支持的触发如下:

  •   LPTIM_IN1,lptim_in1_mux1,lptim_in1_mux2和lptim_in1_mux3

LPTIM_IN2实际对应的是多路选择器的mux0,通过GPIO输入。

lptim_in1_mux1到lptim_in1_mux3对应的是内部输入,以LPTIM1为例,支持的输入信号如下:

  •   LPTIM_IN2,lptim_in2_mux1,lptim_in2_mux2和lptim_in2_mux3

LPTIM_IN2实际对应的是多路选择器的mux0,通过GPIO输入。

lptim_in2_mux1到lptim_in2_mux3对应的是内部输入,以LPTIM1为例,支持的输入信号如下:

  •   Glitch filter(干扰滤波器)

从框图中可以看到有三组Glitch filter,LPTIM_IN1和LPTIM_IN2接入多路选择器后各有一组,LPTIM_ETR接入后,也有一组。Glitch filter的作用是避免任何毛刺和噪声干扰在 LPTIM 内部传播,从而防止产生意外计数或触发。

注意:使用Glitch filter要向LPTIM 提供内部时钟源。

36.2.2 低功耗定时器的基本功能

LPTIM1 – LPTIM5都是16位的低功耗定时器(自动重载寄存器、比较寄存器和计数器都是16位的),相比TIM1 – TIM17这种通用定时器,在睡眠或者停机模式下依然可以工作(待机模式除外)。低功耗模式下要工作,就必然要支持低速时钟LSI、LSE或者外部输入时钟,这点是与通用定时器的本质区别。同时LPTIM的中断还可以唤醒停机模式,这点比较重要(休眠模式是任何中断都可以唤醒的,而停机模式可以LPTIM中断唤醒)。

以下几点是大家在使用中必须要了解到的:

1、  TIM1 – TIM17有专门的分频寄存器,而LPTIM1 – LPTIM5的分频是几种固定的值。

2、  低功耗定时器支持以下6种模式:

  • PWM模式
  • 单脉冲模式
  • 单次模式

在此模式下,当满足匹配条件时,输出可以切换高低电平(如果输出极性配置为高,则为低电平至高电平变化,反之亦然)。

  • 编码器模式
  • 超时模式

有效的边沿触发输入可复位定时器。第一个触发事件将启动计时器,任何连续触发事件将重置计数器并重新开始。

  • 计数器模式:

计数器可用于计算来自Input1的外部事件或用于计算内部时钟周期。

36.2.3 低功耗定时器时钟选择问题(重要)

这个知识点比较重要,可以帮助大家更好的理解LPTIM。下面先看框图:

首先将框图里面两个最重要的标识跟寄存器对上号。

1、lptim_ker_ck接口

内核时钟,供lptim使用。lptim_ker_tim接入到CLKMUX双路选择器的一个输入端,另一个输入端是LPTIM_IN1或者LPTIM_IN2的输入。也就是说LPTIM的计数器可以选择lptim_ker_ck,也可以选择LPTIM_IN1或者LPTIM_IN。

2、最关键的地方来了

(1)  CLKMUX多路选择器对应的是CFGR寄存器的bit0:CKSEL

  • 用于控制内核时钟选择由内部时钟源(APB时钟或LSE、LSI和HSI等任何其他内置振荡器)提供时钟。
  • 也可以选择由外部时钟源通过 LPTIM 外部 Input提供时钟。

(2)  Count mode对应的是CFGR寄存器的bit23:COUNTMODE计数模式位,用于选择 LPTIM 使用哪个时钟源来为计数器提供时钟。

  • 可以选择计数器在每个内部时钟脉冲后递增。
  • 或者在 LPTIM 外部 Input上的每个有效时钟脉冲后递增。

3、应用的时候,我们可以选择

(1) CKSEL = 0 , COUNTMODE = 0

表示LPTIM内核时钟使用的内部时钟源,计数器通过内部时钟脉计数。

(2)  CKSEL = 0 , COUNTMODE = 1

表示LPTIM内核时钟使用的内部时钟源,计数器通过外部输入脉冲计数。

(3)  CKSEL = 1 , COUNTMODE = x

表示LPTIM内核时钟使用的外部时钟源,计数器通过外部输入脉冲计数。

36.2.4 干扰滤波器(Glitch filter)

Glitch filter干扰滤波器的作用是避免任何毛刺和噪声干扰在 LPTIM 内部传播,从而防止产生意外计数或触发。

实现原理就是LPTIM的CFGR寄存器有专门的控制位TRGFLT[1:0](用于滤波外部触发信号)和CKFLT[1:0](用于滤波外部输入时钟)来控制信号,其有效电平变化必须至少稳定2/4/8个时钟周期才能将其视为有效触发。

比如下面的截图,配置为稳定2个时钟周期才算有效信号。

36.2.5 单次触发和连续模式

单次触发的含义就是定时器由触发事件启动,当达到 ARR 值时停止,效果如下:

连续模式的含义是定时器由触发事件启动,并且直到被禁止才会停止,效果如下:

36.2.6 溢出模式

注:这个模式用来做停机模式唤醒比较方便。

检测引脚第1次检查到触发信号,LPTIM就开始工作了,在溢出时间内检测到的触发信号都将复位计数,定时器重新开始工作。如果溢出内没有再接收到触发信号,仅进入溢出中断。

36.2.7 波形输出

通过下面的截图,可以让大家对低功耗定时器的波形输出效果有个全面认识。

LPTIM_ARR是自动重载寄存器,Compare是比较寄存器。当定时器的计数器达到Compare后,GPIO输出高电平还是低电平,是由CFGR寄存器的bit2:1:WAVPOL波形极性决定的。

以PWM输出为例:

  • 如果WAVPOL = 0表示计数器的数值介于Compare和LPTIM_ARR之间时,GPIO输出高电平。其它时间是低电平。
  • 如果WAVPOL = 1表示计数器的数值介于Compare和LPTIM_ARR之间时,GPIO输出低电平。其它时间是高电平。

One–Shot效果跟PWM一样,不过GPIO仅输出1次脉冲。

Set–Once特殊些,计数到ARR后,GPIO输出结果将一直保持达到Compare寄存器数值的输出电平。

36.2.8 低功耗定时器LPTIM1 – LPTIM5的区别

关于这五个低功耗定时器的区别,可以直接通过参考手册里面的框图看它们的区别。我们这里也简单整理下:

  • LPTIM1和LPTIM2的功能是一样的,且支持编码器模式,而LPTIM3,LPTIM4和LPTIM5均不支持。
  • LPTIM3跟LPTIM1的区别是仅有1组LPTIM_IN输入,且不支持LTPTIM_ETR。
  • LPTIM4和LPTIM5的功能是一样的,这两个功能最弱。跟LPTIM1的区别是没有LPTIM_IN输入端,也不支持LPTIM_ETR,仅有一个内部触发lptim_ext_trigx。

36.3 低功耗定时器的HAL库用法

低功耗定时器的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置GPIO、时钟,并根据需要配置NVIC,中断和DMA。下面我们逐一展开为大家做个说明。

36.3.1 定时器寄存器结构体LPTIM_TypeDef

低功耗定时器相关的寄存器是通过HAL库中的结构体LPTIM_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:

typedef struct
{
  __IO uint32_t ISR;      /*!< LPTIM Interrupt and Status register,         Address offset: 0x00 */
  __IO uint32_t ICR;      /*!< LPTIM Interrupt Clear register,              Address offset: 0x04 */
  __IO uint32_t IER;      /*!< LPTIM Interrupt Enable register,             Address offset: 0x08 */
  __IO uint32_t CFGR;     /*!< LPTIM Configuration register,                Address offset: 0x0C */
  __IO uint32_t CR;       /*!< LPTIM Control register,                      Address offset: 0x10 */
  __IO uint32_t CMP;      /*!< LPTIM Compare register,                      Address offset: 0x14 */
  __IO uint32_t ARR;      /*!< LPTIM Autoreload register,                   Address offset: 0x18 */
  __IO uint32_t CNT;      /*!< LPTIM Counter register,                      Address offset: 0x1C */
  uint16_t  RESERVED1;    /*!< Reserved, 0x20                                                    */
  __IO uint32_t CFGR2;    /*!< LPTIM Option register,                      Address offset: 0x24 */
} LPTIM_TypeDef;

这个结构体的成员名称和排列次序和CPU的定时器寄存器是一 一对应的。

__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:

#define     __O     volatile             /*!< Defines ‘write only‘ permissions */
#define     __IO    volatile             /*!< Defines ‘read / write‘ permissions */

下面我们看下LPTIM1,LPTIM2,LPTIM3,LPTIM4和LPTIM5的定义,在stm32h743xx.h文件。

#define PERIPH_BASE         ((uint32_t)0x40000000)
#define D2_APB2PERIPH_BASE   (PERIPH_BASE + 0x00010000)
#define D3_APB1PERIPH_BASE   (PERIPH_BASE + 0x18000000)

#define LPTIM1_BASE           (D2_APB1PERIPH_BASE + 0x2400)
#define LPTIM2_BASE           (D3_APB1PERIPH_BASE + 0x2400)
#define LPTIM3_BASE           (D3_APB1PERIPH_BASE + 0x2800)
#define LPTIM4_BASE           (D3_APB1PERIPH_BASE + 0x2C00)
#define LPTIM5_BASE           (D3_APB1PERIPH_BASE + 0x3000)

#define LPTIM1              ((LPTIM_TypeDef *) LPTIM1_BASE) <----- 展开这个宏,(TIM_TypeDef *) 0x40012400
#define LPTIM2              ((LPTIM_TypeDef *) LPTIM2_BASE)
#define LPTIM3              ((LPTIM_TypeDef *) LPTIM3_BASE)
#define LPTIM4              ((LPTIM_TypeDef *) LPTIM4_BASE)
#define LPTIM5              ((LPTIM_TypeDef *) LPTIM5_BASE)

我们访问LPTIM的ISR寄存器可以采用这种形式:LPTIM->ISR = 0。

36.3.2 定时器句柄结构体LPTIM_HandleTypeDef

HAL库在LPTIM_TypeDef的基础上封装了一个结构体LPTIM_HandleTypeDef,定义如下:

typedef struct
{
      LPTIM_TypeDef              *Instance;         /*!< Register base address     */
      LPTIM_InitTypeDef           Init;             /*!< LPTIM required parameters */
      HAL_StatusTypeDef           Status;           /*!< LPTIM peripheral status   */
      HAL_LockTypeDef             Lock;             /*!< LPTIM locking object      */
   __IO  HAL_LPTIM_StateTypeDef   State;            /*!< LPTIM peripheral state    */

}LPTIM_HandleTypeDef;

这里重点介绍前两个参数,其它参数主要是HAL库内部使用的。

TIM_TypeDef  *Instance

这个参数是寄存器的例化,方便操作寄存器,比如使能定时器的计数器。

SET_BIT(huart->Instance->CR,  LPTIM_CR_CNTSTRT)。

LPTIM_InitTypeDef  Init

这个参数是用户接触最多的,用于配置低功耗定时器的基本参数。

LPTIM_InitTypeDef结构体的定义如下:

typedef struct
{
  LPTIM_ClockConfigTypeDef     Clock;
  LPTIM_ULPClockConfigTypeDef  UltraLowPowerClock;
  LPTIM_TriggerConfigTypeDef   Trigger;
  uint32_t                     OutputPolarity;
  uint32_t                     UpdateMode;
  uint32_t                     CounterSource;
  uint32_t                     Input1Source;
  uint32_t                     Input2Source;
}LPTIM_InitTypeDef;
  • 成员Clock

用于设置时钟源和时钟分频,结构体变量LPTIM_ClockConfigTypeDef的定义如下。

typedef struct
{
  uint32_t Source;
  uint32_t Prescaler;
}LPTIM_ClockConfigTypeDef;

时钟源参数Source可以选择如下两种。

(1)#define LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC  ((uint32_t)0x00U)

表示LPTIM 由内部时钟源(APB 时钟或APB 或 LSE、LSI和HSI等)提供时钟。

(2)#define LPTIM_CLOCKSOURCE_ULPTIM             LPTIM_CFGR_CKSEL

表示LPTIM 由外部时钟源通过 LPTIM 外部 Input1 提供时钟。

分配参数Prescaler可以选择如下八种。

#define LPTIM_PRESCALER_DIV1               ((uint32_t)0x000000U)
#define LPTIM_PRESCALER_DIV2                LPTIM_CFGR_PRESC_0
#define LPTIM_PRESCALER_DIV4                LPTIM_CFGR_PRESC_1
#define LPTIM_PRESCALER_DIV8                ((uint32_t)(LPTIM_CFGR_PRESC_0 | LPTIM_CFGR_PRESC_1))
#define LPTIM_PRESCALER_DIV16               LPTIM_CFGR_PRESC_2
#define LPTIM_PRESCALER_DIV32              ((uint32_t)(LPTIM_CFGR_PRESC_0 | LPTIM_CFGR_PRESC_2))
#define LPTIM_PRESCALER_DIV64              ((uint32_t)(LPTIM_CFGR_PRESC_1 | LPTIM_CFGR_PRESC_2))
#define LPTIM_PRESCALER_DIV128             ((uint32_t)LPTIM_CFGR_PRESC)
  • 成员UltraLowPowerClock

此参数仅在使用超低功耗时钟源时使用,用于设置所选择的外部时钟,结构体变量LPTIM_ULPClockConfigTypeDef定义如下:

typedef struct
{
  uint32_t Polarity;
  uint32_t SampleTime;
}LPTIM_ULPClockConfigTypeDef;

时钟极性参数Polarity用于选择有效的时钟极性,如果使能了双边沿,Auxiliary Clock(一种低功耗振荡器)必须处于激活状态。

采样时间参数SampleTime用于配置时钟干扰滤波器。可以配置的参数如下:

#define LPTIM_CLOCKSAMPLETIME_DIRECTTRANSITION  ((uint32_t)0x00000000U)
#define LPTIM_CLOCKSAMPLETIME_2TRANSITIONS      LPTIM_CFGR_CKFLT_0
#define LPTIM_CLOCKSAMPLETIME_4TRANSITIONS      LPTIM_CFGR_CKFLT_1
#define LPTIM_CLOCKSAMPLETIME_8TRANSITIONS      LPTIM_CFGR_CKFLT
  • 成员Trigger

用于配置触发参数,结构体变量LPTIM_TriggerConfigTypeDef的定义如下:

typedef struct
{
  uint32_t Source;
  uint32_t ActiveEdge;
  uint32_t SampleTime;   

}LPTIM_TriggerConfigTypeDef;

触发源参数Source支持的选择如下:

#define LPTIM_TRIGSOURCE_SOFTWARE               ((uint32_t)0x0000FFFFU)
#define LPTIM_TRIGSOURCE_0                      ((uint32_t)0x00000000U)
#define LPTIM_TRIGSOURCE_1                      ((uint32_t)LPTIM_CFGR_TRIGSEL_0)
#define LPTIM_TRIGSOURCE_2                      LPTIM_CFGR_TRIGSEL_1
#define LPTIM_TRIGSOURCE_3                      ((uint32_t)LPTIM_CFGR_TRIGSEL_0 | LPTIM_CFGR_TRIGSEL_1)
#define LPTIM_TRIGSOURCE_4                      LPTIM_CFGR_TRIGSEL_2
#define LPTIM_TRIGSOURCE_5                      ((uint32_t)LPTIM_CFGR_TRIGSEL_0 | LPTIM_CFGR_TRIGSEL_2)
#define LPTIM_TRIGSOURCE_6                      ((uint32_t)LPTIM_CFGR_TRIGSEL_1 | LPTIM_CFGR_TRIGSEL_2)
#define LPTIM_TRIGSOURCE_7                      LPTIM_CFGR_TRIGSEL

参数ActiveEdge用于设置有效的触发边沿,可以选择上升沿,下降沿或者双边沿触发。

#define LPTIM_ACTIVEEDGE_RISING                LPTIM_CFGR_TRIGEN_0
#define LPTIM_ACTIVEEDGE_FALLING               LPTIM_CFGR_TRIGEN_1
#define LPTIM_ACTIVEEDGE_RISING_FALLING        LPTIM_CFGR_TRIGEN
  • 成员OutputPolarity

用于配置输出极性,可选择高电平或者低电平输出:

#define LPTIM_OUTPUTPOLARITY_HIGH               ((uint32_t)0x00000000U)
#define LPTIM_OUTPUTPOLARITY_LOW                (LPTIM_CFGR_WAVPOL)
  • 成员UpdateMode

用于配置是否立即更新自动重装寄存器和比较寄存器,可以选择立即更新,或者当前周期结束后更新。

#define LPTIM_UPDATE_IMMEDIATE                  ((uint32_t)0x00000000U)
#define LPTIM_UPDATE_ENDOFPERIOD                LPTIM_CFGR_PRELOAD
  • 成员CounterSource

用于配置定时器计数器在每个内部事件或者外部事件后递增计数。可以选择内部或者外部。

#define LPTIM_COUNTERSOURCE_INTERNAL            ((uint32_t)0x00000000U)
#define LPTIM_COUNTERSOURCE_EXTERNAL            LPTIM_CFGR_COUNTMODE
  • 成员Input1Source

用于配置Input1的输入源,可以选择GPIO,比较器输出或者SAI FSA/FSB。

#define LPTIM_INPUT1SOURCE_GPIO         ((uint32_t)0x00000000U)  /*!< For LPTIM1, LPTIM2 and LPTIM3 */
#define LPTIM_INPUT1SOURCE_COMP1        LPTIM_CFGR2_IN1_SEL0    /*!< For LPTIM1 and LPTIM2 */
#define LPTIM_INPUT1SOURCE_COMP2        LPTIM_CFGR2_IN1_SEL1    /*!< For LPTIM2 and LPTIM2 */
#define LPTIM_INPUT1SOURCE_COMP1_COMP2  (LPTIM_CFGR2_IN1_SEL0|LPTIM_CFGR2_IN1_SEL1)  /*!< For LPTIM2 */
#define LPTIM_INPUT1SOURCE_SAI1_FSA     LPTIM_CFGR2_IN1_SEL0                        /*!< For LPTIM3 */
#define LPTIM_INPUT1SOURCE_SAI1_FSB     LPTIM_CFGR2_IN1_SEL1                        /*!< For LPTIM3 */
  • 成员Input2ource

用于配置Input2的输入源,可以选择GPIO和比较器。

注意,此参数仅用于编码器模式,也就是说仅支持LPTIM1和LPTIM2的例化。

#define LPTIM_INPUT2SOURCE_GPIO         ((uint32_t)0x00000000U)        /*!< For LPTIM1 and LPTIM2 */
#define LPTIM_INPUT2SOURCE_COMP2        LPTIM_CFGR2_IN2_SEL0          /*!< For LPTIM1 and LPTIM2 */

下面是LPTIM1的配置例子:

LPTIM_HandleTypeDef     LptimHandle = {0};    

LptimHandle.Instance = LPTIM1;

/* 对应寄存器CKSEL,选择内部时钟源 */
LptimHandle.Init.Clock.Source    = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;        /* 设置LPTIM时钟分频 */
LptimHandle.Init.CounterSource   = LPTIM_COUNTERSOURCE_INTERNAL;/* LPTIM计数器对内部时钟源计数 */
LptimHandle.Init.Trigger.Source  = LPTIM_TRIGSOURCE_SOFTWARE;   /* 软件触发 */
/* 计数器计数到比较寄存器和ARR自动重载寄存器之间数值,输出高电平 */
LptimHandle.Init.OutputPolarity  = LPTIM_OUTPUTPOLARITY_HIGH;
/* 比较寄存器和ARR自动重载寄存器选择更改后立即更新 */
LptimHandle.Init.UpdateMode      = LPTIM_UPDATE_IMMEDIATE;
LptimHandle.Init.Input1Source    = LPTIM_INPUT1SOURCE_GPIO;
LptimHandle.Init.Input2Source    = LPTIM_INPUT2SOURCE_GPIO;    

if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

36.3.3 定时器的底层配置(GPIO、时钟、中断等)

HAL库有个自己的底层初始化回调函数,比如调用函数HAL_LPTIM_Init就会调用HAL_LPTIM_MspInit,此函数是弱定义的。

__weak void HAL_LPTIM_MspInit(LPTIM_HandleTypeDef *hlptim)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hlptim);

  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_LPTIM_MspInit could be implemented in the user file
   */
}

用户可以在其它的C文件重定向,并将相对的底层初始化在里面实现。对应的底层复位函数HAL_LPTIM_MspDeInit是在函数 HAL_LPTIM_DeInit里面被调用的,也是弱定义的。

当然,用户也可以自己初始化,不限制必须在两个函数里面实现。

定时器外设的基本参数配置完毕后还不能使用,还需要配置GPIO、时钟、中断等参数,比如下面配置LPTIM1使用PD13做PWM输出。

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef     GPIO_InitStruct;

  /* 使能LPTIM时钟 */
  __HAL_RCC_LPTIM1_CLK_ENABLE();

  /* 使能GPIO时钟 Enable GPIO PORT */
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /* 配置PD13 */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
  GPIO_InitStruct.Alternate = GPIO_AF1_LPTIM1;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}

总结下来就是以下几点:

  • 配置LPTIM时钟。
  • 配置LPTIM所用到引脚和对应的GPIO时钟。
  • 如果用到定时器中断,还需要通过NVIC配置中断。

关于这个底层配置有以下几点要着重说明下:

  • 定时器所使用引脚的复用模式选择已经被HAL库定义好,放在了stm32h7xx_hal_gpio_ex.h文件里面。比如TIM1有一个复用,
#define GPIO_AF1_LPTIM1        ((uint8_t)0x01)  /* LPTIM1 Alternate Function mapping */

但是却有4个输出通道,每个通道都有几个支持的输出引脚:

LPTIM1_OUT  PG13
LPTIM1_OUT  PD13

具体使用哪个,配置对应引脚的复用即可:

36.3.4 定时器的状态标志清除问题

下面我们介绍__HAL_LPTIM_GET_FLAG函数。这个函数用来检查定时器标志位是否被设置。

/**
  * @brief  Check whether the specified LPTIM flag is set or not.
  * @param  __HANDLE__: LPTIM handle
  * @param  __FLAG__  : LPTIM flag to check
  *            This parameter can be a value of:
  *            @arg LPTIM_FLAG_DOWN    : Counter direction change up Flag.
  *            @arg LPTIM_FLAG_UP      : Counter direction change down to up Flag.
  *            @arg LPTIM_FLAG_ARROK   : Autoreload register update OK Flag.
  *            @arg LPTIM_FLAG_CMPOK   : Compare register update OK Flag.
  *            @arg LPTIM_FLAG_EXTTRIG : External trigger edge event Flag.
  *            @arg LPTIM_FLAG_ARRM    : Autoreload match Flag.
  *            @arg LPTIM_FLAG_CMPM    : Compare match Flag.
  * @retval The state of the specified flag (SET or RESET).
  */
#define __HAL_LPTIM_GET_FLAG(__HANDLE__, __FLAG__)  (((__HANDLE__)->Instance->ISR &(__FLAG__)) == (__FLAG__))

当前做的应用程序,这几个中断标志暂时都还没有被用到。

与标志获取函数__HAL_TIM_GET_FLAG对应的清除函数是__HAL_LPTIM_CLEAR_FLAG:

/**
  * @brief  Clear the specified LPTIM flag.
  * @param  __HANDLE__: LPTIM handle.
  * @param  __FLAG__  : LPTIM flag to clear.
  *            This parameter can be a value of:
  *            @arg LPTIM_FLAG_DOWN    : Counter direction change up Flag.
  *            @arg LPTIM_FLAG_UP      : Counter direction change down to up Flag.
  *            @arg LPTIM_FLAG_ARROK   : Autoreload register update OK Flag.
  *            @arg LPTIM_FLAG_CMPOK   : Compare register update OK Flag.
  *            @arg LPTIM_FLAG_EXTTRIG : External trigger edge event Flag.
  *            @arg LPTIM_FLAG_ARRM    : Autoreload match Flag.
  *            @arg LPTIM_FLAG_CMPM    : Compare match Flag.
  * @retval None.
  */
#define __HAL_LPTIM_CLEAR_FLAG(__HANDLE__, __FLAG__)        ((__HANDLE__)->Instance->ICR  = (__FLAG__))

清除标志函数所支持的参数跟获取函数是一 一对应的。除了这两个函数,还是定时器的中断开启和中断关闭函数,有时候也要用到。

/**
  * @brief  Enable the specified LPTIM interrupt.
  * @param  __HANDLE__    : LPTIM handle.
  * @param  __INTERRUPT__ : LPTIM interrupt to set.
  *            This parameter can be a value of:
  *            @arg LPTIM_IT_DOWN    : Counter direction change up Interrupt.
  *            @arg LPTIM_IT_UP      : Counter direction change down to up Interrupt.
  *            @arg LPTIM_IT_ARROK   : Autoreload register update OK Interrupt.
  *            @arg LPTIM_IT_CMPOK   : Compare register update OK Interrupt.
  *            @arg LPTIM_IT_EXTTRIG : External trigger edge event Interrupt.
  *            @arg LPTIM_IT_ARRM    : Autoreload match Interrupt.
  *            @arg LPTIM_IT_CMPM    : Compare match Interrupt.
  * @retval None.
  */
#define __HAL_LPTIM_ENABLE_IT(__HANDLE__, __INTERRUPT__)    ((__HANDLE__)->Instance->IER  |= (__INTERRUPT__))

 /**
  * @brief  Disable the specified LPTIM interrupt.
  * @param  __HANDLE__    : LPTIM handle.
  * @param  __INTERRUPT__ : LPTIM interrupt to set.
  *            This parameter can be a value of:
  *            @arg LPTIM_IT_DOWN    : Counter direction change up Interrupt.
  *            @arg LPTIM_IT_UP      : Counter direction change down to up Interrupt.
  *            @arg LPTIM_IT_ARROK   : Autoreload register update OK Interrupt.
  *            @arg LPTIM_IT_CMPOK   : Compare register update OK Interrupt.
  *            @arg LPTIM_IT_EXTTRIG : External trigger edge event Interrupt.
  *            @arg LPTIM_IT_ARRM    : Autoreload match Interrupt.
  *            @arg LPTIM_IT_CMPM    : Compare match Interrupt.
  * @retval None.
  */
#define __HAL_LPTIM_DISABLE_IT(__HANDLE__, __INTERRUPT__)   ((__HANDLE__)->Instance->IER  &= (~(__INTERRUPT__)))

注意:操作定时器的寄存器不限制必须要用HAL库提供的API,比如要操作寄存器CR,直接调用LPTIM1->CR操作即可。

36.3.5 定时器初始化流程总结

使用方法由HAL库提供:

第1步:通过函数HAL_LPTIM_Init()做初始化,主要配置的内容如下:

  • 支持LPTIM1 – LPTIM5
  • Clock计数时钟

(1)     Source,可以选择ULPTIM input (IN1)时钟输入,或者内部时钟,如APB, LSE, LSI ,MSI等。

(2)     Prescaler,设置分频。

  • UltraLowPowerClock 超低功耗时钟

只有在Clock计数时钟源选择了ULPTIM input,此参数才有意义。

(1)     Polarity,计数单元有效的边沿极性。

(2)     SampleTime,配置时钟干扰滤波器的时钟。

  • Trigger计数器的触发

(1)     Source,可以选择硬件触发或者软件触发。

(2)     ActiveEdge,仅用于硬件触发,用来设置触发边沿(上升沿、下降沿或者双沿)。

(3)     SampleTime,采样时间,用于配置触发干扰滤波器的时钟。

  • OutputPolarity输出极性配置。

UpdateMode更新模式,用于配置是否立即更新自动重装寄存器和比较寄存器,可以选择立即更新,或者当前周期结束后更新。

  • Input1Source用于配置Input1的输入源,可以选择GPIO,比较器输出或者SAI FSA/FSB。
  • Input2Source用于配置Input2的输入源,可以选择GPIO和比较器。

注意,此参数仅用于编码器模式,也就是说仅支持LPTIM1和LPTIM2的例化。

第2步:低功耗定时器的底层配置是通过函数HAL_LPTIM_MspInit()实现:

  • 使用函数__HAL_RCC_LPTIMx_CLK_ENABLE()使能定时器时钟。
  • 使用函数__HAL_RCC_GPIOx_CLK_ENABLE使能GPIO时钟。
  • 使用函数HAL_GPIO_Init配置GPIO。
  • 如果调用函数HAL_LPTIM_PWM_Start_IT()使能了中断,需要调用HAL_NVIC_SetPriority()设置中断优先级,调用函数HAL_NVIC_EnableIRQ()使能中断,在中断服务程序里面调用 HAL_LPTIM_IRQHandler。

第3步:低功耗定时器支持的6种工作模式:

  • PWM模式

启动此模式可调用HAL_LPTIM_PWM_Start()或 HAL_LPTIM_PWM_Start_IT()用于中断方式。

  • 单脉冲模式

启动此模式可调用HAL_LPTIM_OnePulse_Start()或HAL_LPTIM_OnePulse_Start_IT()用于中断方式。

  • 单次模式

在此模式下,当满足匹配条件时,输出可以切换高低电平(如果输出极性配置为高,则从低电平切至高电平,反之亦然)。启动此模式可调用HAL_LPTIM_SetOnce_Start()或 HAL_LPTIM_SetOnce_Start_IT()用于中断方式。

  • 编码器模式

启动此模式可调用HAL_LPTIM_Encoder_Start()或HAL_LPTIM_Encoder_Start_IT()用于中断方式。

  • 超时模式

有效的边沿触发输入可复位定时器。第一个触发事件将启动计时器,任何连续触发事件将重置计数器并重新开始。启动此模式可调用HAL_LPTIM_TimeOut_Start()或 HAL_LPTIM_TimeOut_Start_IT()用于中断方式。

  • 计数器模式

计数器可用于计算来自Input1的外部事件或用于计算内部时钟周期。启动此模式可调用HAL_LPTIM_Counter_Start()或  HAL_LPTIM_Counter_Start_IT()用于中断方式。

第4步:停止任何模式:

用户可以通过调用相应的API来停止任何模式: HAL_LPTIM_Xxx_Stop 或 HAL_LPTIM_Xxx_Stop_IT(如果此模式已经在中断方式下启动)。

低功耗定时器常用的功能,通过上面这几步即可实现。

36.4 源文件stm32h7xx_hal_lptim.c

此文件涉及到的函数比较多,这里把我们几个常用的函数做个说明:

  • HAL_LPTIM_Init
  • HAL_LPTIM_PWM_Start
  • HAL_LPTIM_TimeOut_Start_IT
  • HAL_LPTIM_TimeOut_Stop_IT

36.4.1 函数HAL_LPTIM_Init

函数原型:

HAL_StatusTypeDef HAL_LPTIM_Init(LPTIM_HandleTypeDef *hlptim)
{
  uint32_t tmpcfgr = 0;

  /* 检测是否是有效句柄 */
  if(hlptim == NULL)
  {
    return HAL_ERROR;
  }

  /* 省略 */

  if(hlptim->State == HAL_LPTIM_STATE_RESET)
  {
/* 默认取消锁 */
    hlptim->Lock = HAL_UNLOCKED;

/* 初始化底层硬件 : GPIO, CLOCK, NVIC */
    HAL_LPTIM_MspInit(hlptim);
  }

  /* 更改LPTIM状态 */
  hlptim->State = HAL_LPTIM_STATE_BUSY;

  /* 获取LPTIMx CFGR数值 */
  tmpcfgr = hlptim->Instance->CFGR;

  if ((hlptim->Init.Clock.Source) ==  LPTIM_CLOCKSOURCE_ULPTIM)
  {
    tmpcfgr &= (uint32_t)(~(LPTIM_CFGR_CKPOL | LPTIM_CFGR_CKFLT));
  }
  if ((hlptim->Init.Trigger.Source) !=  LPTIM_TRIGSOURCE_SOFTWARE)
  {
    tmpcfgr &= (uint32_t)(~ (LPTIM_CFGR_TRGFLT | LPTIM_CFGR_TRIGSEL));
  }

  /* 清除 CKSEL, PRESC, TRIGEN, TRGFLT, WAVPOL, PRELOAD & COUNTMODE 位 */
  tmpcfgr &= (uint32_t)(~(LPTIM_CFGR_CKSEL | LPTIM_CFGR_TRIGEN | LPTIM_CFGR_PRELOAD |
                          LPTIM_CFGR_WAVPOL | LPTIM_CFGR_PRESC | LPTIM_CFGR_COUNTMODE ));

  /* 设置初始化参数 */
  tmpcfgr |= (hlptim->Init.Clock.Source    |
              hlptim->Init.Clock.Prescaler |
              hlptim->Init.OutputPolarity  |
              hlptim->Init.UpdateMode      |
              hlptim->Init.CounterSource);

  if ((hlptim->Init.Clock.Source) ==  LPTIM_CLOCKSOURCE_ULPTIM)
  {
    tmpcfgr |=  (hlptim->Init.UltraLowPowerClock.Polarity |
                hlptim->Init.UltraLowPowerClock.SampleTime);
  } 

  if ((hlptim->Init.Trigger.Source) !=  LPTIM_TRIGSOURCE_SOFTWARE)
  {
    /* Enable External trigger and set the trigger source */
    tmpcfgr |= (hlptim->Init.Trigger.Source     |
                hlptim->Init.Trigger.ActiveEdge |
                hlptim->Init.Trigger.SampleTime);
  }

  /* 写入到配置寄存器 LPTIMx CFGR */
  hlptim->Instance->CFGR = tmpcfgr;

 /* 配置LPTIM input 时钟源 */
  if((hlptim->Instance == LPTIM1) || (hlptim->Instance == LPTIM2))
  {
    assert_param(IS_LPTIM_INPUT1_SOURCE(hlptim->Instance,hlptim->Init.Input1Source));
    assert_param(IS_LPTIM_INPUT2_SOURCE(hlptim->Instance,hlptim->Init.Input2Source));

    /* 配置 LPTIM1/2 Input1 和 Input2 的时钟源 */
    hlptim->Instance->CFGR2 = (hlptim->Init.Input1Source | hlptim->Init.Input2Source);
  }
  else
  {
    if(hlptim->Instance == LPTIM3)
    {
       assert_param(IS_LPTIM_INPUT1_SOURCE(hlptim->Instance,hlptim->Init.Input1Source));

      /* 注,H7库V1.3.0版本这里是个bug,LPTIM3应该配置CFGR3寄存器 */
      hlptim->Instance->CFGR2 = hlptim->Init.Input1Source;
    }
  }
  /* 更改LPTIM状态 */
  hlptim->State = HAL_LPTIM_STATE_READY;

  /* 返回HAL_OK */
  return HAL_OK;
}

函数描述:

此函数用于初始化低功耗定时器的基本参数。

函数参数:

  • 第1个参数是LPTIM_HandleTypeDef类型结构体指针变量,用于配置要初始化的参数。结构体变量成员的详细介绍看本章3.2小节。
  • 返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

注意事项:

  1. 函数HAL_LPTIM_MspInit用于初始化定时器的底层时钟、引脚等功能,需要用户自己在此函数里面实现具体的功能。由于这个函数是弱定义的,允许用户在工程其它源文件里面重新实现此函数。当然,不限制一定要在此函数里面实现,也可以像早期的标准库那样,用户自己初始化即可,更灵活些。
  2. 如果形参hlptim的结构体成员gState没有做初始状态,这个地方就是个坑。特别是用户搞了一个局部变量LPTIM_HandleTypeDef  LptimHandle。

对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个HAL_LPTIM_STATE_RESET = 0x00U。

解决办法有四

方法1:用户自己初始定时器和涉及到的GPIO等。

方法2:定义LPTIM_HandleTypeDef  LptimHandle为全局变量。

方法3:定义为局部变量要赋初始值LPTIM_HandleTypeDef  LptimHandle = {0}。

方法4:下面的方法

if(HAL_LPTIM_DeInit(&LptimHandle)!= HAL_OK)
{
    Error_Handler();
}
if(HAL_LPTIM_Init(&LptimHandle)!= HAL_OK)
{
    Error_Handler();
}

使用举例:

LPTIM_HandleTypeDef     LptimHandle = {0};    

LptimHandle.Instance = LPTIM1;

/* 对应寄存器CKSEL,选择内部时钟源 */
LptimHandle.Init.Clock.Source    = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;        /* 设置LPTIM时钟分频 */
LptimHandle.Init.CounterSource   = LPTIM_COUNTERSOURCE_INTERNAL;/* LPTIM计数器对内部时钟源计数 */
LptimHandle.Init.Trigger.Source  = LPTIM_TRIGSOURCE_SOFTWARE;   /* 软件触发 */
/* 计数器计数到比较寄存器和ARR自动重载寄存器之间数值,输出高电平 */
LptimHandle.Init.OutputPolarity  = LPTIM_OUTPUTPOLARITY_HIGH;
/* 比较寄存器和ARR自动重载寄存器选择更改后立即更新 */
LptimHandle.Init.UpdateMode      = LPTIM_UPDATE_IMMEDIATE;
LptimHandle.Init.Input1Source    = LPTIM_INPUT1SOURCE_GPIO;
LptimHandle.Init.Input2Source    = LPTIM_INPUT2SOURCE_GPIO;    

if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

36.4.2 函数HAL_LPTIM_PWM_Start

函数原型:

HAL_StatusTypeDef HAL_LPTIM_PWM_Start(LPTIM_HandleTypeDef *hlptim, uint32_t Period, uint32_t Pulse)
{
  /* 检查参数 */
  assert_param(IS_LPTIM_INSTANCE(hlptim->Instance));
  assert_param(IS_LPTIM_PERIOD(Period));
  assert_param(IS_LPTIM_PULSE(Pulse));

  /* 设置LPTIM的状态 */
  hlptim->State= HAL_LPTIM_STATE_BUSY;

  /* 复位PWM模式的WAVE位 */
  hlptim->Instance->CFGR &= ~LPTIM_CFGR_WAVE;

  /* 使能LPTIM */
  __HAL_LPTIM_ENABLE(hlptim);

  /* 设置自动重载寄存器ARR数值 */
  __HAL_LPTIM_AUTORELOAD_SET(hlptim, Period);

  /* 设置比较寄存器数值,用于PWM占空比配置 */
  __HAL_LPTIM_COMPARE_SET(hlptim, Pulse);

  /* 定时器以连续模式运行 */
  __HAL_LPTIM_START_CONTINUOUS(hlptim);

  /* 更改定时器状态 */
  hlptim->State= HAL_LPTIM_STATE_READY;

  /* 返回HAL_OK */
  return HAL_OK;
}

函数描述:

调用函数HAL_LPTIM_Init配置了基础功能后,就可以调用此函数启动定时器PWM输出了。

函数参数:

  • 第1个参数是LPTIM_HandleTypeDef类型结构体指针变量。
  • 第2个参数是低功耗定时器的周期配置,范围0 – 0xFFFF。
  • 第3个参数是低功耗定时器PWM的脉宽配置,范围0 -0xFFFF。
  • 返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

注意事项:

  1. 这里重点说下配置了第2个参数后,低功耗定时器的PWM输出频率是多少。比如LPTIM使用的是LSE时钟,频率32768Hz,第2个参数Period = 9。那么

PWM频率 = LSE  / (Period + 1) = 32768 / (9 + 1) = 327Hz。

当第3个参数Pluse = 5

PWM占空比 = 1 – (Pluse + 1)/(Period + 1) = 1 – 5/10 = 50%

使用举例:

LptimHandle.Instance = LPTIM1;

/* 对应寄存器CKSEL,选择内部时钟源 */
LptimHandle.Init.Clock.Source    = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;        /* 设置LPTIM时钟分频 */
LptimHandle.Init.CounterSource   = LPTIM_COUNTERSOURCE_INTERNAL;/* LPTIM计数器对内部时钟源计数 */
LptimHandle.Init.Trigger.Source  = LPTIM_TRIGSOURCE_SOFTWARE;   /* 软件触发 */
/* 计数器计数到比较寄存器和ARR自动重载寄存器之间数值,输出高电平 */
LptimHandle.Init.OutputPolarity  = LPTIM_OUTPUTPOLARITY_HIGH;
/* 比较寄存器和ARR自动重载寄存器选择更改后立即更新 */
LptimHandle.Init.UpdateMode      = LPTIM_UPDATE_IMMEDIATE;
LptimHandle.Init.Input1Source    = LPTIM_INPUT1SOURCE_GPIO;
LptimHandle.Init.Input2Source    = LPTIM_INPUT2SOURCE_GPIO;    

if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

    /*
       ARR是自动重装寄存器,对应函数HAL_LPTIM_PWM_Start的第2个参数
       Compare是比较寄存器,对应函数HAL_LPTIM_PWM_Start的第3个参数

       ---------------------
       LSE = 32768Hz
       分频设置为LPTIM_PRESCALER_DIV1,即未分频
       ARR自动重载寄存器 = 31
       那么PWM频率 = LSE / (ARR + 1) = 32768Hz / (31 + 1) = 1024Hz

       占空比 = 1 - (Comprare + 1)/ (ARR + 1)
              = 1 - (15 + 1)/(31 + 1)
              = 50%

       占空比这里为什么要1减操作,而不是直接的(Comprare + 1)/ (ARR + 1),这是因为前面的配置中
       计数器计数到比较寄存器和ARR自动重载寄存器之间数值,输出高电平。
    */
if (HAL_LPTIM_PWM_Start(&LptimHandle, 31, 15) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

36.4.3 函数HAL_LPTIM_TimeOut_Start_IT

函数原型:

HAL_StatusTypeDef HAL_LPTIM_TimeOut_Start_IT(LPTIM_HandleTypeDef *hlptim, uint32_t Period, uint32_t Timeout)
{
  /* 检查参数 */
  assert_param(IS_LPTIM_INSTANCE(hlptim->Instance));
  assert_param(IS_LPTIM_PERIOD(Period));
  assert_param(IS_LPTIM_PULSE(Timeout));

  /* 设置LPTIM的状态 */
  hlptim->State= HAL_LPTIM_STATE_BUSY;

  /* 使能超时位TIMOUT */
  hlptim->Instance->CFGR |= LPTIM_CFGR_TIMOUT;

  /* 使能比匹配中断 */
  __HAL_LPTIM_ENABLE_IT(hlptim, LPTIM_IT_CMPM);

  /* 使能低功耗定时器 */
  __HAL_LPTIM_ENABLE(hlptim);

  /* 设置自动重载寄存器ARR的数值 */
  __HAL_LPTIM_AUTORELOAD_SET(hlptim, Period);

  /* 设置比较寄存器数值 */
  __HAL_LPTIM_COMPARE_SET(hlptim, Timeout);

  /* 低功耗定时器以连续模式运行 */
  __HAL_LPTIM_START_CONTINUOUS(hlptim);

  /* 设置定时器的状态 */
  hlptim->State= HAL_LPTIM_STATE_READY;

  /* 返回HAL_OK */
  return HAL_OK;
}

函数描述:

调用函数HAL_LPTIM_Init配置了基础功能后,就可以调用此函数启动定时器的超时功能。

函数参数:

  • 第1个参数是LPTIM_HandleTypeDef类型结构体指针变量。
  • 第2个参数是低功耗定时器的周期配置,范围0 – 0xFFFF。
  • 第3个参数是低功耗定时器的超时时间配置,范围0 -0xFFFF。
  • 返回值,返回HAL_ERROR表示配置失败,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示时间溢出。

注意事项:

  1. 超时配置用不到第2个参数。
  2. 此函数开启的是比较匹配中断,所以实际的超时时间由Compare寄存器决定,即第3个参数。

使用举例:

/*
*********************************************************************************************************
*    函 数 名: bsp_StartLPTIM
*    功能说明: 启动LPTIM
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_StartLPTIM(void)
{
    /*
       ARR是自动重装寄存器,对应函数HAL_LPTIM_TimeOut_Start_IT的第2个参数
       Compare是比较寄存器,对应函数HAL_LPTIM_TimeOut_Start_IT的第3个参数

       ---------------------
       LSE = 32768Hz
       分频设置为LPTIM_PRESCALER_DIV8,即8分频(函数bsp_InitLPTIM里面做的初始化配置)
       ARR自动重载寄存器 = 32768
       实际测试发现溢出中断与ARR寄存器无关,全部由第3个参数,Compare寄存器决定

       LPTIM的计数器计数1次的时间是 1 / (32768 / 8) = 8 /32768。
       第三个参数配置的是32767,那么计数到32767就是 (32767 + 1)*(8 /32768) = 8秒,计算的时候要加1。
    */
    if (HAL_LPTIM_TimeOut_Start_IT(&LptimHandle, 0, 32767) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }
}

/*
*********************************************************************************************************
*    函 数 名: LPTIM1_IRQHandler
*    功能说明: LPTIM1中断服务程序
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void LPTIM1_IRQHandler(void)
{
    if((LPTIM1->ISR & LPTIM_FLAG_CMPM) != RESET)
    {
        /* 清除比较匹配中断 */
        LPTIM1->ICR = LPTIM_FLAG_CMPM;

        /* 关闭溢出中断 */
        HAL_LPTIM_TimeOut_Stop_IT(&LptimHandle);

        bsp_LedToggle(4);
    }
}

36.4.4 函数HAL_LPTIM_TimeOut_Stop_IT

函数原型:

HAL_StatusTypeDef HAL_LPTIM_TimeOut_Stop_IT(LPTIM_HandleTypeDef *hlptim)
{
  /* 检查参数 */
  assert_param(IS_LPTIM_INSTANCE(hlptim->Instance));

  /* 设置LPTIM状态 */
  hlptim->State= HAL_LPTIM_STATE_BUSY;

  /* 禁止低功耗定时器 */
  __HAL_LPTIM_DISABLE(hlptim);

  /* 关闭TIMOUT位 */
  hlptim->Instance->CFGR &= ~LPTIM_CFGR_TIMOUT;

  /* 关闭比较匹配中断 */
  __HAL_LPTIM_DISABLE_IT(hlptim, LPTIM_IT_CMPM);

  /* 设置LPTIM状态 */
  hlptim->State= HAL_LPTIM_STATE_READY;

  /* 返回HAL_OK */
  return HAL_OK;
}

函数描述:

此函数用于关闭低功耗定时器的超时模式。

函数参数:

  • 第1个参数是TIM_LPHandleTypeDef类型结构体指针变量。

注意事项:

  1. 推荐在低功耗定时器的中断服务程序里面调用此函数进行关闭,这样可以及时的关闭。

使用举例:

/*
*********************************************************************************************************
*    函 数 名: bsp_StartLPTIM
*    功能说明: 启动LPTIM
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_StartLPTIM(void)
{
    /*
       ARR是自动重装寄存器,对应函数HAL_LPTIM_TimeOut_Start_IT的第2个参数
       Compare是比较寄存器,对应函数HAL_LPTIM_TimeOut_Start_IT的第3个参数

       ---------------------
       LSE = 32768Hz
       分频设置为LPTIM_PRESCALER_DIV8,即8分频(函数bsp_InitLPTIM里面做的初始化配置)
       ARR自动重载寄存器 = 32768
       实际测试发现溢出中断与ARR寄存器无关,全部由第3个参数,Compare寄存器决定

       LPTIM的计数器计数1次的时间是 1 / (32768 / 8) = 8 /32768。
       第三个参数配置的是32767,那么计数到32767就是 (32767 + 1)*(8 /32768) = 8秒,计算的时候要加1。
    */
    if (HAL_LPTIM_TimeOut_Start_IT(&LptimHandle, 0, 32767) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }
}

/*
*********************************************************************************************************
*    函 数 名: LPTIM1_IRQHandler
*    功能说明: LPTIM1中断服务程序
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
void LPTIM1_IRQHandler(void)
{
    if((LPTIM1->ISR & LPTIM_FLAG_CMPM) != RESET)
    {
        /* 清除比较匹配中断 */
        LPTIM1->ICR = LPTIM_FLAG_CMPM;

        /* 关闭溢出中断 */
        HAL_LPTIM_TimeOut_Stop_IT(&LptimHandle);

        bsp_LedToggle(4);
    }
}

36.5 总结

本章节就为大家讲解这么多,低功耗定时器在低功耗场合比较有用,如果有低功耗方面的项目需求,可以考虑使用这个定时器。

原文地址:https://www.cnblogs.com/armfly/p/12149170.html

时间: 2024-12-11 18:01:27

【STM32H7教程】第36章 STM32H7的LPTIM低功耗定时器基础知识和HAL库API的相关文章

【STM32H7教程】第57章 STM32H7硬件JPEG编解码基础知识和HAL库API

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第57章       STM32H7硬件JPEG编解码基础知识和HAL库API 本章节为大家讲解硬件JPEG,实际项目用到图像显示的地方比较多,有了硬件JPEG可以大大加速JPEG图片显示速度. 57.1 初学者重要提示 57.2 硬件JPEG基础知识 57.3 硬件JPEG的HAL库用法 57.4 源文件stm32h7xx_hal_jpeg.c 57.5 总结 57

【STM32H7教程】第37章 STM32H7的LPTIM低功耗定时器应用之PWM

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第37章       STM32H7的LPTIM低功耗定时器应用之PWM 本章教程为大家讲解低功耗定时器的PWM输出.使用LPTIM的好处是系统处于睡眠.停机状态依然可以正常工作(除了待机模式).实际项目中对于功耗有要求的场合,可以使用这种方式,可以一定程度上较低功耗. 37.1 初学者重要提示 37.2 低功耗定时器PWM驱动设计 37.3 低功耗定时器板级支持包(

第13章 &nbsp; 学习 shell脚本之前的基础知识

1. 设置环境变量 HISTSIZE , 使其能够保存10000条命令历史.  vim /etc/profile  把 HISTSIZE=1000 改为 HISTSIZE=10000 2. 为什么如果这样设置PS1 (PS1="[\[email protected]\h \W]\$ ")  显示的结果和我们预想的不一样,那要如何设置才能恢复原来默认的?  应该是 PS1='[\[email protected]\h \W]$  '     (要用单引号) 3. 想办法把当前目录下的文件

第7章 鼠标_7.1-7.4 鼠标基础知识和鼠标消息

7.1 鼠标的基础知识 功能 GetSysMetrics的 参数 返回值 判断是否安装鼠标 SM_MOUSEPRESENT WINNT以上:TRUE己安装.0未安装 Windows98:总是TRUE. 鼠标按钮个数 SM_CMOUSEBUTTONS WINNT以上:0为未安装鼠标 Windows98:有安装鼠标返回按钮个数,没安装鼠标返回2. 鼠标按钮是否被切换 SM_SWAPBUTTON 设定鼠标其他参数(如双击):用SystemParametrsInfo函数获取或设定 (2)鼠标指针:wnd

【STM32H7教程】第45章 STM32H7的ADC应用之定时器触发配合DMA双缓冲

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第45章       STM32H7的ADC应用之定时器触发配合DMA双缓冲 本章教程为大家讲解定时器触发配合DMA双缓冲做ADC数据采集,实际项目中有一定的使用价值,一个缓冲接收数据的时候,另一个缓冲可以做数据处理. 45.1 初学者重要提示 45.2 ADC稳压基准硬件设计 45.3 ADC驱动设计 45.4 ADC板级支持包(bsp_adc.c) 45.5 AD

【STM32H7教程】第46章 STM32H7的ADC应用之DMA方式多通道采样

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第46章       STM32H7的ADC应用之DMA方式多通道采样 本章教程为大家讲解ADC+DMA方式的多通道数据采集,实际项目中有一定的使用价值,使用一路ADC就可以采集多个通道的数据. 46.1 初学者重要提示 46.2 ADC稳压基准硬件设计 46.3 ADC驱动设计 46.4 ADC板级支持包(bsp_adc.c) 46.5 ADC驱动移植和使用 46.

【STM32H7教程】第49章 STM32H7的FMC总线应用之SDRAM

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第49章       STM32H7的FMC总线应用之SDRAM 本章教程为大家讲解SDRAM的驱动实现,后面LCD的显存和大数量的存取都要用到. 49.1 初学者重要提示 49.2 SDRAM硬件设计 49.3 SDRAM驱动设计 49.4 SDRAM板级支持包(bsp_fmc_sdram.c) 49.5 SDRAM驱动移植和使用 49.6 实验例程设计框架 49.

【STM32H7教程】第51章 STM32H7的LTDC应用之LCD汉字显示和2D图形显示

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第51章       STM32H7的LTDC应用之LCD汉字显示和2D图形显示 本章教程为大家讲解LTDC应用中最基本的汉字显示和2D图形显示功能实现. 51.1 初学者重要提示 51.2 LCD相关的基础支持 51.3 LCD硬件设计 51.4 LCD驱动设计 51.5 LCD板级支持包(bsp_ltdc_h7.c和bsp_tft_lcd.c) 51.6 LCD的

【STM32H7教程】第41章 STM32H7的BDMA应用之控制任意IO做PWM和脉冲数控制

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第41章       STM32H7的BDMA应用之控制任意IO做PWM和脉冲数控制 本章教程为大家讲解定时器触发DMAMUX,控制BDMA让GPIO输出PWM以及脉冲数的控制,实际项目中有一定的使用价值. 41.1 初学者重要提示 41.2 定时器触发BDMA驱动设计 41.3 BDMA板级支持包(bsp_tim_dma.c) 41.4 BDMA驱动移植和使用 41