25 April 2013

Tutorial 21: Timer Tricks (PWM)

Since I've done some big, multi-part tutorials lately, I thought I'd do a quick one this time. This tutorial will deal with an extremely versatile use of the Timer module: pulse width modulation.

Pulse width modulation, or PWM, is really simple to do, but it allows you to do so many different things! You can change the brightness of a bulb, or set the speed of a motor. You can generate an analog voltage, or even synthesize sounds, all from a simple use of the timer with two interrupts.

The Basic PWM Signal

Three examples of PWM signals with different
duty cycles. Each is also marked with its
average level.
A PWM signal is a natural extension of a square wave--much like the signals we've used in clocks. For a square wave, the signal turns on and off at regular intervals. When you look at a square wave, you see that the signal is high for the same amount of time it's low. The amount of time the line is high--the pulse width--compared to the period, or spacing between pulses, is called the duty cycle. For a square wave, the length of the pulse is half the length of one cycle, so it has a 50% duty cycle. A PWM signal simply adjusts the duty cycle while maintaining the same frequency of pulses.

We create a PWM signal using two interrupts on a timer. If necessary, we could use the same interrupt and adjust when the interrupt occurs in software, but the timer modules in the MSP430 have at least two interrupts that we can use! One interrupt will set the overall frequency of our signal, while the other will set the duty cycle. The idea is simple: using the compare mode of the Timer while in continuous mode, the smallest Timer_A module has three interrupts. Whenever the timer rolls over after 65536 (or 216) clock cycles, an interrupt is driven which we'll use to set the output of our PWM. Using CCR0, we can set the duty cycle by resetting the output after a specified number of clock cycles. If the CCR0 interrupt resets when the timer count register TAR reaches 32768 (half of 65536), we have a 50% duty cycle; the PWM is set for half the period, and reset for the other half. If we set CCR0 to 6554, we have approximately a 10% duty cycle. Since the smallest Timer_A module has two capture-compare registers, using this method you can have at least two PWM signals from any MSP430 device running independently. Unfortunately, often in practice we're going to loose one of these.

As a first example, consider this first example: LEDPWM_G2231.c. If you run this on your LaunchPad, you'll see both LEDs light up, starting dim and growing in brightness, then decreasing from a maximum brightness, and repeating the cycle. The PWM signals being shown by the LEDs are set to increase their duty cycle from 0 to 100%, then back to 0, and repeat. You'll probably also notice a not very desirable feature: the transitions are not very smooth. In fact, you can detect the LEDs flashing nearly the entire time. Once you've run the code as-is, try uncommenting the lines setting the DCO to a faster frequency and note the effect it has on your perception of the LEDs.

Reader Exercise: Why doesn't my code let the PWM go all the way to 0? Try setting the comparison for switching directions upward to else if (TACCR0 == 0) and see what happens. Can you explain it?

Better PWM

Now that you've seen it in action, consider the output frequency of this PWM scheme. If we let the timer count all the way to 216, the PWM signal is set at a frequency of f/216, where f is the Timer_A operation frequency. At the default DCO frequency of ~1.1 MHz, the frequency between pulses is only about 17 Hz-- easily detectable by the human eye! If we use its maximum speed of 16 MHz, using a 16 MHz DCO, then the maximum frequency for a PWM signal using this method will be 244 Hz. For some applications, that may be fast enough, but for many others you may be shooting for a frequency of about 1 kHz or more! Since we have no way to increase the Timer_A frequency any further, we need to have the timer count to a lower number; we can do this by using up mode instead of continuous mode, at the cost of requiring CCR0 to serve in that capacity. While this means we loose an output, we do gain the flexibility of faster PWM signals.

Counting to a lower number also means that we loose some resolution. If we set CCR0 to 10, for example, there are only 11 possible duty cycles: CCR1 can only be set to an integer value less than (or equal to) 10. That provides 11 duty cycles from 0% to 100%, spaced by 10% intervals. Using CCR0 set to 100, we can select by 1% intervals; set to 1000, we can select 0.1% intervals, and so on. While it may have been nice to have such high resolution using all 16 bits of the Timer_A register, most applications aren't benefited by it. Those that really require high resolution output will have to compromise by operating at a lower frequency.

