We have already discussed a nice way to clock your launchpad using the mspware library of functions provided by TI. How about trying the same thing in assembly language?
Assembly allows you precise control over the MCU although it is arguably more involved than plain C (or mspware assisted) programming. Let's revisit our previous clocking examples and try to implement them in MSP430 assembly.
The Family User's Guide shows all the appropriate registers and operations needed for tasks like setting the clocks, raising Vcore, working with timers, GPIOs and so on. It also describes the internals of the MSP430 and the assembly language instructions. The Assembly Language Tools User's Guide will get you started on the assembler provided with CCS. The F5529 datasheet provides info specific to the chip we are programming. There are also some nice tutorials available on the Internet to get you started with assembly programming on the MSP430. And don't miss out on the examples installed on your development system either:
Have a look in C:\ti\msp430\MSPWare_2_00_00_XX\examples\devices\5xx_6xx (or wherever you installed mspware). Check for folders named Assembly_CCS. You may not find one for the F5529, but there are plenty of examples for other similar devices.
bis.b #BIT4+BIT5,&P5SEL ; Connect XT1 to P5.5, P5.4
; by configuring the ports
; peripheral function
bic.w #XT1DRIVE_3,&UCSCTL6 ; XT1 is now stable, reduce drive
mov.w #PMMCOREV_1,R12
call #setvcore
mov.w #PMMCOREV_2,R12
call #setvcore
mov.w #PMMCOREV_3,R12
call #setvcore
; Default settings in UCSCTL3: SELREF = 000b -> FLLREF = XT1CLK
; FLLREFDIV = 000b -> FLLREFCLK / 1
bis.w #SCG0,SR ; Disable the FLL control loop
clr.w &UCSCTL0 ; Set lowest possible DCOx, MODx
mov.w #DCORSEL_7,&UCSCTL1 ; Select range for 20MHz
mov.w #FLLD_2 + 639,&UCSCTL2 ; Set DCO multiplier
; for DCOCLKDIV
; (FLLN + 1) * (FLLRef/n) * FLLD = DCOCLK
; FLLD_2 = 4
; FLLRef=32768 and n=1
; (n=FLLREFDIV)
; DCOCLKDIV = DCOCLK/FLLD = (FLLN+1)*(FLLRef/n)
; Default settings are DCOCLKDIV for MCLK/SMCLK
bic.w #SCG0,SR ; Enable the FLL control loop
Assembly allows you precise control over the MCU although it is arguably more involved than plain C (or mspware assisted) programming. Let's revisit our previous clocking examples and try to implement them in MSP430 assembly.
Before we Start
To do any serious assembly work, you will need to study at least three documents:- The MSP430x5xx and MSP430x6xx Family User's Guide
- The MSP430 Assembly Language Tools User's Guide
- The MSP430F5529 Datasheet
The Family User's Guide shows all the appropriate registers and operations needed for tasks like setting the clocks, raising Vcore, working with timers, GPIOs and so on. It also describes the internals of the MSP430 and the assembly language instructions. The Assembly Language Tools User's Guide will get you started on the assembler provided with CCS. The F5529 datasheet provides info specific to the chip we are programming. There are also some nice tutorials available on the Internet to get you started with assembly programming on the MSP430. And don't miss out on the examples installed on your development system either:
Have a look in C:\ti\msp430\MSPWare_2_00_00_XX\examples\devices\5xx_6xx (or wherever you installed mspware). Check for folders named Assembly_CCS. You may not find one for the F5529, but there are plenty of examples for other similar devices.
Default Clocks
As we already know, the default MSP430 clock is about 1MHz. Here is some code to prove it:
bis.b #BIT2,&P2DIR ; Set P2.2 as output
bis.b #BIT2,&P2SEL ; Set P2.2 as peripheral output
; P2.2 is multiplexed with SMCLK
; This will output SMCLK to P2.2
bis.b #BIT7,&P7DIR ; Set P7.7 as output
bis.b #BIT7,&P7DIR ; Set P7.7 as output
bis.b #BIT7,&P7SEL ; Set P7.7 as peripheral output
; P7.7 is multiplexed with MCLK
; This will output MCLK to P7.7
; P7.7 is not present in the headers
; P7.7 is not present in the headers
; of the launchpad. It is output pin 60
; on F5529. Top right corner ;)
bis.b #BIT7,&P4DIR ; Set P4.7 as output (Green LED)
bis.b #BIT0,&P1DIR ; Set P1.0 as output (Red LED)
mov.w #3787, R15 ; Delay parameters
mov.w #22, R14
mov.w #22, R14
blink xor.b #BIT0,&P1OUT
call #delay
jmp blink
Download the complete project files here. 'Delay' is a subroutine that mimics the behavior of the __delay_cycles() intrinsic of the C compiler. With the values specified here (in R14 and R15 registers), delay will waste about 250000 cycles (check out comments in the source code and you will figure it out) allowing us to watch the red LED blinking!
If you have a scope, you may connect it to P2.2 where SMCLK is output. Here is what you will see:
If you feel lucky, you may also probe (carefully!) port 7.7. This is MCLK (same frequency as SMCLK here). It is pin no 60 of the F5529, but it is not output on any header pin.
Direct clocking with Crystals
Clocking with crystals is mostly straightforward. Download the complete project files here. We connect the 32.768KHz crystal to ACLK and the 4MHz crystal to MCLK/SMCLK:
bis.b #BIT4+BIT5,&P5SEL ; Connect XT1 to P5.5, P5.4
; by configuring the ports
; peripheral function
bis.b #BIT2+BIT3,&P5SEL ; Connect XT2 to P5.3, P5.2
; by configuring the ports for
; peripheral function
The crystal pins are multiplexed with GPIOs in F5529. We have to first select them as peripheral pins using the P5SEL register.
bic.w #XT1OFF, &UCSCTL6 ; Turn on XT1
bic.w #XT2OFF, &UCSCTL6 ; Turn on XT2
We then turn on the crystals, by clearing their OFF bits in the UCSCTL6 register. UCSCTLx registers define the behavior of many aspects of the Unified Clock System (UCS).
bis.w #XCAP_3, &UCSCTL6 ; Internal load capacitor for XT1
We connect an appropriate capacitor to the low frequency XT1.
waitclear bic.w #XT2OFFG | XT1LFOFFG | DCOFFG, &UCSCTL7
; Clear XT2,XT1,DCO fault flags (XT1 and XT2 only here)
bic.w #OFIFG,&SFRIFG1 ; Clear fault flags
bit.w #OFIFG,&SFRIFG1 ; Test oscillator fault flag
jc waitclear
We have to wait for the crystals to start and stabilize. We continuously clear and test the oscillator fault flag until it is no longer set.
bic.w #XT1DRIVE_3,&UCSCTL6 ; XT1 is now stable, reduce drive
; strength. Low frequency crystals
; take some time
; to start and stabilize
bic.w #XT2DRIVE_0,&UCSCTL6 ; XT2 Drive strength reduced to
; level 0
; for 4-8MHz operation
; for 4-8MHz operation
Finally, we set the right drive strengths for both crystals. The low frequency crystal takes some time to start-up and stabilize. After this time, the drive strength is reduced. This prolongs the life of the crystal and also reduces power consumption.
We are now ready to assign the crystals to our clocks:
mov.w #SELA_0|SELS_5|SELM_5,&UCSCTL4
UCSCTL4 is the register that sets the source for each clock (ACLK, SMCLK, MCLK). SELA_0 selects XT1 for ACLK, while SELS_5 and SELM_5 select XT2 for MCLK and SMCLK. You can easily see the difference by using your scope as before:
Also note the LED blinks a lot more rapidly now, as the MCLK frequency is 4MHz :)
Using the DCO and FLL
Going beyond the crystals we need to configure DCO and provide it with an FLL reference. Going above 8MHz also requires setting Vcore to a higher level. Fortunately, there is an assembly language example in the samples provided by TI. We have implemented this as a subroutine and call it three times to set Vcore to the maximum level (download the complete project files here):
call #setvcore
mov.w #PMMCOREV_2,R12
call #setvcore
mov.w #PMMCOREV_3,R12
call #setvcore
Here is a diagram showing the required Vcore by frequency, as shown in the F5529 datasheet. Launchpad supplies 3.3V to the F5529, so we can clock it up to its maximum operating frequency, as long as we raise PMMCOREV to 3:
TI states that Vcore should only be raised one level at a time, hence the three separate calls here.
TI states that Vcore should only be raised one level at a time, hence the three separate calls here.
Having connected and started the crystals in our previous example, we are halfway to our target already!
; FLLREFDIV = 000b -> FLLREFCLK / 1
bis.w #SCG0,SR ; Disable the FLL control loop
clr.w &UCSCTL0 ; Set lowest possible DCOx, MODx
mov.w #DCORSEL_7,&UCSCTL1 ; Select range for 20MHz
mov.w #FLLD_2 + 639,&UCSCTL2 ; Set DCO multiplier
; for DCOCLKDIV
; (FLLN + 1) * (FLLRef/n) * FLLD = DCOCLK
; FLLD_2 = 4
; FLLRef=32768 and n=1
; (n=FLLREFDIV)
; DCOCLKDIV = DCOCLK/FLLD = (FLLN+1)*(FLLRef/n)
; Default settings are DCOCLKDIV for MCLK/SMCLK
bic.w #SCG0,SR ; Enable the FLL control loop
UCSCTL3 contains important settings: The clock source to use for the FLLREF (default is the low frequency crystal, XT1CLK) and FLLREFDIV which allows FLLREFCLK to be further divided by 1, 2, 4, 8, 12 or 16. We just use the defaults here.
Before changing the DCO setting, we first disable the FLL control loop and clear the UCSCTL0 register. This register will be set automatically after we adjust the FLL parameters. We need to look at the documentation of F5529 to choose the appropriate DCO range. The F5529 datasheet will help us find this one:
For 80MHz operation, we need to go up to the last range, DCORSEL_7 and store it in register UCSCTL1. We set the multiplier and frequency bits in UCSCTL2.
Multiplier is FLLD_2 which is 4
FLLN is 639.
Thus, DCOCLK = (639+1) * (32768/1) * 4 = 83886080 Hz (84MHz)
DCOCLK is not used directly on MCLK/SMCLK (that would be one hell of an overclocking!). DCOCLKDIV is used instead:
DCOCLKDIV = DCOCLK / FLLD = 83886080 / 4 = 20971520 Hz or about 21 MHz.
Although DCO is now set, the actual frequency will take sometime to stabilize. Our delay subroutine comes handy again:
; Worst-case settling time for the DCO when the DCO range bits
; have been changed is n x 32 x 32 x F_fLLREFCLK cycles.
; n = FLLREFDIV
; 1 x 32 x 32 x 20.97152 MHz / 32.768 KHz = 655360
; MCLK cycles for DCO to settle
mov.w #9930,R15
mov.w #22,R14
call #delay
; Total cycles: setup 6+6+2=14
; Internal Loop: 9930*3*22=655380
; Outer loop: 22*5 = 110
; Total: 110+655380+14 = 655504
And we also clear the oscillator fault flag and loop until it no longer sets itself:
; Loop until DCO fault flag is cleared
delay_DCO bic.w #DCOFFG,&UCSCTL7 ; Clear DCO fault flags
bic.w #OFIFG,&SFRIFG1 ; Clear fault flags
bit.w #OFIFG,&SFRIFG1 ; Test oscillator fault flag
jc delay_DCO
Finally, we assign the clock sources to their respective clocks (these are in fact the default settings for UCSCTL4):
mov.w #SELA_0|SELS_4|SELM_4,&UCSCTL4
Checking SMCLK with the oscilloscope:
For this last example, we have adjusted the LED delay loop to 1 million cycles, so we can still watch it blinking!
Guess what - assembly is great for lots of happy afternoons :)