23.02_WAKE-定时唤醒
测试步骤
设置芯片为定时唤醒,唤醒时间为1S。 注意:使用深度休眠时,不能使用调试读写变量(例如debug或lksscope),会导致芯片无法正常唤醒。
实测数据
触发休眠后开始记录电流波形。
休眠前电流为14.412mA。
芯片电流 : 14.436193mA
参考代码
23.02_WAKE_SleepIwdgWake.c
/**
* @brief WAKE-定时唤醒
* @details
* 设置芯片为定时唤醒,唤醒时间为1S。
* 注意:使用深度休眠时,不能使用调试读写变量(例如debug或lksscope),会导致芯片无法正常唤醒。
*/
#include "lks32mc09x.h"
#include "lks32mc09x_adc.h"
#include "lks32mc09x_cmp.h"
#include "lks32mc09x_dac.h"
#include "lks32mc09x_gpio.h"
#include "lks32mc09x_lib.h"
#include "lks32mc09x_opa.h"
#include "lks32mc09x_reg.h"
#include "lks32mc09x_sys.h"
#include "lks32mc09x_tmp.h"
#include "lks32mc09x_wake.h"
/**
* @brief 主函数,用于测试HSI时钟输出及GPIO翻转
*/
int main(void)
{
// 打开所有模块,模拟实际应用场景
ADC_Enable(ADC0);
DAC_Enable(DAC_CHANNEL_0);
DAC_Enable(DAC_CHANNEL_1);
OPA_Enable(OPA_CHANNEL_0);
OPA_Enable(OPA_CHANNEL_1);
OPA_Enable(OPA_CHANNEL_2);
OPA_Enable(OPA_CHANNEL_3);
CMP_Enable(CMP_CHN_0);
CMP_Enable(CMP_CHN_1);
TMP_Enable();
GPIO_Enable();
GPIO_Config(GPIO0, GPIO_PinSource_0, GPIO_Mode_IN, GPIO_AF_GPIO);
GPIO_Config(GPIO2, GPIO_PinSource_6, GPIO_Mode_IN, GPIO_AF_GPIO);
while (1)
{
if (GPIO_ReadInputDataBit(GPIO2, GPIO_Pin_6))
{
WAKE_SetWakeInterval(IWDG_SECOND2IWDGCNT(2.0), IWDG_SECOND2IWDGCNT(1), 1); // 设置为休眠1秒后唤醒,如果没有唤醒,则2S后复位(复位功能需要先打开看门狗)
WAKE_FallSleep(); // 进入休眠状态
}
}
}
void WAKE_IRQHandler()
{
}
使用到的库函数
库函数部分代码
#define CMP_CHN_0 0
#define CMP_CHN_1 1
#define DAC_CHANNEL_0 0
#define DAC_CHANNEL_1 1
#define GPIO_AF_I2C 6
#define IWDG0 ((IWDG_TypeDef *)IWDG_BASE)
#define OPA_CHANNEL_0 0
#define OPA_CHANNEL_1 1
#define OPA_CHANNEL_2 2
#define OPA_CHANNEL_3 3
#define REG_RESET(reg,mask) reg &= ~(mask)
#define REG_SET(reg,mask) reg |= (mask)
#define REG_WRITE(reg,mask) reg = (mask)
#define SYS0 ((SYS_TypeDef *)(SYS_BASE))
#define SYS_MODULE_CMP BIT2
#define SYS_MODULE_GPIO BIT11
/**
* @brief GPIO功能配置结构体句柄
*/
typedef struct GPIO_InitTypeDef {
uint32_t GPIO_Pin;
GPIO_Mode_TypeDef GPIO_Mode;
GPIO_PuPd_TypeDef GPIO_PuPd;
uint32_t GPIO_PODEna;
uint32_t GPIO_PFLT;
};
/**
* @brief GPIO模式选择枚举
*/
typedef enum GPIO_Mode_TypeDef {
GPIO_Mode_IN = 0,
GPIO_Mode_OUT = 1,
GPIO_Mode_ANA = 2,
GPIO_Mode_IO = 3,
}} {enum_name};
/**
* @brief GPIO配置函数
* @param GPIOx: GPIO端口
* @param GPIO_PinSource: GPIO引脚
* @param mode: GPIO模式
* @param GPIO_AF_x: GPIO复用功能选择
*/
void GPIO_Config(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, GPIO_Mode_TypeDef mode, uint32_t GPIO_AF_x)
{
GPIO_PinAFConfig(GPIOx, GPIO_PinSource, GPIO_AF_x);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 1 << GPIO_PinSource;
GPIO_InitStructure.GPIO_Mode = mode;
if (GPIO_AF_x == GPIO_AF_I2C)
{
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
}
else
{
__NOP();
}
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
/**
* @brief DAC使能
* @param DAC_Channel DAC通道选择
*/
void DAC_Enable(uint8_t DAC_Channel)
{
DAC_Cmd(DAC_Channel, ENABLE);
}
/**
* @brief DAC通道时钟使能
* @param DAC_Channel DAC通道选择
* @param state 使能状态,1模块使能 0 模块关闭
*/
void DAC_Cmd(uint8_t DAC_Channel, uint8_t state)
{
SYS0->PROTECT = 0x7a83;
if (DAC_Channel == DAC_CHANNEL_0)
{
if (state)
{
SYS0->AFE_REG5 |= BIT1;
}
else
{
SYS0->AFE_REG5 &= ~BIT1;
}
}
else
{
__NOP();
}
if (DAC_Channel == DAC_CHANNEL_1)
{
if (state)
{
SYS0->AFE_REG5 |= BIT2;
}
else
{
SYS0->AFE_REG5 &= ~BIT2;
}
}
else
{
__NOP();
}
SYS0->PROTECT = 0;
}
/**
* @brief OPA 使能
* @param OPA_CHANNEL_x OPA通道
*/
void OPA_Enable(uint32_t OPA_CHANNEL_x)
{
SYS0->PROTECT = 0x7a83;
switch (OPA_CHANNEL_x)
{
case OPA_CHANNEL_0:
SYS0->AFE_REG1 |= BIT12;
break;
case OPA_CHANNEL_1:
SYS0->AFE_REG1 |= BIT13;
break;
case OPA_CHANNEL_2:
SYS0->AFE_REG1 |= BIT14;
break;
case OPA_CHANNEL_3:
SYS0->AFE_REG1 |= BIT15;
break;
}
SYS0->PROTECT = 0;
}
/**
* @brief gpio使能
*/
void GPIO_Enable(void)
{
SYS_ModuleClockCmd(SYS_MODULE_GPIO, 1);
}
/**
* @brief 使能ADC模块
* @param ADCx: ADC模块指针,指向ADC0或ADC1
*/
void ADC_Enable(ADC_TypeDef *ADCx)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
// 使能ADC模块时钟
REG_SET(SYS0->AFE_REG7, BIT8 | BIT9);
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 使能比较器
* @param CMP_CHN_x: 比较器通道选择,CMP_CHN_0, CMP_CHN_1
*/
void CMP_Enable(uint8_t CMP_CHN_x)
{
SYS_ModuleClockCmd(SYS_MODULE_CMP, ENABLE);
if (CMP_CHN_x == CMP_CHN_0)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG5 |= BIT6;
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
if (CMP_CHN_x == CMP_CHN_1)
{
SYS0->PROTECT = 0x7a83;
SYS0->AFE_REG5 |= BIT7;
SYS0->PROTECT = 0;
}
else
{
__NOP();
}
}
/**
* @brief 使能温度传感器
*/
void TMP_Enable(void)
{
SYS_WR_PROTECT = 0x7a83;
SYS_AFE_REG5 |= BIT3;
SYS_WR_PROTECT = 0;
}
/**
* @brief 初始化GPIO
* @param GPIOx GPIO模块指针
* @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
*/
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
{
// 使能GPIO时钟
SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);
// 配置引脚模式
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 打开输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
{
GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
}
else // GPIO_Mode_ANA
{
GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
}
if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
{
GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
}
else
{
GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
}
// 配置开漏使能
if (GPIO_InitStruct->GPIO_PODEna)
{
GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
}
// 配置滤波使能
if (GPIO_InitStruct->GPIO_PFLT)
{
GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
}
else
{
GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
}
}
/**
* @brief 初始化GPIO结构体为默认值
* @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
*/
void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
{
for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
{
((uint8_t *)GPIO_InitStruct)[i] = 0;
}
}
/**
* @brief 数字模块时钟使能
* @param nModule 模块编号
* @param state 使能或禁用状态
*/
void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
{
REG_WRITE(SYS0->PROTECT, 0x7a83);
if (state)
{
REG_SET(SYS0->CLK_FEN, nModule);
}
else
{
REG_RESET(SYS0->CLK_FEN, nModule);
}
REG_WRITE(SYS0->PROTECT, 0);
}
/**
* @brief 设置唤醒间隔时间
* @param rth 唤醒复位阈值,由于计数器计数阈值是从复位阈值开始计数的,所有唤醒阈值必须大于复位阈值
* @param wth 唤醒阈值
* @param ena 使能标志
*/
void WAKE_SetWakeInterval(uint32_t rth, uint32_t wth, uint8_t ena)
{
if (ena)
{
int32_t val;
IWDG0->CFG |= BIT4;
IWDG0->PSW = PSW_IWDG_PRE;
IWDG0->RTH = rth; // 设置复位阈值
val = rth - wth;
if (val < 0x100)
{
val = 0x100;
}
else
{
__NOP();
}
IWDG0->WTH = val; // 设置唤醒阈值
}
else
{
IWDG0->CFG &= ~BIT4;
}
}
/**
* @brief 读取指定GPIO引脚的输入数据位
* @param GPIOx GPIO模块指针
* @param GPIO_Pin 要读取的引脚
* @return 引脚的输入数据位状态
*/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
return (GPIOx->PDI & GPIO_Pin) ? 1 : 0;
}
/**
* @brief 进入低功耗模式,必须定义WAKE_IRQHandler函数,WAKE_IRQHandler内可以不做任何操作
*/
void WAKE_FallSleep(void)
{
int irqstate;
uint16_t sys_reg_0;
uint16_t sys_reg_1;
uint16_t sys_reg_2;
uint16_t sys_reg_3;
uint16_t sys_reg_4;
uint16_t sys_reg_5;
uint16_t sys_reg_7;
uint32_t sys_clk_fen;
uint32_t sys_clk_cfg;
uint32_t nvic_state; // 各个使能的状态
irqstate = __get_PRIMASK(); // 保存全局中断状态
__disable_irq(); // 关闭全局中断
SYS_WR_PROTECT = 0x7a83; /*开启寄存器写使能*/
sys_reg_0 = SYS_AFE_REG0;
sys_reg_1 = SYS_AFE_REG1;
sys_reg_2 = SYS_AFE_REG2;
sys_reg_3 = SYS_AFE_REG3;
sys_reg_4 = SYS_AFE_REG4;
sys_reg_5 = SYS_AFE_REG5;
sys_reg_7 = SYS_AFE_REG7;
sys_clk_fen = SYS_CLK_FEN;
sys_clk_cfg = SYS_CLK_CFG;
SYS_AFE_REG0 = 0;
SYS_AFE_REG1 = BIT11 | BIT10; // 关闭BGP和RCH
SYS_AFE_REG2 = 0;
SYS_AFE_REG3 = 0;
SYS_AFE_REG4 = 0;
SYS_AFE_REG5 = (sys_reg_5 & (BIT8 | BIT9)); // 保持复位电压阈值不变
SYS_AFE_REG7 = 0;
SYS_CLK_FEN = 0;
SYS_CLK_CFG = 0x0;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_WR_PROTECT = 0x0;
nvic_state = NVIC->ICER[0];
NVIC->ICER[0] = 0xffffffff; // 关闭所有中断
NVIC_EnableIRQ(WAKE_IRQn); /* 使能WAKE中断 */
NVIC_SetPriority(WAKE_IRQn, 0); /* 配置WAKE中断优先级 */
IWDG_PSW = 0xA6B4;
IWDG_CLR = 0x798D;
SCB->SCR |= (1UL << 2);
__enable_irq();
__WFI(); // 使用WFI指令进入休眠模式
// 休眠后程序会停在这里,唤醒后会执行WAKE_IRQHandler中断,随后从这里继续执行
__disable_irq();
SYS_WR_PROTECT = 0x7a83; /*开启寄存器写使能*/
SYS_AFE_REG0 = sys_reg_0;
SYS_AFE_REG1 = sys_reg_1;
SYS_AFE_REG2 = sys_reg_2;
SYS_AFE_REG3 = sys_reg_3;
SYS_AFE_REG4 = sys_reg_4;
SYS_AFE_REG5 = sys_reg_5;
SYS_AFE_REG7 = sys_reg_7;
SYS_CLK_FEN = sys_clk_fen;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_CLK_CFG = sys_clk_cfg;
{
int cnt;
for (cnt = 0; cnt < 4000; cnt++)
{
__NOP();
}
}
SYS_WR_PROTECT = 0x0;
__set_PRIMASK(irqstate); // 还原中断状态
NVIC->ISER[0] = nvic_state;
}
/**
* @brief 配置GPIO引脚复用功能
* @param GPIOx GPIO模块指针
* @param GPIO_PinSource 引脚源
* @param GPIO_AF 复用功能选择
*/
void GPIO_PinAFConfig(GPIO_TypeDef *GPIOx, uint32_t GPIO_PinSource, uint32_t GPIO_AF)
{
uint8_t offset;
uint8_t pins = GPIO_PinSource >> 2;
offset = ((GPIO_PinSource & 0x3) * 4);
switch (pins)
{
case 0:
GPIOx->F3210 = (GPIOx->F3210 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 1:
GPIOx->F7654 = (GPIOx->F7654 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 2:
GPIOx->FBA98 = (GPIOx->FBA98 & ~(0xf << offset)) | (GPIO_AF << offset);
break;
case 3:
GPIOx->FFEDC = (GPIOx->FFEDC & ~(0xf << offset)) | (GPIO_AF << offset);
break;
default:
break;
}
}