包里有关时钟配置的例程一共有9个,其实弄好前面几个,后面只是大同小异而已。
(1)首先,第一个,最简单的一个,因为它根本就没有配置时钟......不过,它还是很有用,因为它给我们呈现了单片机上电后的时钟状况。
// ACLK = REFO = 32.768kHz, MCLK = SMCLK = Default 1MHz
#include <msp430.h>
int main(void)
{
volatile unsigned int i;
WDTCTL = WDTPW+WDTHOLD; // Stop WDT
P1DIR |= BIT1; // P1.1 output
P1DIR |= BIT0; // ACLK set out to pins
P1SEL |= BIT0;
P2DIR |= BIT2; // SMCLK set out to pins
P2SEL |= BIT2;
P7DIR |= BIT7; // MCLK set out to pins
P7SEL |= BIT7;
while(1)
{
P1OUT ^= BIT1;
__delay_cycles(60000); // Delay
}
}
复制代码
博主我刚开始学时就很纠结这PUC后的默认时钟状况:
After a PUC, the UCS module default configuration is:
• XT1 in LF mode is selected as the oscillator source for XT1CLK. XT1CLK is selected for ACLK.
• DCOCLKDIV is selected for MCLK.
• DCOCLKDIV is selected for SMCLK.
• FLL operation is enabled and XT1CLK is selected as the FLL reference clock, FLLREFCLK.
• On devices that have XIN and XOUT shared with general-purpose I/O, XIN and XOUT pins are set to
general-purpose I/Os and XT1 remains disabled until the I/O ports are configured for XT1 operation. If
XIN and XOUT are not shared with general-purpose I/O, XT1 is enabled.
• When available, XT2IN and XT2OUT pins are set to general-purpose I/Os and XT2 is disabled.
刚上电后,说是ACLK和FLL默认以XT1为源,但你又不打开XT1,那问题纠结了我很久,用示波器测试测得ACLK是大约327668Hz,但使用手册后面有说这两个时钟在XT1LF失效后会转向REFOCLK,它是内部的32768Hz的时钟(那时我还没看- -||)。
真相就是这么一回事:
这是我用CCS仿真调试时暂停截的图,图示程序已经执行到while(1)里了,打开寄存器窗口看到XT1LFOFFG和DCOFFG置1了。前者被置位就是因为ACLK和FLL处于默认状态一直在向XT1取源,而XT1被禁止失效所以XT1LFOFFG被置位了,而程序还能跑就是因为XT1LF失效时它们两个时钟暂时向REFOCLK取源。后者为什么也置1了呢?它没有向XT1取源啊。呵呵,上一章说过,默认状态的DCO由FLL稳定,而FLL的时钟源XT1出问题所以DCOFFG就置1了。
因此,即使大家不需要调整单片机的时钟,在程序的开头也请先配置一下时钟,养成这个习惯,至少把XT1打开,因为XT1比REFOCLK稳定多了。
(2)第二个例程,很值得去折腾,它设置了FLL来稳定DCO到8MHz。噢不!是16MHz.
// ACLK = REFO = 32kHz, MCLK = SMCLK = 8MHz
#include <msp430.h>
int main(void)
{
volatile unsigned int i;
WDTCTL = WDTPW+WDTHOLD; // Stop WDT
P1DIR |= BIT1; // P1.1 output
P1DIR |= BIT0; // ACLK set out to pins
P1SEL |= BIT0;
P2DIR |= BIT2; // SMCLK set out to pins
P2SEL |= BIT2;
P7DIR |= BIT7; // MCLK set out to pins
P7SEL |= BIT7;
UCSCTL3 = SELREF_2; // Set DCO FLL reference = REFO
UCSCTL4 |= SELA_2; // Set ACLK = REFO
UCSCTL0 = 0x0000; // Set lowest possible DCOx, MODx
// Loop until XT1,XT2 & DCO stabilizes - In this case only DCO has to stabilize
do
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
// Clear XT2,XT1,DCO fault flags
SFRIFG1 &= ~OFIFG; // Clear fault flags
}while (SFRIFG1&OFIFG); // Test oscillator fault flag
__bis_SR_register(SCG0); // Disable the FLL control loop
UCSCTL1 = DCORSEL_5; // Select DCO range 16MHz operation
UCSCTL2 |= 249; // Set DCO Multiplier for 8MHz
// (N + 1) * FLLRef = Fdco
// (249 + 1) * 32768 = 8MHz
__bic_SR_register(SCG0); // Enable the FLL control loop
// Worst-case settling time for the DCO when the DCO range bits have been
// changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
// UG for optimization.
// 32 x 32 x 8 MHz / 32,768 Hz = 250000 = MCLK cycles for DCO to settle
__delay_cycles(250000);
while(1)
{
P1OUT ^= BIT0; // Toggle P1.0
__delay_cycles(600000); // Delay
}
}
复制代码
18和19行,将这两个时钟的时钟源取为了REFO,这样,就没有任何时钟在向XT1请求时钟源,DCO也没有问题,故而下面的do{}while语句能通过。这个语句就是检测时钟有没有问题,如果晶体正在起振,还没有达到工作状态,会在这个循环里等待晶体起振完毕。XT2OFFG,XT1LFOFFG,DCOFFG,OFIFG都必须由软件清零。另外,POR后,OFIFG是被置位了的。
比如我把UCSCTL4 |= SELA_2;注释掉,这样,ACLK也会向REFO申请时钟源,但程序运行下来就并不一样了。
它会卡在这个循环里出不去,和上面(1)的情况一样,ACLK的寄存器里的默认值还是在向XT1申请时钟源,XT1不干活,XT1LFOFFG就置位了。
20行那里其实可以不配置,因为默认就已经是0x0000,而FLL会自动调整UCSCTL0。
这个程序的精髓就在于30-35这4行(啊咧?4行?),这是配置FLL的标准流程,在调整有关FLL的寄存器前,无比要先禁止FLL,不然可能会产生不可预料的结果。
上一章说到,用FLL稳定DCO,只要设置好FLL的时钟,再就是配置DCORSEL和FLLN就好了。利用公式:
·fDCOCLK= D × (N + 1) × (fFLLREFCLK÷ n)
·fDCOCLKDIV= (N + 1) × (fFLLREFCLK÷ n)
就可以计算出所需的值,纳尼?这和注释里的不一样?呵呵,是的,真的是注释错了。程序里(249 + 1) * 32768 = 8MHz计算的是fDCOCLKDIV!而不是注释里写的fDCOCLK!而注释里的那条看起来像公式的根本就不是个东西!
那为什么我测出来的真的是它说的大约8MHz呢?呵呵,因为它说错了,SMCLK并没有使用DCOCLK,它还是默认地使用了DCOCLKDIV,所以那条式子计算出来的的的确确是SMCLK的频率,但因为它错了两次,1.(N + 1) * FLLRef = Fdco.2.SMCLK使用DCOCLK。所以就被它绕回来了。没想到堂堂TI的例程,也会出错。被我这菜鸟抓到了。
为了揭穿TI的真面目,我把19行改为了
UCSCTL4 &= ~SELS_4; //取消默认的SELS_4
UCSCTL4 |= SELA_2 + SELS_3; //设置SMCLK = DCO
复制代码
测了一下SMCLK,16.828MHz,是接近计算值,结论正确!所以大家在计算使用DCO时要注意DCODIV的存在,在我感觉,DCODIV更像是老大。
而后我把目光盯向了DCORSEL,因为看的资料一直在说只要把公式里面4个量设置好就可以得到想要的DCO或DCODIV频率。那还设置DCORSEL干嘛?
于是,我把UCSCTL1 = DCORSEL_5;注释掉了,RCORSEL默认值是RCORSEL_2,再调试了一遍。
发现DCOFFG被置位了,而SMCLK得频率只有2.456MHz,不是计算值呀!
DCORSEL的作用是设定DCO的频率范围,详见datasheet里的下面这表:
可以看到,DCORSEL = 2时,DCO最大最大也就7.38MHz,没有达到我们要的16点多MHz。
也就是说,就算是使用FLL来稳定DCO,DCO大概的范围即DCORSEL还是需要设置的!
于是,我有把DCORSEL = 3,调了一次
DCOOFFG还是被置位了,SMCLK频率只有4.654MHz,看表,DCORSLE = 3时DCO最大是14.0,还没有达到计算值。
在把DCORSEL设置为4之后,一切都正常了,5,6,7都试过没问题,计算值16.384MHz包含在这些范围里面。所以结论是正确的!还是那句话:就算是使用FLL来稳定DCO,DCO大概的范围即DCORSEL还是需要设置的!这个范围只需要包含目标值就行。
(3)例程2终于被我折腾完了,后面的例程不过是换了下时钟源换了下频率,已经没多少研究的价值,不过这些是我都看了一遍之后的结论。
在例程3和4里,还是设置FLL,不过是3里面换了下数值,do{}while放下面而已,还是使用DCODIV,注释还是错的......
4里面启用了XT1,用了这3句代码:
P5SEL |= BIT4+BIT5; // Port select XT1
UCSCTL6 &= ~(XT1OFF); // XT1 On
UCSCTL6 |= XCAP_3; // Internal load cap
复制代码
打开XIN和XOUT
置0XT1OFF
老实说电容我不知道怎么选,不过这里选了12pf,那就12pf吧。其实上电后XCAP里的值默认就是XCAP_3,所以这里可以不配置
启用了XT1别忘了要等它起振:
// Loop until XT1 fault flag is cleared
do
{
UCSCTL7 &= ~XT1LFOFFG; // Clear XT1 fault flags
}while (UCSCTL7&XT1LFOFFG); // Test XT1 fault flag
复制代码
其他的没什么了,说一下XT1的牛X,这里ACLK和SMCLK的计算值分别是
ACLK = 32768Hz, SMCLK = 2457600Hz。
我测出来的是
ACLK = 32767.5Hz, SMCLK = 2457560Hz。
非常高的精密度,而且非常稳定,数值就没有变化过。相比起使用REFO时的频率总是会有百分之零点几到一点几的误差而且会有波动,当然是XT1更胜一筹。
(4)好了,写了这么多,能分享的就是这些了,关于时钟这一块,就到此结束吧。 |