Εμφάνιση αναρτήσεων με ετικέτα LED. Εμφάνιση όλων των αναρτήσεων
Εμφάνιση αναρτήσεων με ετικέτα LED. Εμφάνιση όλων των αναρτήσεων

Παρασκευή 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.

Σάββατο 30 Αυγούστου 2014

Microcontrollers and LEDs

The (somewhat lengthy) Introduction

Arduino is breeding a new generation of hackers! Those of you old enough to remember the 80s will probably have fond memories of programming a home computer of that era: the ZX Spectrum, the Commodore 64, the TI-99 and all the other wonderful machines that wouldn't do much unless you could use their BASIC language (or learn machine code!) Sure, you could get ready made programs and games on cassette(!) but there were many who would buy them just for the fun of programming. The hardware had a lot of limitations, sure, but the mere joy of squeezing every single bit of power from the machine was unsurpassed.

Computers have evolved a lot since then, but also lost a lot: the interactiveness of the first computer languages, the simplicity of programming, the single point of entry: you would start learning BASIC. What about now? Where does a beginner start programming? Sure, there are many choices, in both programming languages and operating systems. Most of them are free. But here is the problem: the choice is too big. If that is not enough, the changes are rapid and most people won't be able to follow the latest trend or hype: first it's C++, then it's Java, tomorrow is Python and so on. All very nice,  but where does a beginner start? Confusion...

There are other problems too: your first (simple) programs will not be very impressive by today's standards. They will probably be command line and text driven (mind you, I've written a book on learning programming by writting games in Python and Pygame but it's in Greek only for the moment I'm afraid. You can get a PDF copy of it here). People get bored quickly writing the same old computational programs in modern PCs full of colors, sounds and multimedia. Trust me, I teach these subjects. Programming is now mostly viewed like a chore (we are teaching it the wrong way).

Microcontrollers to the rescue! Actually, they share a lot more than you think with the home computers of the 80s.

  • They are "underpowered" (by todays CPU standards)
  • Their programming is simple and has immediate (and spectacular) results
  • They are actually made to be "actively hacked" rather than "passively used"
They also combine tinkering with both hardware and software,  making them suitable for both computing and electronic engineering hackers!

Most people that are comfortable with programming don't necessarily have adequate knowledge of electronics: some basic stuff is required even at the beginner level: how do you connect a LED to a microcontroller output? Let's see.

Connecting a LED to a Microcontroller Output

You have to keep the following things in mind:
  • Some microcontrollers have 5V outputs (arduino), others have 3.3V or less (MSP430)
  • Some microcontrollers can provide enough current (in other words, they have adequately low output resistance) to drive a LED directly (with a suitable resistor). Arduino can drive LEDs with no problems.
  • Other microcontrollers like MSP430 have limited output current capability. You might be able to drive a LED from them directly but at risk of damaging the MCU.
  • No LED can operate directly from an MCU output: you will need a to connect a resistor in series. Typical LEDs operate at low voltages, depending on their color. For example, red leds need about 1.5V. Fancier colors like blue and white need higher voltages.
Assuming you are using a microcontroller like Arduino, all you need to connect your LED to an output is a suitable current limiting resistor.

Connecting a LED to an Arduino

Connecting a LED to an arduino  is very simple, since the outputs of the MCU can source enough current by themselves. With the following facts in mind it is easy to calculate the resistor:
  • An "active" arduino output provides 5V
  • A typical red LED needs 1.5V
  • About 10mA through the LED are usually sufficient for a good brightness.
Our circuit looks like this:


 Calculating R1 is simple:



We know Vout is 5V, Vled is 1.5V and we need 10mA for the LED. Solving for R1:


 Feel free to adjust this a bit up or down to use a value actually available in the E12 series of resistors (either 330Ω or 390Ω will do). Use this same calculation for other LEDs, adjusting for current and LED color (voltage drop) accordingly.

Connecting a LED to an MSP430 (or other low power MCU)

Low power MCU cannot (and should not) drive LEDs directly. Instead, use the output from the MCU to switch on a transistor that carries the load:


Although we show a separate 5V supply for the LED, if you are using a TI launchpad board, you can source this directly from its 5V output pin.

We now have two resistors to calculate, R1 and R2.

Starting with R2, the equation for the output part of our circuit is:





We will be operating our transistor as a switch. In this configuration the transistor is used in two states: cut off (where no current flows) or fully on (saturation) where the transistor shows almost no resistance (the collector-emitter voltage is zero).

In an ideal transistor (which exists only in... an ideal world) the Vcesat would be zero, but in practice it will be around 0.2V.  We still need about 10 mA for the LED. Solving the above equation for R2 yields:


Conveniently, this is an E12 value as well :)
You may adjust a bit up or down if you have happen to not have the exact value.

Since we know the collector current wil be 10mA, we can easily calculate the base current and resistor R1. Assuming the transistor has b (hfe) 300:



The equation for the input:


Solving for R1:

 

69K is not a value of the E12 series but feel free to replace it with either 68K (little more current) or even 56K (if your transistor has a lower hfe).

In any case, you will only be drawing a minute and absolutely safe amount of current from the MCU output: around 30 μΑ!