STM32CUBEMX stm32L151按键低功耗及唤醒

倖福魔咒の 提交于 2020-01-24 01:03:31

一、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左右。有问题可讨论,大家一起进步。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!