STM32CUBEMX stm32L151按键低功耗及唤醒

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 16:19   2274   0

一、CUBEMX配置

1、选择芯片,当前示例选择stm32l151c8t6

2、配置RCC及SYS

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

3、配置时钟树

时钟源采用外部8M晶振,单片机HCLK采用8M,过高运行功耗高,过低程序运行时精准延时(delay_us)误差大,具体视情况定。

4、配置串口,方便调试

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

5、配置外部中断

4+4按键矩阵,PA0~PA3选择推挽输出,默认下拉,PA4~PA7选择外部中断模式,默认上拉;打开中断,设置中断优先级,尽量避免优先级为0,不然延时消抖时使用HAL_Delay会死循环,因为HAL库SYSTIC优先级为0,或者采用时钟摘取法自己实现ms、us延时,推荐原子哥的延时初始化函数。

6、生成代码

选择MDK5,.c/.h文件分开。

二、运行代码

1、使能printf

将以下代码添加至.c文件

int fputc(int ch,FILE *f)
{
 HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
 return 0;
}

2、初始化延时

delay_init(); 当前初始化延时存在问题,按键中断延时消抖会使中断外delay直接结束造成误差,有待改进。

void delay_init()
{
 uint32_t CLKSource = SYSTICK_CLKSOURCE_HCLK; //选择时钟源
 uint32_t DIV = 1;        //分频系数
 if(SystemCoreClock/1000000 > 16)    //系统时钟超过16M则8分频,SysTick->LOAD最大值2^24=16,777,216,16M时钟源延时最大值为1048.576ms
 {
  CLKSource = SYSTICK_CLKSOURCE_HCLK_DIV8;
  DIV = 8;
 }
 
 HAL_SYSTICK_CLKSourceConfig(CLKSource); //选择外部时钟
 fac_us=SystemCoreClock/1000000/DIV;  //为系统时钟的us延时tick数
 fac_ms=(u16)fac_us*1000;    //非OS下,代表每个ms需要的systick时钟数
}            

