SylixOS電源管理之外設功耗管理

1.前言

在這個世界中,任何系統的運轉都需要能量。如樹木依靠光能生長,如馬兒依靠食物奔跑,如計算機系統依靠電能運行。而能量的獲取是有成本的,因此如果能在保證系統運轉的基礎上,儘量節省對能量的消耗,就會大大提升該系統的生存競爭力。這方面,大自然已經做的很好了,如植物的落葉,如動物的冬眠,等等。而在計算機的世界裏稱作電源管理(Power Management)。

本篇以運行SylixOS的mini2440嵌入式平臺爲例,分析SylixOS電源管理的外設功耗管理部分。

2.電源管理系統框架

SylixOS下電源管理結構如圖2-1所示。

圖 2-1  電源管理系統框架

每個電源管理適配器(PM Adapter)可以管理多個設備(由相應的通道號區分),電源管理適配器管理的通道號總數決定了這個適配器可以管理多少個設備。每個電源管理適配器控制相應設備上電(連通設備電源與時鐘)和掉電(斷開設備電源與時鐘)。

SylixOS爲支持電源管理的外設提供了一套方法集,可通過提供的應用層接口去調用實際提供的相應方法去實現設備的各種工作狀態。如表1所示有六種方法:

表 1  電源管理狀態

工作狀態

描述

Suspend

使所有支持休眠功能的外設進入休眠狀態。

Resume

使所有支持休眠功能的外設從休眠狀態恢復正常狀態

SavingEnter

使系統進入省電模式。控制所有支持電源管理的設備進入省電模式,同時設置運行的 CPU 核數目以及能耗級別。

SavingExit

控制系統退出省電模式。 控制所有支持電源管理的設備退出省電模式,同時設置運行的 CPU 核數目以及能耗級別。

IdleEnter

設備功耗管理單元具有看門狗功能,一旦空閒時間超過設置, 系統會將設備變爲空閒狀態。

IdleExit

系統將使設備退出空閒模式,恢復爲正常狀態。

3. SylixOS外設功耗管理

3.1創建電源管理適配器

源文件在工程中所在位置如圖3-1所示。

圖3-1  bspmini2440工程

打開bsp中的bspInit.c,系統啓動之後會調用halBootThread完成多任務狀態下的初始化工作,其中包含了和電源管理相關初始化的內容,如程序清單3-1所示。

程序清單3-1  多任務狀態下的初始化啓動任務

/*********************************************************************************************************
** 函數名稱: halBootThread
** 功能描述: 多任務狀態下的初始化啓動任務
** 輸 入  : NONE
** 輸 出  : NONE
** 全局變量:
** 調用模塊:
*********************************************************************************************************/
static PVOID  halBootThread (PVOID  pvBootArg)
{
    LW_CLASS_THREADATTR     threakattr = API_ThreadAttrGetDefault();    /*  使用默認屬性                */

    (VOID)pvBootArg;

#if LW_CFG_SHELL_EN > 0
    halShellInit();
#endif                                                                  /*  LW_CFG_SHELL_EN > 0         */

#if LW_CFG_POWERM_EN > 0
    halPmInit();
#endif                                                                  /*  LW_CFG_POWERM_EN > 0        */

#if LW_CFG_DEVICE_EN > 0
    halBusInit();
    halDrvInit();
    halDevInit();
    halStdFileInit();
#endif                                                                  /*  LW_CFG_DEVICE_EN > 0        */

#if LW_CFG_LOG_LIB_EN > 0
    halLogInit();
    console_loglevel = default_message_loglevel;                        /*  設置 printk 打印信息等級    */
#endif                                                                  /*  LW_CFG_LOG_LIB_EN > 0       */

    /*
     *  因爲 yaffs 掛載物理卷時, 需要 stdout 打印信息, 如果在 halDevInit() 中被調用, 由於沒有創建
     *  標準文件, 所以會打印警告錯誤信息, 所以將此函數放在這裏!
     *  如果未初始化標準文件會提示錯誤信息
     */
#ifdef __GNUC__
    nand_init();
    mtdDevCreateEx("/n");                                               /*  mount mtddevice             */
#else
    nandDevCreateEx("/n");                                              /*  mount nandflash disk(yaffs) */
#endif

    halStdDirInit();                                                    /*  創建標準目錄                */

    /*
     *  只有初始化了 shell 並獲得了 TZ 環境變量標示的時區, 纔可以調用 rtcToRoot()
     */
    system("varload");                                                  /*  從/etc/profile中讀取環境變量*/
    lib_tzset();                                                        /*  通過 TZ 環境變量設置時區    */
    rtcToRoot();                                                        /*  將 RTC 時間同步到根文件系統 */

    /*
     *  網絡初始化一般放在 shell 初始化之後, 因爲初始化網絡組件時, 會自動註冊 shell 命令.
     */
#if LW_CFG_NET_EN > 0
    halNetInit();
    halNetifAttch();                                                    /*  wlan 網卡需要下載固件       */
#endif                                                                  /*  LW_CFG_NET_EN > 0           */

#if LW_CFG_POSIX_EN > 0
    halPosixInit();
#endif                                                                  /*  LW_CFG_POSIX_EN > 0         */

#if LW_CFG_SYMBOL_EN > 0
    halSymbolInit();
#endif                                                                  /*  LW_CFG_SYMBOL_EN > 0        */

#if LW_CFG_MODULELOADER_EN > 0
    halLoaderInit();
#endif                                                                  /*  LW_CFG_MODULELOADER_EN > 0  */

#if LW_CFG_MONITOR_EN > 0
    halMonitorInit();
#endif                                                                  /*  LW_CFG_MONITOR_EN > 0       */

    system("shfile /etc/startup.sh");                                   /*  執行啓動腳本                */
                                                                        /*  必須在初始化 shell 後調用!! */

    API_ThreadAttrSetStackSize(&threakattr, __LW_THREAD_MAIN_STK_SIZE); /*  設置 main 線程的堆棧大小    */
    API_ThreadCreate("t_main",
                     (PTHREAD_START_ROUTINE)t_main,
                     &threakattr,
                     LW_NULL);                                          /*  Create "t_main()" thread    */

    return  (LW_NULL);
}

