【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("--进入停机模式\r\n");
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
                    SystemClock_Config();   /* 特别注意,退出后要重新配置系统时钟 */
                    break;

                case KEY_DOWN_K3:            /* K3键按下,唤醒停机模式 */
                    printf("--退出停机模式\r\n");
                    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("--进入停机模式\r\n");
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
                    SystemClock_Config();   /* 特别注意,退出后要重新配置系统时钟 */
                    break;

                case KEY_DOWN_K3:            /* K3键按下,唤醒停机模式 */
                    printf("--退出停机模式\r\n");
                    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。

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

时间: 2024-11-05 14:40:36

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

【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 初学者

【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

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

完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第14章       STM32H7的电源,复位和时钟系统 本章教程继续为大家讲解学习STM32H7的必备知识点电源,复位和时钟系统.掌握这三方面的知识点对后面的学习大有裨益. 14.1 初学者重要提示 14.2 电源 14.3 硬件复位 14.4 软件复位 14.5 RCC时钟控制 14.6 总结 14.1 初学者重要提示 1.  电源管理部分涉及到的各种低功

【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教程】第53章 STM32H7的LTDC应用之汉字小字库和全字库制作

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第53章       STM32H7的LTDC应用之汉字小字库和全字库制作 本章教程为大家讲解汉字小字库和全字库的制作方式,实际项目中用到的地方比较多. 53.1 初学者重要提示 53.2 使用MakeDot小软件生成C文件格式小字库方法 53.3 使用MakeDot小软件生成C文件格式全字库方法 53.4 C文件格式汉字使用方法 53.5 汉字显示方法解析 53.6

【STM32H7教程】第58章 STM32H7的硬件JPEG应用之图片解码显示

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第58章       STM32H7的硬件JPEG应用之图片解码显示 本章节为大家讲解硬件JPEG的图片解码功能,实际项目用到的地方比较多,一方面通过硬件JPEG可以加速图片解码,另一方面是JPEG图片比较小. 58.1 初学者重要提示 58.2 硬件JPEG驱动设计 58.3 硬件JPEG驱动移植和使用 58.4 实验例程设计框架 58.5 实验例程说明(MDK)