当前位置: 首页 > news >正文

晚上做设计挣钱的网站怎么做自己的销售网站

晚上做设计挣钱的网站,怎么做自己的销售网站,建设大型网站的公司,长沙网络公关公司【STM32】基于HAL库建立自己的低功耗模式配置库#xff08;STM32L4系列低功耗所有配置汇总#xff09; 文章目录 低功耗模式#xff08;此章节可直接跳过#xff09;低功耗模式简介睡眠模式停止模式待机模式 建立自己的低功耗模式配置库通过结构体的方式来进行传参RTC配置…【STM32】基于HAL库建立自己的低功耗模式配置库STM32L4系列低功耗所有配置汇总 文章目录 低功耗模式此章节可直接跳过低功耗模式简介睡眠模式停止模式待机模式 建立自己的低功耗模式配置库通过结构体的方式来进行传参RTC配置UART配置 通过回调函数来配置时钟通过虚假的回调来初始化低功耗外设初始化函数代码整合调用方式 附录Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作SysTick系统定时器精准延时延时函数阻塞延时非阻塞延时 位带操作位带代码位带宏定义总线函数 一、位带操作理论及实践二、如何判断MCU的外设是否支持位带 此文章是讨论将先前所有的低功耗配置功能整合起来的一个库适用于STM32L4系列 目前除了普通唤醒方式外 加入了UART唤醒和RTC唤醒配置 如果后续有更多唤醒加入如I2C等 将直接在后续的文章中进行讨论 本文所建的库将不再更新其他唤醒方式 但会对某些BUG进行修复 相关函数调用和配置 可以从我之前的文章里找到 gitee库 低功耗模式此章节可直接跳过 【STM32笔记】低功耗模式配置及避坑汇总 低功耗模式简介 系统提供了多个低功耗模式可在 CPU 不需要运行时例如等待外部事件时节省功耗。由用户根据应用选择具体的低功耗模式以在低功耗、短启动时间和可用唤醒源之间寻求最佳平衡。 睡眠模式、停止模式及待机模式中若备份域电源正常供电备份域内的 RTC 都可以正常运行备份域内的寄存器的数据会被保存不受功耗模式影响。 从表中可以看到这三种低功耗模式层层递进运行的时钟或芯片功能越来越少因而功耗越来越低。 模式说明进入方式唤醒对1.8V区域时钟的影响对VDD区域时钟的影响调压器睡眠模式内核停止所有外设包括M3核心的外设如NVIC、系统时钟(SysTick)等仍在运行WFI、WFE命令HAL库直接调用任意中断/事件内核时钟关对其他时钟和ADC时钟无影响无开––––––停止模式所有的时钟都已停止配置PWR_CR寄存器的PDDSLPDS位SLEEPDEEP位WFI或WFE命令任意外部中断EXTI(在外部中断寄存器中设置)关闭所有1.8V区域的时钟HSI和HSE的振荡器关闭开启或处于低功耗模式(依据电源控制寄存器的设定)––––––待机模式1.8V电源关闭配置PWR_CR寄存器的PDDSSLEEPDEEP位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 指令时立即进入睡眠模式。 退出时睡眠 在退出优先级最低的中断服务程序后才进入睡眠模式。 进入方式 内核寄存器的 SLEEPDEEP0 然后调用 WFI 或 WFE 指令即可进入睡眠模式SLEEPONEXIT1 时进入“退出时睡眠”模式。 唤醒方式 如果是使用 WFI 指令睡眠的则可使用任意中断唤醒如果是使用 WFE 指令睡眠的则由事件唤醒。 睡眠时 关闭内核时钟内核停止而外设正常运行在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存的数据。 唤醒延迟 无延迟。 唤醒后 若由中断唤醒先进入中断退出中断服务程序后接着执行 WFI 指令后的程序若由事件唤醒直接接着执行 WFE 后的程序。唤醒后即可开始行动 继续程序 无需配置任何寄存器 睡眠模式和低功耗睡眠模式是两个模式 由PWR_MAINREGULATOR_ON和PWR_LOWPOWERREGULATOR_ON两个变量确定 要进入低功耗睡眠模式 首先得进入低功耗运行模式 HAL_PWREx_EableLowPowerRunMode()且工作频率降低到2MHz以下 唤醒时 睡眠模式直接唤醒 而低功耗睡眠模式唤醒后 会进入到低功耗运行模式 若想正常工作 需用HAL_PWREx_DisableLowPowerRunMode()退出 停止模式 在停止模式中进一步关闭了其它所有的时钟于是所有的外设都停止了工作但由于其 1.8V 区域的部分电源没有关闭还保留了内核的寄存器、内存的信息所以从停止模式唤醒并重新开启时钟后还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒在停止模式中可以选择电压调节器为开模式或低功耗模式。 特性和说明调压器低功耗模式 在停止模式下调压器可工作在正常模式或低功耗模式可进一步降低功耗。 进入方式 内核寄存器的 SLEEPDEEP1PWR_CR 寄存器中的 PDDS0然后调用 WFI 或 WFE 指令即可进入停止模式PWR_CR 寄存器的 LPDS0 时调压器工作在正常模式LPDS1 时工作在低功耗模式。 唤醒方式 如果是使用 WFI 指令睡眠的可使用任意 EXTI 线的中断唤醒如果是使用 WFE 指令睡眠的可使用任意配置为事件模式的 EXTI 线事件唤醒。 停止时 内核停止片上外设也停止。这个状态会保留停止前的内核寄存器、内存的数据。 唤醒延迟 基础延迟为 HSI 振荡器的启动时间若调压器工作在低功耗模式还需要加上调压器从低功耗切换至正常模式下的时间。 唤醒后 若由中断唤醒先进入中断退出中断服务程序后接着执行 WFI 指令后的程序若由事件唤醒直接接着执行 WFE 后的程序。唤醒后STM32 会使用 HSI 作为系统时钟。只能由外部中断唤醒 唤醒后需要重新使能时钟SystemClock_Config(); 建议将一条外部中断线专门作为唤醒中断执行中断后进入回调进行时钟使能 停止模式0和1由PWR_MAINREGULATOR_ON和PWR_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(独立看门狗)复位。 特性和说明进入方式 内核寄存器的 SLEEPDEEP1PWR_CR 寄存器中的 PDDS1PWR_CR 寄存器中的唤醒状态位 WUF0然后调用 WFI 或 WFE 指令即可进入待机模式。 唤醒方式 通过 WKUP RTC 闹钟、唤醒、入侵、时间戳事件或 NRST 引脚外部复位及 IWDG 复位唤醒。 待机时 内核停止片上外设也停止内核寄存器、内存的数据会丢失除复位引脚、RTC_AF1 引脚及 WKUP 引脚其它 I/O 口均工作在高阻态。 唤醒延迟 芯片复位的时间。 唤醒后 相当于芯片复位在程序表现为从头开始执行代码。建立自己的低功耗模式配置库 首先 在先前的文章中 我们通过Enter_Low_PWR来进入低功耗模式 同时 在进入低功耗之前 需要调用唤醒配置函数 退出低功耗后 也要初始化时钟等等 而建立的这个库 就是把所有配置整合到一起 从而使其能直接用一个通用函数代替 文中用于传参的配置结构体是一个全局变量 另外用到了结构体嵌套 回调函数等等 该写法也是TI的SDK常用写法 通过结构体的方式来进行传参 最主要的就是mode_flag参数 该参数决定了四种低功耗模式 其中 停止模式默认是停止1 typedef struct {uint8_t mode_flag; // 0/大于4 不进入任何模式1 进入睡眠2 进入停止13 进入待机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;次级结构体包括进入低功耗的方式 待机模式唤醒引脚配置以及外设唤醒模式和唤醒后的时钟配置回调. 其中 外设唤醒分为外设和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 进入停止13 进入待机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;/*!* 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;}} } 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;这里的时钟源和计数值就是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(); } }【STM32笔记】低功耗模式下的RTC唤醒非闹钟唤醒而是采用RTC_WAKEUPTIMER UART配置 【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒解决串口唤醒和回调无法一起使用的问题 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; UART_WakeUpStruct结构体中 一般把WakeUpEventUART_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线 串口初始化时钟也必须要配置为HSIHAL_UARTEx_StopModeWakeUpSourceConfig(UART_Cfg-uart_handle,UART_Cfg-UART_WakeUpStruct);__HAL_UART_ENABLE_IT(UART_Cfg-uart_handle,UART_IT_WUF); //开启唤醒中断HAL_UARTEx_EnableStopMode(UART_Cfg-uart_handle); //开启模式}else{__HAL_UART_DISABLE_IT(UART_Cfg-uart_handle,UART_IT_WUF); //关闭唤醒中断HAL_UARTEx_DisableStopMode(UART_Cfg-uart_handle); //关闭模式}return 1; } 通过回调函数来配置时钟 typedef void (*SystemClock_Config_Callback)(void);声明了一个函数指针类型 在调用时需要传入函数指针 一般是系统时钟配置 也就是SystemClock_Config 通过虚假的回调来初始化低功耗外设 在低功耗进入前和退出以后 都可以通过把已经打开的外设关掉来降低功耗 关闭 PWR_Device_Init(false);打开 PWR_Device_Init(true);同时 在此函数中 也包含GPIO的配置 这两个函数用的虚假回调方式来编写 在调用时 需要自己补全代码 【STM32笔记】低功耗模式下GPIO省电配置避坑实验闲置引脚配置为模拟输入其实更耗电 【STM32笔记】低功耗模式下GPIO、外设省电配置避坑 初始化函数 这里传参是系统时钟配置函数 当然 你也可以自己写一个 然后就是各个变量的赋值 这里对几个常用变量进行了赋值 void Init_Enter_Low_PWR(SystemClock_Config_Callback SystemClock_Config_Fxn) { uint8_t i0;memset(LP_Entry_Cfg,0,sizeof(LP_Entry_Cfg));LP_Entry_Cfg.SystemClock_Config_FxnSystemClock_Config_Fxn;LP_Entry_Cfg.SLEEPEntry_Cfg.SLEEPEntryPWR_SLEEPENTRY_WFI;LP_Entry_Cfg.STOPEntry_Cfg.STOPEntryPWR_STOPENTRY_WFI;LP_Entry_Cfg.RTC_Cfg.counterRTC_WAKEUPCLOCK_RTCCLK_DIV16; for(i0;isizeof(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.WakeUpEventUART_WAKEUP_ON_READDATA_NONEMPTY;} } 代码整合 #ifndef __LOW_POWER_H__ #define __LOW_POWER_H__ #include stm32l4xx_hal.h #include DELAY.h #include stdbool.h #include stdint.h #include stdio.h #include string.htypedef 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 进入停止13 进入待机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 #include stm32l4xx_hal.h #include LOW_POWER.hLOW_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 PC2PC3 PC4 PC5 PC6PC7 PC8 PC9 PC10PC11 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 PA3PA4 PA5 PA6 PA7PA8 PA9 PA10 PA11PA12 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 PB10PB11 PB12 PB13 PB14PB15 PB3 PB4 PB5PB6 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线 串口初始化时钟也必须要配置为HSIHAL_UARTEx_StopModeWakeUpSourceConfig(UART_Cfg-uart_handle,UART_Cfg-UART_WakeUpStruct);__HAL_UART_ENABLE_IT(UART_Cfg-uart_handle,UART_IT_WUF); //开启唤醒中断HAL_UARTEx_EnableStopMode(UART_Cfg-uart_handle); //开启模式}else{__HAL_UART_DISABLE_IT(UART_Cfg-uart_handle,UART_IT_WUF); //关闭唤醒中断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 i0;if(EnableNotDisable){for(i0;isizeof(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(i0;isizeof(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(); } }/*!* 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 i0;memset(LP_Entry_Cfg,0,sizeof(LP_Entry_Cfg));LP_Entry_Cfg.SystemClock_Config_FxnSystemClock_Config_Fxn;LP_Entry_Cfg.SLEEPEntry_Cfg.SLEEPEntryPWR_SLEEPENTRY_WFI;LP_Entry_Cfg.STOPEntry_Cfg.STOPEntryPWR_STOPENTRY_WFI;LP_Entry_Cfg.RTC_Cfg.counterRTC_WAKEUPCLOCK_RTCCLK_DIV16; for(i0;isizeof(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.WakeUpEventUART_WAKEUP_ON_READDATA_NONEMPTY;} } 调用方式 首先 需要调用初始化函数 同时传入系统时钟初始化函数地址 Init_Enter_Low_PWR(SystemClock_Config);然后在进入低功耗前 需要对其进行配置 LP_Entry_Cfg.mode_flag2;LP_Entry_Cfg.STOPEntry_Cfg.STOPEntryPWR_STOPENTRY_WFI;LP_Entry_Cfg.SystemClock_Config_FxnSystemClock_Config;LP_Entry_Cfg.RTC_Cfg.EnableNotDisabletrue;LP_Entry_Cfg.RTC_Cfg.rtc_handlehrtc;LP_Entry_Cfg.RTC_Cfg.clockRTC_WAKEUPCLOCK_RTCCLK_DIV16;LP_Entry_Cfg.RTC_Cfg.counter300;Enter_Low_PWR(); 最后用Enter_Low_PWR();函数来进入低功耗 附录Cortex-M架构的SysTick系统定时器精准延时和MCU位带操作 SysTick系统定时器精准延时 延时函数 SysTick-LOAD中的值为计数值 计算方法为工作频率值/分频值 比如工作频率/1000 则周期为1ms 以ADuCM4050为例 #include ADuCM4050.hvoid 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 关闭系统定时器 } 其中的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 关闭系统定时器 }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 关闭系统定时器 }但是这种写法有个弊端 那就是输入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 关闭系统定时器 }将等待和关闭定时器语句去掉 在使用时加上判断即可变为阻塞 delay_ms(500); while ((SysTick-CTRL 0x00010000)0); SysTick-CTRL 0;在非阻塞状态下 可以提交定时器后 去做别的事情 然后再来等待 不过这样又有一个弊端 那就是定时器会自动重载 可能做别的事情以后 定时器跑过了 然后就要等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)(bitnum2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))#define GPIO0_ODR_Addr (ADI_GPIO0_BASE20) //0x40020014 #define GPIO0_IDR_Addr (ADI_GPIO0_BASE16) //0x40020010#define GPIO1_ODR_Addr (ADI_GPIO1_BASE20) //0x40020054 #define GPIO1_IDR_Addr (ADI_GPIO1_BASE16) //0x40020050#define GPIO2_ODR_Addr (ADI_GPIO2_BASE20) //0x40020094 #define GPIO2_IDR_Addr (ADI_GPIO2_BASE16) //0x40020090#define GPIO3_ODR_Addr (ADI_GPIO3_BASE20) //0x400200D4 #define GPIO3_IDR_Addr (ADI_GPIO3_BASE16) //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 总线函数 #include ADuCM4050.h #include adi_gpio.h #include GPIO.hvoid GPIO_OUT(unsigned int port,unsigned int pin,unsigned int flag) {switch(port){case 0:{switch(pin){case 0:if(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){adi_gpio_SetHigh((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT0),(ADI_GPIO_PIN_15));};break;default:pin0;break;}}break;case 1:{switch(pin){case 0:if(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){adi_gpio_SetHigh((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT1),(ADI_GPIO_PIN_15));};break;default:pin0;break;}}break;case 2:{switch(pin){case 0:if(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){adi_gpio_SetHigh((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT2),(ADI_GPIO_PIN_15));};break;default:pin0;break;}}break;case 3:{switch(pin){case 0:if(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){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(flag1){adi_gpio_SetHigh((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));}else{adi_gpio_SetLow((ADI_GPIO_PORT3),(ADI_GPIO_PIN_15));};break;default:pin0;break;}}break;default:port0;break;} }void GPIO_BUS_OUT(unsigned int port,unsigned int num) //num最大为0xffff {int i;for(i0;i16;i){GPIO_OUT(port,i,(numi)0x0001);} }void P0_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){P0_O(i)(numi)0x0001;} } unsigned int P0_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(P0_I(i)i)0xFFFF;}return num; }void P1_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){P1_O(i)(numi)0x0001;} } unsigned int P1_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(P1_I(i)i)0xFFFF;}return num; }void P2_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){P2_O(i)(numi)0x0001;} } unsigned int P2_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(P2_I(i)i)0xFFFF;}return num; }void P3_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){P3_O(i)(numi)0x0001;} } unsigned int P3_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(P3_I(i)i)0xFFFF;}return num; } 一、位带操作理论及实践 位带操作的概念其实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) 封装一下 #define PFout(x) *(volatile uint32_t *)(0x42000000 ((uint32_t )GPIOF-ODR – 0x40000000) *32 x*4) 现在 可以把通用部分封装成一个小定义 #define BITBAND(addr, bitnum) ((addr 0xF0000000)0x2000000((addr 0xFFFFF)5)(bitnum2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))那么 设置PF引脚的函数可以定义 #define GPIOF_ODR_Addr (GPIOF_BASE20) //0x40021414 #define GPIOF_IDR_Addr (GPIOF_BASE16) //0x40021410 #define PF_O(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出 #define PF_I(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入若使PF9输入输出则 PF_O(9)1; //输出高电平 uint8_t dat PF_I(9); //获取PF9引脚的值总线输入输出 void PF_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PF_O(i)(numi)0x0001;} } unsigned int PF_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PF_I(i)i)0xFFFF;}return num; }STM32的可用下面的函数 #ifndef __GPIO_H__ #define __GPIO_H__ #include stm32l496xx.h#define BITBAND(addr, bitnum) ((addr 0xF0000000)0x2000000((addr 0xFFFFF)5)(bitnum2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))#define GPIOA_ODR_Addr (GPIOA_BASE20) //0x40020014 #define GPIOB_ODR_Addr (GPIOB_BASE20) //0x40020414 #define GPIOC_ODR_Addr (GPIOC_BASE20) //0x40020814 #define GPIOD_ODR_Addr (GPIOD_BASE20) //0x40020C14 #define GPIOE_ODR_Addr (GPIOE_BASE20) //0x40021014 #define GPIOF_ODR_Addr (GPIOF_BASE20) //0x40021414 #define GPIOG_ODR_Addr (GPIOG_BASE20) //0x40021814 #define GPIOH_ODR_Addr (GPIOH_BASE20) //0x40021C14 #define GPIOI_ODR_Addr (GPIOI_BASE20) //0x40022014 #define GPIOA_IDR_Addr (GPIOA_BASE16) //0x40020010 #define GPIOB_IDR_Addr (GPIOB_BASE16) //0x40020410 #define GPIOC_IDR_Addr (GPIOC_BASE16) //0x40020810 #define GPIOD_IDR_Addr (GPIOD_BASE16) //0x40020C10 #define GPIOE_IDR_Addr (GPIOE_BASE16) //0x40021010 #define GPIOF_IDR_Addr (GPIOF_BASE16) //0x40021410 #define GPIOG_IDR_Addr (GPIOG_BASE16) //0x40021810 #define GPIOH_IDR_Addr (GPIOH_BASE16) //0x40021C10 #define GPIOI_IDR_Addr (GPIOI_BASE16) //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 #include GPIO.hvoid PA_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PA_O(i)(numi)0x0001;} } unsigned int PA_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PA_I(i)i)0xFFFF;}return num; }void PB_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PB_O(i)(numi)0x0001;} } unsigned int PB_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PB_I(i)i)0xFFFF;}return num; }void PC_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PC_O(i)(numi)0x0001;} } unsigned int PC_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PC_I(i)i)0xFFFF;}return num; }void PD_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PD_O(i)(numi)0x0001;} } unsigned int PD_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PD_I(i)i)0xFFFF;}return num; }void PE_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PE_O(i)(numi)0x0001;} } unsigned int PE_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PE_I(i)i)0xFFFF;}return num; }void PF_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PF_O(i)(numi)0x0001;} } unsigned int PF_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PF_I(i)i)0xFFFF;}return num; }void PG_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PG_O(i)(numi)0x0001;} } unsigned int PG_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PG_I(i)i)0xFFFF;}return num; }void PH_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PH_O(i)(numi)0x0001;} } unsigned int PH_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PH_I(i)i)0xFFFF;}return num; }void PI_BUS_O(unsigned int num) //输入值num最大为0xFFFF {int i;for(i0;i16;i){PI_O(i)(numi)0x0001;} } unsigned int PI_BUS_I(void) //输出值num最大为0xFFFF {unsigned int num;int i;for(i0;i16;i){numnum(PI_I(i)i)0xFFFF;}return num; } 二、如何判断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
http://www.pierceye.com/news/582206/