這裏,主要分析與電源管理相關的兩個函數,分別是halPmInit和halDevInit。

其中halPmInit主要實現了目標系統電源管理系統的初始化,halDevInit主要實現了設備的初始化,包括對於支持電源管理設備的電源管理相關的初始化。

下面將詳細說明。

分析halPmInit,其內部實現如程序清單3-2所示。

程序清單3-2   初始化目標系統電源管理系統

/*********************************************************************************************************
** 函數名稱: halPmInit
** 功能描述: 初始化目標系統電源管理系統
** 輸 入  : NONE
** 輸 出  : NONE
** 全局變量:
** 調用模塊:
*********************************************************************************************************/
#if LW_CFG_POWERM_EN > 0

static VOID  halPmInit (VOID)
{
    PLW_PMA_FUNCS  pmafuncs;

    pmafuncs = pmGetFuncs();

    pmAdapterCreate("inner_pm", 21, pmafuncs);
}

#endif                                                                  /*  LW_CFG_POWERM_EN > 0        */

其中LW_CFG_POWERM_EN是一個裁剪宏,可通過改變宏值對電源管理功能進行裁剪。

pmGetFuncs主要是獲取電源管理適配器的操作函數集,包括控制電源設備的上電、斷電等,具體內部實現如程序清單3-3所示。

程序清單3-3  獲取電源管理適配器函數集

/*********************************************************************************************************
** Function name:           pmGetFuncs
** Descriptions:            獲取電源管理適配器函數集
** input parameters:        NONE
** output parameters:       NONE
** Returned value:          pfuncs      驅動程序集
** Created by:              Hanhui
** Created Date:            2014-07-20
**--------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**--------------------------------------------------------------------------------------------------------
*********************************************************************************************************/
PLW_PMA_FUNCS  pmGetFuncs (VOID)
{
    static S3C2440A_PMA     pma;

    pma.pmafuncs.PMAF_pfuncOn   = pmPowerOn;
    pma.pmafuncs.PMAF_pfuncOff  = pmPowerOff;
    pma.pmafuncs.PMAF_pfuncIsOn = pmPowerIsOn;

    return  (&pma.pmafuncs);
}

其中pmPowerOn、pmPowerOff、pmPowerIsOn主要實現了設備的上電、掉電、判斷設備是否上電的功能,具體實現依賴於具體的硬件設備。

pmPowerOn的實現如程序清單3-4所示。

程序清單3-4  pmPowerOn實現

/*********************************************************************************************************
** Function name:           pmPowerOn
** Descriptions:            指定設備上電
** input parameters:        pmadapter   電源管理適配器
** output parameters:       pmdev       設備
** Returned value:          OK
** Created by:              Hanhui
** Created Date:            2014-07-20
**--------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**--------------------------------------------------------------------------------------------------------
*********************************************************************************************************/
static INT  pmPowerOn (PLW_PM_ADAPTER  pmadapter,
                       PLW_PM_DEV      pmdev)
{
    rCLKCON |= (1 << pmdev->PMD_uiChannel);

    return  (ERROR_NONE);
}

