在进行MSP430单片机的编程时,时钟的运用很重要,MSP430一共有5个时钟源,分别是外接的XT1和XT2时钟源,介绍如下:
1.XT1CLK:低频/高频振荡器,我的MSP430F5336使用的就是32768HZ的晶振
2.VLOCLK:内部超低功耗低低频振荡器,典型频率是12khz;
3.REFOCLK:内部调整低频参考振荡器,典型值为32768hz;
4.DCOCLK:内部数字时钟振荡器,可由FLL稳定后得到;
5.XT2CLK:高频外界振荡器,我F5336接的是20MHZ晶振;
以上五个时钟大家可以理解为晶振,有的是内部自带的,如2,3,4,这三个,有的是外接自己设计的,如XT1和XT2,当我们需要用单片机处理一些高速计算时,例如做图像处理或者一些迭代计算,此时要求计算速度比较高,所以可以选用外界的XT2或者DCOCLK调整后得到。如果你需要精确定时或者计时,可以使用REFOCLK或者VLOCLK,因为他们频率较为稳定,计时精确。用的比较多的是32768HZ的频率。DCOCLK的调整比较灵活,有专门的锁频环公式,在datasheet上可以找到DCO频率的设置条件表格,可以根据它直接调整出适合的频率。
但是MSP430的时钟调用却没有那么简单,你可以想象时钟设计为做菜,上面的五个时钟只是五种最基本的食材,通过寄存器的一些特殊调用,可以利用上面的时钟源做出三道菜,分别是ACLK辅助时钟,MCLK主时钟,SMCLK子系统时钟,而我们编程所用的时钟源(也就是我们要吃的菜),是这三种时钟提供的。所以举个栗子说,我需要一个32768hz的时钟来精确定时,同时需要一个1.2M或者的时钟来控制串口通信的波特率,同时我希望我的MCU频率可以到20M,这样我恶意设置MCLK=20Mhz(MCLK时钟是系统时钟),设置ACLK时钟为32768HZ,时钟计时是我选用ACLK为时钟源,设置SMCLK时钟为1.2Mhz,串口通信时选择时钟源为SMCLK,但是我们如何使ACLK为32768hz,使MCLK为20Mhz,使SMCLK为1.2M呢,这就是通过调配这三个时钟的原料为上面的五种不同晶振。所以实际上我们在程序中一旦定下了ACLK,MCLK,SMCLK的频率,之后基本都不用管之前的5个晶振,因为都是和这三个时钟打交道。而时钟的控制一般写成一个初始化函数,在程序中调用一次就可以了。刚刚开始接触时很容易搞不清楚时钟,觉得很复杂,其实弄清楚原理还是很简单的。
现在我用一些程序来说明时钟的用法,以下我看到的一份MSP430G2553的时钟程序库,进行了一些修改,其中的函数功能等均有注释,至于那样设置的原理可以看UserGuide的寄存器说明,里面一些特定的寄存器的位代表了一些信息,例如分频系数,时钟源等等,大家可以自己参考用户手册一句一句翻译对照,相信很快能弄懂时钟的设定原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
#include <msp430g2553.h> /*DCOCTL 寄存器*/ /******************************************************************************** * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 * DCO.2 CCO.1 DCO.0 MOD.4 MOD.3 MOD.2 MOD.1 MOD.0 * DCO.0——DCO.2定义8种频率之一,可分段调节DCOCLK频率,相邻两种频率相差10%。 * 而频率由注入直流发生器的电流定义。 * MOD.O——MOD.4定义在32个DCO周期中插入的fdco+l周期个数,而在余下的DCO周期 * 中为fDco周期,控制切换DCO和DCO+1选择的两种频率。如果DCO常数为7,表示已 * 经选择最高颂率,此时不能利用MOD.O-MOD.4进行频率调整。 * ********************************************************************************/ /*BCSCTL1 寄存器*/ /********************************************************************************** * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 * XT2OFF XTS DIVA.1 DIVA.0 XT5V Rse1.2 Rse1.1 Rse1.0 *XT2OFF控制 XT2 振荡器的开启与关闭。 *XT2OFF=0,XT2振荡器开启; *XT2OFF=1,XT2振疡器关闭(默认XT2关闭)。 *XTS控制 LFXTl 工作模武,选择需结合实际晶体振荡器连接情况。 *XTS=0,LFXTl工作在低频模式 (默认低频模式); *XTS=1,LFXTl工作在高频模式(必须连接有相应高频时钟源)。 *DIVA.0,DIVA.l控制ACLK分频。 *0 不分频(默认不分频); *1 2分频; *2 4分频; *3 8分频。 *XT5V此位设置为0。 *Rse1.0,Rsel.l,Rse1.2三位控制某个内部电阻以决定标称频率。 *Rse1=0,选择最低的频率; *Rse1=7,选择最低的标称频率; ***********************************************************************************/ /*BCSCTL2 寄存器*/ /*********************************************************************************** * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 *SELM.1 SELM.0 DIVM.1 DIVM.0 SELS DIVS.1 DIVS.0 DCOR *SELM.1,SELM.0选择 MCLK 时钟源。 *0 时钟源为 DCOLCK(默认时钟源); *1 时钟源为DCOCLK ; *2 时钟源为LFXTlCLK; *3 时钟源为 LFXT1CLK 。 *DIVM.1,DlVM.0选择 MCLK 分频。 *0 1分频(默认MCLK=DCOCLK); *1 2分频; *2 4分频; *3 8分频。 *DIVS.1,DIVS.0选择 SMCLK 分频。 *0 1分频(默认 SMCLK=MCLK); *1 2分频; *2 4分频; *3 8分频。 ************************************************************************************/ /*BCSCTL3 寄存器*/ /************************************************************************************ * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 * XT2S1 XT2S0 LFXT1S1 LFXT1S0 XCAP1 XCAP0 XT2OF LFXT1OF * XT2S1和XT2S0(2553不支持) * LFXT1S1和LFXT1S0选择LFXT1的范围。 * XCAP1和XCAP0选择LFXT1的匹配电容 * 00 1pf * 01 6pf * 10 10pf * 11 12.5pf ************************************************************************************/ /**************************************************************************************** * 静态函数声明 ********************************************************************/ static void DcoClkSet(unsigned char x,unsigned char y); //msp430g2553datasheet P30 static void MClkSet(unsigned char Div); static void SMClkSet(unsigned char Div); static void AClkSet(unsigned char Div); /************************************************************************ * 函数名 : DcoClkSet * 函数功能 : 对时钟DCOCLK进行配置 * 函数形参 : 传入的形参为x和y,其值参考2553datsheet第28页中DCO频率表 * 函数返回值 : 无 ************************************************************************/ void DcoClkSet(unsigned char x,unsigned char y) // msp430g2553datasheet P30 { DCOCTL &=~( 0xFF); BCSCTL1 &=~( 0xFF); unsigned char temp=(x<<4)+y; switch(temp){ case 0x00: { DCOCTL &=~( DCO0 + DCO1 + DCO2); BCSCTL1 &=~( RSEL0 + RSEL1 + RSEL2 + RSEL3); break; } case 0x03: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 &=~( RSEL0 + RSEL1 + RSEL2 + RSEL3); break; } case 0x13: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL0 ); break; } case 0x23: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL1 ); break; } case 0x33: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL0 + RSEL1 ); break; } case 0x43: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL2); break; } case 0x53: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL0 + RSEL2 ); break; } case 0x63: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL1 + RSEL2 ); break; } case 0x73: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL0 + RSEL1 + RSEL2 ); break; } case 0x83: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL3); break; } case 0x93: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL0+ RSEL3); break; } case 0xA3: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL1 + RSEL3); break; } case 0xB3: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL0 + RSEL1 + RSEL3); break; } case 0xC3: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL2 + RSEL3); break; } case 0xD3: { DCOCTL |= ( DCO0 + DCO1 ); DCOCTL |= ( MOD4 + MOD3 + MOD2 + MOD1 + MOD0 );//微调DCOCLK BCSCTL1 |= ( RSEL0 + RSEL2 + RSEL3); break; } case 0xE3: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL1 + RSEL2 + RSEL3); break; } case 0xF3: { DCOCTL |= ( DCO0 + DCO1 ); BCSCTL1 |= ( RSEL0 + RSEL1 + RSEL2 + RSEL3); break; } case 0xF7: { DCOCTL |= ( DCO0 + DCO1 + DCO2 ); BCSCTL1 |= ( RSEL0 + RSEL1 + RSEL2 + RSEL3); break; } default: { DCOCTL |= ( DCO0 + DCO1 + DCO2 ); BCSCTL1 |= ( RSEL0 + RSEL1 + RSEL2 + RSEL3); } } } /******************************************************************** * 函数名 : MClkSet * 函数功能 : 对时钟MCLK进行配置 * 函数形参 : 传入的形参为Div,对时钟源DCOCLK进行Div分频 * 函数返回值 : 无 ********************************************************************/ void MClkSet(unsigned char Div) { BCSCTL2 &= ~(SELM1+SELM0); //select DCOCLK for MCLK switch(Div){ //1分频 case 0x01:{ BCSCTL2 &=~(DIVM1 + DIVM0); break; } case 0x02:{ //2分频 BCSCTL2 &=~(DIVM1 + DIVM0); BCSCTL2 |=DIVM0; break; } case 0x04:{ //4分频 BCSCTL2 &=~(DIVM1 + DIVM0); BCSCTL2 |=DIVM1; break; } case 0x08:{ //8分频 BCSCTL2 |=(DIVM1 + DIVM0); break; } default :{ //默认不分频 BCSCTL2 &=~(DIVM1 + DIVM0); } } } /******************************************************************** * 函数名 : SMClkSet * 函数功能 : 对时钟MCLK进行配置 * 函数形参 : 传入的形参为Div,对时钟源DCOCLK进行Div分频 * 函数返回值 : 无 ********************************************************************/ void SMClkSet(unsigned char Div) { BCSCTL2 &= ~(SELM1+SELM0); //select DCOCLK for SMCLK switch(Div){ case 0x01:{ //1分频 BCSCTL2 &=~(DIVS_3); break; } case 0x02:{ //2分频 BCSCTL2 &=~(DIVS_3); BCSCTL2 |=(DIVS_1); break; } case 0x04:{ //4分频 BCSCTL2 &=~(DIVS_3); BCSCTL2 |=(DIVS_2); break; } case 0x08:{ //8分频 BCSCTL2 |=(DIVS_3); break; } default :{ //默认不分频 BCSCTL2 &=~(DIVS_3); } } } /******************************************************************** * 函数名 : AClkSet * 函数功能 : 对时钟MCLK进行配置 * 函数形参 : 传入的形参为Div,对时钟源LFXT1CLK进行Div分频 * 函数返回值 : 无 ********************************************************************/ void AClkSet(unsigned char Div) { BCSCTL1 &=~(XTS); //low-frequency mode switch(Div){ case 0x01:{ //1分频 BCSCTL1 &=~(DIVA_3); break; } case 0x02:{ //2分频 BCSCTL1 &=~(DIVA_3); BCSCTL1 |=(DIVA_1); break; } case 0x04:{ //4分频 BCSCTL1 &=~(DIVA_3); BCSCTL1 |=(DIVA_2); break; } case 0x08:{ //8分频 BCSCTL1 |=(DIVA_3); break; } default :{ //默认不分频 BCSCTL1 &=~(DIVA_3); } } BCSCTL3 |= XT2S0 + LFXT1S0 + XCAP_3; //配置ACLK匹配电容 } /******************************************************************** * 名称 : Init_Clk() * 功能 : MSP430时钟系统初始化程序 * 输入 : 无 * 返回值 : 无 ********************************************************************/ void Init_Clk() //时钟系统设置 { DcoClkSet(13,3); //7.84MHz 2553datasheet 第28页 AClkSet(0x08); //8分频LFXT1CLK SMClkSet(0x08); //8分频DCOCLK MClkSet(0x01); //8分频DCOCLK } /************************ end of file *************************/ /* 在编写好代码之后,为了验证是否正确,笔者在CCS4下面建立了一个工程,并在主函数中调用Init_Clk();*/ //#include <msp430g2553.h> //#include "clock.h" void main() { WDTCTL=WDTPW+WDTHOLD; P1DIR |=BIT4; P1SEL |=BIT4; //配置P1.4为SMCLK输出功能 Init_Clk(); } |
然后再说说如何测试自己的程序时钟设置的正不正确呢,MSP430系列的单片机有的引脚的第二功能是专门的时钟输出,所以只要把这些引脚设置为时钟输出格式,并且用示波器观察输出方波的周期即可知道时钟是否正确。例如G2553的引脚第二功能可以这样观察,首先到datasheet中看引脚说明:
不难看出,P1DIR.0=1,P1SEL.0=1,P1SEL2.0=1是,P1.0管教为ACLK输出,所以编程代码为:
P1DIR |=BIT0; // |= BITx; 是MSP430中位置1的语句,如果要多个位置1,那么就使用 |=BIT0+BIT1+BIT2+...的格式
P1SEL |= BIT0; // ^=BITx; 是MSP430中位取反的语句,如果要多个位取反,那么就使用 ^=BIT0+BIT1+BIT2+...的格式
P1SEL2 &=~BIT0; // &=~ BITx; 是MSP430中位置0的语句,如果要多个0,那么就使用 &=~(BIT0+BIT1+BIT2+...)的格式
通过以上的设置,P1.0就会输出ACLK频率的方波,SMCLK是P1.4引脚的第二功能,设置方式以此类推。
举一反三,我们可以发现IO口有很多第二功能,例如P1.0可以做IO口,可以做定时器TA0的时钟输出,可以做ACLK输出,可以做A0,还可以做捕捉比较器A0,之后我们将要用到很多的功能,一个引脚的复用就是用这样的方式进行,这样可以省下许多引脚,提高利用率,但是这也意味着一个引脚上的多个功能是不能同时使用的,所以在引脚功能设置时要根据需求斟酌处理。不要浪费了一些重要的资源。
同样,接下来是F5336时钟配置的代码,要了解代码书写的原因找UserGuide一看便知,里面的代码很多没有经过测试,不确定是正确的,所以大家斟酌使用,主要是了解时钟设置的原理和思路。以下是库函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
/* * clock.h * 内部时钟标准值: * XT1CLK:32768HZ标准晶体或者4-32M的外部时钟源 * VLOCLK:9.3KHZ * REFOCLK:32768HZ * DCOCLK:由FLL稳定后计算得到 书P66页有公式 * XT2CLK:高频振荡器,可以hi标准金针,振荡器或者4-32M的外部时钟源 * P1.0->ACLK P3.4->SMCLK PJ.1->TCLK */ #ifndef CLOCK_H_ #define CLOCK_H_ #endif /* CLOCK_H_ */ static void DcoClkSet(unsigned char x,unsigned char y); //msp430g5336datasheet P50 static void DcoClkDiv(unsigned char Div); static void FllClkSet(unsigned char x); static void FllClkDiv(unsigned char Div); static void AclkSet(unsigned char x); static void SmclkSet(unsigned char x); static void MclkSet(unsigned char x); static void AclkDiv(unsigned char Div); static void DivAclk(unsigned char Div); static void DivSmclk(unsigned char Div); static void DivMclk(unsigned char Div); static void Set_Aclk_XT2(void); static void Set_Smclk_XT2(void); static void Set_Mclk_XT2(void); /************************************************************************ * 函数名 : DcoClkSet * 函数功能 : 对时钟DCOCLK进行配置 * 函数形参 : 传入的形参为x和y,其值参考5336datsheet第50页中DCO频率表 * 函数返回值 : 无 ************************************************************************/ void DcoClkSet(unsigned char x,unsigned char y) // msp430g5336datasheet P50 { UCSCTL0 &=~( 0xFF); UCSCTL1 &=~( 0xFF); unsigned char temp=(x<<2)+y; switch(temp){ case 0x00: {//(0,0)DCO_freq=0.07-0.2MHZ UCSCTL0 &=~( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_0; break; } case 0x04: {//(1,0)DCO_freq=0.15-0.36MHZ UCSCTL0 &=~( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_1; break; } case 0x08: {//(2,0)DCO_freq=0.32-075MHZ UCSCTL0 &=~( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_2; break; } case 0x0c: {//(3,0)DCO_freq=0.64-1.51MHZ UCSCTL0 &=~( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_3; break; } case 0x10: {//(4,0)DCO_freq=1.3-3.2MHZ UCSCTL0 &=~( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_4; break; } case 0x14: {//(5,0)DCO_freq=2.5-6.0MHZ UCSCTL0 &=~( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_5; break; } case 0x18: {//(6,0)DCO_freq=4.6-10.7MHZ UCSCTL0 &=~( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_6; break; } case 0x1c: {//(7,0)DCO_freq=8.5-19.6MHZ UCSCTL0 &=~( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_7; break; } case 0x1f: {//(0,31)DCO_freq=0.7-1.7MHZ UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_0; break; } case 0x23: {//(1,31)DCO_freq=1.47-3.45MHZ UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_1; break; } case 0x27: {//(2,31)DCO_freq=3.17-7.38MHZ UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_2; break; } case 0x2b: {//(3,31)DCO_freq=6.07-14MHZ UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_3; break; } case 0x2f: {//(4,31)DCO_freq=12.3-28.2MHZ UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_4; break; } case 0x33: {//(5,31)DCO_freq=23.7-54.1MHZ UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_5; break; } case 0x37: {//(6,31)DCO_freq=39.0-88.0MHZ UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_6; break; } case 0x3b: {//(7,31)DCO_freq=60-135MHZ UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_7; break; } default: { UCSCTL0 |= ( DCO0 + DCO1 + DCO2 + DCO3 + DCO4); UCSCTL1 = DCORSEL_7; } } } /************************************************************************ * 函数名 : DcoClkDiv * 函数功能 : 对时钟DCOCLK进行分频 * 函数形参 : 传入的形参为div,传入为x,则是x分频(x是2^n) * 函数返回值 : 无 ************************************************************************/ void DcoClkDiv(unsigned char Div) { switch(Div){ //1分频 case 0x01:{ UCSCTL2 &= ~(FLLD0+FLLD1+FLLD2); break; } case 0x02:{ //2分频 UCSCTL2 &= ~(FLLD1+FLLD2); UCSCTL2 |= (FLLD0); break; } case 0x04:{ //4分频 UCSCTL2 &= ~(FLLD0+FLLD2); UCSCTL2 |= (FLLD1); break; } case 0x08:{ //8分频 UCSCTL2 &= ~(FLLD2); UCSCTL2 |= (FLLD0+FLLD1); break; } case 0x10:{ UCSCTL2 &= ~(FLLD0+FLLD1); //16分频 UCSCTL2 |= (FLLD2); break; } case 0x20:{ //32分频 UCSCTL2 &= ~(FLLD1); UCSCTL2 |= (FLLD0+FLLD2); break; } default :{ //默认不分频 UCSCTL2 &= ~(FLLD0+FLLD1+FLLD2); break; } } } /************************************************************************ * 函数名 : FllClkSet * 函数功能 : 对时钟FLLREFCLK的参考时钟源进行配置 * 函数形参 : 传入的形参为x,参考书P71 * 对应关系: * 函数返回值 : 无 ************************************************************************/ void FllClkSet(unsigned char x) { switch(x){ //选择XT1CLK20Mhz case 0x0:{ UCSCTL3 &= ~(SELREF0+SELREF1+SELREF2); break; } case 0x2:{ //REFOCLK32768hz UCSCTL3 &= ~(SELREF0+SELREF2); UCSCTL3 |= (SELREF1); break; } case 0x5:{ //XT2有效时选择XT2,否则为REFOCLK UCSCTL3 &= ~(SELREF1); UCSCTL3 |= (SELREF0+SELREF2); break; } default :{ //XT1CLK UCSCTL3 &= ~(SELREF0+SELREF1+SELREF2); break; } } } /************************************************************************ * 函数名 : FllClkDiv * 函数功能 : 对时钟FllREFCLK进行分频 * 函数形参 : 传入的形参为div,传入为x,则是x分频(x是2^n) * 函数返回值 : 无 ************************************************************************/ void FllClkDiv(unsigned char Div) { switch(Div){ //1分频 case 0x01:{ UCSCTL3 &= ~(FLLREFDIV0+FLLREFDIV1+FLLREFDIV2); break; } case 0x02:{ //2分频 UCSCTL3 &= ~(FLLREFDIV1+FLLREFDIV2); UCSCTL3 |= (FLLREFDIV0); break; } case 0x04:{ //4分频 UCSCTL3 &= ~(FLLREFDIV0+FLLREFDIV2); UCSCTL3 |= (FLLREFDIV1); break; } case 0x08:{ //8分频 UCSCTL3 &= ~(FLLREFDIV2); UCSCTL3 |= (FLLREFDIV0+FLLREFDIV1); break; } case 0x10:{ UCSCTL3 &= ~(FLLREFDIV0+FLLREFDIV1); //16分频 UCSCTL3 |= (FLLREFDIV2); break; } default :{ //默认不分频 UCSCTL3 &= ~(FLLREFDIV0+FLLREFDIV1+FLLREFDIV2); break; } } } /************************************************************************ * 函数名 : AclkSet * 函数功能 : 对时钟Aclk的参考时钟源进行配置 * 函数形参 : 传入的形参为x,参考书P71 * 对应关系: * 函数返回值 : 无 ************************************************************************/ void AclkSet(unsigned char x) { switch(x){ //选择XT1CLK20Mhz case 0x0:{ UCSCTL4 &= ~(SELA0+SELA1+SELA2); break; } case 0x1:{ //选择VCOCLK12Khz UCSCTL4 &= ~(SELA1+SELA2); UCSCTL4 |= (SELA0); break; } case 0x2:{ //REFOCLK32768hz UCSCTL4 &= ~(SELA0+SELA2); UCSCTL4 |= (SELA1); break; } case 0x3:{ //DCOCLK UCSCTL4 &= ~(SELA2); UCSCTL4 |= (SELA0+SELA1); break; } case 0x4:{ //DCOCLKDIV UCSCTL4 &= ~(SELA0+SELA1); UCSCTL4 |= (SELA2); break; } case 0x5:{ //XT2CLK有效时选择XT2CLK,否则为DCOCLKDIV UCSCTL4 &= ~(SELA1); UCSCTL4 |= (SELA0+SELA2); break; } default :{ //XT2CLK有效时选择XT2CLK,否则为DCOCLKDIV UCSCTL4 &= ~(SELA1); UCSCTL4 |= (SELA0+SELA2); break; } } } /************************************************************************ * 函数名 : SmclkSet * 函数功能 : 对时钟Smclk的参考时钟源进行配置 * 函数形参 : 传入的形参为x,参考书P71 * 对应关系: * 函数返回值 : 无 ************************************************************************/ void SmclkSet(unsigned char x) { switch(x){ //选择XT1CLK32768hz case 0x0:{ UCSCTL4 &= ~(SELS0+SELS1+SELS2); break; } case 0x1:{ //选择VCOCLK12Khz UCSCTL4 &= ~(SELS1+SELS2); UCSCTL4 |= (SELS0); break; } case 0x2:{ //REFOCLK32768hz UCSCTL4 &= ~(SELS0+SELS2); UCSCTL4 |= (SELS1); break; } case 0x3:{ //DCOCLK UCSCTL4 &= ~(SELS2); UCSCTL4 |= (SELS0+SELS1); break; } case 0x4:{ //DCOCLKDIV UCSCTL4 &= ~(SELS0+SELS1); UCSCTL4 |= (SELS2); break; } case 0x5:{ //XT2CLK有效时选择XT2CLK,否则为DCOCLKDIV UCSCTL4 = UCSCTL4&(~(SELS_7))|SELS_5; break; } default :{ //XT2CLK有效时选择XT2CLK,否则为DCOCLKDIV UCSCTL4 &= ~(SELS1); UCSCTL4 |= (SELS0+SELS2); break; } } } /************************************************************************ * 函数名 : MclkSet * 函数功能 : 对时钟Mclk的参考时钟源进行配置 * 函数形参 : 传入的形参为x,参考书P71 * 对应关系: * 函数返回值 : 无 ************************************************************************/ void MclkSet(unsigned char x) { switch(x){ //选择XT1CLK20Mhz case 0x0:{ UCSCTL4 &= ~(SELM0+SELM1+SELM2); break; } case 0x1:{ //选择VCOCLK12Khz UCSCTL4 &= ~(SELM1+SELM2); UCSCTL4 |= (SELM0); break; } case 0x2:{ //REFOCLK32768hz UCSCTL4 &= ~(SELM0+SELM2); UCSCTL4 |= (SELM1); break; } case 0x3:{ //DCOCLK UCSCTL4 &= ~(SELM2); UCSCTL4 |= (SELM0+SELM1); break; } case 0x4:{ //DCOCLKDIV UCSCTL4 &= ~(SELM0+SELM1); UCSCTL4 |= (SELM2); break; } case 0x5:{ //XT2CLK有效时选择XT2CLK,否则为DCOCLKDIV UCSCTL4 = UCSCTL4&(~(SELM_7))|SELM_5; break; } default :{ //XT2CLK有效时选择XT2CLK,否则为DCOCLKDIV UCSCTL4 &= ~(SELM1); UCSCTL4 |= (SELM0+SELM2); break; } } } /************************************************************************ * 函数名 : AclkDiv * 函数功能 : 对时钟ACLK进行分频,输出ACLK/n的频率 * 函数形参 : 传入的形参为div,传入为x,则是x分频(x是2^n) * 函数返回值 : 无 ************************************************************************/ void AclkDiv(unsigned char Div) { switch(Div){ //1分频 case 0x01:{ UCSCTL5 &= ~(DIVPA0+DIVPA1+DIVPA2); break; } case 0x02:{ //2分频 UCSCTL5 &= ~(DIVPA1+DIVPA2); UCSCTL5 |= (DIVPA0); break; } case 0x04:{ //4分频 UCSCTL5 &= ~(DIVPA0+DIVPA2); UCSCTL5 |= (DIVPA1); break; } case 0x08:{ //8分频 UCSCTL5 &= ~(DIVPA2); UCSCTL5 |= (DIVPA0+DIVPA1); break; } case 0x10:{ UCSCTL5 &= ~(DIVPA0+DIVPA1); //16分频 UCSCTL5 |= (DIVPA2); break; } case 0x20:{ //32分频 UCSCTL5 &= ~(DIVPA1); UCSCTL5 |= (DIVPA0+DIVPA2); break; } default :{ //默认不分频 UCSCTL5 &= ~(DIVPA1); UCSCTL5 |= (DIVPA0+DIVPA2); break; } } } /************************************************************************ * 函数名 : DivAclk * 函数功能 : 对时钟ACLK进行分频,ACLK/n的频率作为新的ACLK的频率 * 函数形参 : 传入的形参为div,传入为x,则是x分频(x是2^n) * 函数返回值 : 无 ************************************************************************/ void DivAclk(unsigned char Div) { switch(Div){ //1分频 case 0x01:{ UCSCTL5 &= ~(DIVA0+DIVA1+DIVA2); break; } case 0x02:{ //2分频 UCSCTL5 &= ~(DIVA1+DIVA2); UCSCTL5 |= (DIVA0); break; } case 0x04:{ //4分频 UCSCTL5 &= ~(DIVA0+DIVA2); UCSCTL5 |= (DIVA1); break; } case 0x08:{ //8分频 UCSCTL5 &= ~(DIVA2); UCSCTL5 |= (DIVA0+DIVA1); break; } case 0x10:{ UCSCTL5 &= ~(DIVA0+DIVA1); //16分频 UCSCTL5 |= (DIVA2); break; } case 0x20:{ //32分频 UCSCTL5 &= ~(DIVA1); UCSCTL5 |= (DIVA0+DIVA2); break; } default :{ //默认不分频 UCSCTL5 &= ~(DIVA1); UCSCTL5 |= (DIVA0+DIVA2); break; } } } /************************************************************************ * 函数名 : DivSmclk * 函数功能 : 对时钟SMCLK进行分频,SMCLK/n的频率作为新的SMCLK的频率 * 函数形参 : 传入的形参为div,传入为x,则是x分频(x是2^n) * 函数返回值 : 无 ************************************************************************/ void DivSmclk(unsigned char Div) { switch(Div){ //1分频 case 0x01:{ UCSCTL5 &= ~(DIVS0+DIVS1+DIVS2); break; } case 0x02:{ //2分频 UCSCTL5 &= ~(DIVS1+DIVS2); UCSCTL5 |= (DIVS0); break; } case 0x04:{ //4分频 UCSCTL5 &= ~(DIVS0+DIVS2); UCSCTL5 |= (DIVS1); break; } case 0x08:{ //8分频 UCSCTL5 &= ~(DIVS2); UCSCTL5 |= (DIVS0+DIVS1); break; } case 0x10:{ UCSCTL5 &= ~(DIVS0+DIVS1); //16分频 UCSCTL5 |= (DIVS2); break; } case 0x20:{ //32分频 UCSCTL5 &= ~(DIVS1); UCSCTL5 |= (DIVS0+DIVS2); break; } default :{ //默认不分频 UCSCTL5 &= ~(DIVS1); UCSCTL5 |= (DIVS0+DIVS2); break; } } } /************************************************************************ * 函数名 : DivMclk * 函数功能 : 对时钟MCLK进行分频,MCLK/n的频率作为新的MCLK的频率 * 函数形参 : 传入的形参为div,传入为x,则是x分频(x是2^n) * 函数返回值 : 无 ************************************************************************/ void DivMclk(unsigned char Div) { switch(Div){ //1分频 case 0x01:{ UCSCTL5 &= ~(DIVM0+DIVM1+DIVM2); break; } case 0x02:{ //2分频 UCSCTL5 &= ~(DIVM1+DIVM2); UCSCTL5 |= (DIVM0); break; } case 0x04:{ //4分频 UCSCTL5 &= ~(DIVM0+DIVM2); UCSCTL5 |= (DIVM1); break; } case 0x08:{ //8分频 UCSCTL5 &= ~(DIVM2); UCSCTL5 |= (DIVM0+DIVM1); break; } case 0x10:{ UCSCTL5 &= ~(DIVM0+DIVM1); //16分频 UCSCTL5 |= (DIVM2); break; } case 0x20:{ //32分频 UCSCTL5 &= ~(DIVM1); UCSCTL5 |= (DIVM0+DIVM2); break; } default :{ //默认不分频 UCSCTL5 &= ~(DIVM1); UCSCTL5 |= (DIVM0+DIVM2); break; } } } /************************************************************************ * 函数名 : Set_Alck_XT2() * 函数功能 : 20M晶振使能ALCK * 函数形参 : 无 * 函数返回值 : 无 ************************************************************************/ void Set_Aclk_XT2(void) { P7SEL|=BIT2+BIT3; //将IO配置为XT2功能 UCSCTL6|=XT2DRIVE_3; //使能XT2 UCSCTL6&=~(XT2OFF+XT2BYPASS); AclkSet(5); //这一句一定要放在下面的while之前,原因不明 while (SFRIFG1 & OFIFG){ UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // 清除三类时钟标志位 这里需要清除三种标志位,因为任何一种标志位都会将OFIFG置位 SFRIFG1 &= ~OFIFG; // 清除时钟错误标志位 } } /************************************************************************ * 函数名 : Set_Smclk_XT2() * 函数功能 : 20M晶振使能SMCLK * 函数形参 : 无 * 函数返回值 : 无 ************************************************************************/ void Set_Smclk_XT2(void) { P7SEL|=BIT2+BIT3; //将IO配置为XT2功能 UCSCTL6|=XT2DRIVE_3; //使能XT2 UCSCTL6&=~(XT2OFF+XT2BYPASS); SmclkSet(5); while (SFRIFG1 & OFIFG){ UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // 清除三类时钟标志位 这里需要清除三种标志位,因为任何一种标志位都会将OFIFG置位 SFRIFG1 &= ~OFIFG; // 清除时钟错误标志位 } } /************************************************************************ * 函数名 : Set_Smclk_XT2() * 函数功能 : 20M晶振使能SMCLK * 函数形参 : 无 * 函数返回值 : 无 ************************************************************************/ void Set_Mclk_XT2(void) //实际上没有IO口有第二功能输出MCLK,所以此程序未经过测试 { P7SEL|=BIT2+BIT3; //将IO配置为XT2功能 UCSCTL6|=XT2DRIVE_3; //使能XT2 UCSCTL6&=~(XT2OFF+XT2BYPASS); MclkSet(5); while (SFRIFG1 & OFIFG){ UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // 清除三类时钟标志位 这里需要清除三种标志位,因为任何一种标志位都会将OFIFG置位 SFRIFG1 &= ~OFIFG; // 清除时钟错误标志位 } } |
以下是主函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include "msp430f5336.h" void main(void) { WDTCTL = WDTPW+WDTHOLD; P1SEL |= BIT0; P1DIR |= BIT0;//测量ACLK用 P3SEL |= BIT4; P3DIR |= BIT4;//测量SMCLK用 //P7SEL |= BIT7; //P7DIR |= BIT7;//测量MCLK用 P7SEL |= BIT2|BIT3; //将IO配置为XT2功能 UCSCTL6 &= ~XT2OFF + XT2DRIVE_3; //使能XT2 UCSCTL4 = UCSCTL4&(~(SELA_7))|SELA_1; //先将ACLK配置为VLOCLK UCSCTL3 |= SELREF_5; //将REFCLK配置为REFCLK while (SFRIFG1 & OFIFG){ UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // 清除三类时钟标志位 // 这里需要清除三种标志位,因为任何一种 // 标志位都会将OFIFG置位 SFRIFG1 &= ~OFIFG; // 清除时钟错误标志位 } UCSCTL4 = UCSCTL4&(~(SELS_7|SELM_7))|SELS_5|SELM_5; //将SMCLK和MCLK时钟源配置为XT2 while(1); } |
时钟最好在一开始就设置好,例如我使用MSP430F5336做飞控,ACLK设置为32768HZ作为低频计数,MCLK设置为20M作为系统运行时钟,SMCLK为1.04Mhz用以设置波特率等参数,最后我的始终初始化代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void clock_init(void) { WDTCTL = WDTPW+WDTHOLD; P7SEL |= BIT2|BIT3; //将IO配置为XT2功能 UCSCTL6 &= ~XT2OFF + XT2DRIVE_3; //使能XT2 UCSCTL4 |= SELA_2; //先将ACLK配置为REFCLK UCSCTL3 |= SELREF_3; //将REFCLK配置为REFCLK while (SFRIFG1 & OFIFG){ UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); // 清除三类时钟标志位 这里需要清除三种标志位,因为任何一种 标志位都会将OFIFG置位 SFRIFG1 &= ~OFIFG; // 清除时钟错误标志位 } UCSCTL4 = UCSCTL4&(~SELM_7)|SELM_5; //将SMCLK设置为DCOCLKDIV 1.04M MCLK时钟源配置为XT2 } |
所以大家可以根据自己的需要处理时钟,其中DCOCLK的是时钟频率可以自己设置,范围很大,使用很灵活,我没有仔细讲,但是上面的代码都有相关函数,大家可以根据自己的需要利用起来。
最后再讲一讲我对于低功耗的理解,工作模式有AM模式(活动模式),LPM0(低功耗模式0),LPM1(低功耗模式1)……,AM模式所有的始终资源都使用,低功耗模式禁止CPU和部分始终,所以有时候代码跑不出来,可以看看是不是选择了不同的低功耗模式造成的错误,例如你用串口通信波特率始终是SMCLK,但是进入了低功耗模式3(LPM3)是禁止了SMCLK的,这样程序当然有问题,低功耗模式在实际应用中能节省资源,减少功耗。是MSP430单片机的特色之一,可以根据需要选择使用。