zoukankan      html  css  js  c++  java
  • 【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。

  • 相关阅读:
    XML组成部分
    XML语法
    XML概念
    HTTP协议:响应消息的数据格式---Response
    KM HDU 3718
    KM最大匹配 HDU 2255
    匈牙利算法
    母函数
    最长公共子序列 LCS
    hdu 4632 区间DP
  • 原文地址:https://www.cnblogs.com/armfly/p/12157963.html
Copyright © 2011-2022 走看看