可以看出,在本例中是以時鐘的開關來控制設備的上電,其中pmPowerOff和pmPowerIsOn的內部實現和pmPowerOn類似,都是通過控制時鐘進而控制設備的相應狀態。

分析pmAdapterCreate,其內部實現如程序清單3-5所示。

程序清單3-5  創建一個電源管理適配器

/*********************************************************************************************************
** 函數名稱: API_PowerMAdapterCreate
** 功能描述: 創建一個電源管理適配器
** 輸 入  : pcName        電源管理適配器的名字
**           uiMaxChan     電源管理適配器最大通道號
**           pmafuncs      電源管理適配器操作函數
** 輸 出  : 電源管理適配器
** 全局變量: 
** 調用模塊: 
                                           API 函數
*********************************************************************************************************/
LW_API  
PLW_PM_ADAPTER  API_PowerMAdapterCreate (CPCHAR  pcName, UINT  uiMaxChan, PLW_PMA_FUNCS  pmafuncs)
{
    PLW_PM_ADAPTER  pmadapter;
    
    if (!pcName || !uiMaxChan || !pmafuncs) {
        _ErrorHandle(EINVAL);
        return  (LW_NULL);
    }
    
    pmadapter = (PLW_PM_ADAPTER)__SHEAP_ALLOC(sizeof(LW_PM_ADAPTER) + lib_strlen(pcName));
    if (pmadapter == LW_NULL) {
        _ErrorHandle(ERROR_POWERM_FULL);
        return  (LW_NULL);
    }
    
    pmadapter->PMA_uiMaxChan = uiMaxChan;
    pmadapter->PMA_pmafunc   = pmafuncs;
    lib_strcpy(pmadapter->PMA_cName, pcName);
    
    __POWERM_LOCK();
    _List_Line_Add_Ahead(&pmadapter->PMA_lineManage, &_G_plinePMAdapter);
    __POWERM_UNLOCK();
    
    return  (pmadapter);
}

API_PowerMAdapterCreate主要實現一個電源管理適配器的創建。函數中的主要操作有以下兩點:

1.爲電源管理適配器動態分配一片空間,並初始化。

2.將創建的電源管理適配器加入到電源管理適配器鏈表中。

3.2初始化電源管理設備

對於支持電源管理的設備,在設備進行初始化的過程中,將會初始化設備的電源管理功能。系統通過調用halDevInit實現設備的初始化,其內部實現如程序清單3-6所示。

程序清單3-6  初始化目標系統靜態設備

/*********************************************************************************************************
** 函數名稱: halDevInit
** 功能描述: 初始化目標系統靜態設備組件
** 輸 入  : NONE
** 輸 出  : NONE
** 全局變量:
** 調用模塊:
*********************************************************************************************************/
#if LW_CFG_DEVICE_EN > 0

static VOID  halDevInit (VOID)
{
    SIO_CHAN    *psio0 = sioChanCreate(0);                              /*  創建串口 0 通道             */
    SIO_CHAN    *psio1 = sioChanCreate(1);                              /*  創建串口 1 通道             */
    SIO_CHAN    *psio2 = sioChanCreate(2);                              /*  創建串口 2 通道             */

#ifdef MICRO2440_PACKET
    S3C_ONEWIRE_TS  oneWireTs;
#endif

    /*
     *  創建根文件系統時, 將自動創建 dev, mnt, var 目錄.
     */
    rootFsDevCreate();                                                  /*  創建根文件系統              */
    procFsDevCreate();                                                  /*  創建 proc 文件系統          */
    shmDevCreate();                                                     /*  創建共享內存設備            */
    randDevCreate();                                                    /*  創建隨機數文件              */

    ttyDevCreate("/dev/ttyS0", psio0, 30, 50);                          /*  add    tty   device         */
    ttyDevCreate("/dev/ttyS1", psio1, 30, 50);                          /*  add    tty   device         */
    ttyDevCreate("/dev/ttyS2", psio2, 30, 50);                          /*  add    tty   device         */

    yaffsDevCreate("/yaffs2");                                          /*  create yaffs device(only fs)*/
    lcdDevCreate();                                                     /*  create lcd device           */

#ifdef MINI2440_PACKET
    tsDevCreate("/dev/input/touch0");                                   /*  create touch device         */
#elif defined(MICRO2440_PACKET)
    oneWireTs.ucTimerNum        = 0;
    oneWireTs.uiOneWireGpio     = 0;
    oneWireTs.ulTimerIntVector  = LW_IRQ_10;
    oneWireTs.ulPclk            = PCLK;
    s3cOneWireTsDevCreate("/dev/input/touch0", &oneWireTs);             /*  create touch device         */
#endif
}

