前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【STM32H7教程】第37章 STM32H7的LPTIM低功耗定时器应用之PWM

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

作者头像
Simon223
发布2020-01-14 15:01:01
1K0
发布2020-01-14 15:01:01
举报

完整教程下载地址: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。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-01-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第37章       STM32H7的LPTIM低功耗定时器应用之PWM
    • 37.1 初学者重要提示
      • 37.2 低功耗定时器PWM驱动设计
        • 37.2.1 低功耗定时器PWM输出支持的引脚
        • 37.2.2 低功耗定时器时钟选择
        • 37.2.3 低功耗定时器的PWM配置
        • 37.2.4 低功耗定时器待机模式下正常运行
      • 37.3 低功耗定时器板级支持包(bsp_lptim_pwm.c)
        • 37.3.1 函数bsp_InitLPTIMOutPWM
      • 37.4 低功耗定时器驱动移植和使用
        • 37.5 实验例程设计框架
          • 37.6 实验例程说明(MDK)
            • 37.7 实验例程说明(IAR)
              • 37.8 总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档