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

Posted armfly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 低功耗定时器板级支持包(bsp_lptim_pwm.c)

37.4 低功耗定时器驱动移植和使用

37.5 实验例程设计框架

37.6 实验例程说明(MDK)

37.7 实验例程说明(IAR)

37.8 总结

 

 

37.1 初学者重要提示

  1.   学习本章节前,务必优先学习第36章,HAL库的几个常用API均作了讲解和举例。
  2.   使用LPTIM的好处是系统处于睡眠、停机状态依然可以正常工作(除了待机模式)。停机状态可以正常工作的关键是LSE,LSI时钟不会被关闭,同时也可以选择使用外部时钟源。
  3.   STM32H7从停机模式唤醒后要重新配置系统时钟,这点跟F1,F4系列一样。

37.2 低功耗定时器PWM驱动设计

低功耗定时器LPTIM1 – LPTIM5均支持PWM输出。

37.2.1 低功耗定时器PWM输出支持的引脚

STM32H7的低功耗定时器LPTIM1 - LPTIM5可以输出到GPIO的TIM通道整理:

LPTIM1_IN1   PD12  PG12
LPTIM1_IN2   PH2   PE1
LPTIM1_OUT   PG13
LPTIM1_OUT   PD13
LPTIM1_ETR   PG14  PE0

LPTIM2_IN1   PB10  PD12
LPTIM2_IN2   PD11
LPTIM2_OUT   PB13
LPTIM2_ETR   PB11 PE0

LPTIM3_OUT   PA1
LPTIM4_OUT   PA2
LPTIM5_OUT   PA3

 

37.2.2 低功耗定时器时钟选择

由前面的第36章节,我们知道LPTIM1的时钟可以由LSE,LSI,APB或者外部输入时钟提供。使用LSE,LSI或者外部输入的好处是停机状态下,LPTIM1也可以正常工作。

  •   V7开发板使用的LSE晶振是32768Hz。
  •   STM32H743的LSI频率约32KHz。
  •   LPTIM1 – LPTIM5的频率都是100MHz。
System Clock source       = PLL (HSE)
SYSCLK(Hz)                = 400000000 (CPU Clock)
HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
AHB Prescaler             = 2
D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)
D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)
D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)
D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)

因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含这个总线下的LPTIM1
因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;

APB4上面的TIMxCLK没有分频,所以就是100MHz;

APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17

APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5

 

下面为大家讲解下使用LSE,LSI或者APB时钟的配置方法。

  •   选择LSE的配置如下:
#define LPTIM_CLOCK_SOURCE_LSE     /* LSE 时钟32768Hz */

RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);        
}

RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

 

特别注意程序中置红的地方,这几个地方很容易配置错。配置后LPTIM1就会将LSE作为系统时钟。

  •   选择LSI的配置如下:
//#define LPTIM_CLOCK_SOURCE_LSI    /* LSI 时钟约32KHz */
RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = {0};

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);        
}

RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSI;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

 

使用LSI作为LPTIM1的系统是要注意两点:

1、LSI的实现有一定的误差,具体范围在数据手册有给出,由于不支持温补,温度对其也是有影响的。

技术图片 

2、特别注意程序中置红的地方,这几个地方很容易跟LSE搞混淆(复制粘贴的时候容易搞错)。

  •   选择APB时钟的配置如下:
RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};

RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_D2PCLK1;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);

 

使用APB作为LPTIM系统时钟注意以下两点:

1、   LPTIM1 – LPTIM5的最高主频都是100MHz。

2、   注意参数RCC_LPTIM1CLKSOURCE_D2PCLK1。

LPTIM1使用的RCC_LPTIM1CLKSOURCE_D2PCLK1。

LPTIM2使用的RCC_LPTIM2CLKSOURCE_D3PCLK1。

LPTIM3-LPTIM5使用的RCC_LPTIM345CLKSOURCE_D3PCLK1。

37.2.3 低功耗定时器的PWM配置

下面通过低功耗定时器实现一个频率为1024Hz,占空比50%,使用LSE做系统时钟的配置。PWM输出引脚采用PD13。

