控制Windows Mobile的LED之三:SN3101芯片驱动(内核和BOOTLOADER)

来源:互联网 发布:手机网络不好怎么回事 编辑:程序博客网 时间:2024/04/23 16:49

      上次用三个GPIO控制三个LED灯完成后,还没能满足具体的需求。比如:在充电状态时,需要指示灯闪烁。这就需要循环操作,而大家知道充电运行在EBOOT状态,而EBOOT中是没有线程的。如果不用线程用单纯的循环来控制GPIO的LED闪烁,这无疑是个死循环,所以这样实现不了要求。

      后期,我们改用SN3101来完成这个功能,因为它可以满足一次性的控制操作后就可以保持闪烁,不需要循环来控制,在EBOOT中也可以用。SN3101是SPI接口,完成最基本的通讯后,加上程序逻辑控制就行了。

      A,内核中的代码实现如下:

(1)虚拟控制映射。由于SPI要用到一系列函数来初始化,所以这是第一步。在初始化补全内存映射大小:

#define  MFP_SIZE  0x700

       ((sizeof(XLLP_SSP_REGISTER_T) + 4*1024-1)/(4*1024))*4*1024+
        ((sizeof(XLLP_CLKMGR_T) +4*1024-1)/(4*1024))*4*1024+
        ((MFP_SIZE+4*1024-1)/(4*1024))*4*1024;

然后,在全局变量s_Device的结构体中补全要用到的变量:

       XLLP_SSP_REGISTER_T *          m_pSSPReg;  //zhangcheng added 20101220
       P_XLLP_CLKMGR_T                    m_pCLKMgrReg; //zhangcheng added 20101220
       XLLP_VUINT32_T                      *MFPReg;   //zhangcheng added 20101220

(2)得到内存映射地址

//MONAHANS_BASE_REG_PA_SSP4
         dwBasePhysical = MONAHANS_BASE_REG_PA_SSP4;     //是用的SSP4
         bRet = VirtualCopy((LPVOID)pBaseVirtual, (LPVOID)((unsigned long)dwBasePhysical >> 8), sizeof(XLLP_SSP_REGISTER_T), (PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL));
         s_Device.m_pSSPReg= (XLLP_SSP_REGISTER_T *)pBaseVirtual;  

        (BYTE *)pBaseVirtual += ((sizeof(XLLP_SSP_REGISTER_T) +4*1024-1)/(4*1024)) * 4*1024;


//MONAHANS_BASE_REG_PA_CLKMGR
        dwBasePhysical = MONAHANS_BASE_REG_PA_CLKMGR;     //SSP时钟使能
        bRet = VirtualCopy((LPVOID)pBaseVirtual, (LPVOID)((unsigned long)dwBasePhysical >> 8), sizeof(XLLP_CLKMGR_T), (PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL));
        s_Device.m_pCLKMgrReg= (P_XLLP_CLKMGR_T)pBaseVirtual;  //zhangcheng added 20101029
        (BYTE *)pBaseVirtual += ((sizeof(XLLP_CLKMGR_T) +4*1024-1)/(4*1024)) * 4*1024;


//MONAHANS_BASE_REG_PA_MFP

       dwBasePhysical = MONAHANS_BASE_REG_PA_MFP;
       bRet = VirtualCopy((LPVOID)pBaseVirtual, (LPVOID)((unsigned long)dwBasePhysical >> 8), MFP_SIZE, (PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL));
       s_Device.MFPReg= (P_XLLP_VUINT32_T)pBaseVirtual;  //zhangcheng added 20101029
注意,以上的分配虚拟内存在使用完毕后,必须在反初始化中用释放。诸如。

(3)释放虚拟内存