#endif                                                                  /*  LW_CFG_DEVICE_EN > 0        */

由於串口支持電源管理,因此這裏以串口0爲例,分析串口0的初始化過程。通過調用sioChanCreate來實現串口通道的創建,其內部實現如程序清單3-7所示。

程序清單3-7  創建一個 sio 通道

/*********************************************************************************************************
** Function name:           sioChanCreate
** Descriptions:            創建一個 sio 通道
** input parameters:        iChannelNum     硬件通道號
** output parameters:       NONE
** Returned value:          NONE
** Created by:              Hanhui
** Created Date:            2007/12/20
**--------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**--------------------------------------------------------------------------------------------------------
*********************************************************************************************************/
SIO_CHAN    *sioChanCreate (INT   iChannelNum)
{
    static PLW_PM_ADAPTER    pmadapter = LW_NULL;
    __PSAMSUNGSIO_CHANNEL    psamsungsiochanUart;
    SIO_CHAN                *psiochan;
    
    UCHAR                    ucDataBits;
    UCHAR                    ucStopBits;
    UCHAR                    ucParity;
    
    if (pmadapter == LW_NULL) {
        pmadapter =  pmAdapterFind("inner_pm");
        if (pmadapter == LW_NULL) {
            printk(KERN_ERR "can not find power manager.\n");
        }
    }

    switch (iChannelNum) {
    
    case COM0:
        pmDevInit(&__GsamsungsiochanUart0.pmdev, pmadapter, 10, LW_NULL);
        __GsamsungsiochanUart0.pmdev.PMD_pcName = "uart0";
        psamsungsiochanUart              = &__GsamsungsiochanUart0;
        psamsungsiochanUart->pdrvFuncs   = &__GsiodrvUartDrvFunc;       /*  SIO FUNC                    */
        psamsungsiochanUart->iChannelNum = COM0;
        
        API_InterVectorConnect(VIC_CHANNEL_UART0, 
                               (PINT_SVR_ROUTINE)__uartIsr, 
                               (PVOID)&__GsamsungsiochanUart0,
                               "uart0_isr");                            /*  安裝操作系統中斷向量表      */
        break;
    
    case COM1:
        pmDevInit(&__GsamsungsiochanUart1.pmdev, pmadapter, 11, LW_NULL);
        __GsamsungsiochanUart1.pmdev.PMD_pcName = "uart1";
        psamsungsiochanUart              = &__GsamsungsiochanUart1;
        psamsungsiochanUart->pdrvFuncs   = &__GsiodrvUartDrvFunc;       /*  SIO FUNC                    */
        psamsungsiochanUart->iChannelNum = COM1;
        
        API_InterVectorConnect(VIC_CHANNEL_UART1, 
                               (PINT_SVR_ROUTINE)__uartIsr, 
                               (PVOID)&__GsamsungsiochanUart1,
                               "uart1_isr");                            /*  安裝操作系統中斷向量表      */
        break;
    
    case COM2:
        pmDevInit(&__GsamsungsiochanUart2.pmdev, pmadapter, 12, LW_NULL);
        __GsamsungsiochanUart2.pmdev.PMD_pcName = "uart2";
        psamsungsiochanUart              = &__GsamsungsiochanUart2;
        psamsungsiochanUart->pdrvFuncs   = &__GsiodrvUartDrvFunc;       /*  SIO FUNC                    */
        psamsungsiochanUart->iChannelNum = COM2;
        
        API_InterVectorConnect(VIC_CHANNEL_UART2, 
                               (PINT_SVR_ROUTINE)__uartIsr, 
                               (PVOID)&__GsamsungsiochanUart2,
                               "uart2_isr");                            /*  安裝操作系統中斷向量表      */
        break;
    
    default:
        return  (LW_NULL);                                              /*  通道號錯誤                  */
    }
    
    psiochan = (SIO_CHAN *)psamsungsiochanUart;
    
    psamsungsiochanUart->iChannelMode = SIO_MODE_INT;                   /*  使用中斷模式                */
    psamsungsiochanUart->iBaud        = UART_DEFAULT_BAUD;              /*  初始化波特率                */
    psamsungsiochanUart->iHwOption    = UART_DEFAULT_OPT;               /*  硬件狀態                    */
    psamsungsiochanUart->iRs485Flag   = RS485_DIS;                      /*  默認不使用485功能           */
    
    __uartHwOptionAnalyse(UART_DEFAULT_OPT,
                          &ucDataBits,
                          &ucStopBits,
                          &ucParity);                                   /*  獲得具體參數                */
                          
    uartInit(iChannelNum, UNUSE_INF, ucDataBits, ucStopBits, 
             ucParity, UART_DEFAULT_BAUD, LW_NULL);
             
    return  (psiochan);
}

