时钟输出实例
如何判断时钟是否配置正确,最有效的方式就是让配置好的时钟从某个引脚输出,然后在外部通过频率计或示波器来进行检测,现在就来讨论一下这个实例。要使用前面写好的时钟配置函数,还得结合MDK5开发环境来实现。这里先给出一段完整的时钟配置程序,内容如下:
void SystemInit(void)
{
SysCLK_config(); //调用时钟配置函数
}
int main(void)
{
SystemInit (); //调用系统初始化函数
while(1)
{
; //空循环
}
}
从上述程序中可以看出,主函数的写法和普通单片机开发的没有什么两样,都有一个死循环。由于这里只做时钟配置的仿真,所以在调用时钟配置函数后只进行一个空循环。
在上述代码中需要特别注意三点:1、main函数必须是整型(int型)的;2、在程序结束的最后一行要有一个空行(即要回一下车),否则在编译时会出现警告;3、在项目自动加载的启动文件中规定,程序中必须要有一个系统初始化函数(SystemInit函数),否则程序不能编译链接。所以上述程序虽然只调用了一个时钟配置函数,但仍然要把它放入到系统初始化函数中去供主函数调用。
把上述函数及前面的时钟配置函数写到新建好的main.c主程序文件中,点击编译按钮(或按Ctrl+F7快捷键),会发现编译不能通过,显示有4条错误,提示“uint8_t”、“LPC_SYSCON”、“LPC_IOCON”和“LPC_SWM”没有定义。这是因为程序没有包含一些特定头文件的缘故,在这些头文件内包含了相关变量的申明。所以要让程序顺利编译通过,必须要把这些头文件包含进来。但由于开发LPC824所需要的头文件很多,且有些头文件还有彼此的依赖关系,对于不同的开发环境,定义的名称也不完全一致,因此,为了先快速把第一个程序运行起来,减小学习上的挫折感,这里暂时使用不包含头文件的形式,而是把程序用到的所有代码都罗列出来,这样只要把下面的代码全部复制到开发环境中,就可以顺利通过编译了。
时钟输出的完整程序代码如下:
#define __O volatile
#define __IO volatile
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef struct {
__IO uint32_t SYSMEMREMAP;
__IO uint32_t PRESETCTRL;
__IO uint32_t SYSPLLCTRL;
__I uint32_t SYSPLLSTAT;
__I uint32_t RESERVED0[4];
__IO uint32_t SYSOSCCTRL;
__IO uint32_t WDTOSCCTRL;
__IO uint32_t IRCCTRL;
__I uint32_t RESERVED1;
__IO uint32_t SYSRSTSTAT;
__I uint32_t RESERVED2[3];
__IO uint32_t SYSPLLCLKSEL;
__IO uint32_t SYSPLLCLKUEN;
__I uint32_t RESERVED3[10];
__IO uint32_t MAINCLKSEL;
__IO uint32_t MAINCLKUEN;
__IO uint32_t SYSAHBCLKDIV;
__I uint32_t RESERVED4;
__IO uint32_t SYSAHBCLKCTRL;
__I uint32_t RESERVED5[4];
__IO uint32_t UARTCLKDIV;
__I uint32_t RESERVED6[18];
__IO uint32_t CLKOUTSEL;
__IO uint32_t CLKOUTUEN;
__IO uint32_t CLKOUTDIV;
__I uint32_t RESERVED7;
__IO uint32_t UARTFRGDIV;
__IO uint32_t UARTFRGMULT;
__I uint32_t RESERVED8;
__IO uint32_t EXTTRACECMD;
__I uint32_t PIOPORCAP0;
__I uint32_t RESERVED9[12];
__IO uint32_t IOCONCLKDIV6;
__I uint32_t RESERVED10[6];
__IO uint32_t BODCTRL;
__IO uint32_t SYSTCKCAL;
__I uint32_t RESERVED11[6];
__IO uint32_t IRQLATENCY;
__IO uint32_t NMISRC;
__IO uint32_t PINTSEL0;
__IO uint32_t PINTSEL1;
__IO uint32_t PINTSEL2;
__IO uint32_t PINTSEL3;
__IO uint32_t PINTSEL4;
__IO uint32_t PINTSEL5;
__IO uint32_t PINTSEL6;
__IO uint32_t PINTSEL7;
__I uint32_t RESERVED12[27];
__IO uint32_t STARTERP0;
__I uint32_t RESERVED13[3];
__IO uint32_t STARTERP1;
__I uint32_t RESERVED14[6];
__IO uint32_t PDSLEEPCFG;
__IO uint32_t PDAWAKECFG;
__IO uint32_t PDRUNCFG;
__I uint32_t RESERVED15[111];
__I uint32_t DEVICE_ID;
} LPC_SYSCON_Type;
typedef struct {
__IO uint32_t PIO0_17;
__IO uint32_t PIO0_13;
__IO uint32_t PIO0_12;
__IO uint32_t PIO0_5;
__IO uint32_t PIO0_4;
__IO uint32_t PIO0_3;
__IO uint32_t PIO0_2;
__IO uint32_t PIO0_11;
__IO uint32_t PIO0_10;
__IO uint32_t PIO0_16;
__IO uint32_t PIO0_15;
__IO uint32_t PIO0_1;
__I uint32_t RESERVED0;
__IO uint32_t PIO0_9;
__IO uint32_t PIO0_8;
__IO uint32_t PIO0_7;
__IO uint32_t PIO0_6;
__IO uint32_t PIO0_0;
__IO uint32_t PIO0_14;
__I uint32_t RESERVED1;
__IO uint32_t PIO0_28;
__IO uint32_t PIO0_27;
__IO uint32_t PIO0_26;
__IO uint32_t PIO0_25;
__IO uint32_t PIO0_24;
__IO uint32_t PIO0_23;
__IO uint32_t PIO0_22;
__IO uint32_t PIO0_21;
__IO uint32_t PIO0_20;
__IO uint32_t PIO0_19;
__IO uint32_t PIO0_18;
} LPC_IOCON_Type;
typedef struct {
__IO uint32_t PINASSIGN0;
__IO uint32_t PINASSIGN1;
__IO uint32_t PINASSIGN2;
__IO uint32_t PINASSIGN3;
__IO uint32_t PINASSIGN4;
__IO uint32_t PINASSIGN5;
__IO uint32_t PINASSIGN6;
__IO uint32_t PINASSIGN7;
__IO uint32_t PINASSIGN8;
__IO uint32_t PINASSIGN9;
__IO uint32_t PINASSIGN10;
__IO uint32_t PINASSIGN11;
__I uint32_t RESERVED0[100];
__IO uint32_t PINENABLE0;
} LPC_SWM_Type;
#define LPC_SYSCON_BASE 0x40048000UL
#define LPC_IOCON_BASE 0x40044000UL
#define LPC_SWM_BASE 0x4000C000UL
#define LPC_SYSCON ((LPC_SYSCON_Type *) LPC_SYSCON_BASE)
#define LPC_IOCON ((LPC_IOCON_Type *) LPC_IOCON_BASE)
#define LPC_SWM ((LPC_SWM_Type *) LPC_SWM_BASE)
{
uint8_t i;
LPC_SYSCON->PDRUNCFG &= ~(1 << 5); //给系统振荡器上电
LPC_SYSCON->SYSOSCCTRL = 0x00000000; //系统振荡器未旁路,1~12MHz输入
for(i = 0; i < 200; i++) __nop(); //延时等待振荡器稳定
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 18); //使能IOCON时钟
LPC_IOCON->PIO0_8 &= ~(3 << 3); //把P0_8引脚配置为无上下拉电阻方式
LPC_IOCON->PIO0_9 &= ~(3 << 3); //把P0_9引脚配置为无上下拉电阻方式
LPC_SWM->PINENABLE0 &= ~(3 << 6); //把P0_8、P_9引脚配置为XTALIN、XTALOUT引脚
LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 18); //禁止IOCON时钟
LPC_SYSCON->SYSPLLCLKSEL = 0x00000001; //PLL输入选择外部晶体振荡
LPC_SYSCON->SYSPLLCLKUEN = 0x00;
LPC_SYSCON->SYSPLLCLKUEN = 0x01; //先写0后写1更新时钟源
while(!(LPC_SYSCON->SYSPLLCLKUEN & 0x01)); //等待更新完成
LPC_SYSCON->SYSPLLCTRL = 0x00000041; //M=2、P=4,倍频后的时钟为24MHz
LPC_SYSCON->PDRUNCFG &= ~(1 << 7); //给PLL上电
while(!(LPC_SYSCON->SYSPLLSTAT & 0x01)); //等待PLL锁定
LPC_SYSCON->MAINCLKSEL = 0x00000003; //主时钟选择PLL倍频后的时钟
LPC_SYSCON->MAINCLKUEN = 0x00;
LPC_SYSCON->MAINCLKUEN = 0x01; //先写0后写1更新时钟源
while(!(LPC_SYSCON->MAINCLKUEN & 0x01)); //等待更新完成
LPC_SYSCON->SYSAHBCLKDIV = 0x00000001; //AHB为1分频,AHB时钟为24MHz
}
void SystemInit(void)
{
SysCLK_config(); //调用时钟配置函数
}
void CLKOUT_EN(uint8_t CLKOUT_DIV)
{
LPC_SWM->PINASSIGN11 = 0xFF18FFFF; //CLKOUT输出引脚为P0_24
LPC_SYSCON->CLKOUTDIV = CLKOUT_DIV; //输出分频为48/ CLKOUT_DIV
LPC_SYSCON->CLKOUTSEL= 0x00000003; //CLKOUT时钟选择主时钟
LPC_SYSCON->CLKOUTUEN =0x00;
LPC_SYSCON->CLKOUTUEN =0x01; //先写0后写1更新时钟源
while(!(LPC_SYSCON->CLKOUTUEN & 0x01)); //等待更新完成
}
int main(void)
{
SystemInit();
CLKOUT_EN(24);
while(1)
{
;
}
}
先提示一点,在MDK环境中,程序的最后一行都应该多一个空行,否则编译会出现警告。
从上面的程序中可以看到,变量的申明都是以结构体的形式出现的,较为复杂,现在暂时不去讨论它。先来看一个多出的新函数CLKOUT_EN,它的作用是配置要输出哪个时钟、在哪个引脚上输出、以多少分频输出。如果系统使用的是24MHz的主时钟,分频系数CLKOUT_DIV取24,则该函数执行完后,LPC824的PIO0_24脚就会有1MHz的脉冲输出。(2)第8到31位为保留位
(2)第2到31位为保留位。
(2)第1到31位为保留位。