2019-03-25 20:19发布
原帖由 zhdphao 于 2010-7-2 07:06 发表 系统振荡器、IRC和看门狗振荡器都是内部的。 XTALIN和 XTALOUT接晶振,是系统振荡器用的。 主时钟可以选择来自:系统振荡器、IRC和看门狗振荡器或者系统PLL。 系统PLL可以由系统振荡器、IRC驱动。 LPC1343还 ...
最多设置5个标签!
感谢感谢!呵呵,比较清楚了,再次感谢!CLKIN和CLKOUT应该是XTALIN和XTALOUT,写错了。。。
要实现对系统时钟的配置,时钟配置图是必须要看懂的!因为它比文字更具有参考价值,看上这张图配置时钟,绝对不会出现漏洞!(我建议你把这张图打印出来贴到你的墙上,我就是这么做的,你看着办吧!)接下来,我将一步一步引领你彻底看懂这张“时钟配置图”。
注意了,要开始讲图了!(这张图就是数据手册说的时钟产生单元:CGU(Clock generation unit))
LPC1114内部含有3个时钟振荡器:系统振荡器,IRC振荡器,看门狗振荡器。系统振荡器就是需要配合外部晶振工作的振荡器(这是任何一款单片机都有的);IRC振荡器就是内部RC振荡器,就是我在上面“总览LPC1114”中提到的那个LPC1114一上电就默认选择的12MHz时钟振荡器,它的精度没有配合外部晶振的系统振荡器高;看门狗振荡器就是给看门狗提供的时钟振荡器!这么说大家明白了吧,在接下来的叙述里面,一提到系统振荡器就是指利用外部晶振的时钟振荡器,IRC振荡器就是指LPC1114的内部时钟振荡器,可不要搞混了哦!
我们先从图的中心点看起,找到“主时钟”三个字,看“主时钟”的左面,有四条线到了“主时钟”的框上,这四条线就是“主时钟”的来源,它们分别是:IRC振荡器,看门狗振荡器,倍频之前的时钟(sys_pllclkin)和倍频之后的时钟(sys_pllclkout)。也就是主时钟可以在这四个时钟源当中选择一个做为主时钟!通过操纵(人家专业名词不叫“操纵”,叫“访问”)“主时钟源选择寄存器(MAINCLKSEL)”实现。这个32位的主时钟源选择寄存器MAINCLKSEL只用到了两位(谁让两位就可以表示四种状态呢!),剩下的全都是保留位,如下:
位(bit) 符号 值 描述 复位值
1:0 SEL 00 选择IRC振荡器 00
01 选择输入到PLL之前的时钟
10 选择看门狗振荡器
11 选择PLL之后的时钟
31:2 - - 保留 0
看复位值,系统默认情况下就是选择IRC振荡器作为系统的主时钟的。我们为了让LPC1114发挥出它最大的性能,就喜欢选择PLL(PLL就是倍频的意思)后的时钟,在程序中这样写:
SYSCON->MAINCLKSEL = 0x00000003; //主时钟源选择PLL后的时钟
接下来看图上,找到“系统PLL”方框,看它左面倒梯形方框的左面,有三条线,这三条线就是可以做为倍频时钟源的时钟源。这三个时钟源分别是:IRC振荡器,系统振荡器,看门狗振荡器。这不就是LPC1114的三个时钟振荡器么,原来它们都可以做为PLL的时钟源!该选择谁捏?这就要操纵“系统倍频时钟源选择寄存器(SYSPLLCLKSEL)”了。这个32位的寄存器也是只用到了两位:
(两位就可以表示四种状态了,三个状态当然是绰绰有余!)
位(bit) 符号 值 描述 复位值
1:0 SEL 00 选择IRC振荡器 00
01 选择系统振荡器
10 选择看门狗振荡器
11 保留
31:2 - - 保留 0
看复位值,系统默认情况下就是选择IRC振荡器作为PLL输入时钟源的。既然我们外部安插了精确的12M晶振,就是想把它做为时钟源的,选择上面表格当中的01,就是选择了外部12M晶振!(我在先前提到过,“系统振荡器”就是代表外部的晶振,为了防止看的不仔细的朋友存在,我还是再说一遍吧!)
程序中这样写:
SYSCON->SYSPLLCLKSEL = 0x00000001; //PLL时钟源选择“系统振荡器”
当然,操作顺序应该是先选择PLL的时钟源,再选择主时钟源!
到现在,“主时钟”左面的部分就看完了,接下来看“主时钟”右面的!
右面部分从上往下看,首先呢,是“系统时钟分频器”方框,方框的右面横线上写着“系统时钟”四个字。怎么样!迷惑了吧!这里方框中所提到的“系统时钟分频器”其实就是“系统AHB时钟分频器(SYSAHBCLKDIV)”。这个寄存器的名字会把好多人迷惑的!因为这个分频器可不仅仅给AHB(LPC1114的AHB只有GPIO,关于什么是AHB,什么是APB,去百度搜一下吧!介绍需要两页纸哦!)提供时钟的,它除了给AHB提供时钟,还给内核,存储器以及APB提供时钟。一定意义上说,它就是“系统时钟分频器”了,给这个寄存器写0,LPC1114就不工作了;给这个寄存器写1,LPC1114的系统时钟就是主时钟除以1;写2,LPC1114的系统时钟就是主时钟除以2,以此类推!假如把外部晶振倍频了4倍作为主时钟,主时钟就是48MHz,对SYSAHBCLKDIV写4,系统时钟就是12MHz。这时候有人就会有疑问了:“神经病啊!既然都倍频起来了,还要缩小”!其实这是因为有时候我们的电路板上的其它芯片不能够在很快的频率下工作,否则就会出错,比如无线通信芯片NRF24L01的速率就不能超过10MHz,所以某些时候,需要多分频了。规定最多可以分频255,所以你就可以想到,这个寄存器只用8位就可以了:
位(bit) 符号 值 描述 复位值
7:0 DIV 00000000 关闭系统时钟 00000001
00000001 用1除
00000010 用2除
......
......
11111111 用255除
31:8 - - 保留 0
一般情况下,我们写1,程序如下:(这条语句可以不用写,因为默认值就是1)
SYSCON->SYSAHBCLKDIV = 0x01; //AHB时钟分频值为1
再往下看图,数一下,有6个分频器,这6个分频器是:SSP0分频器,SSP1分频器,UART分频器,SysTick分频器,看门狗分频器和CLKOUT引脚分频器。
这些分频器寄存器和SYSAHBCLKDIV是一样的,都是用了8位,都是可以最多分频255,我这里就不把表格画出来了,唯一不同的是,这6个分频器寄存器的复位值为0,而不是1。也就是说,在默认情况下,这些外设都是不工作的(没有时钟怎么工作!)这完全是为了节能做贡献,不用就不让它浪费电,用的时候再开!
看最后两个分频器!通过上面的介绍,你现在也可以看懂了,图上说:看门狗的时钟源可以有3个来源,不仅仅只有“看门狗振荡器”可以给它提供,还可以用主时钟或是IRC振荡器!多么灵活的LPC1114呀!
LPC1114上的第四引脚是:PIO0_1/CLKOUT/CT32B0MAT2。这个脚可以当做P0.1脚,CLKOUT引脚和32位定时器的输出脚。CLKOUT引脚,顾名思义,它是用来输出时钟的,输出时钟有什么用?
用处1:给别的需要时钟的芯片提供时钟;
用处2:用示波器观察此引脚上的频率可以判断你写的时钟配置程序是否正确。
这个引脚在默认的情况下是P0.1脚,假如你要看看到底有没有把外部的12MHz晶振倍频到48MHz,你可以把这只脚配置为CLKOUT引脚,用示波器观察观察!
由图中可知,它可以选择IRC振荡器,系统振荡器,看门狗振荡器以及主时钟源作为时钟源,选择谁作为它的时钟源,你就可以看到谁的频率到底是多少了。
(在下面会给出实现的程序,不要急哦!)我曾经用这个脚观察了一下IRC振荡器的频率,值在12.01MHz和12.00MHz之间来回跳!后来又看了一下外部晶振的频率,稳稳的显示12.00MHz。
到现在,这张图就看完了,你也应该看懂了!
除了上面提到的“选择寄存器”,还需要有“使能寄存器”的配合才能使选择的时钟源起作用。下面是一个典型的时钟配置函数:
/***********************************************/
/* 函数功能:配置系统时钟 */
/* 说明: 选择外部12M晶振作为系统时钟,并通*/
/* 过倍频器(PLL)把时钟倍频4倍 */
/* 注意: 使用其它值晶振时修改倍频值,最后 */
/* 时钟要满足<=50MHz */
/***********************************************/
void SysCLK_config(void)
{
uint8 i;
/*执行以下代码选择外部12M晶振作为时钟源*/
SYSCON->PDRUNCFG &= ~(1 << 5); //系统振荡器上电
SYSCON->SYSOSCCTRL = 0x00000000; //振荡器未被旁路,1~20Mhz频率输入
for (i = 0; i < 200; i++) __nop(); //等待振荡器稳定
SYSCON->SYSPLLCLKSEL = 0x00000001; //PLL时钟源选择“系统振荡器”
SYSCON->SYSPLLCLKUEN = 0x01; //更新PLL选择时钟源
SYSCON->SYSPLLCLKUEN = 0x00; //先写0,再写1达到更新时钟源的目的
SYSCON->SYSPLLCLKUEN = 0x01;
while (!(SYSCON->SYSPLLCLKUEN & 0x01)); //确定时钟源更新后向下执行
/*执行以下代码倍频为48MHz*/
SYSCON->SYSPLLCTRL = 0x00000023; //设置M=4;P=2; FCLKOUT=12*4=48Mhz
SYSCON->PDRUNCFG &= ~(1 << 7); //PLL上电
while (!(SYSCON->SYSPLLSTAT & 0x01)); //确定PLL锁定以后向下执行
/*主时钟源选择倍频以后的时钟*/
SYSCON->MAINCLKSEL = 0x00000003; //主时钟源选择PLL后的时钟
SYSCON->MAINCLKUEN = 0x01; //更新主时钟源
SYSCON->MAINCLKUEN = 0x00; //先写0,再写1达到更新时钟源的目的
SYSCON->MAINCLKUEN = 0x01;
while (!(SYSCON->MAINCLKUEN & 0x01)); //确定主时钟锁定以后向下执行
SYSCON->SYSAHBCLKDIV = 0x01; //AHB时钟分频值为1,使AHB时钟设置为48Mhz
}
程序详解:
在看程序详解之前,你最好先看一遍程序。
(如果你是一位刚刚从51单片机接触ARM单片机的朋友,你会发现这个函数里面的语句书写方式完全和以前写51程序不一样啊,以前给51的寄存器写值,是用这么一种形式:
SBUF = 0X88;
而现在是用这么一种形式:
SYSCON->MAINCLKSEL=0X00000001;
这里为什么不直接写成:
MAINCLKSEL=0X00000001;
这其实是因为在NXPLPC11XX.H文件中对系统寄存器的定义采用了结构体(Struct)的形式。现在,你可以在打开51单片机寄存器的定义文件REG51.H文件看一下,它对寄存器的地址定义是这样的:
sfr SBUF = 0x99;
现在你再打开一下LPC1114对寄存器地址定义的NXPLPC11XX.H文件!全都是结构体的定义,而且是纯C语言写的,再也找不到“sfr”这样的C51语言了。关于NXPLPC11XX.H文件请看瑞嵌制作的《NXPLPC11XX.H文件详解》。)
(在以后的程序中,我们会经常看到&=~(1<<3)和|=(1<<3);这样的句子,这些句子是对位操作用的。因为我们经常要对32位寄存器的某一位操作,还同时不影响其它位的值,所以才有了上面这样的形式。比如我们说我们要对某个寄存器的bit5(注意:可不是第5位,位是从0开始的)写0,这样写:
寄存器&=~(1<<5);
对寄存器的bit5写1,这样写:
寄存器|=(1<<5);
现在运用你的C语言知识分析一下,把十进制的1写成二进制32位数就是:
00000000000000000000000000000001
(1<<5)就是把1右移5下,左面补零,执行完这句话以后数就变成:
00000000000000000000000000100000
~(1<<5)就是再把这个数反相:
11111111111111111111111111011111
最后呢!再把这个数&给寄存器,&的操作即是遇到0与1等于0,1与0或1都还是1,所以执行完以后,除了bit5被改成了0,其它的位都没有变。按照相同的方法,你可以分析一下对bit5写1的操作。)
现在,我们首先来看一下函数里的第一个语句是对PDRUNCFG寄存器操作,如果你的英语好的话,一眼就看出来这个寄存器是干嘛的了,就是“掉电配置寄存器”,之所以不叫“上电配置寄存器”是因为它是对某位写“1”掉电,写“0”上电。这个寄存器的描述请看官方数据手册第三章。看这个寄存器的bit5,该位控制着系统振荡器的上电与掉电,默认是1,就是掉电状态,我们既然已经决定了要用外部晶振作为时钟源,那么现在就该把它上电了,于是就有了这条语句。
接下来这条语句是对SYSOSCCTRL寄存器操作,这个寄存器叫做“系统振荡器控制寄存器”。(在后面的学习中,你会经常看到,系统内部的模块需要好几道门槛配置以后才能用,除了上电,还得控制,有的还需要再允许一下。这样做看似麻烦,其实灵活!)系统控制寄存器只用了2个bit,bit0控制着系统振荡器有没有被旁路,bit1要根据外部晶振的值是多少来写1或0。先说bit0,“被旁路”的意思就是“让它不起作用”;“未被旁路”的意思就是“没有让它不起作用”。写0表示“未被旁路”,写1表示“被旁路”。那么什么时候被旁路呢?答:在有外部的“直接时钟源”的时候。如果你51单片机学的很棒的话,你现在就应该明白了,不明白的那就听我给你解释吧。其实51单片机也有不利用外部晶振而是利用“直接时钟源”的时候,电路图是这个样子的:
这里我们不需要旁路晶振,所以对该位写0。bit1是根据外部晶振的值来定的,对该位写0表示外部晶振频率值在1~20MHz范围内,写1表示外部晶振频率值在15~50MHz范围内。在我们的开发板上用的晶振为12MHz,所以对该位写0。
再往下是一条短暂延时程序,利用__nop();实现。给它一点时间完成任务。
再接下来的5条语句你可以把它看成一个整体,对PLL时钟源的更新都是这个样子的。关于SYSPLLCLKSEL寄存器,前面已经讲过了。SYSPLLCLKUEN是PLL时钟源更新允许寄存器,根据官方数据手册上的规定,要想实现更新,需要对该寄存器toggle一下,也就是对该寄存器先写0,再写1。while语句等待我们刚才写的1运输到SYSPLLCLKUEN里面。时钟源的更新往往是需要一定时间的。
接下来,就该把选择的时钟源翻倍了。SYSPLLCTRL是系统倍频控制寄存器,通过它可以确定倍频的倍数。倍频器是一个很有特点的东西。它除了可以用在单片机当中,还可以用在好多需要它的地方,比如射频无线芯片当中可以用它来提高发射功率。倍频器运用了模拟电子技术和数字电子技术。有时集成到芯片当中,有时单独做成一块芯片!关于LPC1114的倍频器(PLL)的详细描述,请看官方数据手册第三章第九节。SYSPLLCTRL的bit0~bit4确定M值,bit5和bit6确定P值,bit7是DIRECT位,bit8是BYPASS位。其它位保留。bit7和bit8我们现在还不深究(要深究的话,还需要好好学习倍频器的结构),只需要知道它俩是来控制PLL的工作模式的,我们一般让PLL工作在“普通模式”下,保持这俩位的默认值就可以。那么现在只剩M和P了。在普通模式下,PLL输出频率的计算公式如下所示:
看到上式,你可能会产生一个疑问:直接用M乘以PLL的输入频率Fclkin不行吗?答案当然不行!为什么要确定P值呢?这个是PLL的机构决定的,在普通模式下,输出频率实际上是由FCCO产生的,而为了能让PLL正常工作,FCCO需要在156~320MHz之间。现在,我们知道PLL的输入频率Fclkin的值为12MHz,LPC1114的允许最大工作频率为50MHz,现在我们只能把它倍频四倍到48MHz了,所以M值定位4。根据数据手册上的规定,P可以定为四个值,即1,2,4,8。这里只有当P=2的时候,FCCO的值为48*2*2=192,在156~320之间。所以,我们一般情况下,就选M=4,P=2了。SYSPLLSTAT是倍频状态寄存器,专门用来看PLL有没有锁定的,它是一个只读寄存器。
再往下的5条语句是更新主时钟用的。和上面提到的更新PLL时钟的语句如出一辙,我就不多讲了,相信大家现在已经能看懂了!
该函数的最后一句话,就是给SYSAHBCLKDIV写1,确定分频值为1。这个寄存器在前面已经很详细的讲过了,这里就不啰嗦了!
到此!这个函数就都讲完了。很好理解吧!这个函数就是每个工程里main函数里都会出现的初始化函数了。而且是必须的!为了使用方便,我已经把这个函数放到了NXPLPC11XX.C文件里面,在你写的main函数里直接调用函数名就可以了。
一周热门 更多>