低功耗技术旨在减少设备电流消耗,延长电池寿命,可以有效的延长产品的使用寿命。这就需要合理分配运行时间和空闲时间,在空闲时可以通过使MCU进入低功耗模式降低系统的功耗,在需要MCU工作时唤醒MCU,低功耗定时器(LPTIM)有助于降低功耗,特别是当系统处于低功耗模式时(如 stop模式)可以在休眠模式下实现外部脉冲计数功能。通过外部输入的触发信号,能够实现低功耗超时唤醒。LPTIM 具有外部时钟计数,超时唤醒功能和 PWM 输出等多种用途。
本文主要介绍 MM32 全新低功耗系列 MM32L013x 产品中的 LPTIM 外设模块,它允许系统执行简单的任务,同时功耗保持在绝对最小。
1 LPTIM 简介
LPTIM 由一个 16 位计数器组成,可以为用户提供便捷的计数和定时功能。LPTIM 运行在 CORE 电源域,可以工作在低功耗模式下,具有低功耗的特点,当然也可以被当做一个普通的 16 位基础定时器来使用。
2 LPTIM 常见用途
- 脉冲计数功能
- PWM 生成器
- 周期性地超时唤醒sleep模式、stop模式
以上所有功能应用均为普通 TIM 所不能实现的,正是由于 LPTIM 的时钟源具有多样性,使得其能够在所有电源模式(standby模式除外)下保持运行状态。
3 LPTIM 主要特性
- 16 位递增计数器,相当于 LPT_CNT 值由模块时钟驱动加一递增最大到 65535,并且包括一个 16 位比较寄存器和目标值寄存器
- 3 位异步时钟预分频器,可以将输入进模块的时钟进行多种系数的预分频,包括: 1/2/4/8/16/32/64/128 分频
- 多种内部和外部的时钟源可选,包括:LSI/LSE/PCLK2
- 可以选择不同边沿的脉冲极性用作触发计数,触发源包括:LPTIM1_TRIGGER 引脚输入触发、COMP OUT 事件触发
- 可以设定不同的 LPT_CMP 和 LPT_TARGET 值从而通过 LPTIM1_OUT 引脚输出不同占空比的PWM 波形或者方波
- 可以设定不同的 LPT_TARGET 值从而进行不同时长的低功耗唤醒
- 可以开启不同的中断,包括:外部触发、比较匹配和计数器溢出中断,在唤醒 STOP 模式时,除了需要使能相应的中断外,还需配置 EXTI_Line23 并且使能相关的功能
4 LPTIM 功能框图
LPTIM 的功能框图如下,主要包括了:触发源选择模块、时钟输入及分频模块、计数模块以及比较匹配输出单元等。
涉及到时钟选择的部分需要设置RCC_CFGR2[30:29],在 UM 手册中时钟配置寄存器2(RCC_CFGR2):
使能LPTIM时钟需要设置RCC_APB2ENR[31]位:
涉及到 LPTIM1_TRIGGER 和 LPTIM1_OUT 引脚的功能定义情况在 DS 手册中引脚复用及复用列表:
5 LPTIM 寄存器表
由于 MM32L013x 系列产品中只有一个 LPTIM 模块,所以在文档和程序中也习惯称作 LPTIM1,它的基地址为 0x40012800,所有寄存器设计为 16 位,预留出 16位保持 32 位对齐,寄存器占用情况如下:
其中 LPT_CFG 、 LPT_IE 、 LPT_CTRL 、LPT_CMP 、 LPT_TARGET 为可读可写操作的,LPT_IF 为可读并且写 1 清零操作的,LPT_CNT 为只读操作的,具体功能描述参见 UM 对应章节内容。
6 LPTIM 功能实现
6.1 带溢出中断的普通定时器
- 使能 APB2 总线上的 LPTIM 外设时钟,用于同步
- 初始化配置 LPT_CFG 寄存器 MODE=0,计数器被触发后保持运行,直到被关闭为止
- 初始化配置 LPT_CFG 寄存器 TMODE=00,选择普通计数器模式
- 选择外部时钟或内部时钟作为计数器的时钟源
- 配置 LPT_IE =1,使能 LPTIM 计数器溢出中断
- 配置 LPT_CTRL 寄存器 LPTEN=1,使能 LPTIM 计数器
- 计数器使能后有两个周期的同步过程,同步完成后,计数器开始工作,计数达到目标之后回到 0 重新开始计数,并产生溢出中断
按上述流程的代码配置如下:
void LPTIM1_Init(u16 arr)
{
LPTIM_TimeBaseInit_TypeDef init_struct;
RCC_APB2PeriphClockCmd(RCC_APB2ENR_LPTIM1, ENABLE);
LPTIM_TimeBaseStructInit(&init_struct);
init_struct.ClockSource = LPTIM_PCLK_Source;
init_struct.CountMode = LPTIM_CONTINUOUS_COUNT_Mode;
init_struct.OutputMode = LPTIM_NORMAL_WAV_Mode;
init_struct.Waveform = LPTIM_AdjustPwmOutput_Mode;
init_struct.Polarity = LPTIM_Positive_Wave;
init_struct.ClockDivision = LPTIM_CLK_DIV1;
if(init_struct.ClockSource == LPTIM_LSE_Source) {
RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWR | RCC_APB1ENR_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_ON);
DELAY_Ms(5000);
while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));
LPTIM_CLKConfig(LPTIM1, LPTIM_LSE_Source);
}
else if(init_struct.ClockSource == LPTIM_LSI_Source) {
RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWR | RCC_APB1ENR_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_LSICmd(ENABLE);
DELAY_Ms(500);
while(!RCC_GetFlagStatus(RCC_FLAG_LSIRDY));
LPTIM_CLKConfig(LPTIM1, LPTIM_LSI_Source);
}
else { //(init_struct.ClockSource == LPTIM_PCLK_Source)
LPTIM_CLKConfig(LPTIM1, LPTIM_PCLK_Source);
}
LPTIM_TimeBaseInit(LPTIM1, &init_struct);
LPTIM_SetTarget(LPTIM1, arr);
}
void LPTIMER1_IRQHandler(void)
{
if(LPTIM_GetITStatus(LPTIM1, LPTIF_OVIF)) {
LPTIM_ClearITPendingBit(LPTIM1, LPTIF_OVIF);
LED3_TOGGLE();
LED4_TOGGLE();
}
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = LPTIMER1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
s32 main(void)
{
DELAY_Init();
LED_Init();
LPTIM1_Init(4000 - 1);
NVIC_Configuration();
LPTIM_ITConfig(LPTIM1, LPTIE_OVIE, ENABLE);
LPTIM_Cmd(LPTIM1, ENABLE);
while (1)
{
LED1_TOGGLE();
LED2_TOGGLE();
DELAY_Ms(1000);
}
}
6.2 PWM 输出
- 初始化中使用以上相同的步骤,需要额外配置 LPT_CFG 寄存器 PWM=1,选择 PWM 输出模式
- 再配置 LPT_CMP 和 LPT_TARGET 寄存器,设定比较值和目标值,PWM 的占空比由比较值和目标值决定,输出在计数器值等于比较值翻转为 1,在等于目标值时翻转为 0
- 另外需要配置一组 LPTIM1_OUT 引脚用于输出 PWM 波形,这里以 PB14 为例
代码配置如下:
void LPTIM_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_GPIO_ClockCmd(GPIOB, ENABLE);
//set PB14 as LPTIM1 Output Pin
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_3);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void LPTIM1_Init(u16 arr)
{
LPTIM_TimeBaseInit_TypeDef init_struct;
RCC_APB2PeriphClockCmd(RCC_APB2ENR_LPTIM1, ENABLE);
LPTIM_TimeBaseStructInit(&init_struct);
init_struct.ClockSource = LPTIM_PCLK_Source;
init_struct.CountMode = LPTIM_CONTINUOUS_COUNT_Mode;
init_struct.OutputMode = LPTIM_NORMAL_WAV_Mode;
init_struct.Waveform = LPTIM_AdjustPwmOutput_Mode;
init_struct.Polarity = LPTIM_Positive_Wave;
init_struct.ClockDivision = LPTIM_CLK_DIV1;
if(init_struct.ClockSource == LPTIM_LSE_Source) {
RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWR | RCC_APB1ENR_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_ON);
DELAY_Ms(5000);
while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));
LPTIM_CLKConfig(LPTIM1, LPTIM_LSE_Source);
}
else if(init_struct.ClockSource == LPTIM_LSI_Source) {
RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWR | RCC_APB1ENR_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_LSICmd(ENABLE);
DELAY_Ms(500);
while(!RCC_GetFlagStatus(RCC_FLAG_LSIRDY));
LPTIM_CLKConfig(LPTIM1, LPTIM_LSI_Source);
}
else { //(init_struct.ClockSource == LPTIM_PCLK_Source)
LPTIM_CLKConfig(LPTIM1, LPTIM_PCLK_Source);
}
LPTIM_TimeBaseInit(LPTIM1, &init_struct);
}
s32 main(void)
{
DELAY_Init();
LED_Init();
LPTIM1_Init(4000 - 1);
LPTIM_SetCompare(LPTIM1, 4000 / 2 - 1);
LPTIM_SetTarget(LPTIM1, 4000 );
LPTIM_GPIO_Init();
LPTIM_Cmd(LPTIM1, ENABLE);
while (1)
{
LED1_TOGGLE();
LED2_TOGGLE();
DELAY_Ms(1000);
}
}
6.3 Trigger 脉冲触发计数
- 初始化中使用以上相同的步骤,需要修改配置 LPT_CFG 寄存器 TMODE=01,选择 Trigger 脉冲触发计数模式
- 配置 LPT_CFG 寄存器 TRIGSEL = 0,选择外部引脚触发计数
- 配置 LPT_CFG 寄存器 TRIGCFG,选择外部触发信号的有效沿
- 用户可以根据需求选择是否使能滤波器,配置LPT_CFG 寄存器
- 根据需求是否使能外部触发中断位
- 另外需要配置一组 LPTIM1_TRIGGER 引脚用于触发输入,这里以 PB13 为例
代码配置如下:
void LPTIM_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_GPIO_ClockCmd(GPIOB, ENABLE);
//set PB13 as LPTIM1 Trigger Pin
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_3);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void LPTIM1_Init(u16 arr)
{
LPTIM_TimeBaseInit_TypeDef init_struct;
RCC_APB2PeriphClockCmd(RCC_APB2ENR_LPTIM1, ENABLE);
LPTIM_TimeBaseStructInit(&init_struct);
init_struct.ClockSource = LPTIM_PCLK_Source;
init_struct.CountMode = LPTIM_CONTINUOUS_COUNT_Mode;
init_struct.OutputMode = LPTIM_PULSE_TRIG_Mode;
init_struct.Waveform = LPTIM_AdjustPwmOutput_Mode;
init_struct.Polarity = LPTIM_Positive_Wave;
init_struct.ClockDivision = LPTIM_CLK_DIV1;
if(init_struct.ClockSource == LPTIM_LSE_Source) {
RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWR | RCC_APB1ENR_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_ON);
DELAY_Ms(5000);
while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));
LPTIM_CLKConfig(LPTIM1, LPTIM_LSE_Source);
}
else if(init_struct.ClockSource == LPTIM_LSI_Source) {
RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWR | RCC_APB1ENR_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_LSICmd(ENABLE);
DELAY_Ms(500);
while(!RCC_GetFlagStatus(RCC_FLAG_LSIRDY));
LPTIM_CLKConfig(LPTIM1, LPTIM_LSI_Source);
}
else { //(init_struct.ClockSource == LPTIM_PCLK_Source)
LPTIM_CLKConfig(LPTIM1, LPTIM_PCLK_Source);
}
LPTIM_TimeBaseInit(LPTIM1, &init_struct);
LPTIM1->CFGR = LPTIM_ExInputUpEdge;
LPTIM1->CFGR = LPTIM_External_PIN_Trig;
}
6.4 1s 周期性唤醒 STOP 模式
详细可以参见灵动微课堂 | 使用MM32F0270 LPTIM从STOP模式唤醒。
作者:灵动MM32
文章来源:灵动MM32MCU
推荐阅读
- 灵动微课堂 |使用MM32F3270基于Azure RTOS动态内存管理的应用
- 灵动微课堂 |使用MM32F3270基于Azure RTOS信号量的应用
- 灵动微课堂 |MM32F5270 TIM 精准脉冲数量输出
- 灵动微课堂 |MM32F5270定时器单脉冲输出
更多MM32F5系列资料请关注灵动MM32 MCU专栏。如想进行MM32相关芯片技术交流,请添加极术小姐姐微信(id:aijishu20)加入微信群。