Παρασκευή 5 Ιανουαρίου 2018

Flashing the MSP430 LED - The Efficient Way!

So you are into this microcontroller thing right? A lot of people have purchased Arduinos lately and plenty of hackerspaces (including the one we run in my hometown) use them for teaching programming, electronics and maker culture in general. It's an awesome movement, bringing people back to the creative days of the 80s and 90s.

So what is the first  program you write on a microcontroller - any microcontroller? While learning a programming language, your first program is usually "Hello World". In microcontrollers, you flash a LED instead. Displaying a message to a screen needs additional components, like a .. screen (duh!) a library for writing to it and plenty of electrical connections. On the other hand, flashing a LED is easy. A microcontroller, a resistor, a LED. That's all you need. Even better, almost all dev boards have a built-in LED for you to start with. No connections needed at all.

Onboard LEDs of F5529 LaunchPad

The standard Arduino Code for flashing the LED looks like this:

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin 13 (Arduino UNO) as an output.
  pinMode(13, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

And it's really quite straightforward:
  • Assign Pin 13 as an output
  • Write a logical HIGH to it (turning the connected LED on)
  • Wait some time (one second)
  • Write a logical LOW to it (turning the connected LED off)
  • Wait some time
  • Repeat
You could, in a pinch, translate this code to MSP430. Using the MSP430F5529 LaunchPad and Code Composer Studio (CCS), this would be the code:

#include <msp430.h>

void main(void)
{
  WDTCTL = WDTPW | WDTHOLD;        // stop watchdog timer
  P1DIR |= BIT0;                   // configure P1.0 as output

  while(1) {
    P1OUT ^= BIT0;                // toggle P1.0
    __delay_cycles(1000000);      // delay
  }
}


And it's not much different:
  • Assign P1.0 as output. This is BIT0 on Port 1, where the red LED is connected.
  • Repeat XORing the value of BIT0 to turn the LED on and off.
  • Get some delay between flashes (1000000 clock cycles, or 1 sec in the default 1 MHz speed)
There is also this mystery line about a watchdog timer, but more about that later.

So is there anything wrong with these examples? They do the job, right?
Well yes. But none of them is actually optimal. You see, microcontrollers are often used in environments where power is at a premium. Maybe they run off a small watch battery. They probably wait for something to happen, service the event and  and get idle again. And idle isn't even the correct word here:

A microcontroller waiting to service an event should actually be asleep! 

And what are we doing in our example? You guessed it, we are running full power. All the time. CCS actually noticed this:

Power advice from CCS: Let's follow it!

So the recommendation is to use a timer. Let's explore this.

Using the Watchdog Timer to flash the LED

Timers are a wonderful thing and one of the most commonly used peripherals in micrcontrollers. Programming one can be quite complicated but for our purpose we will use the simplest one: the Watchdog timer.

The watchdog timer is a clever idea for guarding a program running on an MSP430. When it is used in its normal function you have to put instructions in your code to reset it from time to time. If the counter rolls back to zero the MSP430 will reset itself. In other words, if your program crashes for whatever reason, the MSP430 will restart it. 

This actually explains the very first line of our sample program:

WDTCTL = WDTPW | WDTHOLD;        // stop watchdog timer

Since we are not really using the watchdog timer, we should stop it - otherwise our board would reset continuously!

Actually, we have another option: we can use the Watchdog timer to perform a useful function, like  flashing the LED once per second. We have to follow these steps:


  • Setup the Watchdog timer as an interval timer with 1 sec period.
  • Enable the interrupts for the Watchdog timer
  • Prepare the P1.0 port for output as before
  • Enable the interrupts for the whole CPU (GIE, General Interrupt Enable) and  change to Low Power Mode 3 (LPM3) allowing the cpu to sleep between flashes
  • Write an interrupt routine to be called to flash the LED when the timer interrupt occurs.
So this is our enhanced code:

#include <msp430.h>

void main(void)
{
    /*
     * WDTPW = Watchdog timer password (always needed)
     * WDTCNTCL = Clear counter (automatically reset)
     * WDTTMSEL = Configure as interval timer
     * WDTSSEL_1 = Configure ACLK as source (32 KHz)
     * WDTIS_4 = Interval timer select. WDT clock / 2^15 (1 sec at 32768 Hz)
     */
    WDTCTL = WDTPW + WDTCNTCL + WDTTMSEL + WDTSSEL_1 + WDTIS_4;

    P1DIR |= BIT0;                 // configure P1.0 as output
    SFRIE1 |= WDTIE;               // Enable WDT interrupts in the status register
    __bis_SR_register(LPM3_bits + GIE);  // Enter Low Power Mode 3 with interrupts enabled
}


#pragma vector = WDT_VECTOR  // Interrupt Service Routine (ISR) for Watchdog Timer
__interrupt void flashLed(void) {
    P1OUT ^= BIT0;       // toggle P1.0
}

Let's explain this a bit:

In order to setup the watchdog as a timer, we have to write a value to the WDTCTL register. This value consists of:
  • The WDTPW - The password. This is needed everytime something is changed in WDT
  • WDTTMSEL - This configures watchdog as an interval timer instead
  • WDTSSEL_1 - This configures the ACLK (auxiliary clock) as the source for the watchdog timer.
  • WDTIS_4 - This selects the timer interval. This value corresponds to 1 Hz blink rate.
  • WDTCNTCL - To clear the timer
In order to get the above values you really need to read the extremely useful (and long!) MSP430x5xx and MSP430x6xx Family User's Guide. Have a look at page 459 for the WDTCTL register.

Just configuring the watchdog as a normal timer is not enough though. We also have to enable its interrupt in the status register:

SFRIE1 |= WDTIE;               // Enable WDT interrupts in the status register

And even this is not enough, since we also have to enable the interrupts in general, otherwise the MSP430 would just ignore them:

__bis_SR_register(LPM3_bits + GIE);  // Enter Low Power Mode 3 with interrupts enabled

And at the same time, we also enter LPM3. The CPU will now sleep and wake up only to service the interrupts, using the flashled routine that follows main.

How does it know this is an ISR (Interrupt Service Routine) for wathcdog? That's easy:

#pragma vector = WDT_VECTOR  // Interrupt Service Routine (ISR) for Watchdog Timer

Information on #pragma compiler directive may be found in the MSP430 Optimizing C/C++ Compiler User's Guide.

Needless to say, our program is still not completely optimized for power (well, flashing a LED can hardly be called power optimization anyway). To minimize power consumption we should really configure all GPIO pins to output and set them to low. A more enhanced version of this program is available in my MSP430 Github repository, here.

Τρίτη 3 Ιανουαρίου 2017

Code Composer Studio (CCS) v7.0 on Arch Linux

People who like to tinker with electronics (at the level of programming MSP430 in assembly, no less) usually like to tinker with computers and operating systems too. With the release of Code Composer Studio (CCS) v7.0, Texas Instruments now has a Linux version of the programming tools that is (mostly) 64bit and does not require the multitude of 32bit libraries to run on 64bit Linux distributions (as was the case with CCS 6.x).

CCSv7 Running on Arch Linux

However, TI as usual provides instructions for running this useful program on Ubuntu only (LTS releases).  As I prefer to run Arch (I like the sense of total control of what gets installed and the ability to know exactly how my system works) I decided to give it a try. Here is how to successfully install and run CCS 7.x in Arch.

Step  1: Download CCS 7.x

Download the full version of CCS 7.x for Linux from this page. Scroll down and get the offline Linux version (it's about 700Mb). As stated in the notes, you will need to install the 32bit libc on your system.

Step 2: Install / Symlink Required Libraries

 Assuming you have the Multilib repository enabled, as root (or with sudo) execute the following command:

# pacman -S lib32-glibc

According to TI, this would be all you need but really this is not the case in an Arch system. While you don't need any other 32bit library, the installation program requires a few other libraries that may already be installed but are different versions than the ones required. These are:
  • libncurses
  • libusb
Additionally, running the ccs installer on a newly installed Arch GNOME system simply quits after a while without displaying an error message on the console and without presenting a GUI to the user. The installer requires libxss which you may install manually from the repository or let it get installed as a dependency of chromium. 

The specific version of libncurses required can simply be bypassed by symlinking the version already in the system:

# cd /lib
# ln -s libncursesw.so.6.0 libncurses.so.5

Libusb is a bit more tricky. You could symlink the required version it to the libusb already present in your system, but there is a more elegant way: simply install the Energia programming environment from yaourt:

# yaourt -S energia

This will install the correct version of the library and you will be good to go!

Step 3: Extract and Install CCS

There is no need to run the CCS installation as root; I recommend installing it in your home directory as a simple user. There is only a driver installation script you will have to run as root (or with sudo) after the main install completes.

Extract the archive (assuming it is in ~/Downloads):

$ cd /Downloads
$ tar xvpzf CCS7.0.0.00042_linux-x64.tar.gz

(Exact filename may of course vary as new versions are released)

Enter the directory created and run the install script:

$ cd CCS7.0.0.00042_linux-x64
$ ./ccs_setup_linux-x64

Want to program this Launchpad on Linux? You are out of luck.

The installer should come up shortly, without complaining about missing libraries (if it does, recheck your previous steps). Run the installation as usual, selecting all components you require (e.g. MSP430, MSP432, TI Arm and so on). Note that some MSP Launchpads are not supported in the Linux version, notably the cheap and cheerful Value Line Launchpad (pity). The older Stellaris launchpads are also not supported anymore (Time to get yourself a TivaC). 

Step 4: Install The Drivers

There is one final step: Get to the directory where you installed the program and run the driver installation script as root. Assuming you installed it in ~/ti, this is what it would like:

$ cd ~/ti/ccsv7/install_scripts
$ sudo ./install_drivers.sh

Now test your installation:

$ cd ~/ti/ccsv7/eclipse
$ ./ccstudio

You are done! Enjoy your new CCS on Arch. Happy New Year!


Σάββατο 4 Ιουλίου 2015

LCD Display Library for Tiva and Stellaris Launchpads

Liquid Crystal Displays (LCDs) are great for creating impressive (and sometimes, useful!) microcontroller projects. The 16 characters / 2 lines display is very cheap and directly supported by the arduino LiquidCrystal library (also works in  MSP430 with Energia).

In this post we implement similar functionality for the Tiva C series and  Stellaris launchpads. This version of the library works in CCS and can be used with Stellaris LM4F120, TivaC TM4C123G and Tiva C Connected TM4C1294 launchpads with minimal changes.

LCD library in action. Also current state of Greek economy :)


You may download the complete project from my github page.

The HD44780 specification

LCDs that use the HD44780 drivers are commonly called COGs (Chip on Glass) as they contain all the necessary circuits to drive the LCD segments and communication with the host processor is generally limited to the following pins:
  • DB0 to DB7 Data bus. The bus is used to communicate commands or characters to be written to the screen, one byte at a time. To save MCU pins, the interface can be configured for 4 bit operation, where only the DB4 to DB7 lines are used.
  • An RS (Register Select) pin. Specifies whether the data bus is carrying a command to be executed or a character to be displayed. When RS is low,  data on the bus represents a command, otherwise it represents a character.
  • An EN pin. This is a strobe pin, It has to be pulsed Low-High-Low for the LCD to accept the data in the bus.

When using 4 bit mode, the above connections require 6 pins of your MCU.

The LCD display will also require additional pins to be connected to Vcc or GND, including:
  • Power and ground for the LCD and HD44780 ICs.
  • Power and ground for the LCD backlight (if applicable)
  • Ground connection of the R/W pin (to enable writing to the LCD).
  • A potentiometer between Vcc and GND to pin 5 for adjusting display contrast. 
Here is the pinout used in my version of the LCD library for the TivaC launchpad. Pin assignment can be easily reconfigured using the appropriate #define statements in display.h file:

LCD pin       TivaC
-------------------
DB4           PD0
DB5           PD1
DB6           PD2
DB7           PD3
RS            PE4
EN            PE5

The Code

Let's have a look at how the library works. It is based on the HD44780 spec and heavily influenced by a similar work for the MSP430 published in the Co-Random Thoughts blog. The display.h file defines some useful macros to represent the pins and ports we use:

#define RS GPIO_PIN_4 // Energia Pin 5
#define EN GPIO_PIN_5 // Energia Pin 6
#define D4 GPIO_PIN_0 // Energia Pin 23
#define D5 GPIO_PIN_1 // Energia Pin 24
#define D6 GPIO_PIN_2 // Energia Pin 25
#define D7 GPIO_PIN_3 // Energia Pin 26
#define ALLDATAPINS  D7 | D6 | D5 | D4
#define ALLCONTROLPINS RS | EN

#define DATA_PORT_BASE GPIO_PORTD_BASE
#define CMD_PORT_BASE GPIO_PORTE_BASE
#define DATA_PERIPH SYSCTL_PERIPH_GPIOD
#define CMD_PERIPH SYSCTL_PERIPH_GPIOE

The DATA_PORT_BASE points to Port D which is configured for the data bus. CMD_PORT_BASE points to Port E, configured for the RS and EN signals.

To start up the LCD in 4 bit data mode, we have to send the following command to the D4-D7 pins twice:

D7 D6 D5 D4
 0  0  1  0

For this, we need to pull RS low, send the command, strobe the LCD via the EN pin, wait a bit and repeat. After this step the LCD is switched to 4 bit mode. All subsequent 8bit commands / characters are sent as  a pair of nibbles.

Here is the relevant code in initLCD:

void initLCD(void)
{
SysCtlPeripheralEnable(DATA_PERIPH);
SysCtlPeripheralEnable(CMD_PERIPH);
GPIOPinTypeGPIOOutput(DATA_PORT_BASE,  ALLDATAPINS);
GPIOPinTypeGPIOOutput(CMD_PORT_BASE, ALLCONTROLPINS);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS ,0);
GPIOPinWrite(CMD_PORT_BASE, ALLCONTROLPINS ,0);

SysCtlDelay(10000);

setCmd();
SysCtlDelay(15000);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, 0b0010);
pulseLCD();
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, 0b0010);
pulseLCD();