//延时nus                 
void delay_us(u32 nus)
{  
 u32 temp;       
 SysTick->LOAD=nus*fac_us;      //时间加载      
 SysTick->VAL=0x00;             //清空计数器
 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数   
 do
 {
  temp=SysTick->CTRL;
 }while((temp&0x01)&&!(temp&(1<<16)));  //等待时间到达   
 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
 SysTick->VAL =0X00;            //清空计数器  
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{         
 u32 temp;     
 SysTick->LOAD=(u32)nms*fac_ms;    //时间加载(SysTick->LOAD为24bit)
 SysTick->VAL =0x00;       //清空计数器
 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数  
 do
 {
  temp=SysTick->CTRL;
 }while((temp&0x01)&&!(temp&(1<<16)));  //等待时间到达   
 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
 SysTick->VAL =0X00;            //清空计数器        
} 

3、按键扫描

行扫描,通过中断管脚判断,4条中断线(PA4~PA7)对应4行;列扫描,PA0~PA3逐个拉高电平,通过中断管脚电平被拉高判断。最后将PA0~PA3电平重新拉低,此时由于电平高低变化会再次产生中断,记得退出按键扫描后清中断。

/* 4*4按键矩阵,key0~15及对应value */
uint8_t KEY_NUM[4][4] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
uint8_t KEY_DATA[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0FF,0xFF};

/* 按键矩阵行对应GPIO管脚 */
#define KEY_PIN_ROW0 GPIO_PIN_4
#define KEY_PIN_ROW1 GPIO_PIN_5
#define KEY_PIN_ROW2 GPIO_PIN_6
#define KEY_PIN_ROW3 GPIO_PIN_7

#define KEY_GPIO_ROW0 GPIOA
#define KEY_GPIO_ROW1 GPIOA
#define KEY_GPIO_ROW2 GPIOA
#define KEY_GPIO_ROW3 GPIOA

/* 按键矩阵列对应GPIO管脚 */
#define KEY_PIN_COLUMN0 GPIO_PIN_0
#define KEY_PIN_COLUMN1 GPIO_PIN_1
#define KEY_PIN_COLUMN2 GPIO_PIN_2
#define KEY_PIN_COLUMN3 GPIO_PIN_3

#define KEY_GPIO_COLUMN0 GPIOA
#define KEY_GPIO_COLUMN1 GPIOA
#define KEY_GPIO_COLUMN2 GPIOA
#define KEY_GPIO_COLUMN3 GPIOA


/* 扫描按键,中断模式下扫描列会反转电平,额外产生一次中断,需清中断 */
uint8_t keyScan(uint16_t GPIO_Pin)
{
 GPIO_TypeDef* GPIOx;
 uint8_t key_row,key_column;
 
 /* 扫描行 */
 switch(GPIO_Pin)
 {
  case KEY_PIN_ROW0:
   key_row = 0;
   GPIOx = KEY_GPIO_ROW0;
   break;
  case KEY_PIN_ROW1:
   key_row = 1;
   GPIOx = KEY_GPIO_ROW1;
   break;
  case KEY_PIN_ROW2:
   key_row = 2;
   GPIOx = KEY_GPIO_ROW2;
   break;
  case KEY_PIN_ROW3:
   key_row = 3;
   GPIOx = KEY_GPIO_ROW3;
   break;
  default:
   key_row = 3;
   break;
 }
 
 /* 扫描列-电平反转,会额外产生一次中断 */
 HAL_GPIO_WritePin(KEY_GPIO_COLUMN0,KEY_PIN_COLUMN0,GPIO_PIN_SET);
 if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin))
 {
  key_column = 0;
 }
 else
 {
  HAL_GPIO_WritePin(KEY_GPIO_COLUMN1,KEY_PIN_COLUMN1,GPIO_PIN_SET);
  if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin))
  {
   key_column = 1;
  }
  else
  {
   HAL_GPIO_WritePin(KEY_GPIO_COLUMN2,KEY_PIN_COLUMN2,GPIO_PIN_SET);
   if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin))
   {
    key_column = 2;
   }
   else
   {
    HAL_GPIO_WritePin(KEY_GPIO_COLUMN3,KEY_PIN_COLUMN3,GPIO_PIN_SET);
    if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin))
    {
     key_column = 3;
    }
   }
  }
 }
 HAL_GPIO_WritePin(KEY_GPIO_COLUMN0,GPIO_PIN_0,GPIO_PIN_RESET);
 HAL_GPIO_WritePin(KEY_GPIO_COLUMN1,GPIO_PIN_1,GPIO_PIN_RESET);
 HAL_GPIO_WritePin(KEY_GPIO_COLUMN2,GPIO_PIN_2,GPIO_PIN_RESET);
 HAL_GPIO_WritePin(KEY_GPIO_COLUMN3,GPIO_PIN_3,GPIO_PIN_RESET);
 
 printf("key%d\r\n",KEY_NUM[key_row][key_column]);
 return KEY_NUM[key_row][key_column];
}

4、进入低功耗

进入低功耗之前关闭所有时钟并将IO口设为输入模式以降低功耗,若接入了外设,则该IO口模式不变以保持电平,使得外设正常工作。

void StopMode_Measure(void)
{
  /* Enter Stop Mode */
 HAL_UART_MspDeInit(&huart1);
 GPIO_RCC_disable();
 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}
/**********关闭GPIO时钟,降低功耗**********/
void GPIO_RCC_disable(void)
{
 /* GPIO Ports Clock Enable */
 __HAL_RCC_GPIOA_CLK_ENABLE();
 __HAL_RCC_GPIOB_CLK_ENABLE();
 __HAL_RCC_GPIOC_CLK_ENABLE();
 __HAL_RCC_GPIOH_CLK_ENABLE();

 GPIOA->MODER = 0xFFFFFFFF;
 GPIOB->MODER = 0xFFFFFFFF;
 GPIOC->MODER = 0xFFFFFFFF;
 GPIOH->MODER = 0xFFFFFFFF;

 __HAL_RCC_GPIOA_CLK_DISABLE();
 __HAL_RCC_GPIOB_CLK_DISABLE();
 __HAL_RCC_GPIOC_CLK_DISABLE();
 __HAL_RCC_GPIOH_CLK_DISABLE();

}

5、退出低功耗

进入按键中断后,系统采用低速时钟,需SystemClock_Config(),重新配置并打开系统时钟(最重要),打开其它被关闭的时钟等。最简单粗暴就把所有外设初始化。

/* 初始化,打开对应外设时钟 */
 SystemClock_Config();
 delay_init(); 
 MX_GPIO_Init();
 HAL_UART_MspInit(&huart1);

我自己测试时(无其它外设),32M时钟工作电流11mA左右,8M时钟工作电流3.5mA左右,进入低功耗电流2.8uA左右。有问题可讨论,大家一起进步。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP