QN902x的低功耗模式分析
系统最终进入何种状态,关键看懂这种表就OK了。
USR 和 BLE只要有一个是Active的, 系统就是Active
进入Deep Sleep的条件是:BLE Sleep, USR Deep Sleep
进入Sleep的条件:BLE Sleep,USR sleep
其他为IDLE
Main 函数里有这一个while(1)循环
while(1)
{
ke_schedule(); // Run Scheduler,检查消息队列,执行消息投递
// Checks for sleep have to be done with interrupt disabled
GLOBAL_INT_DISABLE_WITHOUT_TUNER();
// Check whether the chip can enters sleep mode
//
// Chip enter sleep condition:
// +--------+--------+--------+--------+--------+
// | USR | | | | |
// | BLE | ACTIVE | IDLE | SLEEP | DEEP |
// +--------+--------+--------+--------+--------+
// | ACTIVE | active | active | active | active |
// | IDLE | active | idle | idle | idle |
// | SLEEP | active | idle | sleep | deep |
// +--------+--------+--------+--------+--------+
// Obtain the status of the user program
usr_sleep_st = usr_sleep();
// If the user program can be sleep or deep sleep then check ble status
if(usr_sleep_st != PM_ACTIVE)
{
// Obtain the status of ble sleep mode
ble_sleep_st = ble_sleep(usr_sleep_st);
// Check if the processor clock can be gated
if(((ble_sleep_st == PM_IDLE) || (usr_sleep_st == PM_IDLE))
&& (ble_sleep_st != PM_ACTIVE))
{
enter_sleep(SLEEP_CPU_CLK_OFF,
WAKEUP_BY_ALL_IRQ_SOURCE,
NULL);
}
// Check if the processor can be power down
else if((ble_sleep_st == PM_SLEEP) && (usr_sleep_st == PM_SLEEP))
{
enter_sleep(SLEEP_NORMAL,
(WAKEUP_BY_OSC_EN | WAKEUP_BY_GPIO),
sleep_cb);
}
// Check if the system can be deep sleep
else if((ble_sleep_st == PM_SLEEP) && (usr_sleep_st == PM_DEEP_SLEEP))
{
enter_sleep(SLEEP_DEEP,
WAKEUP_BY_GPIO,
sleep_cb);
}
}
// Checks for sleep have to be done with interrupt disabled
GLOBAL_INT_RESTORE_WITHOUT_TUNER();
}
- 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
ke_schedule(),用来Run Scheduler, 它检查消息队列,执行消息投递
GLOBAL_INT_DISABLE_WITHOUT_TUNER()的定义如下,它是和GLOBAL_INT_RESTORE_WITHOUT_TUNER配合一前一后一起使用的:
#define GLOBAL_INT_DISABLE_WITHOUT_TUNER() \
do { \
uint32_t int_restore; \
int_restore = NVIC->ISER[0]; \
NVIC->ICER[0] = 0x1fffffff;
/** @brief Restore interrupts from the previous global disable.
* @sa GLOBAL_INT_DISABLE
*/
#define GLOBAL_INT_RESTORE_WITHOUT_TUNER() \
NVIC->ISER[0] = int_restore; \
} while(0)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
刚开始时关闭所有中断,最后是恢复原来的中断使能状态。
usr_sleep_st = usr_sleep(); 是用来USR的status
看看这两个语句(这两句位于while(1)之前的初始化部分)
sleep_init();
wakeup_by_sleep_timer(__32K_TYPE);
- 1
- 2
/**
****************************************************************************************
* @brief Init sleep power down modules
* @description
* This function is used to init MCU sleep mode.
*****************************************************************************************
*/
void sleep_init(void)
{
// --------------------------------------------
// sleep
// --------------------------------------------
//23 : PD_XTAL32
//10 : PD_RCO
// 7 : PD_MEM7
// 6 : PD_MEM6
// 5 : PD_MEM5
// 4 : PD_MEM4
// 3 : PD_MEM3
// 2 : PD_MEM2
// 1 : PD_MEM1
// 0 : PL_VREG_D
sleep_env.retention_modules |= QN_MEM_RETENTION;
// power down all module in sleep except retention modules
syscon_SetPGCR0WithMask(QN_SYSCON, 0xF7FFFCFE, (0xFFFFFC00 | QN_MEM_UNRETENTION));
// power down all unretention memory all the time.
// if you want to use the unretention memory in the active mode, remove the following snippet.
syscon_SetPGCR1WithMask(QN_SYSCON,
(SYSCON_MASK_DIS_MEM1
| SYSCON_MASK_DIS_MEM2
| SYSCON_MASK_DIS_MEM3
| SYSCON_MASK_DIS_MEM4
| SYSCON_MASK_DIS_MEM5
| SYSCON_MASK_DIS_MEM6
| SYSCON_MASK_DIS_MEM7),
QN_MEM_UNRETENTION);
}
- 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
syscon_SetPGCR0WithMask(QN_SYSCON, 0xF7FFFCFE, (0xFFFFFC00 | QN_MEM_UNRETENTION)); 这句话是用来控制在sleep mode下memory power是否需要打开,0表示 switch on,1表示switch off。
syscon_SetPGCR1WithMask(QN_SYSCON,
(SYSCON_MASK_DIS_MEM1
| SYSCON_MASK_DIS_MEM2
| SYSCON_MASK_DIS_MEM3
| SYSCON_MASK_DIS_MEM4
| SYSCON_MASK_DIS_MEM5
| SYSCON_MASK_DIS_MEM6
| SYSCON_MASK_DIS_MEM7),
QN_MEM_UNRETENTION);
是在正常模式下是否switch off。
/// Memory retention
#if (defined(CFG_MEM_RETENTION))
#define QN_MEM_RETENTION CFG_MEM_RETENTION
#define QN_MEM_UNRETENTION (~(CFG_MEM_RETENTION) & 0xfe)
#else
#define QN_MEM_RETENTION (MEM_BLOCK1 | MEM_BLOCK2 | MEM_BLOCK3 | MEM_BLOCK4 | MEM_BLOCK5 | MEM_BLOCK6 | MEM_BLOCK7)
#define QN_MEM_UNRETENTION 0
#endif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
#define CFG_MEM_RETENTION (MEM_BLOCK1 | MEM_BLOCK2 | MEM_BLOCK6 | MEM_BLOCK7)
- 1
/**
*
* @param *SYSCON SYSCON Pointer
* @param value The value to set to PGCR0 register
*
* @brief outputs specified value to PGCR0 register
*
* | 31 : SEL_PD
* | 30 : PD_OSC
* | 29 : PD_BG
* | 28 : PD_V2I
* | 27 : PD_BUCK
* | 26 : PD_VREG_A
* | 25 : PD_VREG_D
* | 24 : PD_XTAL
* | 23 : PD_XTAL32
* | 22 : PD_REF_PLL_B0
* | 22 : DIV_RST_SYNC_B1
* | 21 : PD_LO_VCO
* | 20 : PD_LO_PLL
* | 19 : PD_PA
* | 18 : PD_LNA
* | 17 : PD_LNA_PKDET
* | 16 : PD_MIXER
* | 15 : PD_PPF_PKDET
* | 14 : PD_PPF
* | 13 : PD_RX_PKDET
* | 12 : PD_RX_ADC
* | 11 : PD_SAR_ADC
* | 10 : PD_RCO
* | 9 : BOND_EN
* | 8 : RSVD
* | 7 : PD_MEM7
* | 6 : PD_MEM6
* | 5 : PD_MEM5
* | 4 : PD_MEM4
* | 3 : PD_MEM3
* | 2 : PD_MEM2
* | 1 : PD_MEM1
* | 0 : PL_VERG_D
*/
__STATIC_INLINE void syscon_SetPGCR0(QN_SYSCON_TypeDef *SYSCON, uint32_t value)
{
__wr_reg((uint32_t)&SYSCON->PGCR0, value);
}
__STATIC_INLINE void syscon_SetPGCR0WithMask(QN_SYSCON_TypeDef *SYSCON, uint32_t mask, uint32_t value)
{
__wr_reg_with_msk((uint32_t)&SYSCON->PGCR0, mask, value);
}
- 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
/**
*
* @param *SYSCON SYSCON Pointer
* @param value The value to set to PGCR1 register
*
* @brief outputs specified value to PGCR1 register
*
* | 31 : VDD_RCO_SET
* | 30 : DIS_OSC
* | 29 : DIS_BG
* | 28 : DIS_V2I
* | 27 : DIS_BUCK
* | 26 : DIS_VREG_A
* | 25 : DIS_VREG_D
* | 24 : DIS_XTAL
* | 23 : DIS_XTAL32
* | 22 : DIS_REF_PLL
* | 21 : DIS_LO_VCO
* | 20 : DIS_LO_PLL
* | 19 : DIS_PA
* | 18 : DIS_LNA
* | 17 : DIS_LNA_PKDET
* | 16 : DIS_MIXER
* | 15 : DIS_PPF_PKDET
* | 14 : DIS_PPF
* | 13 : DIS_RX_PKDET
* | 12 : DIS_RX_ADC
* | 11 : DIS_SAR_ADC
* | 10 : DIS_RCO
* | 9 : RSVD
* | 8 : RSVD
* | 7 : DIS_MEM7
* | 6 : DIS_MEM6
* | 5 : DIS_MEM5
* | 4 : DIS_MEM4
* | 3 : DIS_MEM3
* | 2 : DIS_MEM2
* | 1 : DIS_MEM1
* | 0 : DIS_SAR_BUF
*/
__STATIC_INLINE void syscon_SetPGCR1(QN_SYSCON_TypeDef *SYSCON, uint32_t value)
{
__wr_reg((uint32_t)&SYSCON->PGCR1, value);
}
__STATIC_INLINE void syscon_SetPGCR1WithMask(QN_SYSCON_TypeDef *SYSCON, uint32_t mask, uint32_t value)
{
__wr_reg_with_msk((uint32_t)&SYSCON->PGCR1, mask, value);
}
- 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
/// Memory block
enum MEM_BLOCK
{
MEM_BLOCK0 = 0, /*!< Memory Block1: 0K ~ 8K */
MEM_BLOCK1 = (1 << 1), /*!< Memory Block1: 8K ~ 16K */
MEM_BLOCK2 = (1 << 2), /*!< Memory Block1: 16K ~ 24K */
MEM_BLOCK3 = (1 << 3), /*!< Memory Block1: 24K ~ 32K */
MEM_BLOCK4 = (1 << 4), /*!< Memory Block1: 32K ~ 40K */
MEM_BLOCK5 = (1 << 5), /*!< Memory Block1: 40K ~ 48K */
MEM_BLOCK6 = (1 << 6), /*!< Memory Block1: 48K ~ 56K */
MEM_BLOCK7 = (1 << 7), /*!< Memory Block1: 56K ~ 64K */
MEM_ALL = (0xFE) /*!< Memory Block1: 56K ~ 64K */
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
64K SRAM被分为了8个bank:bank0-bank7
bank6 bank7 是给BLE Stack用的,Bank0-Bank5 是用户使用的。
下面看一下usr_sleep的具体代码实现:
#ifdef BLE_PRJ
/**
****************************************************************************************
* @brief Check application whether to enter sleep mode
* @return sleep allowed status
****************************************************************************************
*/
int usr_sleep(void)
{
int32_t rt;
rt = sleep_get_pm(); // 获取允许的低功耗状态,这个结合sleep_set_pm()函数看
// If the BLE timer queue is not NULL or BLE event is exist, prevent entering into DEEPSLEEP mode
if(rt == PM_DEEP_SLEEP &&
(!ke_timer_empty() || !ble_evt_empty()))
{
rt = PM_SLEEP;
}
// Check Device status
if((rt >= PM_SLEEP)
&& dev_get_bf())
{
// If any devices are still working, the chip cann't enter into SLEEP/DEEPSLEEP mode.
rt = PM_IDLE;
}
if ((rt >= PM_SLEEP) && (!gpio_sleep_allowed()))
{
return PM_ACTIVE;
}
#if ACMP_WAKEUP_EN == TRUE
if ((rt >= PM_SLEEP) && (!acmp_sleep_allowed()))
{
return PM_ACTIVE;
}
#endif
#if QN_DBG_PRINT
int uart_tx_st = uart_check_tx_free(QN_DEBUG_UART);
if((rt >= PM_SLEEP) && (uart_tx_st == UART_TX_BUF_BUSY))
{
rt = PM_IDLE;
}
else if(uart_tx_st == UART_LAST_BYTE_ONGOING)
{
return PM_ACTIVE; // If CLOCK OFF & POWER DOWN is disabled, return immediately
}
#endif
#if QN_EACI
if ((rt >= PM_SLEEP) &&
( (eaci_env.tx_state!=EACI_STATE_TX_IDLE) // Check EACI UART TX status
|| (eaci_env.rx_state!=EACI_STATE_RX_START)) ) // Check EACI UART RX status
{
rt = PM_IDLE;
}
int tx_st = 0;
#if (defined(CFG_HCI_UART))
tx_st = uart_check_tx_free(QN_HCI_PORT);
if ((rt >= PM_SLEEP) && (tx_st == UART_TX_BUF_BUSY))
{
rt = PM_IDLE;
}
else if (tx_st == UART_LAST_BYTE_ONGOING)
{
return PM_ACTIVE; // If CLOCK OFF & POWER DOWN is disabled, return immediately
}
#elif (defined(CFG_HCI_SPI))
tx_st = spi_check_tx_free(QN_HCI_PORT);
if ((rt >= PM_SLEEP) && (tx_st == SPI_TX_BUF_BUSY))
{
rt = PM_IDLE;
}
else if (tx_st == SPI_LAST_BYTE_ONGOING)
{
return PM_ACTIVE; // If CLOCK OFF & POWER DOWN is disabled, return immediately
}
#endif
#endif
#if !(QN_32K_RCO)
// wait for 32k xtal ready
if(syscon_GetBLESR(QN_SYSCON) & SYSCON_MASK_CLK_XTAL32_RDY)
{
// disable schmitt trigger in 32.768KHz buffer
syscon_SetIvrefX32WithMask(QN_SYSCON, SYSCON_MASK_X32SMT_EN, MASK_DISABLE);
// Set 32.768KHz xtal to normal current
syscon_SetIvrefX32WithMask(QN_SYSCON, SYSCON_MASK_X32ICTRL, 16);
}
else if(rt > PM_ACTIVE)
{
rt = PM_ACTIVE;
}
#endif
return rt;
}
#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
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
下面实际测试下QN902x的功耗,使用QPPS工程,由于手上没有专门的功耗测试仪器,用万用表简单测试下。
修改下QPPS工程,将GAP_ADV_FAST_INTV1和GAP_ADV_FAST_INTV2的值做下改动,
#define GAP_ADV_FAST_INTV1 0x00A0 // 100ms
#define GAP_ADV_FAST_INTV2 0x00A0 // 100ms
- 1
- 2
- 3
快速广播间隔设置为100ms,测得deep sleep功耗为2.8uA,
广播的平均电流为172.6uA
文章来源: blog.csdn.net,作者:TopSemic嵌入式,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/wangwenxue1989/article/details/51682400
- 点赞
- 收藏
- 关注作者
评论(0)