Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

This is a simple introduction to programming on the msp430 MSP430 microcontroller, which was the main microcontroller for Midnight Sun XI.

Prerequisites

  • All code running on our electrical system is written in C. It would be a good idea to become familiar with C before continuing with this tutorial. You can find many resources online about programming in C such as this one.
  • Install code composer studio Code Composer Studio (CCS) Simply make should be installed.
    1. Make an account on TI's website
    , and after you have completed that find the download link for windows, linux, or mac (depending on what you use) from this site. Just run through the installer (default options should be fine) and you will have code composer studio installed.
    1. .
    2. Download and install CCS. Select anything containing "MSP430."
  • A MSP430 EXP430F5529LP launchpadLaunchpad. (There is some available in the bay to use or you can purchase your own here).
  • Read Software 101: Clearing, Toggling, Setting, and Checking a Bit.
  • MSP430F5529 datasheets

Setting up a project

Go to the top menu bar a and click File->New->CCS Project, then make sure the . The dialog that appears looks should look something like this:


Verify that the target and project name are correct, and click finish to create the project.

Blinking an LED the slow way

To get started, we are going to write some code to blink an LED on the launchpad. If you look at the launch pad launchpad board, you can see a small LED beside one of the buttons labeled "LED1" with  with the label "P2.1" on top of P1.0 near it. The key piece of information here is "P2.1" P1.0. This tells us that the LED is connected to "Port 2 1 Pin 10" on the microcontroller. That means to turn on Thus, to control the LED, we just need to turn Port 2 1 Pin 1 0 on and off to blink that LED.

...

General purpose input/output (GPIO) on the msp430 works through the use of PxDIR, PxOUT, and PxIN registers. These are 8 bit registers where each bit in the number corresponds to a certain pin. The "x" pins on the MSP430 are capable of acting as both digital inputs and outputs. GPIO pins are generally broken up into groups known as "ports" for ease of use. GPIO pins are commonly referred to as PX.Y, where X refers to the port the pin belongs to, and Y refers to the pin's 0-indexed place in the port.

Each port is assigned a number of 8-bit memory-mapped registers to control the pins assigned to the port. In this case, the registers of interest are PxDIR, PxOUT, and PxIN. The x corresponds to which port we are currently trying to access, there . There can be any number of ports on an MSP430 (this is something you would look for on the datasheet), but right now, we are only concerned about port 2Port 1.

First lets , let's look at the PxDIR register, or in this specific case P2DIRP2DIR is an 8 bit registers (think of registers as special variables that control input and output in the microcontroller) register. This register is responsible for controlling GPIO direction. In this specific case, that would be P1DIRP1DIR is an 8-bit memory-mapped register, so it would look something like this:

Code Block
languagecpp
themeConfluence
uint8_t P1DIR @ P2DIR;0xABCD; // This is located at some arbitrary memory address

and since we are concerned about the bits in it, by default it would have a value like this in binary:

...


Now the right-most bit in the number is the first bit (BIT0)known as bit 0, and it corresponds to the first pin (Pin 10) connected to that port, which in this case that would be P2P1.10. Now when When that bit is a 0, this tells the IO controller that we want this pin to be an output so that we can turn on the LED. If we were to set this bit to 1 making:input. If it's a 1, it should be an output. For example, if we set bit 0 of P1DIR to 1, then P1.0 would become an output.

Code Block
languagecpp
themeConfluence
P2DIRP1DIR |= BIT0
0b00000001

Then pin 1 would become an input, which would be useful to check if a button has been pressed. But since we are trying to turn the led on leave the bit as 0.

...

; // BITx = (1 << x)
0b00000001

Now that P1.0 is configured as an output, we must now turn it on the output to turn the LED on. This is where the PxOUT (or specifically P2OUT in P1OUT in our case) register comes into play. Like P2DIR PxDIR, P2OUT is PxOUT is an 8-bit memory-mapped register where each bit corresponds to a specific pin on the port.

So just like P2DIR, BIT0 will P1DIR, bit 0 of P1OUT will correspond to pin 1 0 on port 21. That means to turn on the output of P2P1.1 0, we just have to set BIT0 bit 0 to 1. and then to To blink the led LED, we just have to set BIT0 back to 0 and then back to 1 again. toggle that bit. Note that PxIN works similarly, but is a read-only register where each bit represents the digital input on that pin.

This is all the basic information we need to blink an the LED, so now we can start by looking at the code below:

...

Implementation

Code Block
languagecpp
themeConfluence
linenumberstrue
#include <msp430.h> 

//* Include * main.c
 */MSP430-specific defines

int main(void) {
  WDTCTL = WDTPW | WDTHOLD;    // Stop watchdog timer

  P2OUTP1OUT &= ~BIT0; // Clear P1.0's output value
P2DIR  P1DIR |= BIT0; // Set P1.0 as an output

  for (;;) {
    P2OUTP1OUT ^= BIT0; // Toggle P1.0 using bitwise XOR
    __delay_cycles(100001000000); // Software delay
}  }
    
  return 0;
}

Explanation

You can copy and paste this code into the main.c file of your project to get started, and then you can run it by hitting the debug button  (Image RemovedImage Added) and then the run button (Image RemovedImage Added)  (a dialog box might appear telling you to update firmware, just click okay and let it update). The code should begin to run on the microcontroller and you will see the LED begin to blink, congratulations! Now lets , let's go over the rest of the code that has not been explained yet, and figure out how and why it works.

...


