Κυριακή 31 Μαΐου 2015

Clocking the MSP430F5529 Launchpad, Part II: The Assembly Way

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.

Before we Start

To do any serious assembly work, you will need to study at least three documents:


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,&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
; 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
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

 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):

  mov.w #PMMCOREV_1,R12
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.

Having  connected and started the crystals in our previous example, we are halfway to our target already! 

; 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

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 :)

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου