在嵌入式系统的设计中,低功耗设计(Low-Power Design)是许多设计人员必须面对的问题,其原因在于嵌入式系统被广泛应用于便携式和移动性较强的产品中去,而这些产品不是一直都有充足的电源供应,往往是靠电池来供电,所以设计人员从每一个细节来考虑降低功率消耗,从而尽可能地延长电池使用时间。因此,大部分芯片都会有低功耗模式,以CW32L083为例,它就是一个32位低功耗微控制器。
一、芯片模式介绍
1.CW32L083工作模式
CW32L083 支持三种工作模式,由内嵌的电源管理模块自动完成电源的统一管理。三种工作模式是:
• 运行模式(Active mode)
• 休眠模式(Sleep mode)
• 深度休眠模式(DeepSleep mode)
电源上电后,系统自动进入运行模式。用户可通过软件程序,进入休眠或深度休眠两种低功耗运行状态;在低功耗运行状态时,可通过硬件中断触发唤醒机制,使系统返回到运行模式。
2.进入休眠模式或深度休眠模式
使用 M0+ 内核的 ARM 等待中断专用指令,WFI(Wait for Interrupt),配合 M0+ 内核的系统控制寄存器(SCR, System Control Register)的 SLEEPONEXIT 和 SLEEPDEEP 位域,可实现立即进入或退出(中断服务程序)时进 入休眠模式或深度休眠模式。
• 立即进入
执行 WFI 指令,MCU 将立即进入休眠模式(SLEEPDEEP 为 0 时)或深度休眠模式(SLEEPDEEP 为 1 时)
• 退出时进入
将 SLEEPONEXIT 位置 1,当退出最低优先级的中断服务程序后,MCU 会进入休眠模式(SLEEPDEEP 为 0 时) 或深度休眠模式(SLEEPDEEP 为 1 时),而不需执行 WFI 指令 。
在深度休眠模式下,系统将自动关闭高速时钟。如用户需要在深度休眠模式下使部分外设仍保持运行,则须在进入深度休眠模式前,启动相应的低速时钟并将该外设时钟设置为此低速时钟。
3.退出休眠模式或深度休眠模式
在休眠模式或深度休眠模式下,均可通过中断来唤醒 CPU,返回到运行模式。但是,值得注意的是,如果用户在中断服务程序中执行 WFI 命令进入休眠(包括深度休眠),则需要比此中断更高优先级的中断才能唤醒 CPU,因此,我们强烈建议用户在准备进入休眠前,应先处理完所有中断服务程序,并且清除所有中断请求和中断标志。
使用中断退出休眠模式,用户必须在进入休眠(包括深度休眠)前使能此中断的允许位。
中断唤醒退出深度休眠模式时,CPU 运行状态与退出休眠模式相同。
4.UART控制深度休眠模式
UART控制器工作在双时钟域下,支持在深度休眠模式下进行正常的数据收发,并通过接收完成中断唤醒 MCU回到运行模式。
如果设置了传输时钟 UCLK来源为低速时钟,当系统进入深度休眠模式后,高速时钟将停止,低速时钟保持运行,UART仍可以进行正常的数据收发(波特率仅支持 2400 bps、4800 bps 和 9600 bps)。 要实现深度休眠模式下使用 UART 唤醒功能,需在进入深度休眠模式之前使能 UART 接收完成中断(即设置 UARTx_IER.RC 为 1),数据接收完成时,接收完成中断将唤醒MCU恢复到运行模式。
如果设置了传输时钟 UCLK 来源为高速时钟,当系统进入深度休眠模式后,高速时钟会停止运行,UAR不会接收数据。此时,仍可通过GPIO中断唤醒 MCU,实现在深度休眠模式下接收数据,参考配置步骤如下:
步骤 1:使能 UARTx_RXD 对应引脚的 GPIO 下降沿中断;
步骤 2:设置 UARTx_CR1.START 为 1,选择 RXD 信号起始位判定方式为低电平;
步骤 3:使能 UART 接收(即设置 UARTx_CR1.RXEN 为 1);
步骤 4:进入深度休眠模式;
步骤 5:等待主机发送数据,产生 GPIO 下降沿中断,唤醒 MCU;
步骤 6:关闭 RXD 对应引脚的 GPIO 中断功能,等待 RXD 接收完成。
二、实例演示:UART深度休眠模式示例(传输时钟为LSI)
程序运行一段时间后进入深度休眠模式,PC发送数据可唤醒MCU,唤醒后UART轮询接收数据,并存储到TxRxBuffer缓冲区,UART接收到'\n'后不再接收数据,然后将TxRxBuffer缓冲区中的数据回传至PC。传输结束后,LED1闪烁5s,并再次进入深度休眠模式。
1.外设时钟使能
void RCC_Configuration(void)
{
InitTick(8000000); //复位后延时
SysTickDelay(1000);
RCC_HSI_Enable(RCC_HSIOSC_DIV6); //SYSCLK = HSI = 8MHz = HCLK = PCLK
RCC_LSI_Enable();
RCC_AHBPeriphClk_Enable(DEBUG_UART_GPIO_CLK | RCC_AHB_PERIPH_GPIOC, ENABLE);
DEBUG_UART_APBClkENx(DEBUG_UART_CLK, ENABLE); //外设时钟使能
}
2.配置GPIO
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
DEBUG_UART_AFTX; //UART TX RX 复用
DEBUG_UART_AFRX;
GPIO_InitStructure.Pins = DEBUG_UART_TX_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Init(DEBUG_UART_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.Pins = DEBUG_UART_RX_GPIO_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
GPIO_Init(DEBUG_UART_RX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.Pins = GPIO_PIN_3; //PC3 LED1
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Init(CW_GPIOC, &GPIO_InitStructure);
PC03_SETLOW();
}
3.配置UART
void UART_Configuration(void)
{
UART_InitTypeDef UART_InitStructure = {0};
UART_InitStructure.UART_BaudRate = UARTyz_BaudRate; // 波特率
UART_InitStructure.UART_Over = UART_Over_sp; // 专用采样
UART_InitStructure.UART_Source = UART_Source_LSI; // 传输时钟源LSI
UART_InitStructure.UART_UclkFreq = UARTyz_UclkFreq; // 传输时钟UCLK频率
UART_InitStructure.UART_StartBit = UART_StartBit_FE; // 起始位判定方式
UART_InitStructure.UART_StopBits = UART_StopBits_1; // 停止位长度
UART_InitStructure.UART_Parity = UART_Parity_No ; // 校验方式
UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx; // 发送/接收使能
UART_Init(DEBUG_UARTx, &UART_InitStructure);
}
4.配置低功耗模式
void PWR_Configuration(void)
{
PWR_InitTypeDef PWR_InitStructure = {0};//低功耗模式配置结构体指针
PWR_InitStructure.PWR_Sevonpend = PWR_Sevonpend_Disable;
PWR_InitStructure.PWR_SleepDeep = PWR_SleepDeep_Enable; //Deep Sleep使能
PWR_InitStructure.PWR_SleepOnExit = PWR_SleepOnExit_Disable;
PWR_Config(&PWR_InitStructure);// 低功耗模式配置
}
void PWR_GotoLpmMode(void)//进入睡眠模式
{
__WFI();
}
5.配置NVIC中断
void NVIC_Configuration(void)
{
NVIC_SetPriority(DEBUG_UART_IRQ, 0); //优先级,无优先级分组
NVIC_EnableIRQ(DEBUG_UART_IRQ); //UARTx中断使能
}
void UART2_UART5_IRQHandler(void)
{
if(UART_GetITStatus(CW_UART5, UART_IT_RC) != RESET)
{
UART_ClearITPendingBit(CW_UART5, UART_IT_RC);
}
}
6.发送8位数组
void UART_SendBuf_Polling(UART_TypeDef* UARTx, uint8_t *TxBuf, uint8_t TxCnt)
{
while(TxCnt)
{
UART_SendData_8bit(UARTx, *TxBuf);
while(UART_GetFlagStatus(UARTx, UART_FLAG_TXE) == RESET);
TxBuf++;
TxCnt--;
}
while(UART_GetFlagStatus(UARTx, UART_FLAG_TXBUSY) == SET);
}
7.接收8位数组
uint8_t UART_RecvBuf_Polling(UART_TypeDef* UARTx, uint8_t *RxBuf)
{
uint8_t RxCnt = 0;
RxBuf[RxCnt] = UART_ReceiveData_8bit(UARTx);
RxCnt++;
do
{
while(UART_GetFlagStatus(UARTx, UART_FLAG_RC) == RESET); //等待RC
UART_ClearFlag(UARTx, UART_FLAG_RC); //清RC
if(UART_GetFlagStatus(UARTx, UART_FLAG_PE|UART_FLAG_FE)) //ERROR: PE or FE
{
UART_ClearFlag(UARTx, UART_FLAG_PE|UART_FLAG_FE);
RxCnt = 0x00;
}
else
{
RxBuf[RxCnt] = UART_ReceiveData_8bit(UARTx);
RxCnt++;
}
}
while(RxBuf[RxCnt-1] != '\n');
return RxCnt;
}
8.主程序
int32_t main(void)
{
RCC_Configuration();//配置RCC
GPIO_Configuration();//配置GPIO
UART_Configuration();//配置UART
PWR_Configuration();//配置低功耗模式
NVIC_Configuration();//配置NVIC
InitTick(HCLKFREQ); //初始化SysTick
RCC_WAKEUPCLK_Config(RCC_SYSCTRL_WAKEUPCLKDIS); //DeepSleep唤醒时,保持原系统时钟来源
UART_SendString(DEBUG_UARTx, "\r\nCW32L083 UART DeepSleep mode LSE/LSI\r\n");
while(1)
{
//进入深度休眠模式
UART_SendString(DEBUG_UARTx, "\r\nEnter DeepSleep mode\r\n");
UART_SendString(DEBUG_UARTx, "\r\nPC send data to wake up MCU\r\n");
UART_ITConfig(DEBUG_UARTx, UART_IT_RC, ENABLE); //使能UARTx RC中断
PWR_GotoLpmMode();
UART_ITConfig(CW_UART5, UART_IT_RC, DISABLE); //失能UARTx RC中断
//唤醒后轮询收发
TxRxBufferSize = UART_RecvBuf_Polling(DEBUG_UARTx, TxRxBuffer);
UART_SendBuf_Polling(DEBUG_UARTx, TxRxBuffer, TxRxBufferSize);
for(int i = 0; i<10; i++) //闪灯
{
PC03_TOG();
SysTickDelay(500);
}
}
}
9.测试结果
结果显示,通过PC发送123456后唤醒MCU, 唤醒后UART轮询接收数据,并存储到TxRxBuffer缓冲区,UART接收到'\n'后不再接收数据,然后将TxRxBuffer缓冲区中的数据回传至PC收到123456。传输结束后,LED1闪烁5s,并再次进入深度休眠模式。
|