• 【STM32】基于HAL库建立自己的低功耗模式配置库(STM32L4系列低功耗所有配置汇总)


    STM32】基于HAL库建立自己的低功耗模式配置库(STM32L4系列低功耗所有配置汇总)


    此文章是讨论将先前所有的低功耗配置功能整合起来的一个库(适用于STM32L4系列)
    目前除了普通唤醒方式外 加入了UART唤醒和RTC唤醒配置
    如果后续有更多唤醒加入(如I2C等 将直接在后续的文章中进行讨论)
    本文所建的库将不再更新其他唤醒方式 但会对某些BUG进行修复

    相关函数调用和配置 可以从我之前的文章里找到
    gitee库

    低功耗模式(此章节可直接跳过)

    【STM32笔记】低功耗模式配置及避坑汇总

    低功耗模式简介

    系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗。由用户根据应用选择具体的低功耗模式,以在低功耗、短启动时间和可用唤醒源之间寻求最佳平衡。

    睡眠模式、停止模式及待机模式中,若备份域电源正常供电,备份域内的 RTC 都可以正常运行,备份域内的寄存器的数据会被保存,不受功耗模式影响。

    从表中可以看到,这三种低功耗模式层层递进,运行的时钟或芯片功能越来越少,因而功耗越来越低。

    模式说明进入方式唤醒对1.8V区域时钟的影响对VDD区域时钟的影响调压器
    睡眠模式内核停止,所有外设包括M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行WFI、WFE命令(HAL库直接调用)任意中断/事件内核时钟关,对其他时钟和ADC时钟无影响
    停止模式所有的时钟都已停止配置PWR_CR寄存器的PDDS+LPDS位+SLEEPDEEP位+WFI或WFE命令任意外部中断EXTI(在外部中断寄存器中设置)关闭所有1.8V区域的时钟HSI和HSE的振荡器关闭开启或处于低功耗模式(依据电源控制寄存器的设定)
    待机模式1.8V电源关闭配置PWR_CR寄存器的PDDS+SLEEPDEEP位+WFI或WFE命令WKUP、引脚的RTC闹钟事件、NRST引脚上的外部复位、IWDG复位关闭所有1.8V区域的时钟HSI和HSE的振荡器关闭

    L4及L4+的通用模式状态表可见手册
    在这里插入图片描述
    在这里插入图片描述

    【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)

    【STM32笔记】低功耗模式下GPIO省电配置避坑实验(闲置引脚配置为模拟输入其实更耗电)

    【STM32笔记】低功耗模式下GPIO、外设省电配置避坑

    睡眠模式

    在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM3 核心的外设全都还照常运行。有两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是 WFI(wait for interrupt) 和 WFE(wait for event),即由等待“中断”唤醒和由“事件”唤醒。

    特性和说明:
    
    立即睡眠: 在执行 WFI 或 WFE 指令时立即进入睡眠模式。
    退出时睡眠: 在退出优先级最低的中断服务程序后才进入睡眠模式。
    进入方式: 内核寄存器的 SLEEPDEEP=0 ,然后调用 WFI 或 WFE 指令即可进入睡眠模式;SLEEPONEXIT=1 时,进入“退出时睡眠”模式。
    唤醒方式: 如果是使用 WFI 指令睡眠的,则可使用任意中断唤醒;如果是使用 WFE 指令睡眠的,则由事件唤醒。
    睡眠时: 关闭内核时钟,内核停止,而外设正常运行,在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存的数据。
    唤醒延迟: 无延迟。
    唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    唤醒后即可开始行动 继续程序 无需配置任何寄存器

    睡眠模式和低功耗睡眠模式是两个模式 由PWR_MAINREGULATOR_ONPWR_LOWPOWERREGULATOR_ON两个变量确定

    要进入低功耗睡眠模式 首先得进入低功耗运行模式

    HAL_PWREx_EableLowPowerRunMode()
    
    • 1

    且工作频率降低到2MHz以下

    唤醒时 睡眠模式直接唤醒

    而低功耗睡眠模式唤醒后 会进入到低功耗运行模式 若想正常工作 需用HAL_PWREx_DisableLowPowerRunMode()退出

    停止模式

    在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.8V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒,在停止模式中可以选择电压调节器为开模式或低功耗模式。

    特性和说明:
    
    调压器低功耗模式: 在停止模式下调压器可工作在正常模式或低功耗模式,可进一步降低功耗。
    进入方式: 内核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=0,然后调用 WFI 或 WFE 指令即可进入停止模式;PWR_CR 寄存器的 LPDS=0 时,调压器工作在正常模式,LPDS=1 时工作在低功耗模式。
    唤醒方式: 如果是使用 WFI 指令睡眠的,可使用任意 EXTI 线的中断唤醒;如果是使用 WFE 指令睡眠的,可使用任意配置为事件模式的 EXTI 线事件唤醒。
    停止时: 内核停止,片上外设也停止。这个状态会保留停止前的内核寄存器、内存的数据。
    唤醒延迟: 基础延迟为 HSI 振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间。
    唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。唤醒后,STM32 会使用 HSI 作为系统时钟。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    只能由外部中断唤醒 唤醒后需要重新使能时钟(SystemClock_Config();
    建议将一条外部中断线专门作为唤醒中断,执行中断后进入回调进行时钟使能

    停止模式0和1由PWR_MAINREGULATOR_ONPWR_LOWPOWERREGULATOR_ON两个变量确定

    停止模式0和1可以被串口 I2C等设备唤醒(具体看手册)

    停止模式2则在pwr_ex.c中进入

    停止模式2 只能被特定器件(如LPUART等在内部与EXTI有链接的器件)唤醒

    详情见后续关于STOP模式串口唤醒的文章

    【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒(解决串口唤醒和回调无法一起使用的问题)

    待机模式

    翻译成shutdown更为合适
    待机模式,它除了关闭所有的时钟,还把 1.8V 区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,从头开始执行程序。它有四种唤醒方式,分别是 WKUP(PA0)引脚的上升沿,RTC 闹钟事件,NRST 引脚的复位和 IWDG(独立看门狗)复位。

    特性和说明:
    
    进入方式: 内核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=1,PWR_CR 寄存器中的唤醒状态位 WUF=0,然后调用 WFI 或 WFE 指令即可进入待机模式。
    唤醒方式: 通过 WKUP ,RTC 闹钟、唤醒、入侵、时间戳事件或 NRST 引脚外部复位及 IWDG 复位唤醒。
    待机时: 内核停止,片上外设也停止;内核寄存器、内存的数据会丢失;除复位引脚、RTC_AF1 引脚及 WKUP 引脚,其它 I/O 口均工作在高阻态。
    唤醒延迟: 芯片复位的时间。
    唤醒后: 相当于芯片复位,在程序表现为从头开始执行代码。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    建立自己的低功耗模式配置库

    首先 在先前的文章中 我们通过Enter_Low_PWR来进入低功耗模式
    同时 在进入低功耗之前 需要调用唤醒配置函数 退出低功耗后 也要初始化时钟等等
    而建立的这个库 就是把所有配置整合到一起 从而使其能直接用一个通用函数代替
    文中用于传参的配置结构体是一个全局变量
    另外用到了结构体嵌套 回调函数等等 该写法也是TI的SDK常用写法

    通过结构体的方式来进行传参

    最主要的就是mode_flag参数
    该参数决定了四种低功耗模式
    其中 停止模式默认是停止1

    typedef struct
    {
    	uint8_t mode_flag;  // 0/大于4 不进入任何模式,1 进入睡眠,2 进入停止1,3 进入待机,4 关机
    	
    	LOW_POWER_SLEEPEntry_Cfg SLEEPEntry_Cfg;  //进入睡眠模式的方式
    	LOW_POWER_STOPEntry_Cfg STOPEntry_Cfg;  //进入停止模式的方式
    	LOW_POWER_WakeUpPin_Cfg WakeUpPin_Cfg ;   //待机模式的唤醒引脚配置
    	
    	LOW_POWER_RTC_Cfg RTC_Cfg;  //RTC唤醒配置
    	LOW_POWER_Device_Cfg Device_Cfg;	
    	
    	SystemClock_Config_Callback SystemClock_Config_Fxn;	 // 用于传入退出相关低功耗模式后 需要进行配置的系统时钟配置函数
    }LOW_POWER_Entry_Cfg;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    次级结构体包括进入低功耗的方式 待机模式唤醒引脚配置以及外设唤醒模式和唤醒后的时钟配置回调.

    其中 外设唤醒分为外设和RTC
    之所以要把RTC单独列出来 是因为RTC有且仅有一个
    而其他外设唤醒 可能会有UART 也能有I2C SPI等等
    所以我建立的这些函数都是__weak声明 可以结合不同的工程来覆写

    typedef void (*SystemClock_Config_Callback)(void);
    
    typedef struct
    {
    	uint8_t SLEEPEntry;  //SLEEPEntry: 一般是 PWR_SLEEPENTRY_WFI 等待中断 也可以是 PWR_SLEEPENTRY_WFE
    }LOW_POWER_SLEEPEntry_Cfg;
    
    typedef struct
    {
    	uint8_t STOPEntry;  //STOPEntry: 一般是 PWR_STOPENTRY_WFI 等待中断 也可以是 PWR_STOPENTRY_WFE
    }LOW_POWER_STOPEntry_Cfg;
    
    typedef struct
    {
    	uint32_t WakeUpPinPolarity;  //WakeUpPinPolarity: 待机模式下WKUP唤醒引脚极性配置,其他模式无用 有的只能配置一个引脚 所以要看数据手册
    	/*
    	*    PWR_WAKEUP_PIN1_HIGH or PWR_WAKEUP_PIN1_LOW
      *    PWR_WAKEUP_PIN2_HIGH or PWR_WAKEUP_PIN2_LOW
      *    PWR_WAKEUP_PIN3_HIGH or PWR_WAKEUP_PIN3_LOW
      *    PWR_WAKEUP_PIN4_HIGH or PWR_WAKEUP_PIN4_LOW
      *    PWR_WAKEUP_PIN5_HIGH or PWR_WAKEUP_PIN5_LOW
    	*/
    }LOW_POWER_WakeUpPin_Cfg;
    
    typedef struct
    {
    	bool EnableNotDisable;
    	RTC_HandleTypeDef *rtc_handle;
    	uint32_t counter;  //RTC计数值 由于进入低功耗模式会有约10ms消抖 所以建议减去这段时间
    	uint32_t clock;  //RTC时钟源 一般是 RTC_WAKEUPCLOCK_RTCCLK_DIV16
    }LOW_POWER_RTC_Cfg;
    
    typedef struct
    {
    	bool EnableNotDisable;
    	UART_HandleTypeDef *uart_handle;
    	UART_WakeUpTypeDef UART_WakeUpStruct;  //UART唤醒的结构体配置 UART_WakeUpStruct.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY 就是接收数据不为空时唤醒
    }LOW_POWER_UART_Cfg;
    
    typedef struct
    {
    	LOW_POWER_UART_Cfg UART_Cfg[5];  //串口唤醒配置 有五个串口 所以最大buf长度为5
    }LOW_POWER_Device_Cfg;
    
    typedef struct
    {
    	uint8_t mode_flag;  // 0/大于4 不进入任何模式,1 进入睡眠,2 进入停止1,3 进入待机,4 关机
    	
    	LOW_POWER_SLEEPEntry_Cfg SLEEPEntry_Cfg;  //进入睡眠模式的方式
    	LOW_POWER_STOPEntry_Cfg STOPEntry_Cfg;  //进入停止模式的方式
    	LOW_POWER_WakeUpPin_Cfg WakeUpPin_Cfg ;   //待机模式的唤醒引脚配置
    	
    	LOW_POWER_RTC_Cfg RTC_Cfg;  //RTC唤醒配置
    	LOW_POWER_Device_Cfg Device_Cfg;	
    	
    	SystemClock_Config_Callback SystemClock_Config_Fxn;	 // 用于传入退出相关低功耗模式后 需要进行配置的系统时钟配置函数
    }LOW_POWER_Entry_Cfg;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    进入低功耗的函数中 每个模式都有一个10ms的消抖 其实这个可以省略
    但我们用的是HAL库 HAL库初始化时钟以后 会打开SysTick中断 如果这个开着 就一直无法进入低功耗(进入就被中断唤醒) 所以我这里加了个我自己写的延时函数 其中就有关闭SysTick的语句 如果你不用这个消抖 没啥BUG也行 但我还是建议加上去 毕竟也不差那10ms

    /*!
     * @brief       	进入低功耗模式   	
     *
     * @return				None
     */
    __weak void Enter_Low_PWR(void)
    {
    	__HAL_RCC_PWR_CLK_ENABLE();
    	switch(LP_Entry_Cfg.mode_flag)
    	{
    		case 0:
    		{
    			printf("[INFO] 不进入低功耗模式\n");
    			break;
    		}
    		case 1:
    		{
    			printf("[INFO] 进入睡眠模式\n");
    			delay_ms(10);  //消抖
    			PWR_Device_Init(false);
    			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);	
    			Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
    			HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,LP_Entry_Cfg.SLEEPEntry_Cfg.SLEEPEntry);
    			__HAL_RCC_PWR_CLK_ENABLE();			
    			Ctrl_RTC_WakeUp(NULL,false);	
    			PWR_Device_Init(true);
    			break;
    		}
    		case 2:
    		{
    			printf("[INFO] 进入停止模式\n");
    			delay_ms(10);  //消抖
    			PWR_Device_Init(false);			
    			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);	
    			Ctrl_Stop_Mode_WakeUp_Device(true);			
    			Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
    			HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,LP_Entry_Cfg.STOPEntry_Cfg.STOPEntry);
    			__HAL_RCC_PWR_CLK_ENABLE();			
    			LP_Entry_Cfg.SystemClock_Config_Fxn();
    			Ctrl_Stop_Mode_WakeUp_Device(false);
    			Ctrl_RTC_WakeUp(NULL,false);	
    			PWR_Device_Init(true);
    			break;
    		}
    		case 3:
    		{
    			printf("[INFO] 三秒后进入待机模式\n");
    			delay_ms(3000);
    			printf("[INFO] 进入待机模式\n");
    			HAL_PWR_EnableWakeUpPin(LP_Entry_Cfg.WakeUpPin_Cfg.WakeUpPinPolarity);
    			delay_ms(10);  //消抖
    			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    			Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
    			HAL_PWR_EnterSTANDBYMode();
    			break;
    		}
    		case 4:
    		{
    			printf("[INFO] 三秒后进入关机模式\n");
    			delay_ms(3000);
    			printf("[INFO] 进入关机模式\n");
    			HAL_PWR_EnableWakeUpPin(LP_Entry_Cfg.WakeUpPin_Cfg.WakeUpPinPolarity);
    			delay_ms(10);  //消抖
    			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    			Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
    			HAL_PWREx_EnterSHUTDOWNMode();
    			break;
    		}
    		default:
    		{
    			printf("[INFO] 不进入低功耗模式\n");
    			break;
    		}
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    RTC配置

    typedef struct
    {
    	bool EnableNotDisable;
    	RTC_HandleTypeDef *rtc_handle;
    	uint32_t counter;  //RTC计数值 由于进入低功耗模式会有约10ms消抖 所以建议减去这段时间
    	uint32_t clock;  //RTC时钟源 一般是 RTC_WAKEUPCLOCK_RTCCLK_DIV16
    }LOW_POWER_RTC_Cfg;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这里的时钟源和计数值就是HAL_RTCEx_SetWakeUpTimer_IT中的传参

    
    /*!
     * @brief       	配置RTC在低功耗模式下的唤醒   	
     *
     * @param 	[in]	RTC_Cfg: RTC配置
     *
     * @return				None
     */
    __weak void Ctrl_RTC_WakeUp(LOW_POWER_RTC_Cfg *RTC_Cfg,bool EnableNotDisable)
    {	
    	if(EnableNotDisable)
    	{
    		HAL_RTCEx_SetWakeUpTimer_IT(RTC_Cfg->rtc_handle,RTC_Cfg->counter,RTC_Cfg->clock);
    	}
    	else
    	{
    		__HAL_RTC_WAKEUPTIMER_EXTI_DISABLE_IT();	
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    【STM32笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)

    UART配置

    【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒(解决串口唤醒和回调无法一起使用的问题)

    如果串口唤醒波特率比较高 则可能第一个字节接收出错 可以看如下文章:
    【STM32】HAL库的STOP低功耗模式UART串口唤醒BUG,第一个接收字节出错的问题(已解决)

    typedef struct
    {
    	bool EnableNotDisable;
    	UART_HandleTypeDef *uart_handle;
    	UART_WakeUpTypeDef UART_WakeUpStruct;  //UART唤醒的结构体配置 UART_WakeUpStruct.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY 就是接收数据不为空时唤醒
    }LOW_POWER_UART_Cfg;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    UART_WakeUpStruct结构体中 一般把WakeUpEvent=UART_WAKEUP_ON_READDATA_NONEMPTY 表示接收数据不为空时唤醒
    串口最多有五个 所以在结构体中定义的是一个长度为5的数组 然后在配置函数中做判断 为NULL就跳过

    /*!
     * @brief       	配置串口在停止模式下的唤醒   	
     *
     * @param 	[in]	UART_Cfg: UART配置
     *
     * @return				None
     */
    __weak uint8_t Ctrl_UART_StopMode_WakeUp(LOW_POWER_UART_Cfg *UART_Cfg,bool EnableNotDisable)
    {	
    	if (!UART_Cfg->uart_handle)
    	{
    		return 0;
    	}
    	
    	if(EnableNotDisable)
    	{
    		__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI);	//保留唤醒用的HSI线 串口初始化时钟也必须要配置为HSI
    		HAL_UARTEx_StopModeWakeUpSourceConfig(UART_Cfg->uart_handle,UART_Cfg->UART_WakeUpStruct);
    	
    		__HAL_UART_ENABLE_IT(UART_Cfg->uart_handle,UART_IT_WUF);	//开启唤醒中断
    		HAL_UARTEx_EnableClockStopMode(UART_Cfg->uart_handle);
    		HAL_UARTEx_EnableStopMode(UART_Cfg->uart_handle);		//开启模式
    	}
    	else
    	{
    		__HAL_UART_DISABLE_IT(UART_Cfg->uart_handle,UART_IT_WUF);	//关闭唤醒中断
    		HAL_UARTEx_DisableClockStopMode(UART_Cfg->uart_handle);
    		HAL_UARTEx_DisableStopMode(UART_Cfg->uart_handle);		//关闭模式
    	}
    	
    	return 1;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    通过回调函数来配置时钟

    typedef void (*SystemClock_Config_Callback)(void);
    
    • 1

    声明了一个函数指针类型
    在调用时需要传入函数指针 一般是系统时钟配置
    也就是SystemClock_Config

    通过虚假的回调来初始化低功耗外设

    在低功耗进入前和退出以后 都可以通过把已经打开的外设关掉来降低功耗
    关闭:

    PWR_Device_Init(false);
    
    • 1

    打开:

    PWR_Device_Init(true);
    
    • 1

    同时 在此函数中 也包含GPIO的配置
    这两个函数用的虚假回调方式来编写
    在调用时 需要自己补全代码

    【STM32笔记】低功耗模式下GPIO省电配置避坑实验(闲置引脚配置为模拟输入其实更耗电)

    【STM32笔记】低功耗模式下GPIO、外设省电配置避坑

    初始化函数

    这里传参是系统时钟配置函数 当然 你也可以自己写一个 然后就是各个变量的赋值 这里对几个常用变量进行了赋值

    void Init_Enter_Low_PWR(SystemClock_Config_Callback SystemClock_Config_Fxn)
    {	
    	uint8_t i=0;
    	memset(&LP_Entry_Cfg,0,sizeof(LP_Entry_Cfg));
    	LP_Entry_Cfg.SystemClock_Config_Fxn=SystemClock_Config_Fxn;
    	LP_Entry_Cfg.SLEEPEntry_Cfg.SLEEPEntry=PWR_SLEEPENTRY_WFI;
    	LP_Entry_Cfg.STOPEntry_Cfg.STOPEntry=PWR_STOPENTRY_WFI;
    	
    	LP_Entry_Cfg.RTC_Cfg.counter=RTC_WAKEUPCLOCK_RTCCLK_DIV16;	
    	
    	for(i=0;i<sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg)/sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg[0]);i++)
    	{
    		LP_Entry_Cfg.Device_Cfg.UART_Cfg[i].UART_WakeUpStruct.WakeUpEvent=UART_WAKEUP_ON_READDATA_NONEMPTY;
    	}	
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    代码整合

    #ifndef __LOW_POWER_H__
    #define __LOW_POWER_H__
    #include "stm32l4xx_hal.h"
    #include "DELAY.h"
    #include 
    #include 
    #include 
    #include 
    
    /*
    进入低功耗的函数中 每个模式都有一个10ms的消抖 其实这个可以省略
    但我们用的是HAL库 HAL库初始化时钟以后 会打开SysTick中断 如果这个开着 就一直无法进入低功耗(进入就被中断唤醒)
    所以我这里加了个我自己写的延时函数 其中就有关闭SysTick的语句
    如果你不用这个消抖 没啥BUG也行 但我还是建议加上去 毕竟也不差那10ms
    */
    
    typedef void (*SystemClock_Config_Callback)(void);
    
    typedef struct
    {
    	uint8_t SLEEPEntry;  //SLEEPEntry: 一般是 PWR_SLEEPENTRY_WFI 等待中断 也可以是 PWR_SLEEPENTRY_WFE
    }LOW_POWER_SLEEPEntry_Cfg;
    
    typedef struct
    {
    	uint8_t STOPEntry;  //STOPEntry: 一般是 PWR_STOPENTRY_WFI 等待中断 也可以是 PWR_STOPENTRY_WFE
    }LOW_POWER_STOPEntry_Cfg;
    
    typedef struct
    {
    	uint32_t WakeUpPinPolarity;  //WakeUpPinPolarity: 待机模式下WKUP唤醒引脚极性配置,其他模式无用 有的只能配置一个引脚 所以要看数据手册
    	/*
    	*    PWR_WAKEUP_PIN1_HIGH or PWR_WAKEUP_PIN1_LOW
      *    PWR_WAKEUP_PIN2_HIGH or PWR_WAKEUP_PIN2_LOW
      *    PWR_WAKEUP_PIN3_HIGH or PWR_WAKEUP_PIN3_LOW
      *    PWR_WAKEUP_PIN4_HIGH or PWR_WAKEUP_PIN4_LOW
      *    PWR_WAKEUP_PIN5_HIGH or PWR_WAKEUP_PIN5_LOW
    	*/
    }LOW_POWER_WakeUpPin_Cfg;
    
    typedef struct
    {
    	bool EnableNotDisable;
    	RTC_HandleTypeDef *rtc_handle;
    	uint32_t counter;  //RTC计数值 由于进入低功耗模式会有约10ms消抖 所以建议减去这段时间
    	uint32_t clock;  //RTC时钟源 一般是 RTC_WAKEUPCLOCK_RTCCLK_DIV16
    }LOW_POWER_RTC_Cfg;
    
    typedef struct
    {
    	bool EnableNotDisable;
    	UART_HandleTypeDef *uart_handle;
    	UART_WakeUpTypeDef UART_WakeUpStruct;  //UART唤醒的结构体配置 UART_WakeUpStruct.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY 就是接收数据不为空时唤醒
    }LOW_POWER_UART_Cfg;
    
    typedef struct
    {
    	LOW_POWER_UART_Cfg UART_Cfg[5];  //串口唤醒配置 有五个串口 所以最大buf长度为5
    }LOW_POWER_Device_Cfg;
    
    typedef struct
    {
    	uint8_t mode_flag;  // 0/大于4 不进入任何模式,1 进入睡眠,2 进入停止1,3 进入待机,4 关机
    	
    	LOW_POWER_SLEEPEntry_Cfg SLEEPEntry_Cfg;  //进入睡眠模式的方式
    	LOW_POWER_STOPEntry_Cfg STOPEntry_Cfg;  //进入停止模式的方式
    	LOW_POWER_WakeUpPin_Cfg WakeUpPin_Cfg ;   //待机模式的唤醒引脚配置
    	
    	LOW_POWER_RTC_Cfg RTC_Cfg;  //RTC唤醒配置
    	LOW_POWER_Device_Cfg Device_Cfg;	
    	
    	SystemClock_Config_Callback SystemClock_Config_Fxn;	 // 用于传入退出相关低功耗模式后 需要进行配置的系统时钟配置函数
    }LOW_POWER_Entry_Cfg;
    
    extern LOW_POWER_Entry_Cfg LP_Entry_Cfg;
    
    void GPIO_Reset_Init(bool EnableNotDisable);
    void PWR_Device_Init(bool EnableNotDisable);
    uint8_t Ctrl_UART_StopMode_WakeUp(LOW_POWER_UART_Cfg *UART_Cfg,bool EnableNotDisable);
    void Ctrl_RTC_WakeUp(LOW_POWER_RTC_Cfg *RTC_Cfg,bool EnableNotDisable);
    
    void Ctrl_Stop_Mode_WakeUp_Device(bool EnableNotDisable);
    void Enter_Low_PWR(void);
    
    void Init_Enter_Low_PWR(SystemClock_Config_Callback SystemClock_Config_Fxn);
    	
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    #include "stm32l4xx_hal.h"
    #include "LOW_POWER.h"
    
    LOW_POWER_Entry_Cfg LP_Entry_Cfg={0};
    
    /*!
     * @brief       	重置GPIO(都会进行),或再将除外部高低速晶振复用、SWCLK、SWDIO复用的所有GPIO配置为模拟输入(false)
     *								注意:用于串口唤醒等的引脚,不可配置为模拟输入,也不可关闭
     *								在进行GPIO初始化前,先将GPIO_DeInit,但是不做也不影响,不过还是建议跑一下
     *								以优先级顺序来看:
     *								如果这一组GPIO都没用到过 那么直接不开启时钟就最省电
     *								如果这一组GPIO有引脚用过了 时钟不能关 那么就将用过的引脚配置为模拟输入
     *								切记!!!:
     *								不要将没用过的引脚配置为模拟输入 耗电量其实会稍微增加一点!
     *								不要将没用过的GPIO时钟打开以后再配置为模拟输入 耗电量会增加很多 就算配置后再关时钟也没用!
     *								尽量不要勾选CubeMX中的配置闲置引脚为模拟输入的选项 没用到的时钟还开启了会增加很多耗电
     *								低功耗模式配置:
     *								在进入STOP模式时 GPIO会保留原本的状态 所以把开启后不需要再保留的GPIO配置为模拟输入确实省电 时钟的话不用的肯定关 其他的反正都会关(除了保留的时钟)
     *								在进入SLEEP模式时 时钟并不会关闭 所以时钟应手动关闭 且将开启后的GPIO配置为模拟输入
     *								待机模式和关机模式就更不用在意GPIO口耗电了
     *								https://blog.csdn.net/weixin_53403301/article/details/129055530
     *
     * @param 	[in]	EnableNotDisable: 使所有GPIO变成模拟输入或不进行模拟配置
     *
     * @return				None
     */
    __weak void GPIO_Reset_Init(bool EnableNotDisable)
    {
    //	HAL_GPIO_DeInit(GPIOA,GPIO_PIN_2|GPIO_PIN_3);		//用于串口唤醒的引脚 不可变动
    	/*
    	HAL_GPIO_DeInit(GPIOA,GPIO_PIN_0|GPIO_PIN_1
    												|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7
    												|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
    												|GPIO_PIN_12|GPIO_PIN_15);
    	
    	HAL_GPIO_DeInit(GPIOB,GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10
    												|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
    												|GPIO_PIN_15|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
    												|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9);
    	
    	HAL_GPIO_DeInit(GPIOC,GPIO_PIN_13|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2
    												|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6
    												|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
    												|GPIO_PIN_11|GPIO_PIN_12);
    	
    	HAL_GPIO_DeInit(GPIOD,GPIO_PIN_2);
    	
    	HAL_GPIO_DeInit(GPIOH,GPIO_PIN_3);
    	*/
    	if(EnableNotDisable)
    	{
    		/*
    		GPIO_InitTypeDef GPIO_InitStruct = {0};
    		*/
    
    		/* GPIO Ports Clock Enable */
    		/*
    		__HAL_RCC_GPIOC_CLK_ENABLE();
    		__HAL_RCC_GPIOH_CLK_ENABLE();
    		__HAL_RCC_GPIOA_CLK_ENABLE();
    		__HAL_RCC_GPIOB_CLK_ENABLE();
    		__HAL_RCC_GPIOD_CLK_ENABLE();
    		*/
    
    		/*Configure GPIO pins : PC13 PC0 PC1 PC2
    														 PC3 PC4 PC5 PC6
    														 PC7 PC8 PC9 PC10
    														 PC11 PC12 */
    		/*
    		GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2
    														|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6
    														|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
    														|GPIO_PIN_11|GPIO_PIN_12;
    		GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    		GPIO_InitStruct.Pull = GPIO_NOPULL;
    		HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    		*/
    
    		/*Configure GPIO pins : PA0 PA1 PA2 PA3
    														 PA4 PA5 PA6 PA7
    														 PA8 PA9 PA10 PA11
    														 PA12 PA15 */
    		/*
    		GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1
    														|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7
    														|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
    														|GPIO_PIN_12|GPIO_PIN_15;
    		GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    		GPIO_InitStruct.Pull = GPIO_NOPULL;
    		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    		*/
    		
    //		//用于串口唤醒的 不可变动
    //		GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
    //		GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    //		GPIO_InitStruct.Pull = GPIO_NOPULL;
    //		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    		
    		/*Configure GPIO pins : PB0 PB1 PB2 PB10
    														 PB11 PB12 PB13 PB14
    														 PB15 PB3 PB4 PB5
    														 PB6 PB7 PB8 PB9 */
    		/*
    		GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_10
    														|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
    														|GPIO_PIN_15|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5
    														|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
    		GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    		GPIO_InitStruct.Pull = GPIO_NOPULL;
    		HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    		*/
    
    		/*Configure GPIO pin : PD2 */
    		/*
    		GPIO_InitStruct.Pin = GPIO_PIN_2;
    		GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    		GPIO_InitStruct.Pull = GPIO_NOPULL;
    		HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    		*/
    
    		/*Configure GPIO pin : PH3 */
    		/*
    		GPIO_InitStruct.Pin = GPIO_PIN_3;
    		GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    		GPIO_InitStruct.Pull = GPIO_NOPULL;
    		HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
    		*/
    	}
    }
    
    /*!
     * @brief       	所有外设初始化配置,根据使用需求来写
     *
     * @param 	[in]	EnableNotDisable: 使能或者关闭
     *								true: 进行初始化外设(不包含时钟初始化)
     *								false: 或者关闭所有外设,所有GPIO配置为无上拉下拉且模拟输入,仅保留系统时钟和系统所需的GPIO口复用
     *								该函数在进入低功耗前调用(false)
     *								建议在进入该函数前(false)先配置用于唤醒的外设 如指定UART或RTC作为唤醒使用 然后再调用该函数 且不能关闭有唤醒功能的外设
     *								若用于唤醒后的初始化,则建议先初始化时钟,再执行该函数的初始化(true)
     *								在休眠期间使用的外设,不要关闭,也不要关闭GPIO等;相反,外设和GPIO等建议同时关闭(避免出现bug,并且也省电)
     *								未关闭,但唤醒时重复初始化外设并不受影响
     *								若未关闭的外设在运行中改变了初始化值,则建议不在唤醒时运行该初始化(前提是外设的GPIO等也没有作改动)
     *								若需要在初始化后更改初始化值,则建议要么不进行初始化且不关闭(也包括GPIO等),或重新设置新值
     *
     * @return				None
     */
    __weak void PWR_Device_Init(bool EnableNotDisable)
    {
    	if(EnableNotDisable)
    	{
    		//这里是系统最初的初始化值
    		
    		GPIO_Reset_Init(false);  //重置GPIO		
    		/*
    		MX_GPIO_Init();
    		MX_USART2_UART_Init();
    		MX_UART4_Init();
    		MX_ADC1_Init();
    		MX_ADC2_Init();
    		MX_TIM6_Init();
    		MX_RTC_Init();
    		MX_ADC3_Init();
    		*/
    		
    		//这里放初始化后还要更改的配置,若要重新初始化,建议先运行外设DeInit
    //		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8,GPIO_PIN_SET);
    //		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4,GPIO_PIN_SET);
    	}
    	else
    	{
    		/*
    //		HAL_ADC_DeInit(&hadc1);
    //		HAL_ADC_DeInit(&hadc2);
    //		HAL_ADC_DeInit(&hadc3);
    //		HAL_UART_DeInit(&huart2);		//唤醒用的串口 最好不要关闭:若不用于唤醒 则可以关闭 GPIO等同步关闭;若用于唤醒 则不能关闭 GPIO等也不能关闭
    //		HAL_UART_DeInit(&huart4);
    //		HAL_TIM_Base_DeInit(&htim6);
    //		HAL_RTC_DeInit(&hrtc);		//唤醒用的RTC 最好不要关闭	
    	  */
    		GPIO_Reset_Init(true);  //GPIO配置为复用
    		
    	}
    }
    
    /*!
     * @brief       	配置串口在停止模式下的唤醒   	
     *
     * @param 	[in]	UART_Cfg: UART配置
     *
     * @return				None
     */
    __weak uint8_t Ctrl_UART_StopMode_WakeUp(LOW_POWER_UART_Cfg *UART_Cfg,bool EnableNotDisable)
    {	
    	if (!UART_Cfg->uart_handle)
    	{
    		return 0;
    	}
    	
    	if(EnableNotDisable)
    	{
    		__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI);	//保留唤醒用的HSI线 串口初始化时钟也必须要配置为HSI
    		HAL_UARTEx_StopModeWakeUpSourceConfig(UART_Cfg->uart_handle,UART_Cfg->UART_WakeUpStruct);
    	
    		__HAL_UART_ENABLE_IT(UART_Cfg->uart_handle,UART_IT_WUF);	//开启唤醒中断
    		HAL_UARTEx_EnableClockStopMode(UART_Cfg->uart_handle);
    		HAL_UARTEx_EnableStopMode(UART_Cfg->uart_handle);		//开启模式
    	}
    	else
    	{
    		__HAL_UART_DISABLE_IT(UART_Cfg->uart_handle,UART_IT_WUF);	//关闭唤醒中断
    		HAL_UARTEx_DisableClockStopMode(UART_Cfg->uart_handle);
    		HAL_UARTEx_DisableStopMode(UART_Cfg->uart_handle);		//关闭模式
    	}
    	
    	return 1;
    }
    
    /*!
     * @brief       	配置停止模式下的外设唤醒函数 true为开启 false为关闭 (不包含RTC唤醒)
     *
     * @return				None
     */
    __weak void Ctrl_Stop_Mode_WakeUp_Device(bool EnableNotDisable)
    {
    	uint8_t i=0;
    	
    	if(EnableNotDisable)
    	{
    		
    		for(i=0;i<sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg)/sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg[0]);i++)
    		{
    			if(!Ctrl_UART_StopMode_WakeUp(&LP_Entry_Cfg.Device_Cfg.UART_Cfg[i],LP_Entry_Cfg.Device_Cfg.UART_Cfg[i].EnableNotDisable))
    			{
    				break;
    			}
    		}		
    		
    	}
    	else
    	{
    		
    		for(i=0;i<sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg)/sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg[0]);i++)
    		{
    			if(!Ctrl_UART_StopMode_WakeUp(&LP_Entry_Cfg.Device_Cfg.UART_Cfg[i],false))
    			{
    				break;	
    			}				
    		}
    		
    	}
    }
    
    /*!
     * @brief       	配置RTC在低功耗模式下的唤醒   	
     *
     * @param 	[in]	RTC_Cfg: RTC配置
     *
     * @return				None
     */
    __weak void Ctrl_RTC_WakeUp(LOW_POWER_RTC_Cfg *RTC_Cfg,bool EnableNotDisable)
    {	
    	if(EnableNotDisable)
    	{
    		HAL_RTCEx_SetWakeUpTimer_IT(RTC_Cfg->rtc_handle,RTC_Cfg->counter,RTC_Cfg->clock);
    	}
    	else
    	{
    		__HAL_RTC_WAKEUPTIMER_EXTI_DISABLE_IT();	
    	}
    }
    
    /*
    进入低功耗的函数中 每个模式都有一个10ms的消抖 其实这个可以省略
    但我们用的是HAL库 HAL库初始化时钟以后 会打开SysTick中断 如果这个开着 就一直无法进入低功耗(进入就被中断唤醒)
    所以我这里加了个我自己写的延时函数 其中就有关闭SysTick的语句
    如果你不用这个消抖 没啥BUG也行 但我还是建议加上去 毕竟也不差那10ms
    */
    
    /*!
     * @brief       	进入低功耗模式   	
     *
     * @return				None
     */
    __weak void Enter_Low_PWR(void)
    {
    	__HAL_RCC_PWR_CLK_ENABLE();
    	switch(LP_Entry_Cfg.mode_flag)
    	{
    		case 0:
    		{
    			printf("[INFO] 不进入低功耗模式\n");
    			break;
    		}
    		case 1:
    		{
    			printf("[INFO] 进入睡眠模式\n");
    			delay_ms(10);  //消抖
    			PWR_Device_Init(false);
    			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);	
    			Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
    			HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,LP_Entry_Cfg.SLEEPEntry_Cfg.SLEEPEntry);
    			__HAL_RCC_PWR_CLK_ENABLE();			
    			Ctrl_RTC_WakeUp(NULL,false);	
    			PWR_Device_Init(true);
    			break;
    		}
    		case 2:
    		{
    			printf("[INFO] 进入停止模式\n");
    			delay_ms(10);  //消抖
    			PWR_Device_Init(false);			
    			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);	
    			Ctrl_Stop_Mode_WakeUp_Device(true);			
    			Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
    			HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,LP_Entry_Cfg.STOPEntry_Cfg.STOPEntry);
    			__HAL_RCC_PWR_CLK_ENABLE();			
    			LP_Entry_Cfg.SystemClock_Config_Fxn();
    			Ctrl_Stop_Mode_WakeUp_Device(false);
    			Ctrl_RTC_WakeUp(NULL,false);	
    			PWR_Device_Init(true);
    			break;
    		}
    		case 3:
    		{
    			printf("[INFO] 三秒后进入待机模式\n");
    			delay_ms(3000);
    			printf("[INFO] 进入待机模式\n");
    			HAL_PWR_EnableWakeUpPin(LP_Entry_Cfg.WakeUpPin_Cfg.WakeUpPinPolarity);
    			delay_ms(10);  //消抖
    			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    			Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
    			HAL_PWR_EnterSTANDBYMode();
    			break;
    		}
    		case 4:
    		{
    			printf("[INFO] 三秒后进入关机模式\n");
    			delay_ms(3000);
    			printf("[INFO] 进入关机模式\n");
    			HAL_PWR_EnableWakeUpPin(LP_Entry_Cfg.WakeUpPin_Cfg.WakeUpPinPolarity);
    			delay_ms(10);  //消抖
    			__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    			Ctrl_RTC_WakeUp(&LP_Entry_Cfg.RTC_Cfg,LP_Entry_Cfg.RTC_Cfg.EnableNotDisable);
    			HAL_PWREx_EnterSHUTDOWNMode();
    			break;
    		}
    		default:
    		{
    			printf("[INFO] 不进入低功耗模式\n");
    			break;
    		}
    	}
    }
    
    void Init_Enter_Low_PWR(SystemClock_Config_Callback SystemClock_Config_Fxn)
    {	
    	uint8_t i=0;
    	memset(&LP_Entry_Cfg,0,sizeof(LP_Entry_Cfg));
    	LP_Entry_Cfg.SystemClock_Config_Fxn=SystemClock_Config_Fxn;
    	LP_Entry_Cfg.SLEEPEntry_Cfg.SLEEPEntry=PWR_SLEEPENTRY_WFI;
    	LP_Entry_Cfg.STOPEntry_Cfg.STOPEntry=PWR_STOPENTRY_WFI;
    	
    	LP_Entry_Cfg.RTC_Cfg.counter=RTC_WAKEUPCLOCK_RTCCLK_DIV16;	
    	
    	for(i=0;i<sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg)/sizeof(LP_Entry_Cfg.Device_Cfg.UART_Cfg[0]);i++)
    	{
    		LP_Entry_Cfg.Device_Cfg.UART_Cfg[i].UART_WakeUpStruct.WakeUpEvent=UART_WAKEUP_ON_READDATA_NONEMPTY;
    	}	
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371

    调用方式

    首先 需要调用初始化函数
    同时传入系统时钟初始化函数地址

    Init_Enter_Low_PWR(SystemClock_Config);
    
    • 1

    然后在进入低功耗前 需要对其进行配置

    		LP_Entry_Cfg.mode_flag=2;
    		LP_Entry_Cfg.STOPEntry_Cfg.STOPEntry=PWR_STOPENTRY_WFI;
    		LP_Entry_Cfg.SystemClock_Config_Fxn=SystemClock_Config;
    		LP_Entry_Cfg.RTC_Cfg.EnableNotDisable=true;
    		LP_Entry_Cfg.RTC_Cfg.rtc_handle=&hrtc;
    		LP_Entry_Cfg.RTC_Cfg.clock=RTC_WAKEUPCLOCK_RTCCLK_DIV16;
    		LP_Entry_Cfg.RTC_Cfg.counter=300;
    		Enter_Low_PWR();	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最后用Enter_Low_PWR();函数来进入低功耗

    附录:Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作

    SysTick系统定时器精准延时

    延时函数

    SysTick->LOAD中的值为计数值
    计算方法为工作频率值/分频值
    比如工作频率/1000 则周期为1ms

    以ADuCM4050为例:

    #include "ADuCM4050.h"
    
    void delay_ms(unsigned int ms)
    {
    	SysTick->LOAD = 26000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
    	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
    	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系统定时器
    	while(ms--)
    	{
    		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
    	}
    	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
    }
    void delay_us(unsigned int us)
    {
    	SysTick->LOAD = 26000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
    	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
    	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能52MHz的系统定时器
    	while(us--)
    	{
    		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
    	}
    	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    其中的52000000表示芯片的系统定时器频率 32系列一般为外部定时器频率的两倍

    Cortex-M架构SysTick系统定时器阻塞和非阻塞延时

    阻塞延时

    首先是最常用的阻塞延时

    void delay_ms(unsigned int ms)
    {
    	SysTick->LOAD = 50000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
    	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
    	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
    	while(ms--)
    	{
    		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
    	}
    	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
    }
    void delay_us(unsigned int us)
    {
    	SysTick->LOAD = 50000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
    	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
    	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
    	while(us--)
    	{
    		while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
    	}
    	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    50000000表示工作频率
    分频后即可得到不同的延时时间
    以此类推

    那么 不用两个嵌套while循环 也可以写成:

    void delay_ms(unsigned int ms)
    {
    	SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
    	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
    	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
    
    	while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
    
    	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
    }
    void delay_us(unsigned int us)
    {
    	SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
    	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
    	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
    	
    	while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
    
    	SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    但是这种写法有个弊端
    那就是输入ms后,最大定时不得超过计数值,也就是不能超过LOAD的最大值,否则溢出以后,则无法正常工作

    而LOAD如果最大是32位 也就是4294967295

    晶振为50M的话 50M的计数值为1s 4294967295计数值约为85s

    固最大定时时间为85s

    但用嵌套while的话 最大可以支持定时4294967295*85s

    非阻塞延时

    如果采用非阻塞的话 直接改写第二种方法就好了:

    void delay_ms(unsigned int ms)
    {
    	SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
    	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
    	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
    
    	//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
    
    	//SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
    }
    void delay_us(unsigned int us)
    {
    	SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数
    	SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记
    	SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器
    	
    	//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待
    
    	//SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    将等待和关闭定时器语句去掉
    在使用时加上判断即可变为阻塞:

    delay_ms(500);
    while ((SysTick->CTRL & 0x00010000)==0);
    SysTick->CTRL = 0;
    
    • 1
    • 2
    • 3

    在非阻塞状态下 可以提交定时器后 去做别的事情 然后再来等待

    不过这样又有一个弊端 那就是定时器会自动重载 可能做别的事情以后 定时器跑过了 然后就要等85s才能停下

    故可以通过内部定时器来进行非阻塞延时函数的编写

    基本上每个mcu的内部定时器都可以配置自动重载等功能 网上资料很多 这里就不再阐述了

    位带操作

    位带代码

    M3、M4架构的单片机 其输出口地址为端口地址+20 输入为+16
    M0架构的单片机 其输出口地址为端口地址+12 输入为+8
    以ADuCM4050为列:

    位带宏定义
    #ifndef __GPIO_H__
    #define __GPIO_H__
    #include "ADuCM4050.h"
    #include "adi_gpio.h"
    
    #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
    
    #define GPIO0_ODR_Addr    (ADI_GPIO0_BASE+20) //0x40020014
    #define GPIO0_IDR_Addr    (ADI_GPIO0_BASE+16) //0x40020010
    
    #define GPIO1_ODR_Addr    (ADI_GPIO1_BASE+20) //0x40020054
    #define GPIO1_IDR_Addr    (ADI_GPIO1_BASE+16) //0x40020050
    
    #define GPIO2_ODR_Addr    (ADI_GPIO2_BASE+20) //0x40020094
    #define GPIO2_IDR_Addr    (ADI_GPIO2_BASE+16) //0x40020090
    
    #define GPIO3_ODR_Addr    (ADI_GPIO3_BASE+20) //0x400200D4
    #define GPIO3_IDR_Addr    (ADI_GPIO3_BASE+16) //0x400200D0
    
    #define P0_O(n)   	BIT_ADDR(GPIO0_ODR_Addr,n)  //输出 
    #define P0_I(n)    	BIT_ADDR(GPIO0_IDR_Addr,n)  //输入 
    
    #define P1_O(n)   	BIT_ADDR(GPIO1_ODR_Addr,n)  //输出 
    #define P1_I(n)    	BIT_ADDR(GPIO1_IDR_Addr,n)  //输入 
    
    #define P2_O(n)   	BIT_ADDR(GPIO2_ODR_Addr,n)  //输出 
    #define P2_I(n)    	BIT_ADDR(GPIO2_IDR_Addr,n)  //输入 
    
    #define P3_O(n)   	BIT_ADDR(GPIO3_ODR_Addr,n)  //输出 
    #define P3_I(n)    	BIT_ADDR(GPIO3_IDR_Addr,n)  //输入 
    
    #define Port0			(ADI_GPIO_PORT0)
    #define Port1			(ADI_GPIO_PORT1)
    #define Port2			(ADI_GPIO_PORT2)
    #define Port3			(ADI_GPIO_PORT3)
    
    #define Pin0			(ADI_GPIO_PIN_0)
    #define Pin1			(ADI_GPIO_PIN_1)
    #define Pin2			(ADI_GPIO_PIN_2)
    #define Pin3			(ADI_GPIO_PIN_3)
    #define Pin4			(ADI_GPIO_PIN_4)
    #define Pin5			(ADI_GPIO_PIN_5)
    #define Pin6			(ADI_GPIO_PIN_6)
    #define Pin7			(ADI_GPIO_PIN_7)
    #define Pin8			(ADI_GPIO_PIN_8)
    #define Pin9			(ADI_GPIO_PIN_9)
    #define Pin10			(ADI_GPIO_PIN_10)
    #define Pin11			(ADI_GPIO_PIN_11)
    #define Pin12			(ADI_GPIO_PIN_12)
    #define Pin13			(ADI_GPIO_PIN_13)
    #define Pin14			(ADI_GPIO_PIN_14)
    #define Pin15			(ADI_GPIO_PIN_15)
    
    void GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag);
    void GPIO_BUS_OUT(unsigned int port,unsigned int num);
    
    void P0_BUS_O(unsigned int num);
    unsigned int P0_BUS_I(void);
    
    void P1_BUS_O(unsigned int num);
    unsigned int P1_BUS_I(void);
    
    void P2_BUS_O(unsigned int num);
    unsigned int P2_BUS_I(void);
    
    void P3_BUS_O(unsigned int num);
    unsigned int P3_BUS_I(void);
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    总线函数
    #include "ADuCM4050.h"
    #include "adi_gpio.h"
    #include "GPIO.h"
    
    void GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag)
    {
    	switch(port)
    	{
    		case 0:{
    			switch(pin)
    			{
    				case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_0));};break;
    				case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_1));};break;
    				case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_2));};break;
    				case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_3));};break;
    				case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_4));};break;
    				case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_5));};break;
    				case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_6));};break;
    				case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_7));};break;
    				case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_8));};break;
    				case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_9));};break;
    				case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_10));};break;
    				case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_11));};break;
    				case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_12));};break;
    				case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_13));};break;
    				case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_14));};break;
    				case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));};break;
    				default:pin=0;break;
    			}
    		}break;
    		
    		case 1:{
    			switch(pin)
    			{
    				case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_0));};break;
    				case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_1));};break;
    				case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_2));};break;
    				case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_3));};break;
    				case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_4));};break;
    				case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_5));};break;
    				case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_6));};break;
    				case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_7));};break;
    				case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_8));};break;
    				case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_9));};break;
    				case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_10));};break;
    				case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_11));};break;
    				case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_12));};break;
    				case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_13));};break;
    				case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_14));};break;
    				case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));};break;
    				default:pin=0;break;
    			}
    		}break;
    		
    		case 2:{
    			switch(pin)
    			{
    				case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_0));};break;
    				case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_1));};break;
    				case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_2));};break;
    				case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_3));};break;
    				case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_4));};break;
    				case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_5));};break;
    				case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_6));};break;
    				case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_7));};break;
    				case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_8));};break;
    				case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_9));};break;
    				case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_10));};break;
    				case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_11));};break;
    				case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_12));};break;
    				case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_13));};break;
    				case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_14));};break;
    				case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));};break;
    				default:pin=0;break;
    			}
    		}break;
    		
    		case 3:{
    			switch(pin)
    			{
    				case 0:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_0));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_0));};break;
    				case 1:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_1));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_1));};break;
    				case 2:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_2));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_2));};break;
    				case 3:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_3));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_3));};break;
    				case 4:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_4));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_4));};break;
    				case 5:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_5));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_5));};break;
    				case 6:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_6));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_6));};break;
    				case 7:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_7));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_7));};break;
    				case 8:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_8));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_8));};break;
    				case 9:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_9));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_9));};break;
    				case 10:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_10));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_10));};break;
    				case 11:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_11));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_11));};break;
    				case 12:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_12));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_12));};break;
    				case 13:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_13));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_13));};break;
    				case 14:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_14));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_14));};break;
    				case 15:if(flag==1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));};break;
    				default:pin=0;break;
    			}
    		}break;
    		
    		default:port=0;break;
    	}	
    }
    
    void GPIO_BUS_OUT(unsigned int port,unsigned int num)  //num最大为0xffff
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		GPIO_OUT(port,i,(num>>i)&0x0001);
    	}
    }
    
    
    void P0_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		P0_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int P0_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(P0_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void P1_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		P1_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int P1_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(P1_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void P2_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		P2_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int P2_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(P2_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void P3_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		P3_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int P3_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(P3_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190

    一、位带操作理论及实践

    位带操作的概念其实30年前就有了,那还是 CM3 将此能力进化,这里的位带操作是 8051 位寻址区的威力大幅加强版

    位带区: 支持位带操作的地址区

    位带别名: 对别名地址的访问最终作 用到位带区的访问上(注意:这中途有一个 地址映射过程)

    位带操作对于硬件 I/O 密集型的底层程序最有用处

    支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。在CM4中,有两个区中实现了位带。其中一个是SRAM区的最低1MB范围,第二个则是片内外设区的最低1MB范围。这两个区中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

    位操作就是可以单独的对一个比特位读和写,类似与51中sbit定义的变量,stm32中通过访问位带别名区来实现位操作的功能
    STM32中有两个地方实现了位带,一个是SRAM,一个是片上外设。
    在这里插入图片描述
    (1)位带本质上是一块地址区(例如每一位地址位对应一个寄存器)映射到另一片地址区(实现每一位地址位对应一个寄存器中的一位),该区域就叫做位带别名区,将每一位膨胀成一个32位的字。
    (2)位带区的4个字节对应实际寄存器或内存区的一个位,虽然变大到4个字节,但实际上只有最低位有效(代表0或1)

    只有位带可以直接用=赋值的方式来操作寄存器 位带是把寄存器上的每一位 膨胀到32位 映射到位带区 比如0x4002 0000地址的第0个bit 映射到位带区的0地址 那么其对应的位带映射地址为0x00 - 0x04 一共32位 但只有LSB有效 采用位带的方式用=赋值时 就是把位带区对应的LSB赋值 然后MCU再转到寄存器对应的位里面 寄存器操作时 如果不改变其他位上面的值 那就只能通过&=或者|=的方式进行

    在这里插入图片描述

    要设置0x2000 0000这个字节的第二个位bit2为1,使用位带操作的步骤有:
    1、将1写入位 带别名区对应的映射地址(即0x22000008,因为1bit对应4个byte);
    2、将0x2000 0000的值 读取到内部的缓冲区(这一步骤是内核完成的,属于原子操作,不需要用户操作);
    3、将bit2置1,再把值写 回到0x2000 0000(属于原子操作,不需要用户操作)。

    关于GPIO引脚对应的访问地址,可以参考以下公式
    寄存器位带别名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引脚编号4

    如:端口F访问的起始地址GPIOF_BASE

    #define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)

    在这里插入图片描述

    但好在官方库里面都帮我们定义好了 只需要在BASE地址加上便宜即可

    例如:

    GPIOF的ODR寄存器的地址 = GPIOF_BASE + 0x14

    寄存器位带别名 = 0x42000000 + (寄存器的地址-0x40000000)32 + 引脚编号4

    设置PF9引脚的话:

    uint32_t *PF9_BitBand =
    *(uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR– 0x40000000) *32 + 9*4)
    
    
    • 1
    • 2
    • 3

    封装一下:

    #define PFout(x) *(volatile uint32_t *)(0x42000000 + ((uint32_t )&GPIOF->ODR – 0x40000000) *32 + x*4)
    
    
    • 1
    • 2

    现在 可以把通用部分封装成一个小定义:

    #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
    
    • 1
    • 2
    • 3

    那么 设置PF引脚的函数可以定义:

    #define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414   
    #define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
    
    #define PF_O(n)   	BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
    #define PF_I(n)    	BIT_ADDR(GPIOF_IDR_Addr,n)  //输入
    
    • 1
    • 2
    • 3
    • 4
    • 5

    若使PF9输入输出则:

    PF_O(9)=1;  //输出高电平
    uint8_t dat = PF_I(9);  //获取PF9引脚的值
    
    • 1
    • 2

    总线输入输出:

    void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PF_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PF_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    STM32的可用下面的函数:

    #ifndef __GPIO_H__
    #define __GPIO_H__
    #include "stm32l496xx.h"
    
    #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
    
    #define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
    #define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
    #define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
    #define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
    #define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
    #define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
    #define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
    #define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
    #define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     
    
    #define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
    #define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
    #define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
    #define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
    #define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
    #define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
    #define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
    #define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
    #define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
     
    #define PA_O(n)   	BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
    #define PA_I(n)    	BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 
    
    #define PB_O(n)   	BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
    #define PB_I(n)    	BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 
    
    #define PC_O(n)   	BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
    #define PC_I(n)    	BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 
    
    #define PD_O(n)   	BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
    #define PD_I(n)    	BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 
    
    #define PE_O(n)   	BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
    #define PE_I(n)    	BIT_ADDR(GPIOE_IDR_Addr,n)  //输入
    
    #define PF_O(n)   	BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
    #define PF_I(n)    	BIT_ADDR(GPIOF_IDR_Addr,n)  //输入
    
    #define PG_O(n)   	BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
    #define PG_I(n)    	BIT_ADDR(GPIOG_IDR_Addr,n)  //输入
    
    #define PH_O(n)   	BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
    #define PH_I(n)    	BIT_ADDR(GPIOH_IDR_Addr,n)  //输入
    
    #define PI_O(n)			BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
    #define PI_I(n)   	BIT_ADDR(GPIOI_IDR_Addr,n)  //输入
    
    void PA_BUS_O(unsigned int num);
    unsigned int PA_BUS_I(void);
    
    void PB_BUS_O(unsigned int num);
    unsigned int PB_BUS_I(void);
    
    void PC_BUS_O(unsigned int num);
    unsigned int PC_BUS_I(void);
    
    void PD_BUS_O(unsigned int num);
    unsigned int PD_BUS_I(void);
    
    void PE_BUS_O(unsigned int num);
    unsigned int PE_BUS_I(void);
    
    void PF_BUS_O(unsigned int num);
    unsigned int PF_BUS_I(void);
    
    void PG_BUS_O(unsigned int num);
    unsigned int PG_BUS_I(void);
    
    void PH_BUS_O(unsigned int num);
    unsigned int PH_BUS_I(void);
    
    void PI_BUS_O(unsigned int num);
    unsigned int PI_BUS_I(void);
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    #include "GPIO.h"
    
    void PA_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PA_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PA_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PA_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void PB_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PB_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PB_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PB_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void PC_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PC_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PC_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PC_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void PD_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PD_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PD_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PD_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void PE_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PE_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PE_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PE_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void PF_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PF_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PF_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PF_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void PG_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PG_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PG_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PG_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void PH_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PH_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PH_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PH_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    void PI_BUS_O(unsigned int num)  //输入值num最大为0xFFFF
    {
    	int i;
    	for(i=0;i<16;i++)
    	{
    		PI_O(i)=(num>>i)&0x0001;
    	}
    }
    unsigned int PI_BUS_I(void)  //输出值num最大为0xFFFF
    {
    	unsigned int num;
    	int i;
    	for(i=0;i<16;i++)
    	{
    		num=num+(PI_I(i)<<i)&0xFFFF;
    	}
    	return num;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173

    二、如何判断MCU的外设是否支持位带

    根据《ARM Cortex-M3与Cortex-M4权威指南(第3版)》中第6章第7节描述
    在这里插入图片描述
    也就是说 要实现对GPIO的位带操作 必须保证GPIO位于外设区域的第一个1MB中
    第一个1MB应该是0x4010 0000之前 位带不是直接操作地址 而是操作地址映射 地址映射被操作以后 MCU自动会修改对应寄存器的值

    位带区只有1MB 所以只能改0x4000 0000 - 0x400F FFFF的寄存器
    像F4系列 GPIO的首地址为0x4002 0000 就可以用位带来更改

    STM32L476的GPIO就不行:
    在这里插入图片描述
    AHB2的都不能用位带
    ABP 还有AHB1都可以用
    在这里插入图片描述
    但是L476的寄存器里面 GPIO和ADC都是AHB2

  • 相关阅读:
    Vue2技能树(3)-声明式渲染、指令大全、生命周期函数
    JS 中的闭包是什么?什么是闭包、闭包的作用、闭包的解决
    lombok学习
    Python|OpenCV-鼠标自动绘制图像(4)
    Java CountDownLatch 学习总结
    嵌入式开发:技巧和窍门——干净地从引导加载程序跳转到应用程序代码
    3.27每日一题(常系数线性非齐次方程的特解)
    搞创新,我们“REAL”在行!
    CSS调试实例
    Jmeter 逻辑控制之IF条件控制器
  • 原文地址:https://blog.csdn.net/weixin_53403301/article/details/134182195