PulseLCD is a simple function that pulses EN to Low-High-Low states:

void pulseLCD()
{
// Go Low -> High -> Low
GPIOPinWrite(CMD_PORT_BASE, EN, 0);
GPIOPinWrite(CMD_PORT_BASE, EN, EN);
GPIOPinWrite(CMD_PORT_BASE, EN, 0);
}

Similarly the setCmd() and setData() functions switch the RS pin to low / high respectively.

After successfully initializing the LCD to 4bit mode, we sent a few more commands:

sendByte(0x28,FALSE);  // Set two lines
cursorOffLCD();       // Cursor invisible
sendByte(0x06, FALSE); // Set insert mode

After setting insert mode, the LCD is ready to accept characters for printing. The sendByte function sends a byte as a pair of nibbles:

void sendByte(char byteToSend, int isData)
{
if (isData)
setData();
else
setCmd();
SysCtlDelay(400);
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, byteToSend >>4);
pulseLCD();
GPIOPinWrite(DATA_PORT_BASE, ALLDATAPINS, byteToSend);
pulseLCD();
}

The high nibble is set first followed by the low nibble. The display is strobed to accept each nibble. RS is set accordingly for data (high) or command (low).
Timing is important: with the delays currently in the program, the LCD works fine when running at 25MHz frequency. If you need to run at the full 80MHz of the TivaC (or more for the TivaC Connected) you will need to introduce more SysCtlDelay statements  or the display will fail to catch and garbled text will be shown!

