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

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