if(s_Device.m_pSSPReg!= NULL){
       MmUnmapIoSpace((PVOID)s_Device.m_pSSPReg,sizeof(XLLP_SSP_REGISTER_T));
       s_Device.m_pSSPReg = NULL;
}
if(s_Device.m_pCLKMgrReg!= NULL){
       MmUnmapIoSpace((PVOID)s_Device.m_pCLKMgrReg,sizeof(XLLP_CLKMGR_T));
       s_Device.m_pCLKMgrReg = NULL;
}
if(s_Device.MFPReg!= NULL){
       MmUnmapIoSpace((PVOID)s_Device.MFPReg,MFP_SIZE);
       s_Device.MFPReg = NULL;

(4)初始化SSP4

//使能属于SSP4的那些复用端口成SSP功能

 XllpSSPMFPConfigure(XLLP_SSP_SSP4_LED, XLLP_TRUE, (XLLP_UINT32_T)s_Device.MFPReg,s_Device.m_pSSPReg);
 XllpClockEnable(s_Device.m_pCLKMgrReg, XLLP_CLK_SSP4, XLLP_TRUE);   // clock  enable
 XllpSSPPortEnable(s_Device.m_pSSPReg, XLLP_FALSE);  
 XllpSSPConfigLCD(s_Device.m_pSSPReg, 16);   //配置数据宽度,3101是16位格式,高8位地址,低8位数据
 XllpSSPPortEnable(s_Device.m_pSSPReg, XLLP_TRUE);
 XllpSSPPortFlush(s_Device.m_pSSPReg);         //将缓冲区FIFO中的数据全部清空,准备数据传输

 SN3101_Initial();

其中,关键的是SN3101_Initial中要调用到的SN3101的写函数:

static void SN3101_WriteData_16bit(XLLP_SSP_REGISTER_T *pSSP,unsigned int addr,unsigned int data)
{
       unsigned long command = 0;

       ...................
       command = (addr<<8)|data;
       command = (command <<16)|command; 
       pSSP->ssdr = command;

       ....................
       XllpSSPPortFlush(pSSP);
       return;
}

(5)头文件中定义常用的寄存器地址宏定义

#define    Reg_Configuration   0x00
#define Reg_RGB_Mode   0x02
#define Reg_RGB_Current   0x03
#define Reg_OUT0_PwmDuty  0x04
#define Reg_OUT1_PwmDuty  0x05
#define Reg_OUT2_PwmDuty  0x06
#define Reg_OUT0_T0HoldOffTime 0x0a
#define Reg_OUT1_T0HoldOffTime 0x0b
#define Reg_OUT2_T0HoldOffTime 0x0c
#define Reg_OUT0_T1T2RisHoldOn 0x10
#define Reg_OUT1_T1T2RisHoldOn 0x11
#define Reg_OUT2_T1T2RisHoldOn 0x12
#define Reg_OUT0_T3T4FallingOff  0x16
#define Reg_OUT1_T3T4FallingOff  0x17
#define Reg_OUT2_T3T4FallingOff  0x18
#define  Reg_Update    0x1c
#define Reg_OUT210_EN   0x1d

     然后,初始化函数SN3101_Initial函数体:

static void SN3101_Initial(void)
{
       SN3101_WriteData_16bit(s_Device.m_pSSPReg,Reg_Configuration,Configuration_SDRGB_OutEn);
       SN3101_WriteData_16bit(s_Device.m_pSSPReg,Reg_RGB_Current,RGB_Current_30dot9);
}

     B,BOOTLOADER的代码实现如下:

(1)需要将SSP相关操作的硬件寄存器地址进行映射,但方式与在内核中不同。

static XLLP_SSP_REGISTER_T  *m_pSSPReg = NULL;
static P_XLLP_CLKMGR_T  m_pCLKMgrReg = NULL;
static XLLP_VUINT32_T   *MFPReg = NULL;

      以上是全局变量,在函数的初始化中:

m_pSSPReg = (XLLP_SSP_REGISTER_T*)OALPAtoVA(MONAHANS_BASE_REG_PA_SSP4,FALSE);  
m_pCLKMgrReg = (P_XLLP_CLKMGR_T)OALPAtoVA(MONAHANS_BASE_REG_PA_CLKMGR,FALSE);  
MFPReg = (XLLP_VUINT32_T*)OALPAtoVA(MONAHANS_BASE_REG_PA_MFP,FALSE);   

      在文件头加上#include "../../DRIVERS/NLED/nled_pdd.h",获得NLED在内核中的头文件,它的很多函数都可以在BOOTLOADR中调用,如下。

(2)同样,完成SSP的初始化和基本的写操作函数。

 // SSP initial
 XllpSSPPortEnable(m_pSSPReg, XLLP_FALSE);
 XllpSSPConfigLCD(m_pSSPReg, 16);
 XllpSSPPortEnable(m_pSSPReg, XLLP_TRUE);
 XllpSSPPortFlush(m_pSSPReg);

写操作函数与内核中的相同。这样,在手机关机充电时,BOOTLOADR工作的情况下,同样可以控制LED。

原创粉丝点击