Printing to the LCD is  very simple: Just send the characters of your string one by one to the sendByte function:

void printLCD(char *text)
{
char *c;
c = text;

while ((c != 0) && (*c != 0))
{
sendByte(*c, TRUE);
c++;
}
}

The library implements a few more command of the HD44780 protocol. Here are a few interesting hex codes to try:

Function Definition    Hex Code
Set 4 bit mode         0x0
Scroll one char right  0x1E
Scroll one char left   0x18
Goto Home position     0x02
Go one char left       0x10
Go one char right      0x14
Underline cursor on    0x0E
Blinking cursor        0x0F
Invisible cursor       0x0C
Blank display          0x08
Clear display          0x01
Set next position      0x80+Address

More details are available in the HD44780 datasheet.

Happy coding!

Κυριακή 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 :)

Σάββατο 23 Μαΐου 2015

Programming Target and Breakout Boards using a Launchpad

Using the launchpad as a spy bi-wire programmer is not new: People have been doing it with the value line launchpads for ages. But how about using a launchpad to program a chip in LQFP-64, LQFP-80 or LQFP-100 package (or in other SMT style packages). These chips cannot be directly breadboarded but compared to the THT MSPs, they are a lot more powerful, hence a lot more desirable :)

As I found out, there is more than one way to use your trusty launchpad to program these.