由sioChanCreate的內部實現可見,在創建串口通道的過程中,通過調用pmDevInit將串口加入到電源管理設備鏈表中。

其中pmDevInit內部實現如程序清單3-8所示。

程序清單3-8  初始化電源管理設備節點

/*********************************************************************************************************
** 函數名稱: API_PowerMDevInit
** 功能描述: 初始化電源管理設備節點
** 輸 入  : pmdev         電源管理, 設備節點
**           pmadapter     電源管理適配器
**           uiChan        通道號
**           pmdfunc       設備節電操作函數
** 輸 出  : ERROR ok OK
** 全局變量: 
** 調用模塊: 
                                           API 函數
*********************************************************************************************************/
LW_API  
INT  API_PowerMDevInit (PLW_PM_DEV  pmdev,  PLW_PM_ADAPTER  pmadapter, 
                        UINT        uiChan, PLW_PMD_FUNCS   pmdfunc)
{
    if (!pmdev || !pmadapter) {
        _ErrorHandle(EINVAL);
        return  (PX_ERROR);
    }
    
    if (uiChan >= pmadapter->PMA_uiMaxChan) {
        _ErrorHandle(ENXIO);
        return  (PX_ERROR);
    }
    
    pmdev->PMD_pmadapter = pmadapter;
    pmdev->PMD_uiChannel = uiChan;
    pmdev->PMD_uiStatus  = LW_PMD_STAT_NOR;
    pmdev->PMD_pmdfunc   = pmdfunc;
    lib_bzero(&pmdev->PMD_wunTimer, sizeof(LW_CLASS_WAKEUP_NODE));
    
    __POWERM_LOCK();
    _List_Line_Add_Ahead(&pmdev->PMD_lineManage, &_G_plinePMDev);
    __POWERM_UNLOCK();
    
    return  (ERROR_NONE);
}

API_PowerMDevInit主要實現一個電源管理設備的創建。函數中的主要操作有以下兩點:

1.爲電源設備動態分配一片空間,並初始化。

2.將創建的電源設備加入到電源設備鏈表中。

對電源設備的初始化包括指定電源管理適配器,設置電源管理通道號,電源管理設備的狀態,以及初始化電源管理設備的操作函數結構體。

電源管理設備操作函數結構體定義。

typedef struct lw_pmd_funcs {
    INT                (*PMDF_pfuncSuspend)(PLW_PM_DEV  pmdev);         /*  CPU 休眠                    */
    INT                (*PMDF_pfuncResume)(PLW_PM_DEV  pmdev);          /*  CPU 恢復                    */
    
    INT                (*PMDF_pfuncPowerSavingEnter)(PLW_PM_DEV  pmdev);/*  系統進入省電模式            */
    INT                (*PMDF_pfuncPowerSavingExit)(PLW_PM_DEV  pmdev); /*  系統退出省電模式            */
    
    INT                (*PMDF_pfuncIdleEnter)(PLW_PM_DEV  pmdev);       /*  設備長時間不使用進入空閒    */
    INT                (*PMDF_pfuncIdleExit)(PLW_PM_DEV  pmdev);        /*  設備退出空閒                */
    
    INT                (*PMDF_pfuncCpuPower)(PLW_PM_DEV  pmdev);        /*  CPU 改變主頻能級            */
    PVOID                PMDF_pvReserve[16];                            /*  保留                        */
} LW_PMD_FUNCS;
typedef LW_PMD_FUNCS    *PLW_PMD_FUNCS;


通過實現電源管理設備操作函數,並在支持電源管理的設備初始化時與設備相關聯。即可對創建的電源設備進行電源管理操作。

系統提供電源管理接口

VOID  API_PowerMSuspend(VOID);                                          /*  系統休眠                    */
VOID  API_PowerMResume(VOID);                                           /*  系統恢復 (系統休眠恢復調用) */

VOID  API_PowerMSavingEnter(ULONG  ulNCpus, UINT  uiPowerLevel);        /*  系統進入省電模式            */
VOID  API_PowerMSavingExit(ULONG  ulNCpus, UINT  uiPowerLevel);