Try the example code in this second example: LEDPWM2_G2231.c, and note the differences in how it's set up. Before, we had to manually set the PWM lines on an overflow interrupt. The MSP430 provides timer output modes that make use of both TACCRx and TACCR0, so the setting of the PWM line is now automatic. No interrupts are needed! This feature makes for much cleaner code, and saves a lot of memory. Of course, if other functions are needed in conjunction with the PWM signal, an interrupt can still be triggered to handle that. In general, it won't be needed.

You might also notice that one characteristic of the schemes we have looked at is that the pulses are all left-edge aligned, since each PWM line is set high at the same moment (when the timer rolls over to 0). In some cases, it may be desirable to have the pulses center-aligned, so that the center of the pulses occur at the same moment. This setup is done by using the timer in up-down mode, and setting the timer outputs to toggle at their respective CCRx interrupts; if the line starts low (which can be assured by resetting at TAR = 0 if necessary), then the output will go high when CCRx is reached while counting up, then go low when reached again while counting back down. This technique centers the pulse about the high point, allowing multiple PWM signals to be centered on the same moment in time. Since this mode requires TACCR0 to set the value to which the timer will count, you cannot use TA0.0 for PWM in this setup either.

Reader Exercise: You'll note that this time, the PWM does go all the way to 0%. If you couldn't figure out the answer to the first exercise, consider how this code is different. What order do the actions happen with the interrupt method? What order do they happen with the reset/set output method?

Why PWM?

The second code example has the same simple function as before: the PWM increases its duty cycle from 0% to a full 100%, then decreases back to 0% before repeating. When the pulse width is small, on average the LED puts out less light. A 10% duty cycle means the LED puts out light for only 10% of the time as usual, and is therefore perceived as being about 10% as bright. By increasing then decreasing the duty cycle, you create a cool, breathing-like effect from the LED, which is used in many popular electronics devices today. For a more practical application, the technique can be used as a "dimmer" for an LED light source when desired.