The Target Board

Texas Instruments sells boards equipped with ZIF (Zero Insertion Force) sockets for just about every package type they produce an MSP in. The boards alone sell for about 80-100 USD officially, but you can get much better prices on ebay. The target board will allow you to insert your MSP, connect it to a JTAG programmer (sold separately), program it and then remove it and use it in your PCB or prototype board.
Guess what: you don't really need to buy the JTAG programmer (FET interface), since you already have the launchpad!
I recently acquired an LQFP-100 target board (MSP-TS430PZ100B) and a couple of MSP430F6736 and set out to find how to program them using the F5529 launchpad.  It turned out to be pretty easy:


Launchpad to Target board

The connections are straightforward:

Launchpad to target board schematic
Your launchpad will program and also power the chip in the target board. Don't forget to set your target board configuration to Spy bi-wire. There is usually a set of jumpers that you have to move to the SBW position:


While a target board is handy, it will only allow you to program specific types of chips (unless you start collecting target boards, which might become a rather expensive hobby). But there is another way to program MSPs and use them for prototyping too: the breakout boards.

Programming a Breakout Board

Fortunately, eBay is full of sellers for breakout boards of any type. I ordered ten LQFP-100 boards from a seller in China. These were promptly delivered and I soldered a couple of F6736 chips on them:


After soldering some headers on it, I set out to find the minimum circuit that would allow this little board to work and get programmed by my launchpad. It basically comes down to this:


You will have to look at the datasheet for your specific MSP to find out which pins to connect and where. There will be more than a couple of pins that need to be tied to Vcc and GND and also a few that will require capacitors to GND. The spy bi-wire interface needs a few additional components (you can probably ommit the 330 Ohms resistor though). And this is how it looks like:

Launchpad to breakout board

Yes, it's a big mess of wires. But it works!
And of course if you are designing your own PCB, you can just provide a port for the spy bi-wire pins and program your MSP directly in circuit. Either way, MSP programming is a lot of fun!

Κυριακή 12 Απριλίου 2015

Clocking the MSP430F5529 Launchpad

The MSP430F5529 Launchpad is a tinkerers dream! Powerful and complicated, will provide you with many happy afternoons ;) In this post we examine the UCS (Unified Clock System) and how you can use to it clock your Launchpad up to it's maximum speed.



The MSP430F5529 launchpad offers plenty of clock sources and three different clocks to work with:

  • MCLK, the Master Clock - Used for the CPU. Usually fast.
  • SMCLK, the Sub-Master Clock, used for peripherals. Usually fast (equal to MCLK, or a fraction of it).
  • ACLK, the Auxiliary clock, usually slow and used for peripherals when very low power is needed.

And what do you feed to these clocks?

  • VLOCLK: On-chip, very low frequency oscillator. Around 10 KHz and not accurate at all!
  • REFOCLK: Reference oscillator at the usual 32768 Hz (the common RTC frequency). Medium accuracy, again provided on chip.
  • DCO: Digitally Controlled Oscillator. On chip, fast oscillator source.
  • XT1: Onboard crystal at 32768 Hz (like REFOCLK but more accurate, since it is a crystal).
  • XT2: Onboard crystal at 4 MHz.


The above sources can be combined in a bewildering number of ways: different sources to different clocks, divided by a number of dividers or used to synthesize and fine tune the DCO up to the 25MHz maximum clock of the F5529.

Let's see how we can use and set these using the DriverLib (part of MSPWare) provided by TI.

Important Note: TI has changed some function names in the recent version of MSPWare. To follow our examples download the latest CCS (Code Composer Studio) and MSPWare (I've tried to show the differences in the listings).

Initial Investigation


What are the default clocks of your F5529 Launchpad if you make no settings at all? Let's investigate. Create an empty DriverLib project and paste the following code. Use the expression watch in the debugger to examine the values of the three variables (mclk, smclk, aclk):

#include "driverlib.h"

uint32_t mclk = 0;
uint32_t smclk = 0;
uint32_t aclk = 0;

int main(void) {
    WDT_A_hold(WDT_A_BASE);
    aclk=UCS_getACLK();
    mclk=UCS_getMCLK();
    smclk=UCS_getSMCLK();
    while (1);
    return (0);
}

And the results are:

  • MCLK: 1048576 Hz (1 MHz)
  • SMCLK: Same as MCLK
  • ACLK: 32768 Hz

If you never touched your launchpad clocks, you are only running at 1 MHz. We can do a lot better than that!

Setting Clocks using Internal Clock Sources (the simple way)


The two "simple" clock sources are the REFOCLK and the VLOCLK. In a pinch, you
can use some simple functions to change any of the clocks using them as sources. For example, setting the ACLK to use the REFOCLK:

#include "driverlib.h"

void initClocks();

uint32_t mclk = 0;
uint32_t smclk = 0;
uint32_t aclk = 0;

int main(void) {
    WDT_A_hold(WDT_A_BASE);
    initClocks();
    aclk=UCS_getACLK();
    mclk=UCS_getMCLK();
    smclk=UCS_getSMCLK();
    while (1);
    return (0);
}

void initClocks(){
    UCS_initClockSignal( // clockSignalInit in previous driverlib
UCS_ACLK,  // Set the auxiliary clock, using the
UCS_REFOCLK_SELECT, // reference oscillator (32768 Hz) and
UCS_CLOCK_DIVIDER_2 // divide it by this value.
    );
}

Using a divider of 2, yields an ACLK frequency of 16384. You are welcome to try other values for the divider (in powers of 2 up to 32) as well as trying to set MCLK and SMCLK the same way (just substitute UCS_ACLK with UCS_MCLK or UCS_SMCLK). The syntax is  no different with VLOCLK (from now on we only show different versions of the initClocks function):

void initClocks(){
    UCS_initClockSignal(
UCS_ACLK,
UCS_VLOCLK_SELECT,
UCS_CLOCK_DIVIDER_32
    );
}

With a clock divider of 32, we can go as low as 312 Hz!

Using the Crystals - The easy way


The crystals provide very accurate timing and their basic usage is very easy. We need to actually configure the pins where they are connected (they are normally configured for GPIO), start them, and then use them the same way we used the internal sources.

void initClocks(){
    // Important First Steps: Configure Pins for Crystals!
    // All to port P5
    // PIN5 -> XT1 OUT
    // PIN4 -> XT1 IN
    // PIN3 -> XT2 OUT
    // PIN2 -> XT1 IN

    GPIO_setAsPeripheralModuleFunctionInputPin(
GPIO_PORT_P5,
GPIO_PIN4+GPIO_PIN2
    );

    GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P5,
GPIO_PIN5+GPIO_PIN3
    );

    // YOU HAVE to inform the system of the crystal frequencies
    // You probably want to use #defines for these values

    UCS_setExternalClockSource(
32768,  // Frequency of XT1 in Hz.
4000000 // Frequency of XT2 in Hz.
    );

    // Initialize the crystals

    UCS_turnOnXT2( // was UCS_XT2Start in previous driverlib
UCS_XT2_DRIVE_4MHZ_8MHZ
    );

    UCS_turnOnLFXT1( //was UCS_LFXT1Start in previous driverlib
UCS_XT1_DRIVE_0,
UCS_XCAP_3
    );

    // Use the crystals to set the clocks

    UCS_initClockSignal(
UCS_MCLK,
UCS_XT2CLK_SELECT,
UCS_CLOCK_DIVIDER_1
    );

    UCS_initClockSignal(
UCS_SMCLK,
UCS_XT2CLK_SELECT,
UCS_CLOCK_DIVIDER_2
    );

    UCS_initClockSignal(
UCS_ACLK,
UCS_XT1CLK_SELECT,
UCS_CLOCK_DIVIDER_1
    );
}

We have just used the 4 MHz XT2 to set MCLK to 4 MHz and SMCLK to 2MHz. We also used the 32768 Hz XT1 to set ACLK.  This method will allows to clock our system up to the XT2 frequency of 4MHz. To clock up to the full 25 MHz we need to set the DCO, the Digitally Controlled Oscillator.

Setting the DCO


To set the DCO we must initialize the Frequency Locked Loop (FLL) inside the F5529 using either the crystals or the internal clock sources.  Let's try with the internal reference oscillator first (32KHz). We'll start with a few defines which should normally be at the top of your listing:

// MCLK = Master Clock (CPU)

#define MCLK_FREQ_KHZ 4000

// Reference frequency (Frequency Locked Loop)

#define FLLREF_KHZ 32

// Ratio used to set DCO (Digitally Controlled Oscillator)

#define MCLK_FLLREF_RATIO MCLK_FREQ_KHZ/FLLREF_KHZ

Our initClocks function now looks like this:

void initClocks(){
    UCS_initClockSignal(
UCS_FLLREF, // The reference for Frequency Locked Loop
UCS_REFOCLK_SELECT, // Select 32Khz reference osc
UCS_CLOCK_DIVIDER_1
    );

    // Start the FLL and let it settle
    // This becomes the MCLCK and SMCLK automatically

    UCS_initFLLSettle(
MCLK_FREQ_KHZ,
MCLK_FLLREF_RATIO
    );

    /* Option: Further divide the frequency obtained for DCO

    UCS_initClockSignal(
UCS_MCLK,
UCS_DCOCLKDIV_SELECT,
UCS_CLOCK_DIVIDER_4
    ); */

    // Set auxiliary clock

    UCS_initClockSignal(
        UCS_ACLK,
UCS_REFOCLK_SELECT,
UCS_CLOCK_DIVIDER_1
    );
}

We first initialize the FLL reference to the 32 KHz of the reference oscillator (REFOCLK). We then initialize the FLL itself and let it settle (the call returns when the FLL has acquired a stable frequency and all faults are cleared). When initFLLSettle completes, both MCLK and SMCLK are set to the frequency of the DCO. Optionally, we can use the DCOCLKDIV to set the clocks to fractions of the DCO. In a pinch, you could change just one line to speed up to 8 MHz:

#define MCLK_FREQ_KHZ 8000

Keep in mind that going to frequencies higher than 8 MHz requires you to set the core power mode. We are currently at PMM_CORE_LEVEL_0, the default core power mode. Insert this line at the top of the initClocks function:

PMM_setVCore(PMM_CORE_LEVEL_0);

and modify it according to the following table:

up to  8 MHz => PMM_CORE_LEVEL_0
up to 12 MHz => PMM_CORE_LEVEL_1
up to 20 MHz => PMM_CORE_LEVEL_2
up to 25 MHz => PMM_CORE_LEVEL_3

Not all frequencies are available at all operating voltages, but this is not a problem for Launchpad users which always run at the full 3.3V. If you try to set the frequency higher than the current core level would allow, the initFLLSettle function may never return.

We have used the on-chip reference oscillator as the source for the FLL reference. We can also use XT1 or XT2 for the same purpose.

Using XT1/XT2 as the FLL reference


Combining the knowledge we gained in the last two sections we can use either of the crystals as a reference for the FLL. Let's use XT2 to clock our Launchpad to a whopping 20 MHz.

Our initial defines are as follows (delete the ones defined previously if you wish to test this):

// Desired MCLK frequency

#define MCLK_FREQ_KHZ 20000

// On board crystals frequencies (in Hz)

#define XT1_FREQ 32768
#define XT2_FREQ 4000000

#define XT1_KHZ XT1_FREQ/1000
#define XT2_KHZ XT2_FREQ/1000

// Ratio used to set DCO (Digitally Controlled Oscillator)
// We are setting the FLL reference to 1 MHz (XT2/4)
// Remember to use the same divider in UCS_initClock

#define MCLK_FLLREF_RATIO MCLK_FREQ_KHZ/(XT2_KHZ/4)

Our initClocks function is now a little more involved:

void initClocks(){

  // Set core power mode

  PMM_setVCore(PMM_CORE_LEVEL_3);

    // Connect Pins to Crystals

    GPIO_setAsPeripheralModuleFunctionInputPin(
GPIO_PORT_P5,
GPIO_PIN4+GPIO_PIN2
    );

    GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P5,
GPIO_PIN5+GPIO_PIN3
    );

First we set the power mode to the highest one (level 3) for 20 MHz operation.
Then we set the pins where the crystals are connected.

    // Inform the system of the crystal frequencies

    UCS_setExternalClockSource(
XT1_FREQ,  // Frequency of XT1 in Hz.
XT2_FREQ   // Frequency of XT2 in Hz.
    );

    // Initialize the crystals

    UCS_turnOnXT2( 
UCS_XT2_DRIVE_4MHZ_8MHZ
    );

    UCS_turnOnLFXT1(
UCS_XT1_DRIVE_0,
UCS_XCAP_3
    );

We inform the system of the crystal frequencies and initialize the two crystals (For failsafe operation you are advised to check the 'WithTimeout' variants of these functions).

  UCS_initClockSignal(
UCS_FLLREF,  // The reference for Frequency Locked Loop
UCS_XT2CLK_SELECT,  // Select XT2
UCS_CLOCK_DIVIDER_4 // FLL ref. will be 1 MHz (4MHz XT2/4)
  );

We set the FLL reference frequency to 1 MHz by dividing the XT2 frequency by 4. We have already accounted for that in our MCLK_FLLREF_RATIO.

  UCS_initFLLSettle(
MCLK_FREQ_KHZ,
MCLK_FLLREF_RATIO
  );

We initialize the FLL and wait for it to settle. This will set the DCO (and subsequently, MCLK and SMCLK) to our desired frequency of 20 MHz. Optionally, we can set SMCLK to a lower frequency by using the DCOCLKDIV:

    // Optional: set SMCLK to something else than full speed

  UCS_initClockSignal(
UCS_SMCLK,
UCS_DCOCLKDIV_SELECT,
UCS_CLOCK_DIVIDER_1
  );

Finally, we set the ACLK as well:

  // Set auxiliary clock

  UCS_initClockSignal(
UCS_ACLK,
UCS_XT1CLK_SELECT,
UCS_CLOCK_DIVIDER_1
  );
}

You may download this final complete example here.
Happy programming!