【STM32H7教程】第36章 STM32H7的LPTIM低功耗定时器基础知识和HAL库API
2021-01-19 22:16
标签:c语言 || alternate msi 基础上 immediate 限制 can pg1 完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 本章节为大家讲解LPTIM1 – LPTIM5共计5个定时器的基础知识和对应的HAL库API。 36.1 初学者重要提示 36.2 低功耗定时器基础知识 36.3 低功耗定时器的HAL库用户 36.4 源文件stm32h7xx_hal_lptim.c 36.5 总结 下面将低功耗定时器应用中要用到的基础知识做个介绍。 认识一个外设,最好的方式就是看它的框图,方便我们快速的了解定时器的基本功能,然后再看手册了解细节。 下面我们直接看最复杂的LPTIM1&LPTIM2框图: 通过这个框图,我们可以得到如下信息: 主要用于LPTIM的寄存器提供时钟,寄存器操作需要高速时钟,不能像LPTIM的其它部分一样使用LSI,LSE等低速时钟。 用于触发中断。 内核时钟,供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_out表示计数器输出,用于内部触发。 LPTIM_OUT表示GPIO的输出通道,用于PWM。 通过GPIO为LPTIM提供外部触发。 LPTIM的计数器既可以通过软件启动,也可以通过外部触发启动,有8种触发方式可供选择,以LPTIM1为例,支持的触发如下: LPTIM_IN2实际对应的是多路选择器的mux0,通过GPIO输入。 lptim_in1_mux1到lptim_in1_mux3对应的是内部输入,以LPTIM1为例,支持的输入信号如下: LPTIM_IN2实际对应的是多路选择器的mux0,通过GPIO输入。 lptim_in2_mux1到lptim_in2_mux3对应的是内部输入,以LPTIM1为例,支持的输入信号如下: 从框图中可以看到有三组Glitch filter,LPTIM_IN1和LPTIM_IN2接入多路选择器后各有一组,LPTIM_ETR接入后,也有一组。Glitch filter的作用是避免任何毛刺和噪声干扰在 LPTIM 内部传播,从而防止产生意外计数或触发。 注意:使用Glitch filter要向LPTIM 提供内部时钟源。 LPTIM1 – LPTIM5都是16位的低功耗定时器(自动重载寄存器、比较寄存器和计数器都是16位的),相比TIM1 – TIM17这种通用定时器,在睡眠或者停机模式下依然可以工作(待机模式除外)。低功耗模式下要工作,就必然要支持低速时钟LSI、LSE或者外部输入时钟,这点是与通用定时器的本质区别。同时LPTIM的中断还可以唤醒停机模式,这点比较重要(休眠模式是任何中断都可以唤醒的,而停机模式可以LPTIM中断唤醒)。 以下几点是大家在使用中必须要了解到的: 1、 TIM1 – TIM17有专门的分频寄存器,而LPTIM1 – LPTIM5的分频是几种固定的值。 2、 低功耗定时器支持以下6种模式: 在此模式下,当满足匹配条件时,输出可以切换高低电平(如果输出极性配置为高,则为低电平至高电平变化,反之亦然)。 有效的边沿触发输入可复位定时器。第一个触发事件将启动计时器,任何连续触发事件将重置计数器并重新开始。 计数器可用于计算来自Input1的外部事件或用于计算内部时钟周期。 这个知识点比较重要,可以帮助大家更好的理解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 (2) Count mode对应的是CFGR寄存器的bit23:COUNTMODE计数模式位,用于选择 LPTIM 使用哪个时钟源来为计数器提供时钟。 3、应用的时候,我们可以选择 (1) CKSEL = 0 , COUNTMODE = 0 表示LPTIM内核时钟使用的内部时钟源,计数器通过内部时钟脉计数。 (2) CKSEL = 0 , COUNTMODE = 1 表示LPTIM内核时钟使用的内部时钟源,计数器通过外部输入脉冲计数。 (3) CKSEL = 1 , COUNTMODE = x 表示LPTIM内核时钟使用的外部时钟源,计数器通过外部输入脉冲计数。 Glitch filter干扰滤波器的作用是避免任何毛刺和噪声干扰在 LPTIM 内部传播,从而防止产生意外计数或触发。 实现原理就是LPTIM的CFGR寄存器有专门的控制位TRGFLT[1:0](用于滤波外部触发信号)和CKFLT[1:0](用于滤波外部输入时钟)来控制信号,其有效电平变化必须至少稳定2/4/8个时钟周期才能将其视为有效触发。 比如下面的截图,配置为稳定2个时钟周期才算有效信号。 单次触发的含义就是定时器由触发事件启动,当达到 ARR 值时停止,效果如下: 连续模式的含义是定时器由触发事件启动,并且直到被禁止才会停止,效果如下: 注:这个模式用来做停机模式唤醒比较方便。 检测引脚第1次检查到触发信号,LPTIM就开始工作了,在溢出时间内检测到的触发信号都将复位计数,定时器重新开始工作。如果溢出内没有再接收到触发信号,仅进入溢出中断。 通过下面的截图,可以让大家对低功耗定时器的波形输出效果有个全面认识。 LPTIM_ARR是自动重载寄存器,Compare是比较寄存器。当定时器的计数器达到Compare后,GPIO输出高电平还是低电平,是由CFGR寄存器的bit2:1:WAVPOL波形极性决定的。 以PWM输出为例: One–Shot效果跟PWM一样,不过GPIO仅输出1次脉冲。 Set–Once特殊些,计数到ARR后,GPIO输出结果将一直保持达到Compare寄存器数值的输出电平。 关于这五个低功耗定时器的区别,可以直接通过参考手册里面的框图看它们的区别。我们这里也简单整理下: 低功耗定时器的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置GPIO、时钟,并根据需要配置NVIC,中断和DMA。下面我们逐一展开为大家做个说明。 低功耗定时器相关的寄存器是通过HAL库中的结构体LPTIM_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义: 这个结构体的成员名称和排列次序和CPU的定时器寄存器是一 一对应的。 __IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏: 下面我们看下LPTIM1,LPTIM2,LPTIM3,LPTIM4和LPTIM5的定义,在stm32h743xx.h文件。 我们访问LPTIM的ISR寄存器可以采用这种形式:LPTIM->ISR = 0。 HAL库在LPTIM_TypeDef的基础上封装了一个结构体LPTIM_HandleTypeDef,定义如下: 这里重点介绍前两个参数,其它参数主要是HAL库内部使用的。 TIM_TypeDef *Instance 这个参数是寄存器的例化,方便操作寄存器,比如使能定时器的计数器。 SET_BIT(huart->Instance->CR, LPTIM_CR_CNTSTRT)。 LPTIM_InitTypeDef Init 这个参数是用户接触最多的,用于配置低功耗定时器的基本参数。 LPTIM_InitTypeDef结构体的定义如下: 用于设置时钟源和时钟分频,结构体变量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可以选择如下八种。 此参数仅在使用超低功耗时钟源时使用,用于设置所选择的外部时钟,结构体变量LPTIM_ULPClockConfigTypeDef定义如下: 时钟极性参数Polarity用于选择有效的时钟极性,如果使能了双边沿,Auxiliary Clock(一种低功耗振荡器)必须处于激活状态。 采样时间参数SampleTime用于配置时钟干扰滤波器。可以配置的参数如下: 用于配置触发参数,结构体变量LPTIM_TriggerConfigTypeDef的定义如下: 触发源参数Source支持的选择如下: 参数ActiveEdge用于设置有效的触发边沿,可以选择上升沿,下降沿或者双边沿触发。 用于配置输出极性,可选择高电平或者低电平输出: 用于配置是否立即更新自动重装寄存器和比较寄存器,可以选择立即更新,或者当前周期结束后更新。 用于配置定时器计数器在每个内部事件或者外部事件后递增计数。可以选择内部或者外部。 用于配置Input1的输入源,可以选择GPIO,比较器输出或者SAI FSA/FSB。 用于配置Input2的输入源,可以选择GPIO和比较器。 注意,此参数仅用于编码器模式,也就是说仅支持LPTIM1和LPTIM2的例化。 下面是LPTIM1的配置例子: HAL库有个自己的底层初始化回调函数,比如调用函数HAL_LPTIM_Init就会调用HAL_LPTIM_MspInit,此函数是弱定义的。 用户可以在其它的C文件重定向,并将相对的底层初始化在里面实现。对应的底层复位函数HAL_LPTIM_MspDeInit是在函数 HAL_LPTIM_DeInit里面被调用的,也是弱定义的。 当然,用户也可以自己初始化,不限制必须在两个函数里面实现。 定时器外设的基本参数配置完毕后还不能使用,还需要配置GPIO、时钟、中断等参数,比如下面配置LPTIM1使用PD13做PWM输出。 总结下来就是以下几点: 关于这个底层配置有以下几点要着重说明下: 但是却有4个输出通道,每个通道都有几个支持的输出引脚: 具体使用哪个,配置对应引脚的复用即可: 下面我们介绍__HAL_LPTIM_GET_FLAG函数。这个函数用来检查定时器标志位是否被设置。 当前做的应用程序,这几个中断标志暂时都还没有被用到。 与标志获取函数__HAL_TIM_GET_FLAG对应的清除函数是__HAL_LPTIM_CLEAR_FLAG: 清除标志函数所支持的参数跟获取函数是一 一对应的。除了这两个函数,还是定时器的中断开启和中断关闭函数,有时候也要用到。 注意:操作定时器的寄存器不限制必须要用HAL库提供的API,比如要操作寄存器CR,直接调用LPTIM1->CR操作即可。 使用方法由HAL库提供: 第1步:通过函数HAL_LPTIM_Init()做初始化,主要配置的内容如下: (1) Source,可以选择ULPTIM input (IN1)时钟输入,或者内部时钟,如APB, LSE, LSI ,MSI等。 (2) Prescaler,设置分频。 只有在Clock计数时钟源选择了ULPTIM input,此参数才有意义。 (1) Polarity,计数单元有效的边沿极性。 (2) SampleTime,配置时钟干扰滤波器的时钟。 (1) Source,可以选择硬件触发或者软件触发。 (2) ActiveEdge,仅用于硬件触发,用来设置触发边沿(上升沿、下降沿或者双沿)。 (3) SampleTime,采样时间,用于配置触发干扰滤波器的时钟。 UpdateMode更新模式,用于配置是否立即更新自动重装寄存器和比较寄存器,可以选择立即更新,或者当前周期结束后更新。 注意,此参数仅用于编码器模式,也就是说仅支持LPTIM1和LPTIM2的例化。 第2步:低功耗定时器的底层配置是通过函数HAL_LPTIM_MspInit()实现: 第3步:低功耗定时器支持的6种工作模式: 启动此模式可调用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(如果此模式已经在中断方式下启动)。 低功耗定时器常用的功能,通过上面这几步即可实现。 此文件涉及到的函数比较多,这里把我们几个常用的函数做个说明: 函数原型: 函数描述: 此函数用于初始化低功耗定时器的基本参数。 函数参数: 注意事项: 对于局部变量来说,这个参数就是一个随机值,如果是全局变量还好,一般MDK和IAR都会将全部变量初始化为0,而恰好这个HAL_LPTIM_STATE_RESET = 0x00U。 解决办法有四 方法1:用户自己初始定时器和涉及到的GPIO等。 方法2:定义LPTIM_HandleTypeDef LptimHandle为全局变量。 方法3:定义为局部变量要赋初始值LPTIM_HandleTypeDef LptimHandle = {0}。 方法4:下面的方法 使用举例: 函数原型: 函数描述: 调用函数HAL_LPTIM_Init配置了基础功能后,就可以调用此函数启动定时器PWM输出了。 函数参数: 注意事项: PWM频率 = LSE / (Period + 1) = 32768 / (9 + 1) = 327Hz。 当第3个参数Pluse = 5 PWM占空比 = 1 – (Pluse + 1)/(Period + 1) = 1 – 5/10 = 50% 使用举例: 函数原型: 函数描述: 调用函数HAL_LPTIM_Init配置了基础功能后,就可以调用此函数启动定时器的超时功能。 函数参数: 注意事项: 使用举例: 函数原型第36章 STM32H7的LPTIM低功耗定时器基础知识和HAL库API
36.1 初学者重要提示
36.2 低功耗定时器基础知识
36.2.1 定时器的硬件框图
36.2.2 低功耗定时器的基本功能
36.2.3 低功耗定时器时钟选择问题(重要)
36.2.4 干扰滤波器(Glitch filter)
36.2.5 单次触发和连续模式
36.2.6 溢出模式
36.2.7 波形输出
36.2.8 低功耗定时器LPTIM1 – LPTIM5的区别
36.3 低功耗定时器的HAL库用法
36.3.1 定时器寄存器结构体LPTIM_TypeDef
typedef struct
{
__IO uint32_t ISR; /*!*/
__IO uint32_t ICR; /*!*/
__IO uint32_t IER; /*!*/
__IO uint32_t CFGR; /*!*/
__IO uint32_t CR; /*!*/
__IO uint32_t CMP; /*!*/
__IO uint32_t ARR; /*!*/
__IO uint32_t CNT; /*!*/
uint16_t RESERVED1; /*!*/
__IO uint32_t CFGR2; /*!*/
} LPTIM_TypeDef;
#define __O volatile /*!#define __IO volatile /*!
#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) #define LPTIM2 ((LPTIM_TypeDef *) LPTIM2_BASE)
#define LPTIM3 ((LPTIM_TypeDef *) LPTIM3_BASE)
#define LPTIM4 ((LPTIM_TypeDef *) LPTIM4_BASE)
#define LPTIM5 ((LPTIM_TypeDef *) LPTIM5_BASE)
36.3.2 定时器句柄结构体LPTIM_HandleTypeDef
typedef struct
{
LPTIM_TypeDef *Instance; /*!*/
LPTIM_InitTypeDef Init; /*!*/
HAL_StatusTypeDef Status; /*!*/
HAL_LockTypeDef Lock; /*!*/
__IO HAL_LPTIM_StateTypeDef State; /*!*/
}LPTIM_HandleTypeDef;
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;
typedef struct
{
uint32_t Source;
uint32_t Prescaler;
}LPTIM_ClockConfigTypeDef;
#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)
typedef struct
{
uint32_t Polarity;
uint32_t SampleTime;
}LPTIM_ULPClockConfigTypeDef;
#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
typedef struct
{
uint32_t Source;
uint32_t ActiveEdge;
uint32_t SampleTime;
}LPTIM_TriggerConfigTypeDef;
#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
#define LPTIM_ACTIVEEDGE_RISING LPTIM_CFGR_TRIGEN_0
#define LPTIM_ACTIVEEDGE_FALLING LPTIM_CFGR_TRIGEN_1
#define LPTIM_ACTIVEEDGE_RISING_FALLING LPTIM_CFGR_TRIGEN
#define LPTIM_OUTPUTPOLARITY_HIGH ((uint32_t)0x00000000U)
#define LPTIM_OUTPUTPOLARITY_LOW (LPTIM_CFGR_WAVPOL)
#define LPTIM_UPDATE_IMMEDIATE ((uint32_t)0x00000000U)
#define LPTIM_UPDATE_ENDOFPERIOD LPTIM_CFGR_PRELOAD
#define LPTIM_COUNTERSOURCE_INTERNAL ((uint32_t)0x00000000U)
#define LPTIM_COUNTERSOURCE_EXTERNAL LPTIM_CFGR_COUNTMODE
#define LPTIM_INPUT1SOURCE_GPIO ((uint32_t)0x00000000U) /*!#define LPTIM_INPUT1SOURCE_COMP1 LPTIM_CFGR2_IN1_SEL0 /*!#define LPTIM_INPUT1SOURCE_COMP2 LPTIM_CFGR2_IN1_SEL1 /*!#define LPTIM_INPUT1SOURCE_COMP1_COMP2 (LPTIM_CFGR2_IN1_SEL0|LPTIM_CFGR2_IN1_SEL1) /*!#define LPTIM_INPUT1SOURCE_SAI1_FSA LPTIM_CFGR2_IN1_SEL0 /*!#define LPTIM_INPUT1SOURCE_SAI1_FSB LPTIM_CFGR2_IN1_SEL1 /*!
#define LPTIM_INPUT2SOURCE_GPIO ((uint32_t)0x00000000U) /*!#define LPTIM_INPUT2SOURCE_COMP2 LPTIM_CFGR2_IN2_SEL0 /*!
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、时钟、中断等)
__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
*/
}
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);
}
#define GPIO_AF1_LPTIM1 ((uint8_t)0x01) /* LPTIM1 Alternate Function mapping */
LPTIM1_OUT PG13
LPTIM1_OUT PD13
36.3.4 定时器的状态标志清除问题
/**
* @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__))
/**
* @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__)))
36.3.5 定时器初始化流程总结
36.4 源文件stm32h7xx_hal_lptim.c
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;
}
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;
}
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;
}
/*
*********************************************************************************************************
* 函 数 名: 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
文章标题:【STM32H7教程】第36章 STM32H7的LPTIM低功耗定时器基础知识和HAL库API
文章链接:http://soscw.com/index.php/essay/44262.html