This line has the important function of disabling the watchdog timer. The watchdog timer is a special timer on the microcontroller that will restart the microcontroller if it counts to a certain defined value without the timer being reset. This is useful to reset the running program in the even event that it gets stuck in some kind of , such as in an infinite loop. For know now, the watchdog timer will just be confusing to us, so we will disable it. To figure out what the register WDTCTL does  does when you set the bits WDTPW &  and WDTHOLD, we can look at the datasheet for the MSP430x5xx family. Searching for the section on the watchdog timer, we can now find the WDTCTL register  register section on page 461. From here, we can see that WDTHOLD stops  stops the watchdog timer from running and WDTPW is  is the password we need to use to modify the watchdog timer.

I've already explained the P2OUT and P2DIR registers so will PxOUT and PxDIR registers, so we'll skip over them and go to look at the for loop. In the for loop, we are toggling BIT0 using the xor operator (The software 101 guide about bits in prerequisite section explains how this worksbit 0 of P1OUT using the XOR operator (See Clearing, Toggling, Setting, and Checking a Bit) and then we call a special function ", __delay_cycles".

Code Block
languagecpp
themeConfluence
    __delay_cycles(100001000000);


What this function does is that it This function delays the processor for x cycles (10,000 in our case). So whats happening in our loop is . In our case, we chose 1,000,000 because MCLK runs at 1Mhz by default so it should be around 1 second of delay. So, in our loop, we toggle the state of the LED (if its it's on, it turns off, and if its it's off, it turns on) and then we wait for a little bit. This creates the effect of the LED blinking. Now

However, this is the inefficient way to blink an LED, there is a much better way . There are better ways to blink an LED.

Blink an LED with timers

The issue with the above method of blinking an LED is the "__delay_cycles" function function. When this function is running, we aren't doing anything else. This is wasted time that we could be using to compute other things on the microcontroller or to conserve power by sleeping. Luckily, the MSP430 provides a way to run a timer in the background while other code is running. To get started, create a new project like before but this time name it "TestTimerBlink". Now, the code we are going to use is very similar, but it will work slightly differentdifferently.

First, to understand how timers work, it would be a good idea to look at the timer section in the datasheet (Documentation for timer Timer A starts on page 462). Here, we can see all the documentation about the registers that interact with timer Timer A on the microcontroller. From this, we can read find out that in order to start use the time timer, we need to set some information in the Timer A control register (TACTL), we need to set the source select (which clock to use), the mode control, and the

...

(TA0CTL) and handle the interrupts.

  • Set the source select (which clock to use)
  • Set the mode control
  • Enable interrupts globally
  • Enable interrupts for the timer
  • Set the value the timer counts to
  • Create an interrupt service routine

First, to get started, it would probably be a good idea to understand what interrupts are. Interrupts are essentially special events in hardware that call functions known as interrupt service routines (ISRs), interrupting main program execution. In our case, we're going to create an interrupt that gets called when the hardware timer is done counting to its maximum value. For more detail, read Interrupts and Busy-Wait.

Next, we need to figure out what values to set in the timer control registers. We can check the datasheet for that. We want to use the source clock SMCLK (a clock derived from the master clock that is 1Mhz by default) and we want to count in up-down mode. From the datasheet, we can see that we want TASSEL_2 for the source select and MC_2 for the mode control. We are also going to divide the clock frequency (to make it count slower) by 8 using ID_3 (input divider 3, which divides the input clock frequency by 8). We can see this all in action in the code below that you can copy and paste into your editor.

Implementation

Code Block
languagecpp
themeConfluence
linenumberstrue
#include <msp430.h> 

/*

* main.c
 */
int main(void) {
    WDTCTL = WDTPW | WDTHOLD;    // Stop watchdog timer

  P1OUT  TACTL&= ~BIT0;
  P1DIR |= BIT0;


  TA0CTL |= TASSEL_2 | MC_2 | ID_3;
    TACCTL0TA0CCTL0 |= CCIE;
  TA0CCR0  TACCR0 = 65535;
  __enable_interrupt();

 P2OUT &=for ~BIT0;
    P2DIR |= BIT0;

(;;) { }

  return 0;
}

#pragma vector = PORT2TIMER0_A0_VECTOR
__interrupt void PORT2TIMER0_A0_ISR(void) {
    P2OUTP1OUT ^= BIT0;
}

Explanation

At the beginning of the main function, we can see the IO and timer setup code. The IO configuration is the same as before, but the timer setup is new.

First, we setup the Timer A control register (TA0CTL) to use SMCLK  (TASSEL_2), up-down mode (MC_2), and a frequency divider of 8 (ID_3). Next, we set the Timer A capture/compare control register (TA0CCTL0) to enable interrupts locally for the timer. After that, we set the value we will be counting to by setting TA0CCR0 to 65535 (the maximum value it can store). This will cause the timer to start counting. Lastly, we globally enable interrupts by calling __enable_interrupt and loop forever.

Here at the bottom in the #pragma section, we can see the ISR we created for Timer A interrupts. All this service routine does is toggle the state of the LED to make it blink.

Recap

This method is slightly more complicated but is much better. It allows to run other code while you wait for the LED to blink. That way, no time is wasted.

This was a very specific example. If you wanted to learn how to use different peripherals on the MSP430 or any other microcontroller, you could easily look up that specific peripheral in the datasheet and see how to use configure and use it properly. The datasheet should be your go-to place to find out what the MSP430 is capable of and how to actually do things.

Fun Challenges

Think you figured out how all this works? Try these challenges!

  • Blinking an LED using a timer output: Although we used an ISR to blink the LED, timer peripherals can actually output to specific pins on a microcontroller!
  • Make the LED respond to a button press: Learn how to handle inputs and state! How can you communicate between the ISR and main code?
  • Sleep between blinks: We mentioned sleeping to conserve power. When would you do this? How do you wake up again? What stays active?