1.    /* 选择LPTIM的时钟源 */
2.    #define LPTIM_CLOCK_SOURCE_LSE     /* LSE 时钟32768Hz */
3.    //#define LPTIM_CLOCK_SOURCE_LSI   /* LSI 时钟约32KHz */ 
4.    //#define LPTIM_CLOCK_SOURCE_PCLK  /* PCLK 时钟100MHz */
5.    /*
6.    ******************************************************************************************************
7.    *    函 数 名: bsp_InitTIMOutPWM
8.    *    功能说明: LPTIM1时钟默认选择的LSE,而PWM输出使用的PD13引脚,频率1024Hz。
9.    *    形    参: 无
10.    *    返 回 值: 无
11.    ******************************************************************************************************
12.    */
13.    void bsp_InitTIMOutPWM(void)
14.    {
15.        LPTIM_HandleTypeDef        LptimHandle = {0};    
16.        RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};
17.        GPIO_InitTypeDef           GPIO_InitStruct = {0};
18.    
19.        /* ## - 1 - 使能LPTIM时钟和GPIO时钟 ####################################### */
20.        __HAL_RCC_LPTIM1_CLK_ENABLE();
21.    
22.        __HAL_RCC_GPIOD_CLK_ENABLE();
23.    
24.        /* ## - 2 - 配置PD13做PWM输出 ############################################ */    
25.        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
26.        GPIO_InitStruct.Pull = GPIO_PULLUP;
27.        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
28.        GPIO_InitStruct.Alternate = GPIO_AF1_LPTIM1;
29.        GPIO_InitStruct.Pin = GPIO_PIN_13;
30.        HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
31.    
32.        /* ## - 3 - 配置LPTIM时钟,可以选择LSE,LSI或者PCLK ######################## */        
33.    #if defined (LPTIM_CLOCK_SOURCE_LSE)
34.        {
35.            RCC_OscInitTypeDef RCC_OscInitStruct = {0};
36.    
37.            RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
38.            RCC_OscInitStruct.LSEState = RCC_LSE_ON;
39.            RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
40.    
41.            if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
42.            {
43.                Error_Handler(__FILE__, __LINE__);        
44.            }
45.            
46.            RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
47.            RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE;
48.            HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
49.        }
50.    #elif defined (LPTIM_CLOCK_SOURCE_LSI)
51.        {
52.            RCC_OscInitTypeDef RCC_OscInitStruct = {0};
53.    
54.            RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
55.            RCC_OscInitStruct.LSIState = RCC_LSI_ON;
56.            RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
57.    
58.            if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
59.            {
60.                Error_Handler(__FILE__, __LINE__);        
61.            }
62.            
63.            RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
64.            RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSI;
65.            HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
66.        }
67.    #elif defined (LPTIM_CLOCK_SOURCE_PCLK)
68.        RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM1;
69.        RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_D2PCLK1;
70.        HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
71.    #else
72.        #error Please select the LPTIM Clock source inside the bsp_lptim_pwm.c file
73.    #endif
74.    
75.        /* ## - 4 - 配置LPTIM ######################################################## */        
76.        LptimHandle.Instance = LPTIM1;
77.         /* 对应寄存器CKSEL,选择内部时钟源 */
78.        LptimHandle.Init.Clock.Source    = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC; 
79.         /* 设置LPTIM时钟分频 */
80.        LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;  
81.         /* LPTIM计数器对内部时钟源计数 */     
82.        LptimHandle.Init.CounterSource   = LPTIM_COUNTERSOURCE_INTERNAL
83.         /* 软件触发 */
84.        LptimHandle.Init.Trigger.Source  = LPTIM_TRIGSOURCE_SOFTWARE;   
85.         /* 计数器计数到比较寄存器和ARR自动重载寄存器之间数值,输出高电平 */
86.        LptimHandle.Init.OutputPolarity  = LPTIM_OUTPUTPOLARITY_HIGH;   
87.         /* 比较寄存器和ARR自动重载寄存器选择更改后立即更新 */
88.        LptimHandle.Init.UpdateMode      = LPTIM_UPDATE_IMMEDIATE; 
89.         /* 外部输入1,本配置未使用 */     
90.        LptimHandle.Init.Input1Source    = LPTIM_INPUT1SOURCE_GPIO;  
91.         /* 外部输入2,本配置未使用 */   
92.        LptimHandle.Init.Input2Source    = LPTIM_INPUT2SOURCE_GPIO;    
93.    
94.        if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK)
95.        {
96.            Error_Handler(__FILE__, __LINE__);
97.        }
98.        
99.    
100.        /* ## - 5 - 启动LPTIM的PWM模式 ######################################################## */    
101.        /*
102.           ARR是自动重装寄存器,对应函数HAL_LPTIM_PWM_Start的第2个参数
103.           Compare是比较寄存器,对应函数HAL_LPTIM_PWM_Start的第3个参数
104.    
105.           ---------------------
106.           LSE = 32768Hz
107.           分频设置为LPTIM_PRESCALER_DIV1,即未分频
108.           ARR自动重载寄存器 = 31
109.           那么PWM频率 = LSE / (ARR + 1) = 32768Hz / (31 + 1) = 1024Hz
110.        
111.           占空比 = 1 - (Comprare + 1)/ (ARR + 1)
112.                  = 1 - (15 + 1)/(31 + 1)
113.                  = 50%
114.        
115.           占空比这里为什么要1减操作,而不是直接的(Comprare + 1)/ (ARR + 1),这是因为前面的配置中
116.           计数器计数到比较寄存器和ARR自动重载寄存器之间数值,输出高电平。
117.        */
118.        if (HAL_LPTIM_PWM_Start(&LptimHandle, 31, 15) != HAL_OK)
119.        {
120.            Error_Handler(__FILE__, __LINE__);
121.        }
122.    }

 

