简述
LPC2478支持USB Host/Device/OTG功能。使用的是OHCI架构的USB。结合对LPC2478的了解,记录下其启动代码的分析,用于了解其USB的工作方式以及配置流程。
启动代码的流程
从IC上电开始,LPC2478应当首先会执行其内部固话的Boot Loader code,具体是做什么以及源代码不知道,NXP并不会开源的,这部分涉及到IC设计了。
Boot Loader阶段
Boot Loader code的作用就是检查哪块memory进行启动,并做判断,然后对User code做一些初始化,然后跳转到对应memory区域的User code中去。这里一般都是用的Flash Memory启动了,别的也是类似。
User code汇编代码阶段
这里的汇编代码是指NXP提供的.s的汇编代码。因为从Boot Loader结束到跳入User code,还需要有些初始化的配置的,不过NXP都写好了示例的。从NXP的启动代码看到:
- 对各中断模式以及用户代码Stack以及Heap大小的设置;
- 中断向量跳转地址设定;
- Reset Handler的处理:这部分应当是用来跳入User code的前奏了,先设置好各个模式的堆栈(通过CPSR_c寄存器来切换到各模式),当然,最后一个被设置堆栈的模式是User code模式,然后调用TargetResetInit()函数,接着跳入main()函数真正进入到User code中去执行。
TargetResetInit函数
这个函数的作用就是,在跳进main函数之前初始化自己的LPC2478板子的一些配置。
void TargetResetInit(void)
{
#ifdef __DEBUG_RAM
MEMMAP = 0x2;
#endif
#ifdef __DEBUG_FLASH
MEMMAP = 0x1;
#endif
#if USE_USB
PCONP |= 0x80000000;
#endif
ConfigurePLL();
#if (Fpclk / (Fcclk / 4)) == 1
PCLKSEL0 = 0x00000000;
PCLKSEL1 = 0x00000000;
#endif
#if (Fpclk / (Fcclk / 4)) == 2
PCLKSEL0 = 0xAAAAAAAA;
PCLKSEL1 = 0xAAAAAAAA;
#endif
#if (Fpclk / (Fcclk / 4)) == 4
PCLKSEL0 = 0x55555555;
PCLKSEL1 = 0x55555555;
#endif
MAMCR = 0;
#if Fcclk < 20000000
MAMTIM = 1;
#else
#if Fcclk < 40000000
MAMTIM = 2;
#else
MAMTIM = 3;
#endif
#endif
MAMCR = 2;
init_VIC();
return;
}
PLL配置
TargetResetInit函数中有ConfigurePLL这个函数,这里看下LPC2478的PLL:
PLL的框图以及频率输入输出配置的公式
公式中的F_IN是图中pllclkin,F_CCO是图中CCO的输出处288MHz的位置,M是M-DIVIDER,N是N-DIVIDER。
图中PLLC和PLLE是表征PLL的状态的:
/******************************************************************************
** Function name: ConfigurePLL
**
** Descriptions: Configure PLL switching to main OSC instead of IRC
** at power up and wake up from power down.
** This routine is used in TargetResetInit() and those
** examples using power down and wake up such as
** USB suspend to resume, ethernet WOL, and power management
** example
** parameters: None
** Returned value: None
**
******************************************************************************/
void ConfigurePLL ( void )
{
unsigned long MValue, NValue;
if ( PLLSTAT & (1 << 25) )
{
PLLCON = 1;
PLLFEED = 0xaa;
PLLFEED = 0x55;
}
PLLCON = 0;
PLLFEED = 0xaa;
PLLFEED = 0x55;
SCS |= 0x20;
while( !(SCS & 0x40) );
CLKSRCSEL = 0x1;
PLLCFG = PLL_MValue | (PLL_NValue << 16);
PLLFEED = 0xaa;
PLLFEED = 0x55;
PLLCON = 1;
PLLFEED = 0xaa;
PLLFEED = 0x55;
CCLKCFG = CCLKDivValue;
#if USE_USB
USBCLKCFG = USBCLKDivValue;
#endif
while ( ((PLLSTAT & (1 << 26)) == 0) );
MValue = PLLSTAT & 0x00007FFF;
NValue = (PLLSTAT & 0x00FF0000) >> 16;
while ((MValue != PLL_MValue) && ( NValue != PLL_NValue) );
PLLCON = 3;
PLLFEED = 0xaa;
PLLFEED = 0x55;
while ( ((PLLSTAT & (1 << 25)) == 0) );
return;
}
首先需要关闭PLL(因为Boot阶段可能会用到PLL的),然后选择Main OSB作为时钟源,我们的板子使用的是12MHz的,这个时钟源即为上面PLL图中的F_IN,接下来通过PLL_MValue和PLL_NValue分别配置好M和N的值,这样F_CCO就可以计算出来了。接着是配置好USB和系统时钟的分频。最后在检查确认参数是否配置成功。
其中PLL_MValue,PLL_NValue,CCLKDivValue,USBCLKDivValue的值在使用实际值换算时都需要加上1的。这样,可以得出这段PLL config代码的时钟输出为:
F_CCO = (2 * 12 * 12)/1 = 288 MHz
F_USB = F_CCO/6 = 48 MHz
F_CCLK = F_CCO/6 = 48 MHz