調用系統接口時,其內部會調用對應的電源設備操作函數來對電源設備進行休眠、喚醒等操作。具體內容參考《SylixOS電源管理概述以及接口介紹》。

3.3 電源管理設備操作接口

SylixOS的電源管理設備接口提供了對電源設備進行操作的接口,包括以下四種功能:

1.初始化電源設備到電源適配器管理器中。

2.從電源適配器管理器中移除電源設備。

3.控制電源設備的上電。

4.控制電源設備的斷電。

其中對電源設備的初始化分析見3.2節程序清單3-8。

移除電源管理設備節點的內部實現如程序清單3-9所示。

程序清單3-9  移除電源管理設備節點

/*********************************************************************************************************
** 函數名稱: API_PowerMDevTerm
** 功能描述: 結束電源管理設備節點
** 輸 入  : pmdev         電源管理, 設備節點
** 輸 出  : ERROR ok OK
** 全局變量: 
** 調用模塊: 
                                           API 函數
*********************************************************************************************************/
LW_API  
INT  API_PowerMDevTerm (PLW_PM_DEV  pmdev)
{
    if (!pmdev) {
        _ErrorHandle(EINVAL);
        return  (PX_ERROR);
    }
    
    __POWERM_LOCK();
    _List_Line_Del(&pmdev->PMD_lineManage, &_G_plinePMDev);
    __POWERM_UNLOCK();

    return  (API_PowerMDevWatchDogOff(pmdev));
}

控制電源設備的上電內部實現如程序清單3-10所示。

程序清單3-10  打開設備節點

/*********************************************************************************************************
** 函數名稱: API_PowerMDevOn
** 功能描述: 電源管理, 設備節點第一次打開需要調用此函數
** 輸 入  : pmdev         電源管理, 設備節點
** 輸 出  : ERROR ok OK
** 全局變量: 
** 調用模塊: 
                                           API 函數
*********************************************************************************************************/
LW_API  
INT  API_PowerMDevOn (PLW_PM_DEV  pmdev)
{
    PLW_PM_ADAPTER  pmadapter;
    INT             iRet = PX_ERROR;
    
    if (!pmdev) {
        _ErrorHandle(EINVAL);
        return  (PX_ERROR);
    }
    
    pmadapter = pmdev->PMD_pmadapter;
    if (pmadapter && 
        pmadapter->PMA_pmafunc &&
        pmadapter->PMA_pmafunc->PMAF_pfuncOn) {
        iRet = pmadapter->PMA_pmafunc->PMAF_pfuncOn(pmadapter, pmdev);
    }

    return  (iRet);
}

控制電源設備的斷電內部實現如程序清單3-11所示。

程序清單3-11  關閉設備節點

/*********************************************************************************************************
** 函數名稱: API_PowerMDevOff
** 功能描述: 電源管理, 設備節點最後一次關閉需要調用此函數
** 輸 入  : pmdev         電源管理, 設備節點
** 輸 出  : ERROR ok OK
** 全局變量: 
** 調用模塊: 
                                           API 函數
*********************************************************************************************************/
LW_API  
INT  API_PowerMDevOff (PLW_PM_DEV  pmdev)
{
    PLW_PM_ADAPTER  pmadapter;
    INT             iRet = PX_ERROR;
    
    if (!pmdev) {
        _ErrorHandle(EINVAL);
        return  (PX_ERROR);
    }
    
    pmadapter = pmdev->PMD_pmadapter;
    if (pmadapter && 
        pmadapter->PMA_pmafunc &&
        pmadapter->PMA_pmafunc->PMAF_pfuncOff) {
        iRet = pmadapter->PMA_pmafunc->PMAF_pfuncOff(pmadapter, pmdev);
    }
    
    return  (iRet);
}

3.4 電源管理設備空閒時間管理

SylixOS電源管理系統提供了電源管理設備空閒時間管理的功能,即對於支持空閒管理的電源設備(如顯示器等)在長時間不使用時,應該進入休閒模式,對於顯示器來說即關閉屏幕顯示,來降低設備的功耗,該功能對於對產品功耗有要求的開發者來說,可以通過軟件層面降低產品的功耗。

下面將介紹SylixOS電源管理系統提供的電源管理設備的空閒管理功能。

3.4.1 電源管理內核線程分析

在內核啓動過程中,內核將會創建一個電源管理線程“t_power”,下面將會對該線程進行分析,源碼部分位於basemini2440\libsylixos\SylixOS\system\pm\pmIdle.c。

具體實現如程序清單3-12所示。

  程序清單3-12  電源管理線程