相关文章:

  • 沈阳自助建站模板网站建设想法
  • 湖南岳阳网站建设公司黄页顺企网怎样不让网站被收录
  • 有没有专门做翻译的网站安徽建设工程信息网招标公告
  • 保险咨询网站留电话中国十大网络公司排行榜
  • 领手工在家做的网站2019网页设计与实现论文
  • 兰州微信信息平台网站建设绍兴本地网站建设
  • 关于旅游网站策划书千锋前端培训多少钱
  • 温州网站建设结构做代练网站能备案
  • 零基础学习做网站第三方装修评估公司
  • 基础微网站开发动态网站彩票投注员做啥的
  • 西安做网站设计公司爱做网站免费版
  • 效果图网站接单重庆一般建一个网站需要多少钱
  • 网站建设征求意见稿辅料企业网站建设费用
  • 上海网站建设公司服务沅江网站制作
  • 公司网站开发费用计入什么科目虚拟主机怎么建网站
  • 天津网站建设技术网页设计与制作教程版徐洪亮课后答案
  • 旅游网站建设方案简介用asp做的网站打开页面很慢
  • 做影视网站 片源从哪里来做自媒体的上那些网站
  • 邢台网站开发百度云 做网站
  • 淘宝优惠劵网站建设wordpress主题 简洁
  • 自己做电影资源网站揭阳新闻最新消息
  • 北碚免费建站哪家做得好佛山网站建设设计
  • 怎么做网站拍卖的那种wordpress主题搜索图标
  • 三亚网站建设平台查数据的权威网站
  • html网站制作答辩ppt网站备份和备案的区别
  • 网站开发需要工具免费的ps软件
  • 常州网站建设优质商家重庆互联网怎么样
  • 做网站发广告动漫网页设计报告
  • 求职招聘网站建设投标书沈阳网站建设的公司哪家好
  • 做导航网站有发展吗南京企业网站制作哪家好