[其他ST产品] STM32CUBEMX stm32L151按键低功耗及唤醒

[复制链接]
 楼主| 纠结的那些年 发表于 2023-1-27 11:32 | 显示全部楼层 |阅读模式
CUBEMX配置1、选择芯片,当前示例选择stm32l151c8t6

20200109164703738.png

2、配置RCC及SYS

只需配置高速时钟(HSE)和DEBUG

2020010916494547.png

2105863d345b812317.png


 楼主| 纠结的那些年 发表于 2023-1-27 11:33 | 显示全部楼层
配置时钟树
时钟源采用外部8M晶振,单片机HCLK采用8M,过高运行功耗高,过低程序运行时精准延时(delay_us)误差大,具体视情况定。

3063763d345ff3624e.png
 楼主| 纠结的那些年 发表于 2023-1-27 11:51 | 显示全部楼层
本帖最后由 纠结的那些年 于 2023-1-27 11:56 编辑

配置串口,方便调试
当前波特率设为9600,主要考虑系统时钟最低可设为1M,此时功耗最低,不支持高波特率。且调试可不配置串口中断。

2142363d34b5e526c2.png
5726363d34a59b465b.png
 楼主| 纠结的那些年 发表于 2023-1-27 11:57 | 显示全部楼层
配置外部中断
4+4按键矩阵,PA0~PA3选择推挽输出,默认下拉,PA4~PA7选择外部中断模式,默认上拉;打开中断,设置中断优先级,尽量避免优先级为0,不然延时消抖时使用HAL_Delay会死循环,因为HAL库SYSTIC优先级为0,或者采用时钟摘取法自己实现ms、us延时,推荐原子哥的延时初始化函数。
 楼主| 纠结的那些年 发表于 2023-1-27 11:58 | 显示全部楼层
 楼主| 纠结的那些年 发表于 2023-1-27 11:59 | 显示全部楼层
生成代码
选择MDK5,.c/.h文件分开。
4587463d34c2271d66.png


7973063d34c2a64f7b.png
 楼主| 纠结的那些年 发表于 2023-1-27 12:00 | 显示全部楼层
运行代码
1、使能printf
将以下代码添加至.c文件

int fputc(int ch,FILE *f)
{
        HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
        return 0;
}
 楼主| 纠结的那些年 发表于 2023-1-27 12:01 | 显示全部楼层
初始化延时
delay_init(); 当前初始化延时存在问题,按键中断延时消抖会使中断外delay直接结束造成误差,有待改进。
  1. void delay_init()
  2. {
  3.         uint32_t CLKSource = SYSTICK_CLKSOURCE_HCLK;        //选择时钟源
  4.         uint32_t DIV = 1;                                                                //分频系数
  5.         if(SystemCoreClock/1000000 > 16)                                //系统时钟超过16M则8分频,SysTick->LOAD最大值2^24=16,777,216,16M时钟源延时最大值为1048.576ms
  6.         {
  7.                 CLKSource = SYSTICK_CLKSOURCE_HCLK_DIV8;
  8.                 DIV = 8;
  9.         }
  10.        
  11.         HAL_SYSTICK_CLKSourceConfig(CLKSource);        //选择外部时钟
  12.         fac_us=SystemCoreClock/1000000/DIV;                //为系统时钟的us延时tick数
  13.         fac_ms=(u16)fac_us*1000;                                //非OS下,代表每个ms需要的systick时钟数
  14. }                                                                    

  15. //延时nus                                                                                      
  16. void delay_us(u32 nus)
  17. {               
  18.         u32 temp;                     
  19.         SysTick->LOAD=nus*fac_us;                                         //时间加载                           
  20.         SysTick->VAL=0x00;                                                //清空计数器
  21.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数          
  22.         do
  23.         {
  24.                 temp=SysTick->CTRL;
  25.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
  26.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  27.         SysTick->VAL =0X00;                                               //清空计数器         
  28. }
  29. //延时nms
  30. //注意nms的范围
  31. //SysTick->LOAD为24位寄存器,所以,最大延时为:
  32. //nms<=0xffffff*8*1000/SYSCLK
  33. //SYSCLK单位为Hz,nms单位为ms
  34. //对72M条件下,nms<=1864
  35. void delay_ms(u16 nms)
  36. {                                     
  37.         u32 temp;                  
  38.         SysTick->LOAD=(u32)nms*fac_ms;                                //时间加载(SysTick->LOAD为24bit)
  39.         SysTick->VAL =0x00;                                                        //清空计数器
  40.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数  
  41.         do
  42.         {
  43.                 temp=SysTick->CTRL;
  44.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
  45.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  46.         SysTick->VAL =0X00;                                               //清空计数器                      
  47. }
 楼主| 纠结的那些年 发表于 2023-1-27 12:02 | 显示全部楼层
按键扫描
行扫描,通过中断管脚判断,4条中断线(PA4~PA7)对应4行;列扫描,PA0~PA3逐个拉高电平,通过中断管脚电平被拉高判断。最后将PA0~PA3电平重新拉低,此时由于电平高低变化会再次产生中断,记得退出按键扫描后清中断。
 楼主| 纠结的那些年 发表于 2023-1-27 12:02 | 显示全部楼层
  1. /* 4*4按键矩阵,key0~15及对应value */
  2. uint8_t KEY_NUM[4][4] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
  3. uint8_t KEY_DATA[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0FF,0xFF};

  4. /* 按键矩阵行对应GPIO管脚 */
  5. #define KEY_PIN_ROW0        GPIO_PIN_4
  6. #define KEY_PIN_ROW1        GPIO_PIN_5
  7. #define KEY_PIN_ROW2        GPIO_PIN_6
  8. #define KEY_PIN_ROW3        GPIO_PIN_7

  9. #define KEY_GPIO_ROW0        GPIOA
  10. #define KEY_GPIO_ROW1        GPIOA
  11. #define KEY_GPIO_ROW2        GPIOA
  12. #define KEY_GPIO_ROW3        GPIOA

  13. /* 按键矩阵列对应GPIO管脚 */
  14. #define KEY_PIN_COLUMN0        GPIO_PIN_0
  15. #define KEY_PIN_COLUMN1        GPIO_PIN_1
  16. #define KEY_PIN_COLUMN2        GPIO_PIN_2
  17. #define KEY_PIN_COLUMN3        GPIO_PIN_3

  18. #define KEY_GPIO_COLUMN0        GPIOA
  19. #define KEY_GPIO_COLUMN1        GPIOA
  20. #define KEY_GPIO_COLUMN2        GPIOA
  21. #define KEY_GPIO_COLUMN3        GPIOA


  22. /* 扫描按键,中断模式下扫描列会反转电平,额外产生一次中断,需清中断 */
  23. uint8_t keyScan(uint16_t GPIO_Pin)
  24. {
  25.         GPIO_TypeDef* GPIOx;
  26.         uint8_t key_row,key_column;
  27.        
  28.         /* 扫描行 */
  29.         switch(GPIO_Pin)
  30.         {
  31.                 case KEY_PIN_ROW0:
  32.                         key_row = 0;
  33.                         GPIOx = KEY_GPIO_ROW0;
  34.                         break;
  35.                 case KEY_PIN_ROW1:
  36.                         key_row = 1;
  37.                         GPIOx = KEY_GPIO_ROW1;
  38.                         break;
  39.                 case KEY_PIN_ROW2:
  40.                         key_row = 2;
  41.                         GPIOx = KEY_GPIO_ROW2;
  42.                         break;
  43.                 case KEY_PIN_ROW3:
  44.                         key_row = 3;
  45.                         GPIOx = KEY_GPIO_ROW3;
  46.                         break;
  47.                 default:
  48.                         key_row = 3;
  49.                         break;
  50.         }
  51.        
  52.         /* 扫描列-电平反转,会额外产生一次中断 */
  53.         HAL_GPIO_WritePin(KEY_GPIO_COLUMN0,KEY_PIN_COLUMN0,GPIO_PIN_SET);
  54.         if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin))
  55.         {
  56.                 key_column = 0;
  57.         }
  58.         else
  59.         {
  60.                 HAL_GPIO_WritePin(KEY_GPIO_COLUMN1,KEY_PIN_COLUMN1,GPIO_PIN_SET);
  61.                 if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin))
  62.                 {
  63.                         key_column = 1;
  64.                 }
  65.                 else
  66.                 {
  67.                         HAL_GPIO_WritePin(KEY_GPIO_COLUMN2,KEY_PIN_COLUMN2,GPIO_PIN_SET);
  68.                         if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin))
  69.                         {
  70.                                 key_column = 2;
  71.                         }
  72.                         else
  73.                         {
  74.                                 HAL_GPIO_WritePin(KEY_GPIO_COLUMN3,KEY_PIN_COLUMN3,GPIO_PIN_SET);
  75.                                 if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin))
  76.                                 {
  77.                                         key_column = 3;
  78.                                 }
  79.                         }
  80.                 }
  81.         }
  82.         HAL_GPIO_WritePin(KEY_GPIO_COLUMN0,GPIO_PIN_0,GPIO_PIN_RESET);
  83.         HAL_GPIO_WritePin(KEY_GPIO_COLUMN1,GPIO_PIN_1,GPIO_PIN_RESET);
  84.         HAL_GPIO_WritePin(KEY_GPIO_COLUMN2,GPIO_PIN_2,GPIO_PIN_RESET);
  85.         HAL_GPIO_WritePin(KEY_GPIO_COLUMN3,GPIO_PIN_3,GPIO_PIN_RESET);
  86.        
  87.         printf("key%d\r\n",KEY_NUM[key_row][key_column]);
  88.         return KEY_NUM[key_row][key_column];
  89. }
 楼主| 纠结的那些年 发表于 2023-1-27 12:03 | 显示全部楼层