/*********************************************************************************************************
** 函數名稱: _PowerMThread
** 功能描述: 電源管理線程
** 輸 入  : NONE
** 輸 出  : NONE
** 全局變量: 
** 調用模塊: 
*********************************************************************************************************/
static  PVOID  _PowerMThread (PVOID  pvArg)
{
    PLW_PM_DEV              pmdev;
    PLW_CLASS_WAKEUP_NODE   pwun;

    for (;;) {
        ULONG   ulCounter = LW_TICK_HZ;
        
        __POWERM_LOCK();                                                /*  上鎖                        */
                           
        __WAKEUP_PASS_FIRST(&_G_wuPowerM, pwun, ulCounter);
        
        pmdev = _LIST_ENTRY(pwun, LW_PM_DEV, PMD_wunTimer);
        
        _WakeupDel(&_G_wuPowerM, pwun);
        
        if (pmdev->PMD_pmdfunc &&
            pmdev->PMD_pmdfunc->PMDF_pfuncIdleEnter) {
            pmdev->PMD_uiStatus = LW_PMD_STAT_IDLE;
            __POWERM_UNLOCK();                                          /*  暫時解鎖                    */
            pmdev->PMD_pmdfunc->PMDF_pfuncIdleEnter(pmdev);
            __POWERM_LOCK();                                            /*  上鎖                        */
        }
        
        __WAKEUP_PASS_SECOND();
        
        __WAKEUP_PASS_END();
        
        __POWERM_UNLOCK();                                              /*  解鎖                        */
        
        API_TimeSleep(LW_TICK_HZ);                                      /*  等待一秒                    */
    }
    
    return  (LW_NULL);
}

該電源管理線程主要負責循環掃描喚醒表中是否有超時電源設備,如果有超時電源設備,則使該設備進入空閒模式,並從喚醒表中刪除該電源設備。

3.4.2 電源管理設備空閒時間管理接口

SylixOS的電源設備空閒時間管理提供了三個對電源設備進行空閒時間管理的接口,包括設置電源設備進入空閒模式的時間,獲取電源設備進入空閒模式剩餘的時間和關閉電源設備的空閒時間管理功能。下面將介紹接口的內部實現。

設置電源設備進入空閒模式的時間的內部實現如程序清單3-13所示。

程序清單3-13  設備電源空閒時間管理激活

/*********************************************************************************************************
** 函數名稱: API_PowerMDevSetWatchDog
** 功能描述: 設備電源空閒時間管理激活
** 輸 入  : pmdev         電源管理, 設備節點
**           ulSecs        經過指定的秒數, 設備將進入 idle 模式.
** 輸 出  : ERROR ok OK
** 全局變量: 
** 調用模塊: 
                                           API 函數
*********************************************************************************************************/
LW_API  
INT  API_PowerMDevSetWatchDog (PLW_PM_DEV  pmdev, ULONG  ulSecs)
{
    BOOL    bBecomeNor = LW_FALSE;

    if (LW_CPU_GET_CUR_NESTING()) {
        _DebugHandle(__ERRORMESSAGE_LEVEL, "called from ISR.\r\n");
        _ErrorHandle(ERROR_KERNEL_IN_ISR);
        return  (PX_ERROR);
    }
    
    if (!pmdev || !ulSecs) {
        _ErrorHandle(EINVAL);
        return  (PX_ERROR);
    }
    
    __POWERM_LOCK();                                                    /*  上鎖                        */
    if (pmdev->PMD_bInQ) {
        _WakeupDel(&_G_wuPowerM, &pmdev->PMD_wunTimer);
    }
    
    pmdev->PMD_ulCounter = ulSecs;                                      /*  復位定時器                  */
    
    _WakeupAdd(&_G_wuPowerM, &pmdev->PMD_wunTimer);
    
    if (pmdev->PMD_uiStatus == LW_PMD_STAT_IDLE) {
        pmdev->PMD_uiStatus =  LW_PMD_STAT_NOR;
        bBecomeNor          =  LW_TRUE;
    }
    __POWERM_UNLOCK();                                                  /*  解鎖                        */
    
    if (bBecomeNor && 
        pmdev->PMD_pmdfunc &&
        pmdev->PMD_pmdfunc->PMDF_pfuncIdleExit) {
        pmdev->PMD_pmdfunc->PMDF_pfuncIdleExit(pmdev);                  /*  退出 idle                   */
    }
    
    return  (ERROR_NONE);
}

該函數主要實現以下功能:

1.重置電源設備進入idle模式的時間。

2.將電源設備加入到喚醒表中。

3.設置電源設備當前狀態爲正常運行模式。

4.退出idle模式。

