上次用三个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。