进入低功耗
进入低功耗之前关闭所有时钟并将IO口设为输入模式以降低功耗,若接入了外设,则该IO口模式不变以保持电平,使得外设正常工作。
 楼主| 纠结的那些年 发表于 2023-1-27 12:04 | 显示全部楼层
  1. void StopMode_Measure(void)
  2. {
  3.   /* Enter Stop Mode */
  4.         HAL_UART_MspDeInit(&huart1);
  5.         GPIO_RCC_disable();
  6.         HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
  7. }
  8. /**********关闭GPIO时钟,降低功耗**********/
  9. void GPIO_RCC_disable(void)
  10. {
  11.         /* GPIO Ports Clock Enable */
  12.         __HAL_RCC_GPIOA_CLK_ENABLE();
  13.         __HAL_RCC_GPIOB_CLK_ENABLE();
  14.         __HAL_RCC_GPIOC_CLK_ENABLE();
  15.         __HAL_RCC_GPIOH_CLK_ENABLE();

  16.         GPIOA->MODER = 0xFFFFFFFF;
  17.         GPIOB->MODER = 0xFFFFFFFF;
  18.         GPIOC->MODER = 0xFFFFFFFF;
  19.         GPIOH->MODER = 0xFFFFFFFF;

  20.         __HAL_RCC_GPIOA_CLK_DISABLE();
  21.         __HAL_RCC_GPIOB_CLK_DISABLE();
  22.         __HAL_RCC_GPIOC_CLK_DISABLE();
  23.         __HAL_RCC_GPIOH_CLK_DISABLE();

  24. }
 楼主| 纠结的那些年 发表于 2023-1-27 12:05 | 显示全部楼层
退出低功耗
进入按键中断后,系统采用低速时钟,需SystemClock_Config(),重新配置并打开系统时钟(最重要),打开其它被关闭的时钟等。最简单粗暴就把所有外设初始化。

/* 初始化,打开对应外设时钟 */
        SystemClock_Config();
        delay_init();       
        MX_GPIO_Init();
        HAL_UART_MspInit(&huart1);
 楼主| 纠结的那些年 发表于 2023-1-27 12:06 | 显示全部楼层
我自己测试时(无其它外设),32M时钟工作电流11mA左右,8M时钟工作电流3.5mA左右,进入低功耗电流2.8uA左右。有问题可讨论,大家一起进步。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

55

主题

749

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部