这里把几个关键的地方阐释下:

  •   第2行,LPTIM1的系统时钟选项LSE,频率32768Hz。
  •   第15 – 17行,HAL库的这个结构体变量要初始化为0,此问题在第36章的4.1小节有专门说明。
  •   第76 – 97行,第36章的3.2小节对这些参数成员有详细描述。
  •   第118行,启动PWM输出,特别注意PWM的频率和占空比的计算,在前面的注释中已经讲解的比较清楚。

37.2.4 低功耗定时器待机模式下正常运行

这里先补充三个知识点。

  •   LPTIM的好处是系统处于睡眠,停机状态依然可以正常工作,但停机模式不能再正常工作。
  •   对于睡眠模式,任何受NVIC控制的中断都可以唤醒休眠模式。进入睡眠模式调用函数HAL_PWR_EnterSLEEPMode即可。
  •   在系统停止模式下,1.2V供电域中的所有时钟都停止,PLL,HSI和HSE RC振荡器被禁用。内部SRAM和寄存器内容保留。而LSE和LSI是可以正常工作的,所以LPTIM系统时钟使用LSE或者LSI依然可以在停机模式下工作。

进入停机模式调用函数HAL_PWR_EnterSTOPMode即可。

 

对于停机模式,本章节配套的例子是通过GPIO的EXTI Event唤醒。配套如下:

/*
*********************************************************************************************************
*    函 数 名: PwrExitStopMode
*    功能说明: 起初按键K3的GPIO配置为输入模式用于按键后,再次配置EXTI Event模式后,按键功能依然可以正常用。
*              配置为EXTI Event是因为可以唤醒停机模式。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void PwrExitStopMode(void)
{
    GPIO_InitTypeDef              GPIO_InitStruct = {0};
        
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Mode = GPIO_MODE_EVT_FALLING; /* 配置为下降沿触发Event */     
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}

 

37.3 低功耗定时器板级支持包(bsp_lptim_pwm.c)

低功耗定时器驱动文件bsp_lptim_pwm.c供用户调用的只有如下一个函数:

  •   bsp_InitLPTIMOutPWM

 

下面将这个函数的使用为大家做个说明。

37.3.1 函数bsp_InitLPTIMOutPWM

函数原型:

void bsp_InitLPTIMOutPWM(void)

函数描述:

