NXP

[ARM7--LPC2478]带USB功能的启动代码的分析

2019-07-12 13:44发布

简述

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; /* remap to internal RAM */ #endif #ifdef __DEBUG_FLASH MEMMAP = 0x1; /* remap to internal flash */ #endif #if USE_USB PCONP |= 0x80000000; /* Turn On USB PCLK, PCONP寄存器通过bit来控制其中的外设模块的使能与否,类似STM32中的XXXCmd接口的作用 */ #endif /* Configure PLL, switch from IRC to Main OSC */ ConfigurePLL(); /* Set system timers for each component */ #if (Fpclk / (Fcclk / 4)) == 1 PCLKSEL0 = 0x00000000; /* PCLK is 1/4 CCLK */ PCLKSEL1 = 0x00000000; #endif #if (Fpclk / (Fcclk / 4)) == 2 PCLKSEL0 = 0xAAAAAAAA; /* PCLK is 1/2 CCLK */ PCLKSEL1 = 0xAAAAAAAA; #endif #if (Fpclk / (Fcclk / 4)) == 4 PCLKSEL0 = 0x55555555; /* PCLK is the same as CCLK */ PCLKSEL1 = 0x55555555; #endif /* Set memory accelerater module*/ MAMCR = 0; #if Fcclk < 20000000 MAMTIM = 1; #else #if Fcclk < 40000000 MAMTIM = 2; #else MAMTIM = 3; #endif #endif MAMCR = 2; // GPIOResetInit(); 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的状态的:
这里写图片描述

ConfigurePLL

/****************************************************************************** ** 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; /* Get PLLC: PLL connected or not. */ if ( PLLSTAT & (1 << 25) ) { /* PLL connected when boot loader */ PLLCON = 1; /* Enable PLL, disconnected */ /* Feed sequence make PLLCON and PLLCFG take effect */ PLLFEED = 0xaa; PLLFEED = 0x55; } PLLCON = 0; /* Disable PLL, disconnected */ PLLFEED = 0xaa; PLLFEED = 0x55; SCS |= 0x20; /* Enable main OSC */ while( !(SCS & 0x40) ); /* Wait until main OSC is usable */ CLKSRCSEL = 0x1; /* select main OSC, 12MHz, as the PLL clock source */ /* PLL_MValue = 11, PLL_NValue = 0 */ PLLCFG = PLL_MValue | (PLL_NValue << 16); PLLFEED = 0xaa; PLLFEED = 0x55; PLLCON = 1; /* Enable PLL, disconnected */ PLLFEED = 0xaa; PLLFEED = 0x55; CCLKCFG = CCLKDivValue; /* Set clock divider, 5 */ #if USE_USB USBCLKCFG = USBCLKDivValue; /* usbclk = 288 MHz/6 = 48 MHz */ #endif while ( ((PLLSTAT & (1 << 26)) == 0) ); /* Check lock bit status */ MValue = PLLSTAT & 0x00007FFF; NValue = (PLLSTAT & 0x00FF0000) >> 16; while ((MValue != PLL_MValue) && ( NValue != PLL_NValue) ); PLLCON = 3; /* enable and connect */ PLLFEED = 0xaa; PLLFEED = 0x55; while ( ((PLLSTAT & (1 << 25)) == 0) ); /* Check connect bit status */ 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