Since LEDs are nearly digital in their behavior (they're either on or off, nothing in between), why do we perceive the light as being dimmer rather than observing the blinking they're actually doing? The human eye has a relatively slow response to changes in light-- as long as the blinking is faster than the human eye can perceive, we don't detect the rapid transitions and only perceive less total light accumulation in our eyes. This is the same trick that makes movies possible--videos with a frame rate of about 25 frames per second or more seem smooth and seamless to the human eye. But this doesn't mean that ideally you should be flickering the LED as fast as possible; since LEDs aren't perfectly digital, you can probably anticipate that by having the PWM frequency set too high you might artificially lower the brightness even further, as the LED itself (or at least the resistance and capacitance of the circuitry connecting the LED to the signal) can't react quickly enough. In practice, 1-10 kHz is usually sufficient for PWM, though some applications do benefit from higher frequencies. Those situations will require careful design to ensure the stray resistance and capacitance of the circuit don't slow down the transitions too much.

Putting PWM to Work

In reality, that extra resistance and capacitance is sometimes a benefit, however. For example, by intentionally putting a large enough resistor-capacitor pair on the PWM output, you can prevent the signal from ever dropping significantly. Each pulse of the PWM recharges the capacitor a little, and each dead time lets the capacitor discharge slowly. The effect keeps a relatively constant voltage between pulses. By choosing the RC values carefully, you can successfully maintain a voltage that is proportional to the duty cycle: you've successfully created a digital to analog converter!

That's it for this tutorial. Hopefully this answers some of your questions about PWM, and gives you some ideas on how to use it.

Reader Exercise: Try making a 10-bit DAC. You'll want an RC combination that gives a time constant longer than the period between pulses; so if you use a 1 kHz PWM frequency (such as that from the default DCO, or the 1 MHz calibrated DCO for that matter, and setting TACCR0 to 1000) shoot for an RC value greater than 0.001. Write your code in a way that lets you put out a few different duty cycles, cycling either by a button press, a UART command, or automatically with a long delay between changes. Measure the output with a multimeter; the output should be close to the duty cycle multiplied by the voltage of your LaunchPad. (If you're running on USB, that's 3.3 V.) If you have an oscilloscope, look at how clean your DC voltage is. How much ripple do you get? Try raising or lowering the RC time constant. How does that affect the output?

23 February 2013

Tutorial 20c: The 24xx08

While setting up the USI for I2C is more complicated, the value of that complexity becomes apparent when you look at a real implementation of the I2C protocol. The Microchip 24AA08 EEPROM is the I2C equivalent to the 25AA080 we used as an example for SPI. The I2C version of this EEPROM is substantially simpler.

Instructions

This chip, unlike its SPI counterpart, has no status register. Write protection is handled strictly by hardware through a single pin connected to either ground (write enable) or Vcc (write protect). There are really only two instructions, and the read and write instructions can be done by single bytes or by up to 16 bytes (a page). The seek instruction is really nothing more than a write instruction without sending any data to write.

  • Seek: The 24xx08 has a register that points to the current address in its memory for the next read/write command. We can change the current address by sending the slave address + write bit (a 0 in the 8th bit sent) followed by the address to which the slave should point. Each of these is, of course, followed by an acknowledge bit. The master issues a stop condition immediately after the address.
  • Write: We write a value to the 24xx08 by sending the slave address + write bit followed by the memory address to which the value should be written. This is followed by the 8-bit value to write and a stop condition. We can write up to 16 bytes in contiguous memory space by sending subsequent values before the stop condition.
  • Read: We read a value from the 24xx08 by sending the slave address + read bit (a 1 in the 8th bit sent). After acknowledging the command, the slave sends the 8 bit value at the current address on the next 8 clock cycles and increments the address pointer to the next space. If the master acknowledges, the slave will repeat by sending the next value. When all values required are read, the master sends a nack, indicating no more data is to be sent, and then issues a stop condition. If a value is required from a different address than the one to which the 24xx08's register currently points, a seek instruction should be sent before the read instruction.

Design Considerations

A quick look at the datasheet for the 24xx08 is worthwhile. There are a few things to note about this device.
  • Address: The 7-bit binary address for this device is 0b1010XBB. The bit labeled X is unused in this device, and can take either value. The last two bits BB refer to the block within the chip. The memory space is divided into four, 256-byte blocks. (Each byte within a block has an 8-bit address between 0 and 255. The X bit here would be used in a larger device with 8 blocks in memory.) Note that this means that only one of this particular device can be used on a given I2C bus; adding a second 24xx08 device, or any Microchip I2C EEPROM in fact, would create an address conflict on the bus. Adding the 8th R/W bit, this corresponds to slave addresses from 0xA0 to 0xA7 in hex. (That uses 0 as the X bit. Technically a 1 can be used here as well, so the addresses 0xA8 through 0xAF are respectively equivalent in this device.)
  • Most packages of this chip have three pins labeled A0, A1, and A2. In this particular device, these pins are unused and unconnected internally. Larger devices would use these in place of the block bits to determine the chip's address, allowing for up to 8 of the same chip on the bus. For this chip, the pins can be grounded, tied to Vcc, or even left floating.
  • Most of the timing requirements specified in the datasheet are good to see, but not an issue at the transmission rates we'll be using. (The device is rated for up to 400 kHz, and even at that rate the minimum requirements are well below what is actually used.) The important one for our purposes, however, is the maximum rise and fall times, TR and TF. These define the maximum RC time constant for the SDA and SCL lines. This device is rated with a 10 pF capacitance on the lines. The GPIO pins of the MSP430 are rated at 5 pF. The lines connecting the two chips will add a little more capacitance. Using a worst-case estimate of 30 pF and the maximum rise/fall times of 300 ns, we get a maximum resistance value of 10 kΩ.
  • Faster rates, as you recall, will benefit from lower resistances, at the expense of power consumption. The internal resistors available in the MSP430 are between 20 and 50 kΩ. For slow data rates, these are probably alright to use. But faster rates may require an external resistor in the 1 to 10 kΩ range.
  • Like the SPI version of this chip, the write process is not initiated until a stop condition is seen, and a 5 ms settling period is required for write operations whether writing a single byte or a full 16-byte page. Using a similar setup to the previous tutorial, we'll have to keep that in mind when using a 9600 baud UART to send the data being written. If any instruction is issued to the device during this settling time, the chip will not respond. The master can identify the nack by polling, in this case, and wait until an ack bit is sent before sending the next instruction.
And that's really about all you need to know to use this EEPROM successfully with the MSP430! The wire connections are just as straight forward, as only 4 wires are needed: Vcc, ground, SDA, and SCL. The WP pin of the 24xx08 should be tied to ground for write operations.

Examples

This example starts off simply, writing a single value to every memory space one by one. Since we need 5 ms between write commands, a simple delay is used in the code. Ideally, you would allow the MSP430 to continue with other tasks, and have a timer to indicate when it is ready to write again, but for this example a simple method works fine. The code provided uses a default fill value of 0xff, to act as an eraser of the flash memory. Feel free to change the fill_char value to whatever you wish; hex, decimal, and C character values are all acceptable here.
This example reads the entire contents of the EEPROM and dumps the data via UART. If you ran the i2cerase_G2231.c code previously, you should see 1024 instances of 0xff appear in your terminal (assuming it has the capability of displaying this code; you might change to a visible character if not). In this example, I've used a low power mode to hold the main function until a value has been read by I2C  Uncommenting the _low_power_mode_off_on_exit() command at the end of USI_ISR signals the data is ready to be transmitted by UART. A delay loop is used again to delay the next read, though ideally a low power mode could be used again here.
The final example demonstrates writing in full pages of 16 bytes. The first 1024 characters received by UART are written to the EEPROM. Like the SPI example, you can use a text file to send particular ASCII data to the EEPROM. After sending the data, use i2cread_G2231.c again to display the message on your terminal. The page write itself is used to accommodate the EEPROM settling time: the amount of time it takes to send 16 bytes at 9600 baud is longer than the required 5 ms.

Race Conditions

When designing these examples, I ran into a bug that took me a long while to solve, so I thought I'd mention it here. What I finally figured out was that I was hitting what's called a race condition. Originally, my main function in i2cwrite_G2231.c looked like this:
    for(i=0; i<64; i++) {
     slave_address = SAdrs + i/16; // Current Block Address
     pageBuffer[0] = (i%16)*16; // EEPROM Write Address
     for(j=1; j<17; j++) {
     _BIS_SR(LPM0_bits + GIE);   // Standby for UART RX
    pageBuffer[j] = RXBuffer;   // Extract RXBuffer
    }
    while(SCFG & BIT4) // Ensure previous transmission is complete
    _nop();
     SCFG |= BIT3; // Mark transmission/write instruction
    USICTL1 |= USIIFG; // Set USI Interrupt to start
    // I2C communication
    }
The problem was that the first element of pageBuffer would be overwritten shortly after the USI interrupt was started-- before the USI could transmit the original value! Once you start mixing peripherals and making extensive use of interrupts, you need to be very careful of race conditions: when values you want to use are changed before you actually use them. The solution here, as seen in the linked code example, was to not change the slave and memory addresses until a new page of 16 bytes was ready to be sent. That prevented the race condition by ensuring the value was not changed until the USI was clear.

Note that there is still a potential race condition due to the UART: if you speed up the UART (likely you'd need to slow down the I2C bus too), you could receive new data before the old data is transmitted to the EEPROM. Of course, increasing the UART speed could cause problems with the settling time as well.

That concludes this particular tutorial; I'll move on to some other topics now, and bring a new experiment to the blog that will illustrate a fun use of I2C.

06 February 2013

Tutorial 20b: USI & I2C

Implementing I2C on the MSP430 USI is more involved than SPI. One of the major problems is that part of the implementation must be done in software; the USCI module is a more complete hardware implementation that makes I2C communication easier, but not all devices have that module. For those devices that don't have a serial module at all, I would recommend using SPI instead. It's possible to do I2C completely in software and timers, but it's not easy. SPI is much simpler for "bit-banging".

When you consider how a device should react to an interrupt, there are three possibilities. First, there may be only one task to be done. This reaction is the simplest, and is straightforward to code. A good example is using the timer interrupt to toggle the state of an LED. Second, there may be a set of possible tasks, and a given set of circumstances determines which task is performed at the interrupt. Often this is accomplished through use of if/else statements. Finally, there may be a sequence of tasks to be performed, each step timed by a succession of interrupts. This final reaction is also described as a "state machine".

The USI module has, of course, only one interrupt. (Well, there are two, technically, if your device is configured as a slave: there is a separate interrupt for signalling the start condition for an I2C transaction.) This interrupt is triggered every time a non-zero value is written to USICNT.  Every I2C transaction will require more than one write to USICNT, and we can make use of that to set up the module as a state machine. Doing so makes it easier to understand what is happening and to debug problems, and also allows you to spend more time in a low power mode. Let's examine a flow chart describing the state machine for an I2C master:

I've tried to group these in a suggestive way, separating the different states as they will appear in code. Note that in particular the logic analyzing the Ack/Nack bit will be in the same section of code that handles the transmit/receive of the data. I've also left out the Ack/Nack handling for dealing with multiple bytes of data to keep from cluttering the diagram, but be aware that those will also be handled.

Note that the state machine diagram for a slave configuration is not much different--replace the first two steps with a receive address and acknowledge if needed. (A "Nack" response would simply be to go right back to idle in this case.) Instead of generating the stop condition, a slave would simply clean up (perhaps taking a new measurement or otherwise preparing for the next command) and go back to idle.

The state machine concept lends itself very easily to the switch statement in C. Looking at this state diagram, we should be able to code the USI interrupt with 6 different states. The states can be assigned a number (eg. 1-5 + default for idle), or by using the enum construction in C, making the code much more readable.

Configuring the USI for I2C

The USICTL0 register is handled similarly to the setup we did for SPI. The main difference is that we no longer need P1.5, since there is only one data line in I2C. In USICTL1, we also need only enable the USII2C bit. For I2C operation, as you recall, we want the phase/polarity to match Mode 3 in SPI; USICKCTL should be set to whatever clock source and division are desired, with the USICKPL bit enabled.

Finally, when operating the USI in I2C mode you should disable automatic clearing of the interrupt flag by enabling the USIIFGCC bit in USICNT. The reason for this will become apparent when we look at the interrupt service routine. Since one of the upper bits in USICNT is now enabled, we can't start transmission/reception simply by assigning the number of bits to this register. For that reason, we'll start the clock by using an or operator to avoid changing any of the upper bits in this configuration.

The code to setup the USI for I2C may look something like this:


USICTL0 = USIPE6 + USIPE7 + USIMST + USISWRST;  
// Enable ports and set as master
USICTL1 = USII2C + USIIE;       // I2C mode, enable interrupts
USICKCTL = USISSEL_2 + USIDIV_3 + USICKPL;      
// Use SMCLK/8, Mode 3 phase/polarity
USICNT |= USIIFGCC;             // Disable automatic clear control
USICTL0 &= ~USISWRST;           // Enable USI
USICTL1 &= ~USIIFG;             // Clear any early flags

Setting Up the ISR

This (updated and verified!) code fragment is a simple way to set up the ISR in a way that allows both transmission and reception with I2C. There are a few things to note about this code:

  • The format for usage of the enum C type is:
    enum{item_1, item_2, ... , item_n} var_name = initial_state
    The numbering as done in this example is redundant, but serves to illustrate that explicit enumeration values can be given. This variable is declared as static to retain its value between calls to the ISR.
  • Recall that the start and stop conditions are given by changes in SDA while the clock is high. This is handled by enabling the output latch for the SDA line using the USIGE bit in USICTL0. The SDA line will immediately take whatever value is in the first bit to send in USISRL, and so that register is set to 0x00 for start and 0xFF for stop. (Only the most significant bit need be set, but this method has the benefit of being both convenient and comforting.)
  • Also recall that in I2C, the SDA line can be changed by master or slave. When receiving, control of the line must be relinquished to whomever is transmitting. This is accomplished by setting the USIOE bit in USICTL0 to control the line, and clearing it to relinquish the line.
  • The SCFG flag I have used in the prior tutorials on serial communication adds two more bits used here: bit 3 indicates transmit when set, receive when clear. Bit 4 is used to indicate a new receive value has been saved to the buffer. Presumably the buffers ITXBuffer and IRXBuffer have been defined previously. (The names here are thinking ahead to using I2C in parallel with UART.)
  • I have also used a hopefully self-explanatory variable slave_address, presumably defined earlier.
  • Note the inclusion of an additional state labeled "PrepStop". This state is necessary before sending a stop condition, as the line must be low before a stop can be sent. Note that each case in the switch statement ends with setting one or more of the first 5 bits in USICNT and setting the next state. After the break, the USIIFG is cleared. When the USI module has finished transmitting everything, an interrupt is generated, and the routine will call into the next state as a result. The PrepStop state is needed in order to allow the previous ack/nack to finish before stopping the communication.
  • Finally, clearing USIIFG is handled manually in order to stretch the clock when necessary for a slow communication. If something delays the master, clearing it manually ensures that every step in the ISR is followed before another interrupt can be allowed.

That finishes this tutorial on the configuration of the USI module for the I2C protocol. Next time we'll put it to use with the 24xx08 serial EEPROM, as we did for the SPI example.