[笔记]Freescal Cortex-M4 休眠模式备忘录
本帖最后由 Gorgon_Meducer 于 2012-12-10 20:25 编辑版权图片,引用前请注明出处
/***************************************************************************
* Copyright(C)2009-2012 by Gorgon Meducer<Embedded_zhuoran@hotmail.com> *
* *
* This program is free software; you can redistribute it and/or modify*
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA02111-1307, USA. *
***************************************************************************/
#ifndef __DRIVER_COMMON_SLEEP_H__
#define __DRIVER_COMMON_SLEEP_H__
/*============================ INCLUDES ======================================*/
#include ".\app_cfg.h"
/*============================ MACROS ========================================*/
#ifndef SLEEP_REQ_SET
#warning No defined sleep request set: SLEEP_REQ_SET
#endif
#ifndef SLEEP_REQ_COUNT
#warning No defined sleep request number: SLEEP_REQ_COUNT
#define SLEEP_REQ_COUNT (1u)
#endif
#if SLEEP_REQ_COUNT <= 8
# define SLEEP_REQ_WORD_COUNT 1
#elif SLEEP_REQ_COUNT <= 16
# define SLEEP_REQ_WORD_COUNT 2
#elif SLEEP_REQ_COUNT <= 24
# define SLEEP_REQ_WORD_COUNT 3
#elif SLEEP_REQ_COUNT <= 32
# define SLEEP_REQ_WORD_COUNT 4
#else
#error It dose not make any sense that there are more than 32 vote candidates!
#endif
/*============================ MACROFIED FUNCTIONS ===========================*/
//! brief add a new sleep request item to SLEEP_REQ_SET
#define ADD_SLEEP_REQ(__NAME) uint32_t __NAME : 4;
//! brief macro used to request a sleep level
#define REQ_SLEEP(__AGENT, __LEVEL) \
do {\
SAFE_ATOM_CODE (\
g_tSleepRequests.tMembers.__AGENT = (__LEVEL) \
) \
} while(false)
#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
//! \brief macro used to reques a sleep level without updating run mode
#define REQ_RUN_AS(__AGENT, __LEVEL) \
do {\
SAFE_ATOM_CODE (\
g_tSleepRequests.tMembers.__AGENT = (__LEVEL) \
) \
k20_core_try_to_sleep();\
} while(false)
#else
#define REQ_RUN_AS(__AGENT, __LEVEL) REQ_SLEEP(__AGENT, __LEVEL)
#endif
/*============================ TYPES =========================================*/
//! \name core rum mode
//! @{
typedef enum {
NORMAL_RUN_MODE = 1, //!< normal run mode: support full speed
LOW_POWER_RUN_MODE = 2 //!< low power run mode: support 4M max clock
}run_mode_t;
//! @}
//! \name sleep mode
//! @{
typedef enum {
SLEEP_KEEP_CURRENT_RUN_MODE = 0, //!< keep current run mode
SLEEP_RUN = 1, //!< normal run mode
SLEEP_VLP_RUN = 2, //!< very low power run mode
SLEEP_NONE = 3, //!< none sleep mode
SLEEP_WAIT = 4, //!< wait mode
SLEEP_VLP_WAIT = 5, //!< very low power wait mode
SLEEP_STOP = 6, //!< stop mode
SLEEP_VLP_STOP = 7, //!< very low power stop mode
#if CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
SLEEP_LL_STOP = 8, //!< low leakage stop mode
SLEEP_DEEPEST_RECOVERABLE_SLEEP = 8, //!< deepest recoverable sleep mode
#else
SLEEP_DEEPEST_RECOVERABLE_SLEEP = 7,
#endif
#if CORE_SLEEP_VERY_LOW_LEAKAGE_STOP == ENABLED
SLEEP_VLL_STOP_LV3 = 9, //!< very low leakage stop mode 3
SLEEP_VLL_STOP_LV2 = 10, //!< very low leakage stop mode 2
SLEEP_VLL_STOP_LV1 = 11, //!< very low leakage stop mode 1
SLEEP_VLL_STOP_LV0 = 12, //!< very low leakage stop mode 0
SLEEP_DEEPEST_SLEEP = 12,
#elif CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
SLEEP_DEEPEST_SLEEP = 8,
#else
SLEEP_DEEPEST_SLEEP = 7,
#endif
SLEEP_DO_NOT_CARE = 0x0F //!< don't care about the sleep mode
}sleep_mode_t;
//! @}
/*============================ GLOBAL VARIABLES ==============================*/
//! \brief sleep request set
extern volatile union {
uint32_t wRequests; //!< sleep requests
#ifdef SLEEP_REQ_SET
struct {
SLEEP_REQ_SET
}tMembers;
#endif
} g_tSleepRequests;
/*============================ LOCAL VARIABLES ===============================*/
/*============================ PROTOTYPES ====================================*/
/*! \brief initialize sleep service
*! \param none
*! \return none
*/
extern void sleep_init(void);
/*! \brief enter specified sleep mode directly
*! \param tMode target sleep mode
*! \return access result
*/
extern vsf_err_t k20_core_sleep(sleep_mode_t tMode);
/*! \brief try to enter a voted sleep mode
*! \param none
*! \return access result
*/
extern vsf_err_t k20_core_try_to_sleep(void);
#endif
/* EOF */
sleep.c
/***************************************************************************
* Copyright(C)2009-2012 by Gorgon Meducer<Embedded_zhuoran@hotmail.com> *
* *
* This program is free software; you can redistribute it and/or modify*
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation; either version 2 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA02111-1307, USA. *
***************************************************************************/
/*============================ INCLUDES ======================================*/
#include ".\app_cfg.h"
/*============================ MACROS ========================================*/
#ifndef SLEEP_REQ_SET
#warning No defined sleep request set: SLEEP_REQ_SET
#endif
#ifndef SLEEP_REQ_COUNT
#warning No defined sleep request number: SLEEP_REQ_COUNT
#define SLEEP_REQ_COUNT (1u)
#endif
#if SLEEP_REQ_COUNT <= 8
# define SLEEP_REQ_WORD_COUNT 1
#elif SLEEP_REQ_COUNT <= 16
# define SLEEP_REQ_WORD_COUNT 2
#elif SLEEP_REQ_COUNT <= 24
# define SLEEP_REQ_WORD_COUNT 3
#elif SLEEP_REQ_COUNT <= 32
# define SLEEP_REQ_WORD_COUNT 4
#else
#error It dose not make any sense that there are more than 32 vote candidates!
#endif
/*============================ MACROFIED FUNCTIONS ===========================*/
//! brief add a new sleep request item to SLEEP_REQ_SET
#define ADD_SLEEP_REQ(__NAME) uint32_t __NAME : 4;
#define __REQ_INIT(__COUNT, __VALUE) (__VALUE),
/*============================ TYPES =========================================*/
//! \name core rum mode
//! @{
typedef enum {
NORMAL_RUN_MODE = 1, //!< normal run mode: support full speed
LOW_POWER_RUN_MODE = 2 //!< low power run mode: support 4M max clock
}run_mode_t;
//! @}
//! \name sleep mode
//! @{
typedef enum {
SLEEP_KEEP_CURRENT_RUN_MODE = 0, //!< keep current run mode
SLEEP_RUN = 1, //!< normal run mode
SLEEP_VLP_RUN = 2, //!< very low power run mode
SLEEP_NONE = 3, //!< none sleep mode
SLEEP_WAIT = 4, //!< wait mode
SLEEP_VLP_WAIT = 5, //!< very low power wait mode
SLEEP_STOP = 6, //!< stop mode
SLEEP_VLP_STOP = 7, //!< very low power stop mode
#if CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
SLEEP_LL_STOP = 8, //!< low leakage stop mode
SLEEP_DEEPEST_RECOVERABLE_SLEEP = 8, //!< deepest recoverable sleep mode
#else
SLEEP_DEEPEST_RECOVERABLE_SLEEP = 7,
#endif
#if CORE_SLEEP_VERY_LOW_LEAKAGE_STOP == ENABLED
SLEEP_VLL_STOP_LV3 = 9, //!< very low leakage stop mode 3
SLEEP_VLL_STOP_LV2 = 10, //!< very low leakage stop mode 2
SLEEP_VLL_STOP_LV1 = 11, //!< very low leakage stop mode 1
SLEEP_VLL_STOP_LV0 = 12, //!< very low leakage stop mode 0
SLEEP_DEEPEST_SLEEP = 12,
#elif CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
SLEEP_DEEPEST_SLEEP = 8,
#else
SLEEP_DEEPEST_SLEEP = 7,
#endif
SLEEP_DO_NOT_CARE = 0x0F //!< don't care about the sleep mode
}sleep_mode_t;
//! @}
#define SLEEP_MODE_RUN _BV(0)
#define SLEEP_MODE_STOP _BV(1)
#define SLEEP_MODE_VLPR _BV(2)
#define SLEEP_MODE_VLPW _BV(3)
#define SLEEP_MODE_VLPS _BV(4)
#define SLEEP_MODE_LLS _BV(5)
#define SLEEP_MODE_VLLS _BV(6)
/*============================ GLOBAL VARIABLES ==============================*/
//! \brief sleep request set
volatile union {
uint32_t wRequests; //!< sleep requests
#ifdef SLEEP_REQ_SET
struct {
SLEEP_REQ_SET
}tMembers;
#endif
} g_tSleepRequests = { MREPEAT(SLEEP_REQ_WORD_COUNT, __REQ_INIT, 0xFFFFFFFF) };
/*============================ LOCAL VARIABLES ===============================*/
/*============================ PROTOTYPES ====================================*/
#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
static vsf_err_t try_to_run_as(sleep_mode_t tVoteResult);
#endif
static sleep_mode_t sleep_vote(sleep_mode_t tMode);
/*============================ IMPLEMENTATION ================================*/
/*! \brief initialize sleep service
*! \param none
*! \return none
*/
void sleep_init(void)
{
/*Enable all operation modes because this is a write once register*/
SMC_PMPROT = (
#if CORE_SLEEP_VERY_LOW_LEAKAGE_STOP == ENABLED
SMC_PMPROT_AVLLS_MASK |
#endif
#if CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
SMC_PMPROT_ALLS_MASK|
#endif
SMC_PMPROT_AVLP_MASK);
}
#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
/*! \brief enter very low power run mode
*! \param none
*! \retval true succeed
*! \retval false failed
*/
static bool enter_very_low_power_run_mode(void)
{
/*! you need not check current run mode here before doing anything */
#ifdef ON_ENTERING_LOW_POWER_RUN_MODE
/*! \note raise entering low power run mode event, you can change system
clock setting in the event handler*/
ON_ENTERING_LOW_POWER_RUN_MODE
#endif
/*! try to enter very low power run mode */
SMC_PMCTRL = (SMC_PMCTRL & ~SMC_PMCTRL_RUNM_MASK) | SMC_PMCTRL_RUNM(2);
/*! wait REGONS cleared */
while((PMC_REGSC & PMC_REGSC_REGONS_MASK));
#ifdef ON_CONFIRM_LOW_POWER_RUN_MODE
/*! \note raise confirm low power run mode event, you can do necessary work
here to handle the possible failure or initialize peripherals.*/
ON_CONFIRM_LOW_POWER_RUN_MODE(
(SLEEP_MODE_VLPR == (SMC_PMSTAT & SMC_PMSTAT_PMSTAT_MASK)) );
#endif
return true;
}
/*! \brief enter normal run mode
*! \param none
*! \return none
*/
static void enter_normal_run_mode(void)
{
/*! you need not check current run mode here before doing anything */
#ifdef ON_LEAVING_LOW_POWER_RUN
/*! \note raise leaving low power run mode event, you can change system
clock setting here in the event handler */
ON_LEAVING_LOW_POWER_RUN
#endif
/*! enter normal run mode */
SMC_PMCTRL &= ~SMC_PMCTRL_RUNM_MASK;
/*! wait REGONS cleared */
while((PMC_REGSC & PMC_REGSC_REGONS_MASK));
#ifdef ON_ENTER_NORMAL_RUN_MODE
/*! \note raise enter normal run mode event, you can initialize peripherals
here. */
ON_ENTER_NORMAL_RUN_MODE
#endif
}
#endif
/*! \brief enter specified sleep mode
*! \param chMode sleep mode
*! \return none
*/
static void enter_sleep_mode(vsf_uint8_t chMode)
{
const static uint8_t c_chMode[] = {
{SMC_PMCTRL_STOPM(0x00), 0}, //!< 3: wait
{SMC_PMCTRL_STOPM(0x00), 0}, //!< 4: vlpw
{SMC_PMCTRL_STOPM(0x00), 0}, //!< 5: stop
{SMC_PMCTRL_STOPM(0x02), 0}, //!< 6: vlps
{SMC_PMCTRL_STOPM(0x03), 0}, //!< 7: lls
{SMC_PMCTRL_STOPM(0x04), SMC_VLLSCTRL_VLLSM(3)},//!< 8: vlls3
{SMC_PMCTRL_STOPM(0x04), SMC_VLLSCTRL_VLLSM(2)},//!< 9: vlls2
{SMC_PMCTRL_STOPM(0x04), SMC_VLLSCTRL_VLLSM(1)},//!< 8: vlls1
{SMC_PMCTRL_STOPM(0x04), SMC_VLLSCTRL_VLLSM(0)} //!< 9: vlls0
};
SAFE_ATOM_CODE(
//!< read SMC_PMCTRL
uint32_t tTempPMCTRL = SMC_PMCTRL & ~(SMC_PMCTRL_STOPM_MASK |
SMC_PMCTRL_LPWUI_MASK
);
//! check current run mode
if (SLEEP_MODE_VLPR == (SMC_PMSTAT & SMC_PMSTAT_PMSTAT_MASK)) {
tTempPMCTRL &= ~SMC_PMCTRL_LPWUI_MASK;
//! very low power run mode
if ((SLEEP_STOP - SLEEP_WAIT) == chMode) {
#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
/*! \note can not transfer from vlpr mode to stop mode directly
*! so enter normal run mode first
*/
enter_normal_run_mode();
//! update tempPMCTRL
tTempPMCTRL &= ~SMC_PMCTRL_RUNM_MASK;
tTempPMCTRL |= (SMC_PMCTRL & SMC_PMCTRL_RUNM_MASK);
tTempPMCTRL |= SMC_PMCTRL_LPWUI_MASK;
#else
/*! \note can not transfer from vlpr mode to stop mode directly
*! so enter very low power stop instead
*/
chMode = (SLEEP_VLP_STOP - SLEEP_WAIT);
#endif
}
} else {
//! normal run mode
tTempPMCTRL |= SMC_PMCTRL_LPWUI_MASK;
if ((SLEEP_VLP_WAIT - SLEEP_WAIT) == chMode) {
#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
/*! \note normal run -> very low power wait
*! enter very low power run mode first. if it is failed
*! in entering very low power run mode, normal wait mode
*! will be entered.
*/
enter_very_low_power_run_mode();
//! update tempPMCTRL
tTempPMCTRL &= ~SMC_PMCTRL_RUNM_MASK;
tTempPMCTRL |= (SMC_PMCTRL & SMC_PMCTRL_RUNM_MASK);
#else
/*! \note normal run-> very low power wait was configured that
*! it's not supported here, so use normal wait instead.
*/
chMode = (SLEEP_WAIT - SLEEP_WAIT);
#endif
}
}
//! set PMC CONTROL register
tTempPMCTRL |= c_chMode;
//! update SMC registers
SMC_PMCTRL = tTempPMCTRL;
SMC_VLLSCTRL = c_chMode;
if (chMode < (SLEEP_VLP_STOP - SLEEP_WAIT)) {
/*! \note Clear the SLEEPDEEP bit to make sure we go into WAIT (sleep) mode
* insteadof deep sleep.
*/
SCB_SCR &= ~SCB_SCR_SLEEPDEEP_MASK;
} else {
/*! Set the SLEEPDEEP bit to enable deep sleep mode (STOP) */
SCB_SCR |= SCB_SCR_SLEEPDEEP_MASK;
}
ENABLE_GLOBAL_INTERRUPT();
//! raise entering_sleep_mode event
#ifdef ON_ENTERING_SLEEP_MODE
ON_ENTERING_SLEEP_MODE((chMode + SLEEP_WAIT))
#endif
#if __IS_COMPILER_IAR__
__WFI();
#else
__asm__ __volatile__ ("WFI");
#endif
//! raise wake_up_from_sleep_mode event
#ifdef ON_WAKE_UP_FROM_SLEEP_MODE
ON_WAKE_UP_FROM_SLEEP_MODE((chMode + SLEEP_WAIT))
#endif
)
}
/*! \brief enter specified sleep mode directly
*! \param tMode target sleep mode
*! \return access result
*/
vsf_err_t k20_core_sleep(sleep_mode_t tMode)
{
if (tMode < SLEEP_WAIT) {
return VSFERR_INVALID_RANGE;
} else if ( tMode > SLEEP_DEEPEST_SLEEP) {
tMode = SLEEP_DEEPEST_SLEEP;
}
//! enter sleep mode
enter_sleep_mode(tMode - SLEEP_WAIT);
return VSFERR_NONE;
}
/*! \brief sleep voting algorithm
*! \param tMode maximum allowed sleep mode
*! \return voting result
*/
static sleep_mode_t sleep_vote(sleep_mode_t tMode)
{
vsf_uint8_t n = SLEEP_REQ_COUNT;
vsf_uint8_t chIndex = 0;
volatile uint32_t *pwRequest = g_tSleepRequests.wRequests;
uint32_t wRequestWord;
//! sleep volte algorithm
do {
vsf_uint8_t chTemp;
if (!(chIndex & 0x03)) {
SAFE_ATOM_CODE(
wRequestWord = *pwRequest++;
)
}
chTemp = (wRequestWord & 0x0F);
if (chTemp < tMode) {
tMode = (sleep_mode_t)chTemp;
#if VSF_CORE_ALTERNATE_RUN_MODE != ENABLED
if (tMode < SLEEP_WAIT) {
break;
}
#endif
}
chIndex++;
} while (--n);
return tMode;
}
/*! \brief try to enter a voted sleep mode
*! \param none
*! \return access result
*/
vsf_err_t k20_core_try_to_sleep(void)
{
sleep_mode_t tMode = sleep_vote(SLEEP_DEEPEST_SLEEP);
if (tMode < SLEEP_WAIT) {
#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
//! no sleep mode allowed
try_to_run_as(tMode);
#endif
return VSFERR_NOT_AVAILABLE;
}
//! enter voted sleep mode
return k20_core_sleep(tMode);
}
#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
static vsf_err_t try_to_run_as(sleep_mode_t tVoteResult)
{
SAFE_ATOM_CODE(
do {
if (tVoteResult >= SLEEP_NONE) {
break;
}
//! get voting result
sleep_mode_t tCurrentMode =
(SLEEP_MODE_VLPR == (SMC_PMSTAT & SMC_PMSTAT_PMSTAT_MASK)) ?
SLEEP_VLP_RUN : SLEEP_RUN;
if ( (tVoteResult == tCurrentMode)
/*||(SLEEP_NONE == tVoteResult)*/) {
//! already run in the target mode, need to do nothing at all
break;
} else if (SLEEP_KEEP_CURRENT_RUN_MODE == tVoteResult) {
//! no run mode changing is allowed
EXIT_SAFE_ATOM_CODE();
return VSFERR_NOT_AVAILABLE;
}
if ( (SLEEP_VLP_RUN == tVoteResult)
&&(SLEEP_RUN ==tCurrentMode )) {
//! enter normal run mode
enter_normal_run_mode();
} elseif (/*(SLEEP_RUN == (sleep_mode_t)tMode) ||*/
(SLEEP_VLP_RUN ==tCurrentMode )){
//! enter very low power run mode
enter_very_low_power_run_mode();
}
} while (false);
)
return VSFERR_NONE;
}
#endif
/* EOF */
占个楼~ 顶一下 {:biggrin:}{:biggrin:}{:biggrin:}顶 顶!!!!!!!!!!!!!!!!! 本帖最后由 Gorgon_Meducer 于 2012-12-10 20:20 编辑
更新图片,和范例代码(未来得及严格测试,仅编译通过) 挖个古墓。送给Freescale板块 M4也这么多模式,还没注意,和M0+好像 学习好多,感谢分享
页:
[1]