獲取電源設備進入空閒模式剩餘的時間的內部實現如程序清單3-14所示。

程序清單3-14  設備電源空閒剩餘時間

/*********************************************************************************************************
** 函數名稱: API_PowerMDevGetWatchDog
** 功能描述: 設備電源空閒剩餘時間
** 輸 入  : pmdev         電源管理, 設備節點
**           pulSecs       設備進入 idle 模式剩餘的時間.
** 輸 出  : ERROR ok OK
** 全局變量: 
** 調用模塊: 
                                           API 函數
*********************************************************************************************************/
LW_API  
INT  API_PowerMDevGetWatchDog (PLW_PM_DEV  pmdev, ULONG  *pulSecs)
{
    if (LW_CPU_GET_CUR_NESTING()) {
        _DebugHandle(__ERRORMESSAGE_LEVEL, "called from ISR.\r\n");
        _ErrorHandle(ERROR_KERNEL_IN_ISR);
        return  (PX_ERROR);
    }
    
    if (!pmdev || !pulSecs) {
        _ErrorHandle(EINVAL);
        return  (PX_ERROR);
    }
    
    __POWERM_LOCK();                                                    /*  上鎖                        */
    if (pmdev->PMD_bInQ) {
        _WakeupStatus(&_G_wuPowerM, &pmdev->PMD_wunTimer, pulSecs);
    
    } else {
        *pulSecs = 0ul;
    }
    __POWERM_UNLOCK();                                                  /*  解鎖                        */
    
    return  (ERROR_NONE);
}

該函數主要功能是獲取喚醒表中電源設備進入idle模式的剩餘時間。

關閉電源設備的空閒時間管理功能內部實現如程序清單3-15所示。

程序清單3-15  關閉電源設備的空閒時間管理功能

/*********************************************************************************************************
** 函數名稱: API_PowerMDevWatchDogOff
** 功能描述: 設備電源空閒時間管理停止
** 輸 入  : pmdev         電源管理, 設備節點
** 輸 出  : ERROR ok OK
** 全局變量: 
** 調用模塊: 
                                           API 函數
*********************************************************************************************************/
LW_API  
INT  API_PowerMDevWatchDogOff (PLW_PM_DEV  pmdev)
{
    BOOL    bBecomeNor = LW_FALSE;

    if (LW_CPU_GET_CUR_NESTING()) {
        _DebugHandle(__ERRORMESSAGE_LEVEL, "called from ISR.\r\n");
        _ErrorHandle(ERROR_KERNEL_IN_ISR);
        return  (PX_ERROR);
    }
    
    if (!pmdev) {
        _ErrorHandle(EINVAL);
        return  (PX_ERROR);
    }

    __POWERM_LOCK();                                                    /*  上鎖                        */
    if (pmdev->PMD_bInQ) {
        _WakeupDel(&_G_wuPowerM, &pmdev->PMD_wunTimer);
    }
    if (pmdev->PMD_uiStatus == LW_PMD_STAT_IDLE) {
        pmdev->PMD_uiStatus =  LW_PMD_STAT_NOR;
        bBecomeNor          =  LW_TRUE;
    }
    __POWERM_UNLOCK();                                                  /*  解鎖                        */
    
    if (bBecomeNor && 
        pmdev->PMD_pmdfunc &&
        pmdev->PMD_pmdfunc->PMDF_pfuncIdleExit) {
        pmdev->PMD_pmdfunc->PMDF_pfuncIdleExit(pmdev);                  /*  退出 idle                   */
    }
    
    return  (ERROR_NONE);
}

通過調用以上三個接口,可以實現電源設備的空閒時間管理。

4.Kernel中電源管理相關的source code彙整

在RealEvo-IDE開發環境中新建mini2440base工程和mini2440bsp工程,電源管理有關的Source code分別位於:

bsp工程中:

bspmini2440\SylixOS\driver\pm\s3c2440a_pm.c

bspmini2440\SylixOS\driver\pm\s3c2440a_pm.h

base工程中:

basemini2440\libsylixos\SylixOS\system\pm\pmAdapter.c

basemini2440\libsylixos\SylixOS\system\pm\pmAdapter.h

basemini2440\libsylixos\SylixOS\system\pm\pmDev.c

basemini2440\libsylixos\SylixOS\system\pm\pmDev.h

basemini2440\libsylixos\SylixOS\system\pm\pmIdle.c

basemini2440\libsylixos\SylixOS\system\pm\pmIdle.h

basemini2440\libsylixos\SylixOS\system\pm\pmSystem.c

basemini2440\libsylixos\SylixOS\system\pm\pmSystem.h

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章