使用低功耗定时器LPTIM1实现一个频率为1024Hz,占空比50%,使用LSE做LPTIM1的系统时钟。

注意事项:

  1. 关于此函数的相关注意事项在本章的37.2小节有详细说明。

使用举例:

初始化函数在bsp.c文件的bsp_Init函数里面调用。

37.4 低功耗定时器驱动移植和使用

低功耗定时器的移植比较简单:

  •   第1步:复制bsp_lptim_pwm.c和bsp_lptim_pwm.h到自己的工程目录,并添加到工程里面。
  •   第2步:这几个驱动文件主要用到HAL库的GPIO和LPTIM驱动文件,简单省事些可以添加所有HAL库.C源文件进来。
  •   第3步,应用方法看本章节配套例子即可。如果用到按键唤醒的话,看main.c文件里面的函数PwrExitStopMode即可。

37.5 实验例程设计框架

通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:

技术图片

  第1阶段,上电启动阶段:

这部分在第14章进行了详细说明。

  第2阶段,进入main函数:

  •   第1步,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器,LED和串口。
  •   第2步,借助按键消息实现低功耗定时器的效果测试。

37.6 实验例程说明(MDK)

配套例子:

V7-021_低功耗定时器PWM输出

实验目的:

  1. 学习低功耗定时器PWM输出。

实验内容:

  1. 使用LPTIM的好处是系统处于睡眠,停机状态依然可以正常工作(除了待机模式)。停机状态可以正常工作的关键是LSE,LSI时钟不会被关闭,同时也可以选择使用外部时钟源。
  2. 例子默认用的LSE时钟供LPTIM1使用,大家可以通过bsp_lptim_pwm.c文件开头宏定义切换到LSI或者PLCK。
  3. PWM输出引脚采用的PD13,输出频谱1024Hz,占空比50%。

实验操作:

  1. K1键按下,进入低功耗的停机模式,LED2停止闪烁。
  2. K3键按下,退出停机模式,LED2继续闪烁。

PD13的位置:

技术图片 

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1

技术图片 

程序设计:

  系统栈大小分配:

技术图片 

  RAM空间用的DTCM:

技术图片 

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIV优先级分组为4。
     */
    HAL_Init();

    /* 
       配置系统时钟到400MHz
       - 切换使用HSE。
       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并开启 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    bsp_InitTimer();      /* 初始化滴答定时器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
    bsp_InitLed();        /* 初始化LED */    
}

 

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 数 名: CPU_CACHE_Enable
*    功能说明: 使能L1 Cache
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  主功能:

主程序实现如下操作:

  •   K1键按下,进入低功耗的停机模式,LED2停止闪烁。
  •   K3键按下,退出停机模式,LED2继续闪烁。
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按键代码 */
    

    bsp_Init();           /* 硬件初始化 */
    PwrExitStopMode(); /* 配置K3按键用于唤醒停机模式 */
    
    PrintfLogo();    /* 打印例程名称和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */

    bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
    
    /* 进入主程序循环体 */
    while (1)
    {
        bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */

        /* 判断定时器超时时间 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 进来一次 */  
            bsp_LedToggle(2);
        }

        /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
        ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1键按下,进入停机模式 */
                    printf("--进入停机模式
");
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
                    SystemClock_Config();   /* 特别注意,退出后要重新配置系统时钟 */
                    break;
                
                case KEY_DOWN_K3:            /* K3键按下,唤醒停机模式 */
                    printf("--退出停机模式
");
                    break;

                default:
                    /* 其它的键值不处理 */
                    break;
            }
        }
    }
}

 

通过GPIO为EXTI Event可以唤醒停机模式:

/*
*********************************************************************************************************
*    函 数 名: PwrExitStopMode
*    功能说明: 起初按键K3的GPIO配置为输入模式用于按键后,再次配置EXTI Event模式后,按键功能依然可以正常使*              用。配置为EXTI Event是因为可以唤醒停机模式。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void PwrExitStopMode(void)
{
    GPIO_InitTypeDef              GPIO_InitStruct = {0};
        
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Mode = GPIO_MODE_EVT_FALLING; /* 配置为下降沿触发Event */     
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}

 

