Thanks, everyone, who has been a part of this blog! It's been way too long since I've worked on this, but it's not a topic I've intended to give up forever. I do intend to expand on these tutorials, and branch out into a few other topics as well, using other Launchpads, perhaps FPGAs, and other transducers and tools to work in scientific instrumentation.
I'm adding a post here to let you all know that more will come, hopefully in early 2015, but that it will come in the form of an entirely independent website. The tutorials here will be updated and refreshed for that site and new content will start showing up. I hope you all look forward to it as much as I do!
11 November 2014
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.
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?
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?
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.
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?
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.
|
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
- Simple writing: i2cerase_G2231.c
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.
- Simple reading: i2cread_G2231.c
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.
- Page writing: i2cwrite_G2231.c
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.
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.
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
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.
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.
29 September 2012
Tutorial 20a: Introducing I2C
The SPI protocol is a simple and useful system, but is not without its disadvantages. In the example we looked at, we noted that at any given time, despite the ability to exchange messages simultaneously, only the master or the slave was sending any meaningful data. The system is capable of linking multiple devices together, but the technique is much like an old switchboard operator: each devices gets its own unique and private line, and the master selects which device it is talking to at any given moment. In the mean time, lines not being used are taking up space and resources that could be allocated to other tasks. This technique is not the only way to communicate, however. What if instead of a switchboard model, we looked more to radio communication: if you tune in to a specific frequency, anything transmitted can be heard by all, and (at least at some frequencies) all can transmit at any time. This type of system is far more conducive to linking multiple devices together as it greatly simplifies the connections, both physically and conceptually.
In electrical systems, we call any line that links multiple parts together a "bus". In the sense of serial communication, a bus is a line on which devices can watch for signals, or generate signals of their own. A bus-type system reduces the number of connections needed between components and links multiple systems together in potentially complex ways. One of the most popular implementations of a true serial communications bus was developed in the 1980's by Philips Semiconductors: the Inter-integrated Circuit Bus, or I2C.
Problem 1: Since multiple devices control the same line, what happens when one device actively pulls the line low, while another actively pulls the line high? This creates a short between the low and high states, likely causing irreparable damage to the system. To get around this, the I2C specification requires devices to only actively pull a line low. The line is tied with a resistor to Vcc, so if no device is active it defaults to a high state. (This reason is similar to the need for the resistor(s) in the SPI example.) In terms of the MSP430 GPIO, think of it as setting the output of a pin to 0, then toggling whether the pin is an output (pulling the line low) or an input (allowing the line to be pulled high). In fact, if you ever find yourself required to bit-bang I2C, this is exactly the technique you'll have to use to prevent any damage to the systems involved.
Problem 2: As a result of the use of resistors to pull the line state high, we are limited on the speed at which we can communicate with I2C. Any capacitance in the line, combined with the resistor, will lengthen the amount of rise time in the line. Too much resistance, and the line doesn't pull high soon enough to get the right message sent. Too little resistance, and you draw far too much power through the resistor when the line is pulled low. The standard specification for I2C limits transmission speeds to a 100 kHz clock. For the 3 V power used on many MSP430 designs, a resistance between 1 and 10 kΩ is typical; if you're concerned about speed, aim for the 1 kΩ end. Other specifications allow for clock speeds of up to 400 kHz (fast mode), or 1 MHz (fast mode plus). These modes will require smaller resistors, and inherently use more power than standard mode.
Problem 3: Since all devices are listening in on the same line, if the master asks for a data value to be reported, which device responds? I2C assigns a call sign, of sorts, to each device in the form of a 7-bit address. (An 8th bit is added to specify if the master wants to read from or write to the device.) This address is usually hard-coded into the device, and reading its datasheet will give the information needed to address it correctly. Some devices hard code some of the bits, allowing the user to select the specific address by setting the other bits accordingly. This technique allows the use of more than one of the same device in a system.
Problem 4: In addition to knowing who needs to respond, devices (including the master) need to know when they can send signals; if the line is tied up by another device, other devices must hold on their own messages until the line is signaled as being free. This coordination is effected in I2C by the timing of changes in the data line compared to the clock line. Communication in I2C is initiated by a falling edge in the data line, and marked complete by a rising edge in the data line, much like the use of the chip select in the SPI example we did. Since this is the same line where data is transmitted, I2C specifies that a "start condition" occurs when the data line is pulled low while the clock is high. A "stop condition" occurs when the line is pulled high while the clock is high. Data transmission occurs between these two conditions.
Data is sent after signaling a start condition by changing the line state while the clock is low, and the listening device reading the state while the clock is high. (Note that this is essentially equivalent to Mode 3 for SPI.) It is essential that devices sending messages do not change the state of the data line while the clock is high unless marking a start or stop condition. Other devices need to be aware of start and stop conditions to avoid sending messages of their own at the same time. If a device sees a start condition, it will listen for the address. If not its own, it must wait until the line is freed up by a stop condition before sending its message.
Look at this image of the first part of an I2C transmission-- this portion highlights the start condition and the addressing portions. Note that the state of the clock (bottom curve) is idling high. The clock does not start until after the master pulls SDA low. The start condition is signaled by the falling edge on SDA while SCL is high. The start signal is followed by 9 pulses of the clock on SCL. Remember: at this point, neither the master nor the slave changes the state of SDA while the clock is high. Notice that the changes in SDA sending the data occur between pulses-- you can read out the transmitted value at the points where the clock is high. In this example, the data reads 0b01000001. The first 7 bits of this value are the address of the slave (in this case, 0b0100000). These bits are followed by a final 1, which designates a read command. The following data will be sent by the slave, reporting the data it has ready to send.
Did you note that there were 9 pulses, but only 8 bits? The 9th clock is done for some basic handshaking, and is called the "acknowledge" bit. In this case, the master addresses the slave at the specified address and releases the line after sending the 8 bits. If the slave recognizes the call, it pulls the line low; the master reads the 9th bit to confirm that the slave has heard the instruction. What happens if the slave doesn't hear, or fails to acknowledge the signal? Since the line is tied high and the master has relinquished control, the line automatically pulls high. If the master reads the 9th bit and sees it high, it aborts the transmission. (Whether it tries again or not depends on how the master is built, of course.)
At this point (assuming the slave acknowledged the instruction), it is important that the master not signal a stop condition (by sending a rising edge on SDA while the clock is high) to give the slave the time to report back the data it was asked to read. The remainder of this example looks like this:
How we interpret the remainder of the instruction depends on the exact device being read. For simplicity, let's assume that we're dealing with a device that has a single, 2-byte register. No further addressing is necessary, so when the slave receives the instruction to read, it reports back the 16 bit value it has. Data is transmitted one byte at a time, with an acknowledge bit between bytes. In this example, we see the slave reporting back the two byte value corresponding to the ASCII characters "%%". After the first byte, the slave relinquishes SDA and watches for the master to acknowledge receiving the data (again, by holding SDA low). After the final byte, notice that the acknowledge bit is now high. On the last cycle, the master does not send an acknowledge bit, indicating that no more data is expected.
Finally, the master pulls SDA low again, then releases the clock so that SCL is high. The master then releases SDA to give a stop condition. At this point, any other devices needing to start a transmission are free to do so; if between the start and stop any other device has a message to send, it must wait until it sees the stop condition before doing so.
Now that we've got a handle on some basic I2C formatting, we'll turn our attention to the USI module and how it's used in I2C in the next part of this tutorial.
Reader Exercise: Using the second image, identify which bits (including acknowledge bits) in the data stream on SDA are sent by the master, and which are sent by the slave, according to the description given in this tutorial. What is the slave doing while the master is sending data? What is the master doing while the slave is sending data? Who has control of SDA at what times?
In electrical systems, we call any line that links multiple parts together a "bus". In the sense of serial communication, a bus is a line on which devices can watch for signals, or generate signals of their own. A bus-type system reduces the number of connections needed between components and links multiple systems together in potentially complex ways. One of the most popular implementations of a true serial communications bus was developed in the 1980's by Philips Semiconductors: the Inter-integrated Circuit Bus, or I2C.
Problems With a Serial Bus
As you might expect if you've ever heard radio interference, some significant issues come up when rather than each device getting its own, private communication line they have to share a line. Much like a crowded household of entitled teenagers (at least before cell phones), a crowded bus in an electrical system needs to lay down rules for who can talk and when.Problem 1: Since multiple devices control the same line, what happens when one device actively pulls the line low, while another actively pulls the line high? This creates a short between the low and high states, likely causing irreparable damage to the system. To get around this, the I2C specification requires devices to only actively pull a line low. The line is tied with a resistor to Vcc, so if no device is active it defaults to a high state. (This reason is similar to the need for the resistor(s) in the SPI example.) In terms of the MSP430 GPIO, think of it as setting the output of a pin to 0, then toggling whether the pin is an output (pulling the line low) or an input (allowing the line to be pulled high). In fact, if you ever find yourself required to bit-bang I2C, this is exactly the technique you'll have to use to prevent any damage to the systems involved.
Problem 2: As a result of the use of resistors to pull the line state high, we are limited on the speed at which we can communicate with I2C. Any capacitance in the line, combined with the resistor, will lengthen the amount of rise time in the line. Too much resistance, and the line doesn't pull high soon enough to get the right message sent. Too little resistance, and you draw far too much power through the resistor when the line is pulled low. The standard specification for I2C limits transmission speeds to a 100 kHz clock. For the 3 V power used on many MSP430 designs, a resistance between 1 and 10 kΩ is typical; if you're concerned about speed, aim for the 1 kΩ end. Other specifications allow for clock speeds of up to 400 kHz (fast mode), or 1 MHz (fast mode plus). These modes will require smaller resistors, and inherently use more power than standard mode.
Problem 3: Since all devices are listening in on the same line, if the master asks for a data value to be reported, which device responds? I2C assigns a call sign, of sorts, to each device in the form of a 7-bit address. (An 8th bit is added to specify if the master wants to read from or write to the device.) This address is usually hard-coded into the device, and reading its datasheet will give the information needed to address it correctly. Some devices hard code some of the bits, allowing the user to select the specific address by setting the other bits accordingly. This technique allows the use of more than one of the same device in a system.
Problem 4: In addition to knowing who needs to respond, devices (including the master) need to know when they can send signals; if the line is tied up by another device, other devices must hold on their own messages until the line is signaled as being free. This coordination is effected in I2C by the timing of changes in the data line compared to the clock line. Communication in I2C is initiated by a falling edge in the data line, and marked complete by a rising edge in the data line, much like the use of the chip select in the SPI example we did. Since this is the same line where data is transmitted, I2C specifies that a "start condition" occurs when the data line is pulled low while the clock is high. A "stop condition" occurs when the line is pulled high while the clock is high. Data transmission occurs between these two conditions.
Data is sent after signaling a start condition by changing the line state while the clock is low, and the listening device reading the state while the clock is high. (Note that this is essentially equivalent to Mode 3 for SPI.) It is essential that devices sending messages do not change the state of the data line while the clock is high unless marking a start or stop condition. Other devices need to be aware of start and stop conditions to avoid sending messages of their own at the same time. If a device sees a start condition, it will listen for the address. If not its own, it must wait until the line is freed up by a stop condition before sending its message.
I2C in Action
If after reading all of that you feel like I2C is really complicated, it's not. Well, it's certainly more complicated than SPI, but once you see how the protocol works it's not too difficult to understand. So let's look at a typical I2C transmission and analyze what's going on.Look at this image of the first part of an I2C transmission-- this portion highlights the start condition and the addressing portions. Note that the state of the clock (bottom curve) is idling high. The clock does not start until after the master pulls SDA low. The start condition is signaled by the falling edge on SDA while SCL is high. The start signal is followed by 9 pulses of the clock on SCL. Remember: at this point, neither the master nor the slave changes the state of SDA while the clock is high. Notice that the changes in SDA sending the data occur between pulses-- you can read out the transmitted value at the points where the clock is high. In this example, the data reads 0b01000001. The first 7 bits of this value are the address of the slave (in this case, 0b0100000). These bits are followed by a final 1, which designates a read command. The following data will be sent by the slave, reporting the data it has ready to send.
Did you note that there were 9 pulses, but only 8 bits? The 9th clock is done for some basic handshaking, and is called the "acknowledge" bit. In this case, the master addresses the slave at the specified address and releases the line after sending the 8 bits. If the slave recognizes the call, it pulls the line low; the master reads the 9th bit to confirm that the slave has heard the instruction. What happens if the slave doesn't hear, or fails to acknowledge the signal? Since the line is tied high and the master has relinquished control, the line automatically pulls high. If the master reads the 9th bit and sees it high, it aborts the transmission. (Whether it tries again or not depends on how the master is built, of course.)
At this point (assuming the slave acknowledged the instruction), it is important that the master not signal a stop condition (by sending a rising edge on SDA while the clock is high) to give the slave the time to report back the data it was asked to read. The remainder of this example looks like this:
How we interpret the remainder of the instruction depends on the exact device being read. For simplicity, let's assume that we're dealing with a device that has a single, 2-byte register. No further addressing is necessary, so when the slave receives the instruction to read, it reports back the 16 bit value it has. Data is transmitted one byte at a time, with an acknowledge bit between bytes. In this example, we see the slave reporting back the two byte value corresponding to the ASCII characters "%%". After the first byte, the slave relinquishes SDA and watches for the master to acknowledge receiving the data (again, by holding SDA low). After the final byte, notice that the acknowledge bit is now high. On the last cycle, the master does not send an acknowledge bit, indicating that no more data is expected.
Finally, the master pulls SDA low again, then releases the clock so that SCL is high. The master then releases SDA to give a stop condition. At this point, any other devices needing to start a transmission are free to do so; if between the start and stop any other device has a message to send, it must wait until it sees the stop condition before doing so.
Now that we've got a handle on some basic I2C formatting, we'll turn our attention to the USI module and how it's used in I2C in the next part of this tutorial.
Reader Exercise: Using the second image, identify which bits (including acknowledge bits) in the data stream on SDA are sent by the master, and which are sent by the slave, according to the description given in this tutorial. What is the slave doing while the master is sending data? What is the master doing while the slave is sending data? Who has control of SDA at what times?
21 September 2012
Tutorial 19c: The 25xx080
Now that we have a handle on configuring the USI module to communicate using the SPI protocol, we can implement it in a real situation. Today we'll be using a Microchip Serial EEPROM (I'm using specifically the 25AA080C, though in principle this should work with any Microchip SPI EEPROM). Looking at the datasheet for this chip, there are a few things to understand.
Another technique you will see used in these examples was used in the UART tutorials: a custom flag is used to indicate when SPI transmission is occuring. This flag is polled to hold the program until the transmission has completed, ensuring that the USISR is not changed until all bits have been transmitted. This flag is cleared in the interrupt routine for the USI. The chip select is done manually in each example below. See the Reader Exercise at the end of the tutorial for an idea on a better way to handle the chip select.
Instructions and the Status Register
This chip has 6, 8-bit instructions. In this design, the Chip Select pin is actually used to mark the end of an instruction, and so is a necessary signal that should be enabled (set to 0) before and disabled (set to 1) after each of the following:- READ (0x03): Sending these 8 bits followed by the 16-bit address of the location in memory you want to read instructs the EEPROM to send the 8-bit value at the specified address on the next 8 clock cycles. The whole instruction takes 32 clock cycles (8 for the command, 16 for the address, and 8 for the read-back).
- WRITE (0x02): This command can be used in two different ways. First, after sending the command, a 16-bit address is sent followed by the 8-bit value to write. Second, rather than write single bytes at a time, the chip can write a group of bytes (called a page) at once. The 25AA080C has a page size of 16, so up to 16 bytes can be written in one instruction. This mode uses the command bits, followed by the initial address (each byte is written in the subsequent address of the last), and then the values for each byte to write. Note: writing does not occur until the chip select has been disabled (set). In addition, the chip needs a small amount of time (about 5 ms, according to the datasheet) to perform the writing. Any subsequent write commands must be sent after the settling time to prevent interruption. We'll examine this point a little more later. This instruction takes 24 + 8n clock cycles, where n is the number of bytes being written (max of 16 on this chip).
- WRDI (0x04): This simple, 8-bit only instruction disables writing to the EEPROM. Data can still be read, but write instructions will have no effect. This instruction takes only 8 clock cycles.
- WREN (0x06): The opposite of WRDI, sending these 8 bits enables writing to the EEPROM. When a write cycle completes, the write-enable latch is reset, so this instruction must be issued before every write instruction. This instruction also only takes 8 clock cycles.
- RDSR (0x05): The EEPROM also includes a Status Register that indicates the configuration of the chip. The 8-bit value can be read by issuing this command followed by 8 clock cycles to read the value back. This instruction takes 16 clock cycles.
- WRSR (0x01): The Status Register can be written directly under certain circumstances. When those conditions are met, a write is done by issuing a WREN instruction, followed by this command and the 8 bits to write to the Status Register. This instruction takes 16 clock cycles, but like the WRITE instruction must be preceded by a WREN.
The Status Register uses 5 of the 8 bits in this EEPROM chip:
- WIP (Bit 0): This bit is a flag that is set when the chip is in the middle of writing to the flash. Write instructions should not be issued until this is clear, which should take no more than 5 ms.
- WEL (Bit 1): This bit flags when writing is enabled on the chip. A WREN command will set this bit, while a WRDI will clear it. Writing to the flash only occurs if this bit is set. Writing to the Status Register may be possible while set, depending on the chip's configuration. (See WPEN below.)
- BP0 and BP1 (Bits 2-3): These bits enable write protection for individual blocks in the EEPROM. The 25AA080C is built with two blocks, corresponding to the upper half and lower half of the flash memory respectively. Setting both BP0 and BP1 will protect the entire chip from being over-written. These bits are set through WRSR instructions.
- WPEN (Bit 7): This bit determines whether the Write Protect pin affects the writeability of the Status Register. If this bit is clear, the Status Register can be written to regardless of the state of WP (as long as WREN has been done, of course). If this bit is set, then the Status Register can be written to if WP is held high, but is protected if this bit is held low.
All of these instructions and register definitions can be encapsulated in a C header file. As an example, look at 25xx080c.h. This file was written with this single device specifically in mind, but can be expanded to generalize to any SPI EEPROM from Microchip. I will be using this header file in all of the example code below, in addition to the calibrations.h header file. If you run these code examples, be sure to include the two header files. If they are linked to, be sure to adjust the CCS project properties to search for the directory they reside in for both the compiler and the linker, and set the debugger to clear only the main memory so the DCO calibration doesn't get overwritten. (If that happens, go back to the flash memory programming tutorial and re-calibrate your MSP430.)
MSP430 Issues
As you get more involved with MSP430 design, you'll want to become aware of problems in the hardware itself. Every device on TI's website has a link to a "Device Errata", that details known problems with the hardware. The MSP430G2231 Device Errata lists a known problem labeled as "USI5", which applies to using the SPI protocol. For some reason, this device will send an extra clock cycle the first time it is used; so instead of sending 8 clock cycles after writing 8 to USICNT, it will send 9. There are two ways to get around this: the errata suggests writing one less to USICNT the first time, so writing 7 instead of 8. I think that makes for messy code, since the first transmission has to be treated differently, so instead I clear the bug in initializing USI. Before enabling the SDO and SDI pins on the MSP430, but after starting the USI, I send a command to transmit a single bit. Two pulses are actually sent, due to the bug, but since P1.6 and P1.7 haven't been enabled in USI yet, it has no effect on any devices attached to the MSP430. Once the two pulses have cleared, the pins are enabled, and the MSP430 is free to use the module as expected. The code I used to do this looks like this:
void USI_init(void) {
USICTL0 = USIPE5 + USIMST + USIOE + USISWRST;
// Enable SCLK, Master mode, enable output and reset USI
USICTL1 = USICKPH + USIIE; // Mode 0 requires CKPH=1, enable interrupt
USICKCTL = USIDIV_3 + USISSEL_2; // SMCLK div 8 -> 921.6 kHz USI clock
// One write command should take ~50 us at this rate,
// while 1 bit via UART @ 9600 should take ~100 us.
USICTL0 &= ~USISWRST; // Clear USI for use
USICNT = 1; // clear 1st transmit due to errata USI5
__delay_cycles(50); // finish clearing (minimum (n+1)*16 cycles)
USICTL0 |= USIPE7 + USIPE6; // Enable SDI/SDO pins.
USICTL1 &= ~USIIFG;
} // USI_init
Another technique you will see used in these examples was used in the UART tutorials: a custom flag is used to indicate when SPI transmission is occuring. This flag is polled to hold the program until the transmission has completed, ensuring that the USISR is not changed until all bits have been transmitted. This flag is cleared in the interrupt routine for the USI. The chip select is done manually in each example below. See the Reader Exercise at the end of the tutorial for an idea on a better way to handle the chip select.
Example Code
Writing, Method 1
First up, eepromwrite_spiG2231.c demonstrates writing to the EEPROM device. The code uses P1.4 as the chip select signal, and that pin should be connected to pin 1 of the EEPROM. Be sure to connect P1.5/SCLK to SCK (pin 6), P1.6/SDO to SI (pin 5), and P1.7/SDI to SO (pin 2). Recall that the SCLK, SDO, and SDI functions are enabled in the MSP430 in the USI registers, not in P1SEL. It might not hurt to remove the jumper to the LED on P1.6 on the LaunchPad. In addition, though the code may work without it, it's a good idea to tie the SDI-SO line (pin 2) to Vcc. The MSP430 does not automatically set this line when idling, as one would expect, so a simple resistor (say ~10k) will do the trick. Finally, be sure to tie HOLD (pin 7) to Vcc as well.
RealTerm lets you send data from a text file, and
add the necessary 5 ms delay between characters.
|
Once running, this code will write the first 1024 bytes received by UART (at 9600 baud) to the EEPROM. Once that has completed, it lights the LED on P1.0 and goes into a low power mode, signaling that the write process is done. Now, if you think about it, there's a problem with this code. At 9600 baud, each byte takes just over 1 ms to transmit. While that's more than enough time to send the write command to the EEPROM at the USI clock rate being used, it's not enough time for the write to settle; there should be roughly 5 ms between write commands. If you are transmitting each character by hand, you'll probably be ok. If you're like me and really don't want to press keys 1024 times just to test this code, you'll want to write a 1024 character text file, and transmit that directly. For that to work, you need to have some delay between characters in the transmission. I used the Windows program RealTerm, which in addition to having a feature to transmit from a file has a feature to introduce a manual delay between characters. With that configuration, I was able to transmit the file mspsci.txt and have it write successfully to the EEPROM.
Reading
To get this file to display nicely, you need to enable the
newLine mode in RealTerm. Displaying an extra line or
two also helps.
|
Second, eepromread_spiG2231.c will read back the contents of the EEPROM and send them via UART (at 9600 baud) for display. If you run this code before any writing, it will display whatever the contents of your chip are prior to doing either of the write programs. If your chip is new, it will likely be very boring, consisting of the value 0xFF in every address. If you run either of the write methods, running this program should result in a display much like what is shown here.
If you're using my text file and RealTerm, note that I've enabled newLine mode; the character being transmitted by default only initiates a line feed, without a carriage return. This mode adds the carriage return, so that it displays properly. If you lose a line of the display, increase the number of rows as well.
Writing, Method 2
Having to wait 5 ms between characters is a pain; we've effectively reduced our transmission rate to a crawling 200 baud. Fortunately, being able to write up to a single page at once saves us, since even after writing 16 bytes the settling time is still only 5 ms! At 9600 baud, each character takes just over 1 ms to transmit, so as long as we write more than 5 characters at a time, the amount of time it takes to transmit and store those characters is longer than the settling time needed between write commands. The disadvantage, of course, is that we need to employ a buffer to hold all of those characters in memory in the MSP430 while we wait. The MSP430 devices should have sufficient RAM to do this, however, so it's not much of a sacrifice. (But it is something of which we need to be aware in more complicated programs that write to EEPROM!)
In eepromwrite2_spiG2231.c, I implement the same technique as before, but by writing a full page of 16 bytes at once. Received values are stored in the buffer[] array, and then transmitted to the EEPROM to be written. The amount of time it takes to send the complete write command is less than the time it takes to receive the next character, so the code returns to the for loop waiting for the next 16 characters before the next character is received. If we operate SPI at a slower rate such that this condition isn't met, we can run into a race condition.
SPI Signal Details
Let's take a look at some of the details of the signals being passed back and forth in these programs.
These two images show signals from the first program, eepromwrite_spiG2231.c. The SPI lines are labeled from the perspective of the EEPROM, the UART from the MSP430. Notice in the first panel that the SPI lines only operate every 5 ms, between received characters. There's a lot of wasted time in this method. The second panel shows the important signal lines in detail. Note that there are two instructions being used, since CS shows two pulses. The SCLK is configured correctly for the chosen mode, idling low and reading on the rising edge of the clock. In a write command, the EEPROM is sending nothing back, so SDO is high throughout the instructions. The EEPROM sees the instruction 0x06 (WREN) followed by 0x02 (WRITE). Then the 16-bit address 0x06 is received followed by the value 0x2A to write to that address. (0x2A corresponds to the asterisk symbol.)
For the program eepromread_spiG2231.c, the UART signals now follow the SPI instructions (as makes sense; the UART is transmitting the values read). There is much less dead time in this program, since no settling is required between reads as there was between writes. Zooming in on the bottom panel, we see a single instruction. 0x03 (READ) is followed by an address, 0x08 in this case, then 8 dummy bits. At the same time, the SDO line idles until the last 8 bits, where it reports the value 0x2A as the value stored at address 0x08 in the EEPROM. After a short delay (due to steps taken in the MSP430), the next UART transmission is begun.
One thing to note in this chart; the SDI line is not idling at high all the time. This is due to the same reason we wanted to put a resistor on the SDO line to tie it to Vcc; likely it's a good idea to do this to both lines. By leaving it off, I was able to demonstrate that the process still works fine; it's only convention that requires us to have a specified idle state.
Finally, we have three panels to illustrate the eepromwrite2_spiG2231.c program. The first and third are analogous to those shown previously, giving a view of the overall signals being sent and a zoomed in picture of the SPI signals themselves. Notice there is almost no dead time in the UART signals received. The middle panel shows that even though we now have to send 16 bytes of data to write, the total length of the write instruction on SPI is less than the time to receive a single byte via UART. If this were not the case, and a UART transmission were to complete first, it could mess up the transmission by changing the values in the buffer array. Fortunately, SPI can operate very quickly compared to this transmission rate, even faster than is implemented in this code.
This post completes the tutorial on SPI (for now, anyway!), and next time we'll switch gears and learn to use USI with the I2C protocol.
Reader Exercise: There's a lot of commands that are retyped in each of these examples... wouldn't it be nice if instead of manually writing each instruction you could call a function that handles it for you? Write a C function that handles write instructions and read instructions, passing the address of interest, and the value to write (in the write instruction case) as parameters. A general write function for multiple bytes may be tricky to do; can you write one that handles both single-byte and multiple-byte cases?
08 September 2012
Tutorial 19b: USI SPI
Previously, I hinted at the variety of configurations possible for SPI communication. The MSP430's USI peripheral is very flexible for all of these configurations, so which we use will primarily be determined by the devices we connect to it. That being said, it's important to know how to decide which configuration we need to use.
When CPOL/CKPL = 0, the line idles low. An example of the timing of a mode using low polarity and using clock high to read is shown here. (These example diagrams come from John Davies' excellent book MSP430 Microcontroller Basics.) Note that we determine the phase by looking at the edge that appears in the middle of a bit. In this example, we see a rising edge in the middle, followed by a falling edge between bits. Whichever edge occurs in the middle of the bit is a read, and the edge between bits is a write, and the order these occur (not which direction the edge is!) determines the phase. This tells us we want to use CPHA = 0 (or CKPH = 1 for the MSP430) which reads first, then writes. This mode is often referred to as either Mode 0,0 or Mode 0.
Here's another example using CPOL/CKPL = 1, with the line idling high. In this example, the rising edge is still in the middle of the bit, and the falling edge is between, but the write is first and the read second. For this setup we would use CPHA = 1 (or CKPH = 0 for the MSP430). This mode is called Mode 1,1 or Mode 3 (as in 0b11 = 3).
Now let's look at a real-world example. The Microchip 25AA080C chip is an SPI EEPROM with 1 kB (1024 bytes) of memory. In its datasheet, we see this timing diagram:
The diagram specifies Mode 0,0 (alternatively Mode 1,1), but we can verify that ourselves because (1) the line idles low, and (2) the read occurs before the write. For this device, we want to use CKPL = 0 and CKPH = 1 for the MSP430. Note also that the order of the transmission is specified as MSB first.
USICTL0 (lower byte)
The two parts of this register are USICKCTL and USICNT, for the clock and the counter.
USICKCTL (lower byte)
The two byte-sized registers here are USISRL (the lower byte) and USISRH (the upper, or high byte). USISRH is ignored if the USI is configured in 8 bit mode (ie. USI16B is cleared in USICNT).
For the 25AA080C, we'll configure the MSP430 to be in master mode by setting USIMST, and select an appropriate clock speed for transmission. (At 3.3V, the 25AA080C works up to 5 MHz.) Using Mode 0, we'll clear USICKPL and set USICKPH. The commands (which we'll examine in the next tutorial) are 8 bits in length with MSB first, so we'll use the 8-bit shift register mode by clearing USI16B and use MSB first by clearing USILSB. Since we'll need to connect the two data lines and the clock to the EEPROM, we need to be sure to set all of the USIPEx bits. While not essential, we'll also use one extra P1 output to control the chip select for the EEPROM. Got all that? Great. Next time we'll actually do it!
If you're following along exactly, be sure you get an SPI EEPROM device before doing the next post. These are quite inexpensive, and available directly from Microchip. If you use a size other than the 8 kbit one I'm using, most everything will be the same.
SPI Modes
There are four primary modes for SPI, depending on the signal's polarity (whether it idles high or low) and its phase (do we read when the clock is low and write when the clock is high, or vice-versa). Common notation for these two values are CPOL and CPHA, respectively. TI, however, uses the values CKPL and CKPH, with CKPH being inverted from the standard definitions for CPHA. (That is, if CPHA = 0, CKPH = 1 and if CPHA = 1, CKPH = 0.)When CPOL/CKPL = 0, the line idles low. An example of the timing of a mode using low polarity and using clock high to read is shown here. (These example diagrams come from John Davies' excellent book MSP430 Microcontroller Basics.) Note that we determine the phase by looking at the edge that appears in the middle of a bit. In this example, we see a rising edge in the middle, followed by a falling edge between bits. Whichever edge occurs in the middle of the bit is a read, and the edge between bits is a write, and the order these occur (not which direction the edge is!) determines the phase. This tells us we want to use CPHA = 0 (or CKPH = 1 for the MSP430) which reads first, then writes. This mode is often referred to as either Mode 0,0 or Mode 0.
Mode 0 Timing Diagram From [Davies, 2008]. |
Mode 3 Timing Diagram From [Davies, 2008]. |
The diagram specifies Mode 0,0 (alternatively Mode 1,1), but we can verify that ourselves because (1) the line idles low, and (2) the read occurs before the write. For this device, we want to use CKPL = 0 and CKPH = 1 for the MSP430. Note also that the order of the transmission is specified as MSB first.
Configuring the USI
The USI uses 6 registers in the MSP430. One new thing about these registers that we haven't seen before is that, since each register is only one byte in size, we can also access them as 3 2-byte registers: the USI Control Register (USICTL), the USI Clock and Counter Control Register (USICCTL), and the USI Shift Register (USISR). (Alternatively, you can manipulate each register individually. There are convenient times to use both methods.)USICTL
The control register is comprised of two parts: USICTL0 and USICTL1. The pertinent bits are described here.USICTL0 (lower byte)
- USIPEx (bits 7-5): these bits enable the USI functions on the MSP430 pins. (The MSP430G2211 and G2231 use pins P1.5-7 for USI functions.) These pins are already connected to other peripherals with the P1SEL register, so they are configured for USI through its own register, each pin individually.
- USILSB (bit 4): when set, data is transmitted least significant bit first rather than the default most significant bit first.
- USIMST (bit 3): when set, the MSP430 will act in the master role, and SCLK is connected to the USI clock as an output. When cleared (the default configuration), it's in slave mode, and SCLK is an input.
- USIOE (bit 1): when set, output is enabled. This function is equivalent to the chip select in other devices; since the MSP430 doesn't have an explicit pin configuration for chip select, it has to be done in software if its use is needed.
- USISWRST (bit 0): software reset for the USI; when set, operation is held to allow configuration. This bit must be cleared to start the USI.
- USICKPH (bit 7): Sets the clock phase; inverted from the standard CPHA. When set, allows modes 0 and 2. When cleared, allows modes 1 and 3.
- USII2C (bit 6): Clear this bit to use the SPI protocol.
- USIIE (bit 4): enable interrupts for the USI counter; flags an interrupt when the specified number of bits have been transmitted.
- USIIFG (bit 0): interrupt flag for USIIE. This flag can either be cleared automatically or manually, though as we'll see later manual clearing is usually desireable.
USICCTL
The two parts of this register are USICKCTL and USICNT, for the clock and the counter.
USICKCTL (lower byte)
- USIDIVx (bits 7-5): Divide clock by powers of two up to 128.
- USISSELx (bits 4-2): Source select for the clock. USI has a wide variety of clock possibilities, including the Timer_A capture/compare registers. The variety allows for a lot of fine tuning for the exact transmission speed needed. (These bits are not used if the USI is used in slave mode.)
- USICKPL (bit 1): determines the clock polarity (set -> idle high, clear -> idle low).
- USISWCLK (bit 0): One option for the clock is to do everything in software. In this case, toggling this bit serves as the clock.
- USI16B (bit 6): When set, USI will transmit up to 16 bits . When clear, USI will transmit up to 8 bits.
- USIIFGCC (bit 5): When set, the interrupt flag must be cleared manually.
- USICNTx (bits 4-0): Writing a value to these bits starts transmission, which continues until the number of bits specified is reached (eg. to transmit 6 bits, write 0b00110 to these bits).
USISR
The two byte-sized registers here are USISRL (the lower byte) and USISRH (the upper, or high byte). USISRH is ignored if the USI is configured in 8 bit mode (ie. USI16B is cleared in USICNT).
For the 25AA080C, we'll configure the MSP430 to be in master mode by setting USIMST, and select an appropriate clock speed for transmission. (At 3.3V, the 25AA080C works up to 5 MHz.) Using Mode 0, we'll clear USICKPL and set USICKPH. The commands (which we'll examine in the next tutorial) are 8 bits in length with MSB first, so we'll use the 8-bit shift register mode by clearing USI16B and use MSB first by clearing USILSB. Since we'll need to connect the two data lines and the clock to the EEPROM, we need to be sure to set all of the USIPEx bits. While not essential, we'll also use one extra P1 output to control the chip select for the EEPROM. Got all that? Great. Next time we'll actually do it!
If you're following along exactly, be sure you get an SPI EEPROM device before doing the next post. These are quite inexpensive, and available directly from Microchip. If you use a size other than the 8 kbit one I'm using, most everything will be the same.
Subscribe to:
Posts (Atom)