目录

23.01_WAKE-IO唤醒

测试步骤

P2.6设置为高电平之后进入休眠状态,P0.0输入高电平之后芯片唤醒。 注意:使用深度休眠时,不能使用调试读写变量(例如debug或lksscope),会导致芯片无法正常唤醒。

实测数据

开机
芯片电流 : 13.895975mA
触发芯片进入休眠状态
芯片电流 : 1.681591mA
使用IO唤醒芯片
芯片电流 : 1.681912mA

参考代码

23.01_WAKE_SleepGpioWake.c

/**
 * @brief WAKE-IO唤醒
 * @details
 * P2.6设置为高电平之后进入休眠状态,P0.0输入高电平之后芯片唤醒。
 * 注意:使用深度休眠时,不能使用调试读写变量(例如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"
volatile int ena = 0; // 1 进入休眠

/**
 * @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 (ena == 0)
        {
            if (GPIO_ReadInputDataBit(GPIO2, GPIO_Pin_6))
            {
                ena = 1;
                WAKE_SetWakeIO(WAKE_IO_P0_0, WAKE_TRIG_HIGH, 0, 1); // 设置为P0.0高电平触发芯片唤醒,不使用gpio滤波
                WAKE_FallSleep();                                   // 进入休眠状态
            }
        }
    }
}

void WAKE_IRQHandler()
{
}

使用到的库函数

库函数部分代码

#define AON0 ((AON_TypeDef *)AON_BASE)
#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 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
#define WAKE_FLT_EN 0x02
/**
 * @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 低功耗IO唤醒电平枚举成员
 */
typedef enum WAKE_TRIG_x {
    WAKE_TRIG_LOW = 0,
    WAKE_TRIG_HIGH = 1,
}} {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 设置唤醒IO
 * @param wakeIO 唤醒IO选择,必须打开对应的gpio输入使能(GPIO设置为输入模式),可以复用成任何模式
 * @param trigLevel 触发电平
 * @param flt 滤波使能
 * @param ena 使能标志
 */
void WAKE_SetWakeIO(uint32_t WAKE_IO_x, WAKE_TRIG_x trigLevel, uint32_t flt, uint8_t ena)
{
    if (ena)
    {
        AON_IO_WAKE_EN |= WAKE_IO_x;
        // 使能IO唤醒
        AON0->IO_WAKE_EN |= WAKE_IO_x;

        // 设置触发电平
        if (trigLevel == WAKE_TRIG_HIGH)
        {
            AON0->IO_WAKE_POL |= WAKE_IO_x;
        }
        else
        {
            AON0->IO_WAKE_POL &= ~WAKE_IO_x;
        }

        // 设置滤波使能
        if (flt == WAKE_FLT_EN)
        {
            AON0->PWR_CFG |= BIT1;
        }
        else
        {
            AON0->PWR_CFG &= ~BIT1;
        }
    }
    else
    {
        // 禁用IO唤醒
        AON0->IO_WAKE_EN &= ~WAKE_IO_x;
    }
}
/**
 * @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;
    }
}