37.7 实验例程说明(IAR)

配套例子:

V7-021_低功耗定时器PWM输出

实验目的:

  1. 学习低功耗定时器PWM输出。

实验内容:

  1. 使用LPTIM的好处是系统处于睡眠,停机状态依然可以正常工作(除了待机模式)。停机状态可以正常工作的关键是LSE,LSI时钟不会被关闭,同时也可以选择使用外部时钟源。
  2. 例子默认用的LSE时钟供LPTIM1使用,大家可以通过bsp_lptim_pwm.c文件开头宏定义切换到LSI或者PLCK。
  3. PWM输出引脚采用的PD13,输出频谱1024Hz,占空比50%。

实验操作:

  1. K1键按下,进入低功耗的停机模式,LED2停止闪烁。
  2. K3键按下,退出停机模式,LED2继续闪烁。

PD13的位置:

技术图片 

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1

 技术图片

程序设计:

  系统栈大小分配:

技术图片 

  RAM空间用的DTCM:

技术图片 

  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

/*
*********************************************************************************************************
*    函 数 名: bsp_Init
*    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
       - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
       - 设置NVIV优先级分组为4。
     */
    HAL_Init();

    /* 
       配置系统时钟到400MHz
       - 切换使用HSE。
       - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder并开启 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
    bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
    bsp_InitTimer();      /* 初始化滴答定时器 */
    bsp_InitUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
    bsp_InitLed();        /* 初始化LED */    
}

 

  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。

/*
*********************************************************************************************************
*    函 数 名: MPU_Config
*    功能说明: 配置MPU
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 数 名: CPU_CACHE_Enable
*    功能说明: 使能L1 Cache
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  主功能:

主程序实现如下操作:

  •   K1键按下,进入低功耗的停机模式,LED2停止闪烁。
  •   K3键按下,退出停机模式,LED2继续闪烁。
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: c程序入口
*    形    参: 无
*    返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;        /* 按键代码 */
    

    bsp_Init();           /* 硬件初始化 */
    PwrExitStopMode(); /* 配置K3按键用于唤醒停机模式 */
    
    PrintfLogo();    /* 打印例程名称和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */

    bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
    
    /* 进入主程序循环体 */
    while (1)
    {
        bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */

        /* 判断定时器超时时间 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 进来一次 */  
            bsp_LedToggle(2);
        }

        /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
        ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:            /* K1键按下,进入停机模式 */
                    printf("--进入停机模式
");
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
                    SystemClock_Config();   /* 特别注意,退出后要重新配置系统时钟 */
                    break;
                
                case KEY_DOWN_K3:            /* K3键按下,唤醒停机模式 */
                    printf("--退出停机模式
");
                    break;

                default:
                    /* 其它的键值不处理 */
                    break;
            }
        }
    }
}

 

通过GPIO为EXTI Event可以唤醒停机模式:

/*
*********************************************************************************************************
*    函 数 名: PwrExitStopMode
*    功能说明: 起初按键K3的GPIO配置为输入模式用于按键后,再次配置EXTI Event模式后,按键功能依然可以正常使*              用。配置为EXTI Event是因为可以唤醒停机模式。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void PwrExitStopMode(void)
{
    GPIO_InitTypeDef              GPIO_InitStruct = {0};
        
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Mode = GPIO_MODE_EVT_FALLING; /* 配置为下降沿触发Event */     
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
}

 

37.8 总结

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

以上是关于STM32H7教程第37章 STM32H7的LPTIM低功耗定时器应用之PWM的主要内容,如果未能解决你的问题,请参考以下文章

STM32H7教程第14章 STM32H7的电源,复位和时钟系统

STM32H7教程第39章 STM32H7的DMAMUX基础知识(重要)

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

STM32H7的DSP教程第33章 STM32H7不限制点数FFT实现

STM32H7教程第89章 STM32H7的CAN FD总线基础之前世今生

STM32H7教程第92章 STM32H7的FDCAN总线应用之双FDCAN实现(支持经典CAN)