tag:blogger.com,1999:blog-88538205652113629662024-03-05T09:21:35.910-05:00Scientific Instruments Using the TI MSP430Tutorials and explanations on the MSP430 microprocessor for the uninitiated. This blog is a collection of notes as I learn to use this microprocessor in a scientific laboratory venue and geared specifically to developing science instruments.Unknownnoreply@blogger.comBlogger64125tag:blogger.com,1999:blog-8853820565211362966.post-28559243348750860132014-11-11T12:47:00.000-05:002014-11-11T12:47:17.116-05:00What's Coming UpThanks, 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.<br />
<br />
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!Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-8853820565211362966.post-34929426997016952822013-04-25T18:49:00.003-04:002013-04-25T18:49:47.847-04:00Tutorial 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.<br />
<br />
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.<br />
<h3>
The Basic PWM Signal</h3>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoBdSmTJywIAgTfPUbyBjZpcNCnhdwpOMFceC1ka9RJef5cGv0Ruvfk3Do6LHc1NuIa9ZkvYoOO500OJ3G1naNnEb632zYsF2DeJDVpd_UpKLLglRbLXsuut0IwpxyIfLmnAL2Ag-_Hn9G/s1600/pwm_example.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoBdSmTJywIAgTfPUbyBjZpcNCnhdwpOMFceC1ka9RJef5cGv0Ruvfk3Do6LHc1NuIa9ZkvYoOO500OJ3G1naNnEb632zYsF2DeJDVpd_UpKLLglRbLXsuut0IwpxyIfLmnAL2Ag-_Hn9G/s400/pwm_example.png" width="263" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><div style="text-align: left;">
Three examples of PWM signals with different</div>
<div style="text-align: left;">
duty cycles. Each is also marked with its</div>
<div style="text-align: left;">
average level.</div>
</td></tr>
</tbody></table>
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.<br />
<br />
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 2<sup>16</sup>) 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.<br />
<br />
As a first example, consider this first example: <a href="https://sites.google.com/site/mspscifiles/tutorials/LEDPWM_G2231.c?attredirects=0&d=1">LEDPWM_G2231.c</a>. 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.<br />
<br />
<span style="color: #990000; font-size: x-small;">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 <span style="font-family: Courier New, Courier, monospace;">else if (TACCR0 == 0)</span> and see what happens. Can you explain it?</span><br />
<h3>
Better PWM</h3>
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 2<sup>16</sup>, the PWM signal is set at a frequency of f/2<sup>16</sup>, 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.<br />
<br />
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.<br />
<br />
Try the example code in this second example: <a href="https://sites.google.com/site/mspscifiles/tutorials/LEDPWM2_G2231.c?attredirects=0&d=1">LEDPWM2_G2231.c</a>, 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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxKe7ZuOq-8WORG-wkuJqgdiFUxp9IZi0w0UTkivlNliA01YpYKXtSSeAUHihG_cSnc1g06lm4GqKRHT6xwsO5rgmhIMVHsh2QIUmQ-GweZxqGskg3_95d7XR2FfEdB7ewEmCO7Pw0WLMw/s1600/pwm_types.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxKe7ZuOq-8WORG-wkuJqgdiFUxp9IZi0w0UTkivlNliA01YpYKXtSSeAUHihG_cSnc1g06lm4GqKRHT6xwsO5rgmhIMVHsh2QIUmQ-GweZxqGskg3_95d7XR2FfEdB7ewEmCO7Pw0WLMw/s320/pwm_types.png" width="320" /></a></div>
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.<br />
<br />
<span style="color: #990000; font-size: x-small;">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?</span><br />
<h3>
Why PWM?</h3>
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.<br />
<br />
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.<br />
<h3>
Putting PWM to Work</h3>
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!<br />
<br />
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.<br />
<br />
<span style="color: #990000; font-size: x-small;">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?</span>Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-8853820565211362966.post-79533451220170163442013-02-23T18:05:00.004-05:002013-02-23T18:05:56.448-05:00Tutorial 20c: The 24xx08While setting up the USI for I<sup>2</sup>C is more complicated, the value of that complexity becomes apparent when you look at a real implementation of the I<sup>2</sup>C protocol. The Microchip 24AA08 EEPROM is the I<sup>2</sup>C equivalent to the 25AA080 we used as an example for SPI. The I<sup>2</sup>C version of this EEPROM is substantially simpler.<br />
<h3>
Instructions</h3>
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.<br />
<br />
<ul>
<li>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.</li>
<li>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.</li>
<li>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.</li>
</ul>
<div>
<h3>
Design Considerations</h3>
</div>
<div>
A quick look at the datasheet for the 24xx08 is worthwhile. There are a few things to note about this device.</div>
<div>
<ul>
<li>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 I<sup>2</sup>C bus; adding a second 24xx08 device, or any Microchip I<sup>2</sup>C 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.)</li>
<li>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.</li>
<li>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, T<sub>R</sub> and T<sub>F</sub>. 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Ω.</li>
<li>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.</li>
<li>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.</li>
</ul>
<div>
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.</div>
</div>
<h3>
Examples</h3>
<div>
<ul>
<li>Simple writing: <a href="https://sites.google.com/site/mspscifiles/tutorials/i2cerase_G2231.c?attredirects=0&d=1">i2cerase_G2231.c</a></li>
</ul>
<div>
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 <span style="font-family: Courier New, Courier, monospace;">0xff</span>, to act as an eraser of the flash memory. Feel free to change the <span style="font-family: Courier New, Courier, monospace;">fill_char</span> value to whatever you wish; hex, decimal, and C character values are all acceptable here.</div>
<div>
<ul>
<li>Simple reading: <a href="https://sites.google.com/site/mspscifiles/tutorials/i2cread_G2231.c?attredirects=0&d=1">i2cread_G2231.c</a></li>
</ul>
<div>
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 <span style="font-family: Courier New, Courier, monospace;">0xff</span> 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 I<sup>2</sup>C Uncommenting the <span style="font-family: Courier New, Courier, monospace;">_low_power_mode_off_on_exit()</span> 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.</div>
</div>
<div>
<ul>
<li>Page writing: <a href="https://sites.google.com/site/mspscifiles/tutorials/i2cwrite_G2231.c?attredirects=0&d=1">i2cwrite_G2231.c</a></li>
</ul>
<div>
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 <a href="https://sites.google.com/site/mspscifiles/tutorials/mspsci_i2cdemo.txt?attredirects=0&d=1">text file</a> 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.</div>
</div>
<h3>
Race Conditions</h3>
<div>
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:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span style="color: #a64d79;">for</span>(i=0; i<64; i++) {<!--64--></span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span class="Apple-tab-span" style="white-space: pre;"> </span>slave_address = SAdrs + i/16;<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #6aa84f;">// Current Block Address</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span class="Apple-tab-span" style="white-space: pre;"> </span>pageBuffer[0] = (i%16)*16;<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #6aa84f;">// EEPROM Write Address</span></span></div>
</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span class="Apple-tab-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small; white-space: pre;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><span style="color: #a64d79;">for</span>(j=1; j<17; j++) {<!--17--></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span class="Apple-tab-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small; white-space: pre;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">_BIS_SR(LPM0_bits + GIE); </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><span style="color: #6aa84f;">// Standby for UART RX</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span class="Apple-tab-span" style="white-space: pre;"> </span>pageBuffer[j] = RXBuffer; <span style="color: #6aa84f;">// Extract RXBuffer</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span class="Apple-tab-span" style="white-space: pre;"> </span>}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #a64d79;">while</span>(SCFG & BIT4)<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #6aa84f;">// Ensure previous transmission is complete</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: purple;">_nop</span>();</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span class="Apple-tab-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small; white-space: pre;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">SCFG |= BIT3;</span><span class="Apple-tab-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small; white-space: pre;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><span style="color: #6aa84f;">// Mark transmission/write instruction</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span class="Apple-tab-span" style="white-space: pre;"> </span>USICTL1 |= USIIFG;<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #6aa84f;">// Set USI Interrupt to start</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #6aa84f;">// I2C communication</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
</div>
<div>
The problem was that the first element of pageBuffer would be overwritten shortly after the USI interrupt was started-- <i><b>before</b></i> 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.</div>
<div>
<br /></div>
<div>
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 I<sup>2</sup>C 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.<br />
<br />
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 I<sup>2</sup>C.</div>
</div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8853820565211362966.post-49861077562612110452013-02-06T23:19:00.001-05:002013-02-23T18:07:21.170-05:00Tutorial 20b: USI & I2CImplementing I<sup>2</sup>C 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 I<sup>2</sup>C 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 I<sup>2</sup>C completely in software and timers, but it's not easy. SPI is much simpler for "bit-banging".<br />
<br />
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".<br />
<br />
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 I<sup>2</sup>C transaction.) This interrupt is triggered every time a non-zero value is written to USICNT. Every I<sup>2</sup>C 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 I<sup>2</sup>C master:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-A4mcICwM4B_w0DSxDFJDqKo7kDQj-M6GLCC4lU7LjcPcn3L-SUM2lskWaYPtBgQEENwIpTLO_XH3SJkkCWW_Rj8R6Z4bqMsP7TzsdFHG1sR1A7v2SVIMFC6sStmE85PBJI9FildFADY2/s1600/I2C+State+Machine.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-A4mcICwM4B_w0DSxDFJDqKo7kDQj-M6GLCC4lU7LjcPcn3L-SUM2lskWaYPtBgQEENwIpTLO_XH3SJkkCWW_Rj8R6Z4bqMsP7TzsdFHG1sR1A7v2SVIMFC6sStmE85PBJI9FildFADY2/s400/I2C+State+Machine.png" width="307" /></a></div>
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.<br />
<br />
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.<br />
<br />
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 <span style="font-family: Courier New, Courier, monospace;">enum</span> construction in C, making the code much more readable.<br />
<h3>
Configuring the USI for I<sup>2</sup>C</h3>
The <span style="font-family: Courier New, Courier, monospace;">USICTL0</span> 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 I<sup>2</sup>C. In <span style="font-family: Courier New, Courier, monospace;">USICTL1</span>, we also need only enable the <span style="font-family: Courier New, Courier, monospace;">USII2C</span> bit. For I<sup>2</sup>C operation, as you recall, we want the phase/polarity to match Mode 3 in SPI; <span style="font-family: Courier New, Courier, monospace;">USICKCTL</span> should be set to whatever clock source and division are desired, with the <span style="font-family: Courier New, Courier, monospace;">USICKPL</span> bit enabled.<br />
<br />
Finally, when operating the USI in I<sup>2</sup>C mode you should disable automatic clearing of the interrupt flag by enabling the <span style="font-family: Courier New, Courier, monospace;">USIIFGCC</span> bit in <span style="font-family: Courier New, Courier, monospace;">USICNT</span>. 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.<br />
<br />
The code to setup the USI for I<sup>2</sup>C may look something like this:<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">USICTL0 = USIPE6 + USIPE7 + USIMST + USISWRST; </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #38761d;">// Enable ports and set as master</span></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">USICTL1 = USII2C + USIIE; <span style="color: #38761d;"> // I2C mode, enable interrupts</span></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">USICKCTL = USISSEL_2 + USIDIV_3 + USICKPL; </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #38761d;">// Use SMCLK/8, Mode 3 phase/polarity</span></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">USICNT |= USIIFGCC; <span style="color: #38761d;">// Disable automatic clear control</span></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">USICTL0 &= ~USISWRST; <span style="color: #38761d;">// Enable USI</span></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">USICTL1 &= ~USIIFG; <span style="color: #38761d;">// Clear any early flags</span></span><br />
<br />
<h3>
Setting Up the ISR</h3>
This (updated and verified!) <a href="https://sites.google.com/site/mspscifiles/tutorials/i2c_isr.c?attredirects=0&d=1">code fragment</a> is a simple way to set up the ISR in a way that allows both transmission and reception with I<sup>2</sup>C. There are a few things to note about this code:<br />
<br />
<ul>
<li>The format for usage of the enum C type is:<br /><span style="font-family: Courier New, Courier, monospace;">enum{item_1, item_2, ... , item_n} var_name = initial_state</span><br />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 <span style="font-family: Courier New, Courier, monospace;">static</span> to retain its value between calls to the ISR.</li>
<li>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 <span style="font-family: Courier New, Courier, monospace;">USIGE</span> bit in <span style="font-family: Courier New, Courier, monospace;">USICTL0</span>. The SDA line will immediately take whatever value is in the first bit to send in <span style="font-family: Courier New, Courier, monospace;">USISRL</span><span style="font-family: inherit;">, and so that register is set to </span><span style="font-family: Courier New, Courier, monospace;">0x00</span><span style="font-family: inherit;"> for start and </span><span style="font-family: Courier New, Courier, monospace;">0xFF</span><span style="font-family: inherit;"> for stop</span>. (Only the most significant bit need be set, but this method has the benefit of being both convenient and comforting.)</li>
<li>Also recall that in I<sup>2</sup>C, 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 <span style="font-family: Courier New, Courier, monospace;">USIOE</span> bit in <span style="font-family: Courier New, Courier, monospace;">USICTL0</span> to control the line, and clearing it to relinquish the line.</li>
<li>The <span style="font-family: Courier New, Courier, monospace;">SCFG</span> 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 <span style="font-family: Courier New, Courier, monospace;">ITXBuffer</span> and <span style="font-family: Courier New, Courier, monospace;">IRXBuffer</span> have been defined previously. (The names here are thinking ahead to using I<sup>2</sup>C in parallel with UART.)</li>
<li>I have also used a hopefully self-explanatory variable <span style="font-family: Courier New, Courier, monospace;">slave_address,</span> presumably defined earlier.</li>
<li>Note the inclusion of an additional state labeled "<span style="font-family: Courier New, Courier, monospace;">PrepStop</span>". 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 <span style="font-family: Courier New, Courier, monospace;">USICNT</span> and setting the next state. After the break, the <span style="font-family: Courier New, Courier, monospace;">USIIFG</span> 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 <span style="font-family: Courier New, Courier, monospace;">PrepStop</span> state is needed in order to allow the previous ack/nack to finish before stopping the communication.</li>
<li>Finally, clearing <span style="font-family: Courier New, Courier, monospace;">USIIFG</span> 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.</li>
</ul>
<br />
That finishes this tutorial on the configuration of the USI module for the I<sup>2</sup>C protocol. Next time we'll put it to use with the 24xx08 serial EEPROM, as we did for the SPI example.<br />
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8853820565211362966.post-84485835690108228902012-09-29T21:31:00.000-04:002012-09-29T21:31:06.936-04:00Tutorial 20a: Introducing I2CThe 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.<br />
<br />
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 I<sup>2</sup>C.<br />
<h3>
Problems With a Serial Bus</h3>
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.<br />
<br />
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 I<sup>2</sup>C 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 I<sup>2</sup>C, this is exactly the technique you'll have to use to prevent any damage to the systems involved.<br />
<br />
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 I<sup>2</sup>C. 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 I<sup>2</sup>C 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.<br />
<br />
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? I<sup>2</sup>C 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.<br />
<br />
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 I<sup>2</sup>C by the timing of changes in the data line compared to the clock line. Communication in I<sup>2</sup>C 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, I<sup>2</sup>C 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.<br />
<br />
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 <i>do not</i> 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.<br />
<h3>
I<sup>2</sup>C in Action</h3>
If after reading all of that you feel like I<sup>2</sup>C 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 I<sup>2</sup>C transmission and analyze what's going on.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc_TCGQhHN-hAOHky2TRSXYolRgt25o4k0SZeL16JCyaTD1L4taYW1xZQhPIZ3dJkRXmfkjqWUH-qalMZ_AhrWj_AKJi_6UXW6CDILLeJO5FPwhUX6aPGZEjtNTx6-Wd9pBrWTDAdolNNV/s1600/i2c_read.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="339" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc_TCGQhHN-hAOHky2TRSXYolRgt25o4k0SZeL16JCyaTD1L4taYW1xZQhPIZ3dJkRXmfkjqWUH-qalMZ_AhrWj_AKJi_6UXW6CDILLeJO5FPwhUX6aPGZEjtNTx6-Wd9pBrWTDAdolNNV/s640/i2c_read.png" width="640" /></a></div>
<br />
Look at this image of the first part of an I<sup>2</sup>C 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 <span style="font-family: Courier New, Courier, monospace;">0b01000001</span>. The first 7 bits of this value are the address of the slave (in this case, <span style="font-family: Courier New, Courier, monospace;">0b0100000</span>). 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.<br />
<br />
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.)<br />
<br />
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:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirK6Cza8S7w-y_DyHIwc-F0n58NVfwA2M5IQ62g3WJ3H2WBtTpTvH0gyIrlAAyN7gqwN9JIy1YbyHOXxQ8n8Nte2CTORqJEVTPIjgDklgUanjTmdjcho-5cDJc2XSTPTMwC_fAtcTZK3Rw/s1600/i2c_read_full.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="340" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirK6Cza8S7w-y_DyHIwc-F0n58NVfwA2M5IQ62g3WJ3H2WBtTpTvH0gyIrlAAyN7gqwN9JIy1YbyHOXxQ8n8Nte2CTORqJEVTPIjgDklgUanjTmdjcho-5cDJc2XSTPTMwC_fAtcTZK3Rw/s640/i2c_read_full.png" width="640" /></a></div>
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.<br />
<br />
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.<br />
<br />
Now that we've got a handle on some basic I<sup>2</sup>C formatting, we'll turn our attention to the USI module and how it's used in I<sup>2</sup>C in the next part of this tutorial.<br />
<br />
<span style="color: #990000; font-size: x-small;">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?</span>Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-8853820565211362966.post-81406433775973929582012-09-21T16:18:00.004-04:002012-09-22T11:47:39.765-04:00Tutorial 19c: The 25xx080Now 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 <a href="http://ww1.microchip.com/downloads/en/DeviceDoc/22151A.pdf">datasheet</a> for this chip, there are a few things to understand.<br />
<h3>
Instructions and the Status Register</h3>
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:<br />
<br />
<ul>
<li>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).</li>
<li>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. <i><span style="color: #990000;">Note: writing <b>does not occur</b> 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.</span></i> 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).</li>
<li>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.</li>
<li>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.</li>
<li>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.</li>
<li>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.</li>
</ul>
<div>
The Status Register uses 5 of the 8 bits in this EEPROM chip:</div>
<div>
<ul>
<li>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.</li>
<li>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.)</li>
<li>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.</li>
<li>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.</li>
</ul>
<div>
All of these instructions and register definitions can be encapsulated in a C header file. As an example, look at <a href="https://sites.google.com/site/mspscifiles/tutorials/25xx080c.h?attredirects=0&d=1">25xx080c.h</a>. 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 <a href="https://sites.google.com/site/mspscifiles/tutorials/calibrations.h?attredirects=0&d=1">calibrations.h</a> 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 <a href="http://mspsci.blogspot.com/2011/11/tutorial-16e-programming-flash-memory.html">flash memory programming tutorial</a> and re-calibrate your MSP430.)</div>
</div>
<h3>
MSP430 Issues</h3>
<div>
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 <a href="http://www.ti.com/lit/er/slaz061b/slaz061b.pdf">MSP430G2231 Device Errata</a> 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:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span style="color: purple;">void</span> <b>USI_init</b>(<span style="color: purple;">void</span>) {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> USICTL0 = USIPE5 + USIMST + USIOE + USISWRST;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> <span style="color: #38761d;">// Enable SCLK, Master mode, enable output and reset USI</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> USICTL1 = USICKPH + USIIE;<span class="Apple-tab-span" style="white-space: pre;"> </span><span style="color: #38761d;">// Mode 0 requires CKPH=1, enable interrupt</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> USICKCTL = USIDIV_3 + USISSEL_2; <span style="color: #38761d;">// SMCLK div 8 -> 921.6 kHz USI clock</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> <span style="color: #38761d;">// One write command should take ~50 us at this rate,</span></span></div>
<div>
<span style="color: #38761d; font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> // while 1 bit via UART @ 9600 should take ~100 us.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> USICTL0 &= ~USISWRST; <span style="color: #38761d;">// Clear USI for use</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> USICNT = 1;<span class="Apple-tab-span" style="white-space: pre;"> </span> <span style="color: #38761d;">// clear 1st transmit due to errata USI5</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> __delay_cycles(50); <span style="color: #38761d;">// finish clearing (minimum (n+1)*16 cycles)</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> USICTL0 |= USIPE7 + USIPE6;<span class="Apple-tab-span" style="white-space: pre;"> </span> <span style="color: #38761d;">// Enable SDI/SDO pins.</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> USICTL1 &= ~USIIFG;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">} // USI_init</span></div>
</div>
<br />
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.<br />
<h3>
Example Code</h3>
<h4>
Writing, Method 1</h4>
<div>
First up, <a href="https://sites.google.com/site/mspscifiles/tutorials/eepromwrite_spiG2231.c?attredirects=0&d=1">eepromwrite_spiG2231.c</a> 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.</div>
<div>
<br /></div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0h4hw5qFFPCyTvzpcKODtaKDD0svFxiCB5s_IGOuKNwQu9x_gDgn6D4kJYmSO1T4CdXT1EhzYUBTlkUHsp-Y8f8kHJ-PeLfRCVRWyvPEnhN5GxgEWZU74DUAIPdIOlfuHDJv4R155Ly_l/s1600/spi_write.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0h4hw5qFFPCyTvzpcKODtaKDD0svFxiCB5s_IGOuKNwQu9x_gDgn6D4kJYmSO1T4CdXT1EhzYUBTlkUHsp-Y8f8kHJ-PeLfRCVRWyvPEnhN5GxgEWZU74DUAIPdIOlfuHDJv4R155Ly_l/s320/spi_write.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><div style="text-align: left;">
RealTerm lets you send data from a text file, and</div>
<div style="text-align: left;">
add the necessary 5 ms delay between characters.</div>
</td></tr>
</tbody></table>
<div>
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 <i>not</i> 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.</div>
<h4>
Reading</h4>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYLh8qzntHpVT5vluGPeQ9i4x0WtKDylc97R7JWMiBcCw0lmqKiyA2e_Uxq7jyTfMsdirpfIbcf423rjZrO2H11T2j9KCbkjsYASG14Z32V6YyGhz-0kAhiVzXx4ptEkYaXSE89-U9CJd6/s1600/spi_read.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYLh8qzntHpVT5vluGPeQ9i4x0WtKDylc97R7JWMiBcCw0lmqKiyA2e_Uxq7jyTfMsdirpfIbcf423rjZrO2H11T2j9KCbkjsYASG14Z32V6YyGhz-0kAhiVzXx4ptEkYaXSE89-U9CJd6/s320/spi_read.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><div style="text-align: left;">
To get this file to display nicely, you need to enable the</div>
<div style="text-align: left;">
newLine mode in RealTerm. Displaying an extra line or</div>
<div style="text-align: left;">
two also helps.</div>
</td></tr>
</tbody></table>
<div>
Second, <a href="https://sites.google.com/site/mspscifiles/tutorials/eepromread_spiG2231.c?attredirects=0&d=1">eepromread_spiG2231.c</a> 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.</div>
<div>
<br /></div>
<div>
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.</div>
<h3>
Writing, Method 2</h3>
<div>
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!) </div>
<div>
<br /></div>
<div>
In <a href="https://sites.google.com/site/mspscifiles/tutorials/eepromwrite2_spiG2231.c?attredirects=0&d=1">eepromwrite2_spiG2231.c</a>, 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. </div>
<h3>
SPI Signal Details</h3>
<div>
Let's take a look at some of the details of the signals being passed back and forth in these programs.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhpOFzIrP9lHJj2BUyfv8dPyJ0DWZ4Kr0hiHYvnMrhLpubAUxOMu4-yjCZxju1A54Z7g6uhYWBgPNL1MO6xC1kHAEbADDXIuBSn_7BkPDGtka4HdXBIZ-7qSygBEOEdWlhPHpuBEkN66Kv/s1600/write_signal1-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="345" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhpOFzIrP9lHJj2BUyfv8dPyJ0DWZ4Kr0hiHYvnMrhLpubAUxOMu4-yjCZxju1A54Z7g6uhYWBgPNL1MO6xC1kHAEbADDXIuBSn_7BkPDGtka4HdXBIZ-7qSygBEOEdWlhPHpuBEkN66Kv/s640/write_signal1-1.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQYU22YFGbZrZ0N9mejeGkBO4nBmNn3PwOTQgINyhT3OBNerMDtOkcHM3J76Pn3Tj-1qF5oIKba8wlI4scORfVArI9rwJGDpjEJjlU8KllMZo4vEFMa_TRXJ097MXu0BHb8bVw3GfLVGGl/s1600/write_signal1-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQYU22YFGbZrZ0N9mejeGkBO4nBmNn3PwOTQgINyhT3OBNerMDtOkcHM3J76Pn3Tj-1qF5oIKba8wlI4scORfVArI9rwJGDpjEJjlU8KllMZo4vEFMa_TRXJ097MXu0BHb8bVw3GfLVGGl/s640/write_signal1-2.png" width="640" /></a></div>
<div>
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.)</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiByo6Xf7o_bfHqt_3ZAxvUcz6NPaSgBcNS0TywVmYdI29GY4kE02cBB0jjU7GCHRGRymazhwECUZHx5R4wwJ21ywRcsZuvfBhnCFdbdGkzK2MV-YMJy7tE8gZ05BwUmP-G0lsy6rjtIEDA/s1600/read_signal-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiByo6Xf7o_bfHqt_3ZAxvUcz6NPaSgBcNS0TywVmYdI29GY4kE02cBB0jjU7GCHRGRymazhwECUZHx5R4wwJ21ywRcsZuvfBhnCFdbdGkzK2MV-YMJy7tE8gZ05BwUmP-G0lsy6rjtIEDA/s640/read_signal-1.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKjpKjkZdn9ox5h7Tvz6hlHOm3c2z1t_Bz38nOHK7I7asYm3_HnbRXlJKUDdPW3ULRum4w_qo_Le_bOYfHTsXuY-8LPLM8U8YgTmrZGQ_2Wnhr1NxLdQ_4z1X_x6MPTOfiIFq-OxCitGD_/s1600/read_signal-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKjpKjkZdn9ox5h7Tvz6hlHOm3c2z1t_Bz38nOHK7I7asYm3_HnbRXlJKUDdPW3ULRum4w_qo_Le_bOYfHTsXuY-8LPLM8U8YgTmrZGQ_2Wnhr1NxLdQ_4z1X_x6MPTOfiIFq-OxCitGD_/s640/read_signal-2.png" width="640" /></a></div>
<div>
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.</div>
<div>
<br /></div>
<div>
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.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0TgXJWkjEOIu359UITeyOe3Jh6CdPj7ZDRrLRuxBgm7Z5LXcjVVk_vINF9eN-zEjdELq56iaMbXwpFY4Dh2SLNAbjZ0Ou9Xa2O-5OxDL84sGs4Ddljz5rJ9lgrKr9EH4x61QiI9BGicIq/s1600/write_signal2-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0TgXJWkjEOIu359UITeyOe3Jh6CdPj7ZDRrLRuxBgm7Z5LXcjVVk_vINF9eN-zEjdELq56iaMbXwpFY4Dh2SLNAbjZ0Ou9Xa2O-5OxDL84sGs4Ddljz5rJ9lgrKr9EH4x61QiI9BGicIq/s640/write_signal2-1.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaeN5u6CpPSY6mZTz6usJ5u7muTXnaRDniWBlVmbi9JUZ13_gK5V_G8x9tZsxQhhxruPWOpioyWHLbrfoCHoAGxjZdO0F5JAH26IhAq5hcigDxQBK89NvwyRcKGAcMwV_t6lN3Zq5fAn4-/s1600/write_signal2-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaeN5u6CpPSY6mZTz6usJ5u7muTXnaRDniWBlVmbi9JUZ13_gK5V_G8x9tZsxQhhxruPWOpioyWHLbrfoCHoAGxjZdO0F5JAH26IhAq5hcigDxQBK89NvwyRcKGAcMwV_t6lN3Zq5fAn4-/s640/write_signal2-2.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGcaytyiatZSkD5Ua6ISfH2SILAYxqs4h2quC1obVNOA_u2F5tRNCKHjEuCDOr-a72l9L5Nl2SCXxYf4kedOUdklzQ_w5TU-_Erjx7asv2l87U52OM5wGGblCB0oQjO8HZ7kQdtjCFKHe-/s1600/write_signal2-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGcaytyiatZSkD5Ua6ISfH2SILAYxqs4h2quC1obVNOA_u2F5tRNCKHjEuCDOr-a72l9L5Nl2SCXxYf4kedOUdklzQ_w5TU-_Erjx7asv2l87U52OM5wGGblCB0oQjO8HZ7kQdtjCFKHe-/s640/write_signal2-3.png" width="640" /></a></div>
<div>
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.</div>
<div>
<br /></div>
<div>
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.</div>
<div>
<br /></div>
<div>
<span style="color: #660000; font-size: x-small;">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?</span></div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8853820565211362966.post-3080440891421011732012-09-08T12:35:00.000-04:002012-09-08T12:35:10.819-04:00Tutorial 19b: USI SPIPreviously, 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.<br />
<h3>
SPI Modes</h3>
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 <b><i>inverted</i></b> from the standard definitions for CPHA. (That is, if CPHA = 0, CKPH = 1 and if CPHA = 1, CKPH = 0.)<br />
<br />
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 <a href="http://www.amazon.com/gp/product/0750682760/ref=as_li_ss_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=0750682760&linkCode=as2&tag=sciinsusithet-20">MSP430 Microcontroller Basics</a><img alt="" border="0" height="1" src="http://www.assoc-amazon.com/e/ir?t=sciinsusithet-20&l=as2&o=1&a=0750682760" style="border: none !important; margin: 0px !important;" width="1" />.)
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.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDARlegdklHBZ8Jtof4wJwIdIOKwFTDXMN47eygmVWTIj5dIOAfXjCJ60vezIRIUJg_9VMlp5wK4a_j_38Y1wd3D7RhEzkaX_VApCUmkdJcGHwAGRRx-vwLMioJT2TpPHzy45sCfSgYj6C/s1600/SPI_0-0.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDARlegdklHBZ8Jtof4wJwIdIOKwFTDXMN47eygmVWTIj5dIOAfXjCJ60vezIRIUJg_9VMlp5wK4a_j_38Y1wd3D7RhEzkaX_VApCUmkdJcGHwAGRRx-vwLMioJT2TpPHzy45sCfSgYj6C/s400/SPI_0-0.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Mode 0 Timing Diagram<br />
From <i>[Davies, 2008]</i>.</td></tr>
</tbody></table>
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).<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQycROzn-O4kTXMgzNQYdbNsftXT4Asp80j-Kb0Lnphej1hro_2xsFB10sdD0GDrY0MF6UU5ljf6BFPR64ox6rZSnYf9NnkFAco8Hx8aRyMUdkYIPB1UlzVwQI8WhHHWPszIdkDf24pTai/s1600/SPI_1-1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQycROzn-O4kTXMgzNQYdbNsftXT4Asp80j-Kb0Lnphej1hro_2xsFB10sdD0GDrY0MF6UU5ljf6BFPR64ox6rZSnYf9NnkFAco8Hx8aRyMUdkYIPB1UlzVwQI8WhHHWPszIdkDf24pTai/s400/SPI_1-1.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Mode 3 Timing Diagram<br />
From <i>[Davies, 2008]</i>.</td></tr>
</tbody></table>
Now let's look at a real-world example. The <a href="http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en542152">Microchip 25AA080C</a> chip is an SPI EEPROM with 1 kB (1024 bytes) of memory. In its datasheet, we see this timing diagram:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyUZgYYfhFQ4fXQY0xTTKnzDk8wJkUXeyd5-cexUWCC-tb4wlK4-zmgrmIphQongvr3xcwDhLFLyTzyLFOEMCZ_Veq9UzJN_4g2ic5FSelbOy3U4kH28_fyofvLvNX7lsxgJEM5oAQAdm-/s1600/25xx080c_timing.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyUZgYYfhFQ4fXQY0xTTKnzDk8wJkUXeyd5-cexUWCC-tb4wlK4-zmgrmIphQongvr3xcwDhLFLyTzyLFOEMCZ_Veq9UzJN_4g2ic5FSelbOy3U4kH28_fyofvLvNX7lsxgJEM5oAQAdm-/s640/25xx080c_timing.png" width="640" /></a></div>
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.<br />
<h3>
Configuring the USI</h3>
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.)<br />
<h4>
USICTL</h4>
The control register is comprised of two parts: USICTL0 and USICTL1. The pertinent bits are described here.<br />
<br />
USICTL0 (lower byte)<br />
<br />
<ul>
<li>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.</li>
<li>USILSB (bit 4): when set, data is transmitted least significant bit first rather than the default most significant bit first.</li>
<li>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.</li>
<li>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.</li>
<li>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.</li>
</ul>
USICTL1 (upper byte)<br />
<br />
<ul>
<li>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.</li>
<li>USII2C (bit 6): Clear this bit to use the SPI protocol.</li>
<li>USIIE (bit 4): enable interrupts for the USI counter; flags an interrupt when the specified number of bits have been transmitted.</li>
<li>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.</li>
</ul>
<h4>
USICCTL</h4>
<br />
The two parts of this register are USICKCTL and USICNT, for the clock and the counter.<br />
<br />
USICKCTL (lower byte)<br />
<br />
<ul>
<li>USIDIVx (bits 7-5): Divide clock by powers of two up to 128.</li>
<li>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.)</li>
<li>USICKPL (bit 1): determines the clock polarity (set -> idle high, clear -> idle low).</li>
<li>USISWCLK (bit 0): One option for the clock is to do everything in software. In this case, toggling this bit serves as the clock.</li>
</ul>
USICNT (upper byte)<br />
<br />
<ul>
<li>USI16B (bit 6): When set, USI will transmit up to 16 bits . When clear, USI will transmit up to 8 bits.</li>
<li>USIIFGCC (bit 5): When set, the interrupt flag must be cleared manually.</li>
<li>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).</li>
</ul>
<h4>
USISR</h4>
<br />
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).<br />
<br />
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!<br />
<br />
<i><span style="color: #3d85c6; font-size: x-small;">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.</span></i>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8853820565211362966.post-39085447342304929962012-09-07T21:11:00.000-04:002012-09-08T08:48:23.873-04:00Tutorial 19a: SPI TheoryWhen we looked at the UART, we imagined a setup where two people agreed that at a specific time and at specific intervals, one would send a message to the other one letter at a time. Doing so required both sender and receiver to have an accurate clock to prevent frame errors, or mis-reading the message by skipping or duplicating one of the letters. Using a similar analogy, we can think of the Serial Peripheral Interface as having the two people write a message to each other on a piece of paper, then exchange messages by handing them to each other at the same time. As a serial communication, it's still done letter by letter, though there are variations of SPI that allow for exchanging multiple bits simultaneously. We'll focus on single-bit-at-a-time transmission, since that is the only mode available in the USI and USCI peripherals.<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoqA9W1IOD14mDRso1G1EQHIfeSxx9AH3PexZUZvAf4Nu6RjaAY315gulOnjKC3hXGSb4gMQg-y54MVe7GkIv9K8KtD_yCcpifcM_vRj5hnRcZYR6GJDwMGuZqqnIeyqt3RmBT7s_LRwIv/s1600/Simple_SPI_Steps.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoqA9W1IOD14mDRso1G1EQHIfeSxx9AH3PexZUZvAf4Nu6RjaAY315gulOnjKC3hXGSb4gMQg-y54MVe7GkIv9K8KtD_yCcpifcM_vRj5hnRcZYR6GJDwMGuZqqnIeyqt3RmBT7s_LRwIv/s320/Simple_SPI_Steps.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">There are 3 basic steps in SPI:<br />
<div style="text-align: left;">
(1) Write bit 7 to the output.<br />
(2) Shift register to the left.<br />
(3) Write the other device's output to bit 0.</div>
</td></tr>
</tbody></table>
<br />
Let's say our two devices have 8-bit registers. At a given signal, each writes their most significant bit (bit 7) to their output. At the next signal, each shifts their register up one place, and sets the least significant bit (bit 0) to whatever value is on the other device's output. After 8 cycles, the 8 bit value that was in device A's register is now in device B, while the value that was in device B is now in A. Both read the new value (and probably do something with it), and then perhaps write a new message in their registers for the next exchange. This exchange of information makes it possible to very quickly send data from one device to another.<br />
<br />
The key to getting the process to work properly is to have all the shifting occur at the same time--the two devices need to be synchronized. This timing is accomplished by sharing a clock between the two devices; one of the two generates its own clock and includes it as an output for the other device's use. By convention, we refer to the device that generates the clock signal as the "master", and any devices listening in as "slaves". Such nomenclature may seem a little non-politically correct, but at the same time it's a reasonable description of how SPI works. We cannot necessarily deem one device as a "sender" and the other as a "receiver", for example, because both devices are simultaneously sending and receiving. Referring to both as "transceivers" doesn't really work either, because the sending and receiving are in fact the same process. Rather, the devices are "exchangers". One device serves as a control of the exchange, while the other depends on the controller in order to make the exchange. Note that, contrary to the connotation of the nomenclature, the master does not necessarily have to be the device that issues commands, nor does a slave have to only receive instructions. In fact, one could envision a system where the clock is external to both master and slave, and neither device operates entirely independently of the other, but rather a third device mediates the exchange between the two by providing the clock synchronization. Generally, however, it's simpler to let one of the exchanging devices mediate, and that device become designated as the master.<br />
<br />
You can imagine that the technique used for the SPI protocol lends itself to many variations. (Eg. do we pass the data starting from most to least significant bit or vice-versa? How many bits do we pass at a time?) Do we start the exchange on a rising edge or a falling edge of the clock? (The edge choice also leads to whether a quiet line idles high or low, of course.) To make it more confusing, for whatever reason various manufacturers have created different naming schemes for the lines between the two devices. In particular for our purposes, TI has the three lines between the devices named as Serial Data In (SDI), Serial Data Out (SDO), and Serial Clock (SCLK). Like for UART, the data in line of one device is connected to the data out line of the other: SDI_A-SDO_B and SDO_A-SDI_B for devices A and B. Note that because we must include the clock, SPI requires three lines (plus one more for ground) between the devices as opposed to two lines (plus ground) in the basic UART. (There are some situations where if one of the devices doesn't need to communicate anything to the other ever you can get away with just two lines, but in general you need three.)<br />
<br />
<i><span style="color: #0b5394;">The other common nomenclature for SPI connections was given by Motorola: each device has a Master Out-Slave In (MOSI), and a Master In-Slave Out (MISO) rather than SDI and SDO. In this nomenclature, you connect like pins rather than opposite pins: MOSI_A-MOSI_B and MISO_A-MISO_B. Other devices use a similar nomenclature to TI, dropping the "D" in the abbreviations: SI and SO, connecting as in the TI scheme. You may also encounter SPSCK and SCK for the clock, and devices enabled with a chip select can be labeled as CS, SS (slave select), or sometimes CE (chip enable). Be certain to verify whether tying this pin high or low enables the device. In this blog, I'll stick with TI's nomenclature, and use SDI, SDO, SCLK, and CE.</span></i><br />
<br />
SPI can also extend to communication with multiple devices by including a "Chip Select" pin-- a slave will only listen when this pin is high or low, depending on the implementation. Thus other peripherals can be attached, and instructions/data can be exchanged only between the master and the pertinent slave. The disadvantage to this means of multiple-party communication is that it requires one more line for each device that needs to communicate independently.<br />
<br />
So how do we know what conventions we should use in our designs? Unfortunately, you need to get familiar with reading data sheets, and my experience has been it takes some practice to read data sheets describing exact communication protocols. In the next tutorial, we'll look at how to implement SPI using the USI peripheral, and delve more into the details of how to determine the particular protocol being used and configure the MSP430 properly.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8853820565211362966.post-21129380701891391562012-09-05T15:11:00.001-04:002012-09-05T15:11:54.084-04:00Tutorial 19: Going Beyond the UARTWe've already looked at asynchronous serial communication, and seen how useful it can be for a scientific instrument as a data logging system. Serial communication has a lot more power, however, especially when we begin looking at the "control" aspect of the microcontroller.<br />
<br />
There are a lot of sensors and devices designed to work with microcontrollers that use synchronous communication protocols. The primary difference for these modes of communication are the inclusion of a single clock between the sender and receiver, rather than having to rely on precise and accurate clocks. Having to share a clock limits the separation to a fairly close proximity, but it does simplify the communication process a great deal.<br />
<br />
The number of protocols designed for serial communication is quite large, but a few have stood out as standard for many devices. The MSP430 has many devices that, for example, have built-in hardware for communication using the Serial Peripheral Interface (SPI) and the Inter-integrated Circuit Bus (I<sup>2</sup>C or I2C -- pronounced either as eye-two-see or eye-squared-see, your preference).<br />
<br />
Most MSP430 devices have one of two forms of serial communication hardware. The less expensive devices have the Universal Serial Interface (USI), which can do SPI and I2C. Larger devices come with the Universal Serial Communication Interface (USCI), in at least one of two flavors: USCI_A handles asynchronous communication (UART), and USCI_B handles synchronous communication (SPI/I2C). If for any reason you must use a device without either of these modules, you can resort to bit-banging, but aside from the previous tutorial on UART we won't cover that any time soon; most modules have at least USI, and it's far more economical to use the built in hardware.<br />
<br />
To work with this tutorial, you'll need at least one of the following:<br />
<br />
<ul>
<li>Two devices that communicate via SPI and I2C, such as a <a href="http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2697">serial EEPROM produced by Microchip</a>. </li>
<li>USB-SPI/I2C interfaces, such as the <a href="http://www.ftdichip.com/FT-X.htm">FTDI devices</a>. (Their chips include one that is great for UART at speeds other than 9600 bps too!)</li>
<li>A second MSP430 device.</li>
</ul>
<div>
Owners of a LaunchPad already have two devices that include USI, but keep in mind you'll need a few other components to power the chip off of the LaunchPad. I'll do tutorials that will demonstrate all three methods, but for the beginning I will be working with Microchip serial EEPROM chips. (I will use the 25AA080C for SPI and the 24AA08 for I2C, but any size will do.)</div>
<div>
<br /></div>
<div>
Of the two synchronous protocols used by the MSP430, I2C will likely be the one used most frequently (at least by me), for the reason that the protocol is designed to work with multiple devices on a single communication line. Many sensors and external peripherals are already available that use I2C. However, SPI is a little simpler to understand, and we'll start with that. Once I've gone over the USI peripheral, I'll begin working on a new experiment that will make use of a <a href="http://www.mouser.com/ProductDetail/Honeywell/HSCSANN150PG2A3/?qs=%2fha2pyFadugTBu3RRt7JfU0xS0o4L34KItkU58TgnV8jAFbM2TWLCg%3d%3d">pressure sensor</a>, and design an MSP430-driven <a href="http://www.mouser.com/ProductDetail/Avago-Technologies/HDSP-B47G/?qs=%2fha2pyFaduic3N0PYQHaBZjpVhnxgGYyX57RjM0srjI%3d">4-digit LED display</a>. Both will communicate over I2C as a digital pressure gauge. The project will require designing and fabricating a couple of printed circuit boards, so we'll go over a little of that process as well.</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8853820565211362966.post-62846207781230295312012-09-05T15:11:00.000-04:002012-09-05T15:11:12.263-04:00New Tutorials are Coming!Hi everyone; it's great to be back. It's been a crazy few months, what with finishing my Ph.D. and all the other changes in my life. But, it's high time I start learning some new things and building some cool toys. I have a lot in mind for the coming weeks, and hopefully you'll find some of it helpful as well.<br />
<br />
These tutorials will be a little different; call them "intermediate-level tutorials" if you will. Mostly, these tutorials will require the use of more parts and tools outside of the LaunchPad, much like the LCM tutorial from before. I'll try to keep the tools I use within a reasonable budget for beginners and amateurs, but some of the parts will probably cost a little more. (I just purchased a sensor for the next project that was $35.) If you have any suggestions or requests for topics, let me know!<br />
<br />
Thanks, all of you who read this blog; I have a lot of fun writing this, and I'm glad so many of you have expressed your appreciation for its contribution to the MSP430 community. Let's see what more we can learn!
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8853820565211362966.post-18805063333723476552012-08-18T16:20:00.000-04:002012-08-18T16:27:31.867-04:00Experiment: Battery Profiling UpdateI want to get the final results of the Battery Profiling experiment put up for everyone to see, as it came up with an interesting result.<br />
<br />
<h2>
150 Ω Circuit</h2>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj86C45R1jUGPSdn-Ntthn3vWBCC1akNeWcAZB1MSI8JJTCAUx0g27KGQq02Ivp_jrdG3J8RhgJSBdjoQVCzoEBJ1q3dYWNkP5WmNGDbV2AS6rFctjdPry8C_5_pUmOfSnmWxr6gad-7Y04/s1600/R150.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="478" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj86C45R1jUGPSdn-Ntthn3vWBCC1akNeWcAZB1MSI8JJTCAUx0g27KGQq02Ivp_jrdG3J8RhgJSBdjoQVCzoEBJ1q3dYWNkP5WmNGDbV2AS6rFctjdPry8C_5_pUmOfSnmWxr6gad-7Y04/s640/R150.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Average of two runs using matched resistors.</td></tr>
</tbody></table>
The immediate conclusion is that Duracell does in fact last longer than Energizer. But stopping there doesn't really tell the whole story; yes, the Duracells did maintain a voltage for about 10 hours more time, but look more carefully at the initial period-- the Energizers actually perform better for the first half. In fact, the Energizers stayed consistently at a higher voltage until around 120 hours, where both batteries were found to be at 1.05 V. At that point the Energizer drops off quickly, while the Duracell holds on for a little longer. Big deal you say? Well, that depends on how you're using the batteries.<br />
<br />
Consider a flashlight as a case example. The higher the voltage, the brighter the light. So the Energizer powered flashlight will stay brighter than the Duracell powered flashlight for a significantly longer time. The Duracell powered light may last longer overall, but how useful is that last 10 hours, really?<br />
<br />
This behavior is consistent even at higher power draws:<br />
<br />
<h2>
33 Ω Circuit</h2>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV0DAYmtW1i8Pi3YzMpCShPEX1Czr6Hec_EyM8eNOenOoPDHNJjZFY7aqd9pIqfSvGvuiTinZt6sdp-ecsvNrMh_iKQui8h8etufhDrwtvrAbAjx4mP_G3kz8907E81BoF6ExWxq08_0Yl/s1600/R33.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="478" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV0DAYmtW1i8Pi3YzMpCShPEX1Czr6Hec_EyM8eNOenOoPDHNJjZFY7aqd9pIqfSvGvuiTinZt6sdp-ecsvNrMh_iKQui8h8etufhDrwtvrAbAjx4mP_G3kz8907E81BoF6ExWxq08_0Yl/s640/R33.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Average of two runs using matched resistors.</td></tr>
</tbody></table>
The same result is found for the higher current case, with the cross again occurring at 1.05 V and the Duracell lasting about 10 hours longer. This result suggests that the Energizer design optimizes maintaining the higher voltage at the expense of overall lifetime. Integrating the actual current of each circuit over time gives an average energy capacity of 1380 mAh for Duracell and 1340 mAh for Energizer-- roughly the same! If the Energizer maintains a higher current for a longer period of time, it would make sense for it to finish draining first as it uses more of its capacity during that initial time.<br />
<br />
What we conclude from this is that the brand you choose may depend just on your needs-- specifically on the minimum voltage required in your circuit. If you need the voltage to stay above 1 V to power your application, Energizer will give you that power for a longer time. If you only need the voltage to be above a threshold below 1 V, go with the Duracell.<br />
<br />
<h2>
What Next?</h2>
One reason I'm including these little experiments is as a catalyst for others to start investigating. This simple idea could lead, for example, to an excellent science fair project for a young student. Or perhaps someone is curious enough to justify buying lots of batteries to run through their paces. In any case, here are a few ideas of other questions that could be examined.<br />
<br />
<ul>
<li>This experiment used a constant resistance circuit-- how do the lifetimes compare in a constant current application?</li>
<li>Is the behavior seen here consistent for other sizes of battery? (Be aware that discharging a D-cell through even 33 Ω may take a while...)</li>
<li>Do batteries discharge evenly? What happens if two otherwise identical batteries are placed in series? Measure the voltage at both batteries-- ideally the center voltage should be half of the overall voltage at all times. Is it? Is there any difference if you mix brands together?</li>
<li>My measurement of the energy capacity of the two brands shows them as being similar, but the Energizer capacity was lower than the Duracell in both cases. Two measurements is not enough for the difference to be statistically significant-- measuring a larger number of batteries could tell you if Duracell does indeed have just a little more capacity. How many batteries would you need to measure to show that?</li>
<li>In this experiment, the circuit was constantly on. What happens if the current is only on intermittently? How much does a battery recover after being used for a while?</li>
<li>Both runs were done at room temperature. How much of an affect does temperature have on the battery? Be aware that the temperature difference will change the resistance as well, so that should be accounted in the results.</li>
<li>(For those with a lot of time to spare:) People claim that batteries last longer in storage if they are chilled. How quickly does a battery sitting on a shelf decay? How about a battery being stored in a cool environment?</li>
</ul>
<div>
I'm sure there are plenty of others; feel free to add any ideas to the comments!</div>
<div>
<br /></div>
<div>
<span style="color: #990000; font-family: Helvetica Neue, Arial, Helvetica, sans-serif; font-size: x-small;">Disclaimer: Electricity can be dangerous, and you should be aware of the hazards of working with even common alkaline batteries. For example, mixing a drained (or even partially drained) battery with a fresh battery can result in leakage, or possibly fire. The materials inside an alkaline battery are not something to mess with if you're not sure of what you're doing-- so be careful, and if you see anything that looks wrong, stop. If you're young, get help from a parent or teacher. Dispose of waste properly and responsibly.</span></div>
<div>
<span style="color: #990000; font-family: Helvetica Neue, Arial, Helvetica, sans-serif; font-size: x-small;"><br /></span></div>
<div>
<span style="color: #990000; font-family: Helvetica Neue, Arial, Helvetica, sans-serif; font-size: x-small;">Any experiments operated using this blog as a basis are done at your own risk and responsibility; I am not liable for any damages that may be caused by duplicating this work or running any experiment derived from this work. You and you alone are accountable for any consequences of your actions, so if you are unsure about anything, find someone who can help. </span></div>
Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8853820565211362966.post-59286358815390519432012-06-20T14:13:00.000-04:002012-06-20T14:21:31.277-04:00Experiment: Battery Profiling<h3>
Background</h3>
Most of us probably purchase batteries with little regard to the brand. We may purchase whatever is cheapest at the time; we may get a particular brand out of loyalty; we may have just had better luck with one particular brand. Is there actually any difference between them, though?<br />
<br />
I had an experience that indicates there is, though it may not manifest itself in ways that are apparent to the general consumer. On a sounding rocket experiment, we had trouble with a particular power supply. Some investigation revealed that the brand of battery in the supply had problems if it was squeezed in a particular way, shorting out the battery internally. After taking a few batteries apart, we found that they were, in fact, constructed with slight differences. How those differences affect performance can be examined using a technique that is ideally suited to a microcontroller.<br />
<br />
Here's the idea: run a battery through a particular configuration and monitor its voltage throughout. In this experiment, I'll keep it simple, but you could easily come up with all kinds of scenarios and configurations in which to compare different batteries. This demonstration uses only the two major brands, in the AAA form factor, and compares the voltage profile over the lifetime of the batteries at equal current draws. This is done by having two matched resistors (as measured by a multimeter) draining each battery. If there is any difference between the two brands, it will be seen by a different discharge rate over time. Four samples are run, using two resistance values. Each run uses one of each brand, to eliminate temperature differences. Two samples are taken at each resistance, swapping the circuits for the two brands in each. The batteries purchased were selected to have the same expiration date marked on the packages.<br />
<br />
Searching online, we find that many people have done a similar experiment, though typically done in ways that are more subjective, such as putting different brands in identical flashlights, see which one gets dim first. Using a microcontroller, we can measure the actual voltage at the battery at specified intervals to get a picture of how the energy is drained from the battery over time.<br />
<br />
<h3>
MSP430 Peripherals Used</h3>
<ul>
<li>TimerA with interrupts: Continuous mode, interrupts at CCR0, CCR1, and TAR Rollover</li>
<li>ADC10 with interrupts: Sequence Mode, Single Conversion</li>
<li>Software UART (Full-Duplex)</li>
</ul>
<br />
<h3>
Equipment Needed</h3>
<div>
<ul>
<li>LaunchPad with 32 kHz crystal</li>
<li>MSP430G2231 (or any device with ADC10)</li>
<li>Computer that can be left on for a few days</li>
<li>Multimeter</li>
<li>Breadboard</li>
<li>2 single AAA battery holders</li>
<li>2 150 Ω, 1/4 W Resistors</li>
<li>2 33 Ω, 1/4 W Resistors</li>
<li>DPST Toggle Switch, rated at least 0.5 A @ 6 V</li>
<li>Various Wires</li>
</ul>
<h3>
Experiment Design</h3>
</div>
<div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghw83HCoeo_BlnZmZ7V8qA94xoP3YXDZxNkr2c7FL2wiiQvscidNJFPDg3KtuEn3YWKk_2LKoxvlpqocbSRXfnOQWpQARtdPbEcKAS4NSMiE1qO-iR6GevLi29UEYcIaYvqmAmm8s7lr4i/s1600/schematic.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghw83HCoeo_BlnZmZ7V8qA94xoP3YXDZxNkr2c7FL2wiiQvscidNJFPDg3KtuEn3YWKk_2LKoxvlpqocbSRXfnOQWpQARtdPbEcKAS4NSMiE1qO-iR6GevLi29UEYcIaYvqmAmm8s7lr4i/s400/schematic.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Schematic for Battery Profiler</td></tr>
</tbody></table>
The circuit is quite simple: R3, R4, and the LED on P1.6 are built into the LaunchPad. Q1 is soldered to the LaunchPad and used solely to calibrate the DCO; if this is already done, Q1 is optional. Remove the jumpers for the Rx, Tx, and LED on P1.0 from the LaunchPad, and jumper Rx to P1.4, Tx to P1.5.<br />
<br />
R1 and R2 are matched to be equal resistance, measured with a multimeter. S1 is a DPST or DPDT toggle switch. Note that you must use P1.1 and P1.0 to use Sequence Mode for the ADC10 module.<br />
<br />
The code for the MSP430G2231 is found in <a href="https://sites.google.com/site/mspscifiles/tutorials/battery_profiler.c?attredirects=0&d=1">battery_profiler.c</a>. Most of this code should be straightforward for anyone who has followed through Tutorial 18. There are a few things to point out, though:<br />
<br />
<ul>
<li>Each sample is an average of four measurements. Since floating point math (particularly division) is a bit expensive in the MSP430, you can use a trick: note that shifting right by one bit is dividing by two. If you are averaging a number of samples equal to a power of 2, you can add the samples together and then shift right by the appropriate number of bits to get division. Rounding can be done by adding 1 to the highest bit not being included-- in this example, using 4 samples, the second to last bit is the highest not included, so if <span style="font-family: 'Courier New', Courier, monospace;">V</span> holds the sum of 4 measurements, <span style="font-family: 'Courier New', Courier, monospace;">((V + BIT1) >> 2)</span> gives the rounded average. Since ADC10 uses only 10 of the 16 bits available in the register, you can average 2, 4, 8, 16, 32, or 64 samples with this technique. If necessary, floating point numbers and division can be used instead, but it does require more resources of your MSP430 device; plan accordingly.</li>
<li>Another issue is transmitting the data; you can transmit the raw bytes back easily, but the data is more easily analyzed afterward if the values are sent in ASCII text. The MSP430 can use the C printf function in the stdio.h library, but this library is large and increases the size of your code significantly. This code makes use of a slick scheme to reduce the code size-- the <span style="font-family: 'Courier New', Courier, monospace;">tx_uint()</span> function will transmit a 5 character string corresponding to the zero-padded value of whatever value is passed to it by making use of integer division and modulus operations. Transmitting the floating point value of the measured voltage is a bit trickier, so the ADC10 conversion is transmitted instead. These values are converted to a voltage during analysis, using <span style="font-family: 'Courier New', Courier, monospace;">2.5 V = 1024</span>.</li>
<li>The code makes use of the Sequence Mode for the ADC10. This mode samples each channel starting at that specified by INCH_x in ADC10CTL1, and stepping through to channel A0. In this example, A1 is measured first, then A0. This mode operates by waiting for the ENC and SC bits to be set, starting the conversions. When the first conversion is done, an interrupt is flagged. In this code, the interrupt resets the flag bit in UART_FG, signaling the code to continue to the next step. The next channel is sampled <i><b>when ENC sees another rising edge</b>. </i>This tripped me up for a while; waiting for a rising edge on ENC allows you to control the timing between samples in the sequence arbitrarily. If you would rather the ADC10 automatically start the next conversion immediately, you must set the MSC bit in ADC10CTL0 as well when initializing the ADC. Once the whole sequence is done, ADC10 will wait to have ENC and SC set again to start the next sequence. This code is timed to do one sequence every 10 seconds.</li>
<li>Finally, this code uses all three Timer_A interrupts possible on this device: CCR0, CCR1, and TAR overflow. This example should help anyone trying to understand the various interrupts that can happen for the Timer_A module.</li>
</ul>
</div>
<h3>
Results</h3>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOTiNsNTQuH4N5oI0X2Zb60knvgL_WPq7xu9CkEBfNhhKtdOFnlmCFApZI733SSEaW__NwrKxxyn1ITnd3d5xFD5mxbjnmmn1KhXWPyiygJWsUvA4DsKsLZDm1lETD4k9hiLJZyFxqMlpr/s1600/trial_run.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="298" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOTiNsNTQuH4N5oI0X2Zb60knvgL_WPq7xu9CkEBfNhhKtdOFnlmCFApZI733SSEaW__NwrKxxyn1ITnd3d5xFD5mxbjnmmn1KhXWPyiygJWsUvA4DsKsLZDm1lETD4k9hiLJZyFxqMlpr/s400/trial_run.png" width="400" /></a></div>
This experiment takes a good deal of time to finish, and I've decided to post the experiment before getting the actual results. Here is an example of what we'll be looking at to start-- this is a trial run using two random AAA batteries I had on hand, done just to be certain everything behaved as expected. As you can see, at 150 Ω it takes days to drain the batteries completely in this setup. After a few days, I decided to call it a good proof-of-concept, and the experiment is ready to run. I'll let it run for the next little while, and when I have official results I'll post them here, along with a detailed write-up of what is happening.<br />
<br />
Don't worry-- I have two LaunchPads, so I'll be able to post a couple more things while waiting for the experiment to run.<br />
<br />
Let me know if you like this post design-- is there any more information that you think would be helpful to have here?</div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8853820565211362966.post-71998789947146092122012-05-29T12:15:00.001-04:002012-05-29T12:15:07.634-04:00AdSenseIf you haven't already noticed, I've decided to try enabling AdSense on the blog. I figure I'll give it a shot; if it's not too intrusive I'll leave it there. Send me feedback and let me know what you think-- there may be better ways to fund my hobby.Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-8853820565211362966.post-24847239332505432162012-05-25T14:31:00.002-04:002012-05-25T14:31:55.819-04:00Tutorial 18: Finishing the UART TransceiverThis last tutorial in this series will be fairly short, because there's very little to be done! If we combine <a href="http://mspsci.blogspot.com/2012/05/tutorial-16f-transmitter.html">tutorial 16f</a> and <a href="http://mspsci.blogspot.com/2012/05/tutorial-17-receiver.html">tutorial 17</a>, we're nearly done building a full-duplex UART transceiver. There are a few little details that need to be explained in order to get it to work properly, however.<br />
<br />
<ul>
<li>A bit counter will be needed for both transmitting and receiving, if you're to be able to do them simultaneously. This takes another 8 bytes of memory, of course.</li>
<li>While not necessary for the UART aspect, this code uses interrupts on two pins of P1. Any output on GPIO, even if interrupts are disabled for the pin, will set the corresponding bit of P1IFG on a rising or falling edge, depending on the setting of the corresponding bit in P1IES. Because the two LEDs are being used, any time the LED is turned on we have a rising edge on that GPIO, which sets the bit in P1IFG. The method used in this code to deal with P1 interrupts fails in those cases, since P1IFG would not be exactly P1RX or BTN1. To fix this, a mask is introduced so the switch statement in the P1 ISR only considers the bits for P1RX and BTN1, ignoring any others set bits of P1IFG. There is one disadvantage to this particular scheme: if the UART receives a start bit at the same time as a button press occurs, one or both will be ignored (depending on the order and timing of the two events).</li>
<li>A new function is introduced to encapsulate sending a string of characters over UART. This function makes use of a pointer, allowing us to write new values to the variable <span style="font-family: 'Courier New', Courier, monospace;">message</span>. Be aware that other values in RAM could be overwritten if you're not careful. Also, the function assumes a message length of exactly 16 characters, and will print garbage (or remnants of the last message) if you write a shorter message. A longer message will be truncated.</li>
<li>Because the DCO is executing commands at a faster rate than the transmission, a short delay of <span style="font-family: 'Courier New', Courier, monospace;">bit_time</span> is added to <span style="font-family: 'Courier New', Courier, monospace;">tx_byte()</span>. This delay lets the transmission start before returning control to the <span style="font-family: 'Courier New', Courier, monospace;">main()</span> function. Without it, the code could call <span style="font-family: 'Courier New', Courier, monospace;">tx_byte()</span> again before transmitting actually starts, changing the value of <span style="font-family: 'Courier New', Courier, monospace;">TXBuffer</span> before it has a chance to get sent. </li>
</ul>
<div>
The transceiver code can be found in <a href="https://sites.google.com/site/mspscifiles/tutorials/uartTXCVRG2231.c?attredirects=0&d=1">uartTXCVRG2231.c</a>. I've added a few features to demonstrate the full-duplex capability of the transceiver. The commands 'r', 'g', and 'z' are retained from the receiver tutorial. To this has been added 'c', which prints a continuous message until another command (eg. 'z' to sleep) is sent. Note that the command can be sent in the midst of a byte transmission and still work, since the receiver is operating on a separate timer/interrupt configuration. I verified this to be the case using my logic analyzer, and found that a command was correctly received even while starting in the middle of a transmission.</div>
<div>
<br /></div>
<div>
In addition to the 'c' command, another command is set from a button press (using a non-keyboard ASCII character 0xAF), and can be done at any time, showing that the code can also work with physical interface interrupts as well as the serial transmission of commands. The button will also stop the continual printing of a 'c' command as well, as it also resets the <span style="font-family: 'Courier New', Courier, monospace;">command</span> variable to 'z'.</div>
<div>
<br /></div>
<div>
Remember to use 9600 baud transmission and to configure CCS to build and link to the header file with the UART calibration, as well as not erase the information memory segment containing the <a href="http://mspsci.blogspot.com/2011/11/tutorial-16e-programming-flash-memory.html">UART DCO calibration</a>.</div>
<div>
<br /></div>
<div>
And with that, we've reached the conclusion of what I'll term as the "basic" tutorials for the MSP430. A big "Thank you!" to all of you; I've received numerous notes of encouragement from many of my readers, and it's been very helpful. This project and blog have been a great deal of fun, and wer a key addition to the training I received as a Ph.D. student. This isn't the end of the blog, of course. I'll be back with more advanced tutorials, and, more importantly, examples of scientific measurements and experiments done with the MSP430. In fact, my next post will start describing a project I've had in mind for a while. In the mean time, I will start recompiling these first 18 tutorials into a single volume. I have as-yet to decide just how it would be distributed, and exactly what form it would take. In any case, I hope all of you stay with me and enjoy exploring the MSP430.</div>Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-8853820565211362966.post-73575764701825110472012-05-22T12:14:00.003-04:002012-05-22T12:14:32.530-04:00Tutorial 17: The ReceiverOnce you've built a UART transmitter, the receiver is not much different! The way we'll build the receiver here will be done in anticipation of putting both the transmitter and receiver on the same device, and we'll look at a few new things.<br />
<h2>
What's the Same</h2>
We'll use Timer_A to work with the timings, using the same bit_time value as the transmitter. The timer will need an accurate clock, and we'll use the calibrated UART frequency from last time as well. The interrupt service routine for the timer will handle all of the work for reading the serial data as it's transmitted to the device.<br />
<h2>
What's Different</h2>
There are a number of differences in how the receiver is handled, but most of them are quite minor. We'll use interrupts on the GPIO to trigger receiving, for one. Once we've seen a falling edge on the GPIO (signalling a start bit being received), we need to wait one half bit_time so that we're sampling in more or less the center of each bit, thus we need a definition for a half bit_time as well. (We could just divide bit_time by 2, but that's actually kind of slow-- it's probably more conducive to just define another variable with the exact value we want.) All of this will be handled by an interrupt service routine for the GPIO port.<br />
<br />
In addition, we want to use TA1 instead of TA0 for the timing-- this will allow us to send and receive simultaneously, as each has its own independent timer. TA1 has a slightly different way of handling the ISR, however. It's worth noting that there's nothing special about TA0 for transmitting nor TA1 for receiving-- we could just as easily have reversed the two. Which one you use depends on which pins you connect for transmit and receive. On the LaunchPad (the earlier versions, anyway), Tx is connected to P1.1 and Rx to P1.2. Looking at the datasheet for the devices that connect to the LaunchPad, P1.1 is connected to TA0.0, letting us use the output feature of Timer_A to change the bit automatically. P1.5 can also be connected to the TA0.0 output, so that is another option. P1.2, P1.6, and P2.6 can all be connected to the TA0.1 output, and are all possibilities for the transmit pin if you change from CCR0 to CCR1 for the transmitter's timer. The receiver will be reading a GPIO, and could in principle be used with any GPIO pin. (We'll look in a short while to how we can do transmitting on any GPIO as well.)<br />
<h2>
The Specifics</h2>
Alright, let's dig into the code. First, we need to define our bit_time again, as well as a half_bit_time. For 9600 baud at the UART calibrated frequency (7.3728 MHz), these correspond to 768 and 384 respectively. We'll make use of the UART_FG flag variable again, leaving BIT0 for a transmitting flag and using BIT1 for a receiving flag. The data will be loaded into a buffer again before being saved, using the int value RXBuffer. The bit_count variable comes into play again, and will be used to count down the bits as they are received. When we combine the transmitter with the receiver, we'll need one of these for each of the parts so they can count independently. For now, I've just used the same variable name bit_count.<br />
<br />
The DCO and Timer are initialized identically to the transmitter-- the differences in the code happen in the interrupt routines. P1 is configured to read from our chosen receive pin (P1.2), enabling interrupts on a falling edge. When the interrupt occurs (presumably at the start bit of a transmission), CCR1 is initialized to a half bit-time later, CCR interrupts are enabled, and P1 interrupts temporarily disabled until the data has been received. This method precludes having to wait for the receive flag to clear before the next P1 interrupt, so no while loop is needed here.<br />
<br />
The CCR1 interrupt is handled differently than the CCR0 interrupt. For one, CCR0 triggers the interrupt flag TAIFG in the TACTL register. All other capture/compare registers in a device will trigger flags in the TAIV register. There is a single interrupt for this register, and so TAIV must be read to know which CCR caused the interrupt. (Though in the LaunchPad devices there is only CCR1, the interrupt must be handled the same way as though there were further CCR's in the device.) I've done this by using a C switch statement, looking specifically for a CCR1 flag, which is marked by bit 1 in TAIV. TAIFG also appears in TAIV, but as the value 0xA rather than 0x2, so a case 2: is sufficient for identifying a CCR1 interrupt flag. The flag is automatically cleared with the interrupt is serviced. The bits are then read in bit_time intervals from the GPIO input. When all 10 bits have been read, the timer interrupts are disabled and P1 interrupts re-enabled.<br />
<br />
Now that we've covered essentially how the code works, you can test it out with <a href="https://sites.google.com/site/mspscifiles/tutorials/uartRXG2231.c?attredirects=0&d=1">uartRXG2231.c</a>. (Don't forget to configure it to not erase the information segments!) Look at the main function here-- it's set up to look for single-character commands, recognizing the characters 'r' to toggle the red LED, 'g' to toggle the green LED, and 'z' to do nothing but go into a low power mode and wait for a command. If an invalid command is sent, the code holds on to the current state of P1OUT and flashes the LEDs to signal an error and returns to the saved state. After each command, the device returns to a sleep mode. To use the code, be sure you have at least exited the debugger-- you may find that serial terminals don't like trying to communicate through the same port that is opened in CCS in debug mode; exiting debug mode releases the serial port for use. When connected to the proper port (at 9600 baud, of course), you should be able to send single character commands to toggle the red and green LEDs. Try sending an invalid character to see the error signal.<br />
<br />
This is the simplest way of sending commands to the MSP430. Obviously there are uses for multiple-character commands, or sending data directly. We'll look at those techniques at some point in the future, but next time we'll combine the two parts into a full-duplex transceiver.<br />
<br />
<span style="color: #cc0000; font-size: x-small;">Reader Exercise: Add a command to clear both LEDs. See if you can come up with another function to add a command for.</span>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-8853820565211362966.post-36204384881672012002012-05-19T12:02:00.000-04:002012-05-19T12:02:04.352-04:00Tutorial 16f: The TransmitterWe now have all the tools to build a UART transmitter on the MSP430. We have an accurate clock, which operates at a frequency that gives us the best accuracy on standard transmission rates with the DCO. The best way to control the timing of our serial transmission will be to use the Timer_A peripheral, so let's look at how to configure it.<br />
<br />
First, we need to ensure we have our DCO set to our custom calibration. The way I've chosen to do this is to use definitions in the header:<br />
<span style="font-family: 'Courier New', Courier, monospace;"><span style="color: purple;"> #define</span> CALDCO_UART *(<span style="color: purple;">char</span> *)0x10BE</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><span style="color: purple;"> #define</span> CALBC1_UART *(<span style="color: purple;">char</span> *)0x10BF</span><br />
Then in our code, we can set the DCO just as we would for the 1 MHz calibration:<br />
<span style="font-family: 'Courier New', Courier, monospace;"> BCSCTL1 = CALBC1_UART;</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> DCOCTL = CALDCO_UART;</span><br />
To make this even easier, you can put the #defines in a header file (I've called mine calibrations.h), and then include it in any program where you need to use the custom DCO calibration.<br />
<br />
Now to set up our timer. First, we need to choose our transmission rate. The commonly selected rate is 9600 baud, and if you use only the LaunchPad's USB connection it may be the only rate that works. (It's a little unclear still how the USB connection is set up-- the MSP430 UART connects to the MSP430 in the emulator, which has separate pins to forward the data to the TUSB chip interfacing with the USB port.) This DCO frequency can reliably do transmission up to a rate of 921,600 baud. Any faster, and there may not be enough time to run the transmission code between bits. The code you can download at the end of the tutorial is configured for 9600 baud.<br />
<br />
The next thing we need is to know how many clock cycles occur during the time to transmit one bit. We'll transmit at 9600 bits per second, and our clock oscillates at 7,372,800 times per second, so there are 7,372,800/9600 = 768 clock cycles in each bit. (Note that this is exact; there's no fractional part being truncated in this division, which is why we chose this DCO frequency to start with.) This period is often called the bit time.<br />
<span style="font-family: 'Courier New', Courier, monospace;"> bit_time = 24576; <span style="color: #38761d;">// bit time for 300 baud</span></span><br />
<span style="color: #38761d; font-family: 'Courier New';"> <span style="color: black;">bit_time = 768; </span><span style="color: #38761d;">// bit time for 9600 baud</span></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> bit_time = 64; <span style="color: #38761d;">// bit time for 115,200 baud</span></span><br />
<br />
The LaunchPad is set up to send the transmission over P1.1. Looking in the datasheet, we see that this pin can be used as the TA0.0 output<span style="font-size: medium;">–</span>using Timer_A, we can toggle this output when the TAR counter reaches the value stored in TACCR0. We have two options for this: we can use the timer's up-mode so that the counter resets after reaching TACCR0 of bit_time, or we can use continuous mode and reset TACCR0 to be bit_time cycles beyond the current value of TAR when it triggers an interrupt. (Doing this automatically accounts for roll-over.) The first would be an easy solution for our transmitter, but as we're working towards a full-duplex receiver/transmitter, and the receiver looks at P1.2 (using TA0.1, based on TACCR1 for timing, of course), it makes more sense to use the second method in anticipation of setting up the receiver next time so that we don't have conflicts in using the timer for both tasks.<br />
<br />
The idle state for UART is logic high. When we configure the timer, we'll ensure that the TA0.0 defaults to setting P1.1. There's no need to start the timer until we're ready to transmit, but on the other hand there's no need to stop it unless you're concerned about power consumption. We'll go ahead and let the timer run for now, since as a peripheral it runs independently and won't interfere with any code running. We will, however, wait to enable interrupts until we're ready to transmit.<br />
<br />
Once we're ready to transmit a character, we need to enable interrupts, and configure Timer_A to reset the TA0.0 output to signal the start of a character. TACCR0 is incremented to bit_time counts later, so the next interrupt occurs at the timing necessary for our chosen transmission rate. We then step through the bits being sent and tell the MSP430 whether the next bit should be set or reset, depending on our encoding. We'll use the standard 8N1, little-endian encoding, so it is determined by the lsb of the character we're sending. When all 8 bits have been sent, we then set TA0.0 to mark a stop bit, making a total of 10 bits for each character. Interrupts are then disabled until we have another character to send.<br />
<br />
Take a look at the code in <a href="https://sites.google.com/site/mspscifiles/tutorials/uartTXG2231.c?attredirects=0&d=1">uartTXG2231.c</a>. Note first of all the routine DCO_init(). This includes a trap–since we're accessing the flash memory directly, we run the risk of setting the DCO to its highest setting if we use a device that hasn't had the DCO calibration written to that memory address! This type of check will prevent that, and is not a bad thing to include even when using the factory calibrations. I've created an LED signal specifically to indicate a DCO calibration error: three flashes of LED1, repeated with a pause between sets. If you try this code with an uncalibrated G2231, it should immediately indicate this error. Note that it cannot tell if a written code is correct or not, only if the addresses we're accessing have been erased.<br />
<br />
If you've jumped right in and tried to run the code after calibrating your DCO, you may still encounter the error. CCS by default erases both the main memory and the information memory segments (except for INFO_A) when you program the chip; so your calibrations in INFO_B have been erased and the code won't run. Rerun the <a href="https://sites.google.com/site/mspscifiles/tutorials/uartcalG2231.c?attredirects=0&d=1">UART calibration</a> code, and then open the project properties. (When uartTXG2231 is the [Active -Debug] project, you can find the properties in the File menu, or right-clicking the project name in the Project Explorer tab.) In the menu on the left, select Debug. In the window that comes up, select MSP430 Properties. Here you'll see a list of Download Options; be sure to choose "Erase Main Memory Only" and close the properties window. Now when you load this project into your device, it will only erase the Main Memory and leave the information memory segments alone.<br />
<br />
This code is set up to demonstrate a few ways to transmit data. Each loop is initiated by pressing the button on P1.3. First, a string is stepped through and sent over UART character by character. Individual characters are also sent, as is the value of a variable count, which is set to repeatedly count from 1 through 9. Single digits can be sent as characters easily as they appear in order in ASCII-- the digit 0 is ascii code 48 = 0 + 48, 1 is 49 = 1 + 48, and so on. A multiple digit number could be pushed through using code similar to what we used for the LCM. These are some simple ways of putting data and messages through UART that take very little code space in your device. <br />
<br />
The real magic happens in the <span style="font-family: 'Courier New', Courier, monospace;">tx_byte()</span> and <span style="font-family: 'Courier New', Courier, monospace;">CCR0_ISR()</span> routines. When <span style="font-family: 'Courier New', Courier, monospace;">tx_byte</span> is called, it first polls the UART_FG flag and waits for the TXbit to clear. The byte being sent is loaded into a buffer and formatted to include the stop bit. The TXbit is set in UART_FG, an LED turned on to indicate transmission, and the timer is configured to reset at the next interrupt. A character cannot be transmitted until at least bit_time after the previous transmission; since the MSP430 clock is so much faster than the data rate, you could potentially run into a problem of initiating a new character send microseconds after the last, rather than leaving at least one bit_time space between. To prevent this, any present interrupt flag is cleared and the next interrupt is set for one bit_time after the current time. This configuration leaves just slightly more than one bit time between characters. The interrupts run the <span style="font-family: 'Courier New', Courier, monospace;">CCR0_ISR</span> routine, which sets the next interrupt to be bit_time later, configures TA0.0 to the next bit state for the next interrupt, and shifts the transmission buffer down to pull the next bit to the front.<br />
<br />
To see all of this happen, you'll need a serial terminal, such as <a href="http://realterm.sourceforge.net/">RealTerm</a> or <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/">PuTTY</a>. When your terminal is directed to watch the port your LaunchPad is on at the 9600 baud rate, you should see the message display every time you push the button.<br />
<br />
Next time we'll use Timer_A to receive data, getting ready for a complete UART transceiver.Unknownnoreply@blogger.com9tag:blogger.com,1999:blog-8853820565211362966.post-53305678402555975932012-05-18T11:33:00.003-04:002012-05-18T11:38:41.309-04:00Addendum to Tutorial 16cAs I've been reviewing what I've done so far, I started thinking about some way to test my claims in the tutorial discussing the need for accurate clocks in UART. I recently acquired a logic analyzer, which is fast proving to be an extremely useful tool for me! I'll probably use it quite a bit in the coming months and years trying to understand and debug some of the designs I'm working through.<br />
<br />
In any case, I thought it might be interesting to use it to show what happens when clocks are off slightly. The four channels shown here are all measuring the same signal, with the Red channel clocking at the correct rate (9600 baud), Yellow at 3% Error (effectively 9888 baud), Green at 6% Error (the theoretical error from using two +/- 3% clocks, effectively 10,176 baud), and Blue at 10% Error (effectively 10,560 baud).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEis37pIQWZYYfSzIbVR-E0nznO5RzYqPy6uEtnGn81F0dXWo_0IM4mpzOq1TOXBjjy2qfrdCrj-WdFnPWaDw0jdrUb0xKOba10hv9ErIEKH1sEValoAIJbo8A6WkRm39SZ0SzNOFJAldmun/s1600/UART+Accuracy.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="109" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEis37pIQWZYYfSzIbVR-E0nznO5RzYqPy6uEtnGn81F0dXWo_0IM4mpzOq1TOXBjjy2qfrdCrj-WdFnPWaDw0jdrUb0xKOba10hv9ErIEKH1sEValoAIJbo8A6WkRm39SZ0SzNOFJAldmun/s320/UART+Accuracy.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifn717d1zbdoRAPHscfUSq1iDH8xsrO6fx5d6RlFiek0DnsWaC8WGURIANEPOZqRzwOQJm6FGLcPFSpLZbkcRmnDpJOY1QN8sLAV-e60aROWTudSAyVPdhl81J-1BiPCa6Thk-DSEK9SOd/s1600/UART+Accuracy+Zoom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifn717d1zbdoRAPHscfUSq1iDH8xsrO6fx5d6RlFiek0DnsWaC8WGURIANEPOZqRzwOQJm6FGLcPFSpLZbkcRmnDpJOY1QN8sLAV-e60aROWTudSAyVPdhl81J-1BiPCa6Thk-DSEK9SOd/s320/UART+Accuracy+Zoom.png" width="320" /></a></div>
<br />
The second image zooms in to the first characters so you can see the frame errors more clearly. The dots placed on each plot show where each channel is sampling the data stream. At 6% error, the software is smart enough to decode the characters, but the stop bit on each is missed, making communication unreliable. In practice, you need better than 5% combined error to have reliable communication in UART. (Using identical systems, each needs a clock that is accurate to better than 2.5%.) <br />
<br />
Note that this isn't really true in all cases; I'm using 8N1 encoding here. If I were to use larger frames than the 10 bits used for 8N1, the smaller clock errors may eventually cause a frame error as well. If you use a protocol that sends more than 10 bits, you'll need even more accuracy in your clocks.<br />
<br />
Just an interesting little test I put together to try out my fancy logic analyzer.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-8853820565211362966.post-61745090025084356212012-05-07T19:17:00.001-04:002012-05-07T19:17:22.589-04:00Dr. OlsonBoy, it's been a long time since I've worked on this blog. The good news is that, as of this month, I have finished my doctoral degree and will now (hopefully) have more time to dedicate to working on it! There are still many, many more ideas and projects I have in mind, so I hope those who have been here before will stick around, and those looking for new help will find it. <br />
<br />
Thanks, everyone, for the patience and kind words that have consistently rolled in over the past few months. There are still a number of comments I have yet to moderate, mostly because I need to give them some thought before I can respond. I don't want to just post them, because I'm afraid I'll never respond if I do. =)Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-8853820565211362966.post-86576866554105741522011-11-03T17:36:00.000-04:002011-11-03T17:43:16.955-04:00Tutorial 16e: Programming the Flash MemoryWhen we call the calibration values CALDCO_1MHZ and CALBC1_1MHZ in the code for our MSP430, those values refer to the address in SegmentA where the values are stored. They're not located in a place that's easy to find, however; they're stored in the linker command file, specific to each device. (If you're curious, take a look at the file in ${base dir}/Texas Instruments/ccsv4/msp430/include/msp430g2231.cmd, where your base directory is wherever you've installed CCS (likely in C:\Program Files for Windows). Be careful not to change anything in this file, though; this file is essential for proper programming of the G2231 device, as each is to their respective devices.) We could make a copy of this file and use it in our linker when we program the MSP430, but that requires a number of steps that are difficult to remember. All we really need is a way to tell our program to look at the specific address where we've saved the calibration values for our UART. All we really need is a pointer.<br />
<br />
<span class="Apple-style-span" style="font-size: large;"><b>Pointers</b></span><br />
We've not dealt with pointers up to this tutorial, but they're not really a difficult concept. Let's say we have code that declares the following two items:<br />
<div style="text-align: left;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;"> int</span> var;</span></div>
<div style="text-align: left;">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;"> int</span> *ptr;</span></div>
<div style="text-align: left;">
The first is what we're used to seeing; we declare a variable of type int (16 bits in MSP430), which will store a signed integer value. The second is also a declaration of type int, but rather than a variable, it is a pointer. The stored number in <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ptr</span> doesn't refer to the value of a variable, but rather the <i>address</i> where a variable of type int is stored. (The actual size of the pointer in an MSP430 is 16 bits, so <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;">char</span> *ptr;</span> would also be a 16 bit value, even though it points to memory where an 8 bit value is being stored.)</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
In the C language, we have a way to refer to the address of a specific variable by using the & character. So from this point, if we assign <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ptr = &var;</span> then <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ptr</span> now points to the address where we've stored the int value <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var</span>. Similarly, we can reference the value stored in a pointer with the * character. <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;">int</span> var2 = *ptr;</span> would store the value at address <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">ptr</span> in the new variable <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">var2</span>. If we want to assign a specific address to a pointer, rather than reference the address of a variable, we can use a literal of the form <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">(<span class="Apple-style-span" style="color: purple;">int</span> *)0x1234</span>.<br />
<br />
In the MSP430, an int variable takes 16 bits, while a char variable take 8. When we want to reference a specific word, we'll use a pointer of type <span class="Apple-style-span" style="color: purple; font-family: 'Courier New', Courier, monospace;">int</span>. If we want to reference a specific byte, such as the calibration values for the DCO, we'll use a pointer of type <span class="Apple-style-span" style="color: purple; font-family: 'Courier New', Courier, monospace;">char</span>. To use the DCO values we'll program in SegmentB, we can use the following:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;"> char</span> *CALBC1_UART = (<span class="Apple-style-span" style="color: purple;">char</span> *) 0x10BE;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;"> char</span> *CALDCO_UART = (<span class="Apple-style-span" style="color: purple;">char</span> *) 0x10BF;</span></div>
or, if we'd rather not use extra memory, define in the header:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;"> #define</span> CALBC1_UART *(<span class="Apple-style-span" style="color: purple;">char</span> *) 0x10BE</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;"> #define</span> CALDCO_UART *(<span class="Apple-style-span" style="color: purple;">char</span> *) 0x10BF</span><br />
<br />
<b><span class="Apple-style-span" style="font-size: large;">The Flash Memory Controller</span></b><br />
Ok, now we've got a handle on how to reference portions of the flash memory. Now it's time to learn how to actually write to it. The MSP430 has a peripheral designed specifically to handle managing the flash memory called the Flash Memory Controller. Since it would be dangerous to manipulate the flash memory willy-nilly, it's set up like the Watchdog Timer, so that a key is needed to change any of its four control registers. The key value is 0xA5, conveniently provided in the header files as the value FWKEY. Here are the portions we'll need to know about:<br />
<br />
FCTL1<br />
<br />
<ul>
<li>FWKEY (bits 15-8): The key is read as the value 0x96. Each register has this.</li>
<li>BLKWRT (bit 7): control bit for block write mode. We will be writing word by word, so we'll save this function for a future tutorial.</li>
<li>WRT (bit 6): write mode enable. Before we can write to any flash segment, this bit must be enabled.</li>
<li>MERAS & ERASE (bits 2 and 1): Control the mode of erasure. You can erase a single segment, the entire main memory, or the main memory and information segments both. (Depending on whether SegA is unlocked.)</li>
</ul>
<div>
FCTL2</div>
<div>
<ul>
<li>FSSELx (bits 7-6): These bits select which clock source to use for the programming.</li>
<li>FNx (bits 5-0): These bits allow you to divide the source clock by any factor up to 64. (Divide by FNx + 1)</li>
</ul>
<div>
FCTL3</div>
</div>
<div>
<ul>
<li>FAIL (bit 7): We don't want to see this bit set! For our purposes, it could mean the clock source has failed for some reason. If this happens, it needs to be reset by software before anything else can be done.</li>
<li>LOCKA (bit 6): I won't go into how this works; this bit controls whether SegA can be erased/written. If you find you absolutely must change something in this segment, you can read in the Family User's Guide to learn how to use this bit.</li>
<li>LOCK (bit 4): When this bit is set, the flash memory is completely locked, and cannot be erased or written.</li>
<li>WAIT (bit 3): Indicates when the flash memory is being erased or written.</li>
</ul>
<div>
FCTL4</div>
</div>
<div>
<ul>
<li>This register is not available on all devices; it controls the 'marginal read mode'. We won't be using it here.</li>
</ul>
<div>
To do a successful programming of the Information Segment, we first set the clock for the controller. The actual frequency isn't terribly important, as long as it falls between 257 and 476 kHz. The default DCO setting of the MSP430 is about 1.1 MHz, and so a division of 3 (FNx = 2) works well, giving us roughly 370 kHz. We'll calibrate the DCO to 7.3728 MHz, so a division of 20 (FNx = 19) will give us about the same.</div>
</div>
<div>
<br /></div>
<div>
If we have anything we want to save in the segment, then the entire contents would need to be saved to a buffer. Likely, your SegB is blank, so we'll just proceed forward. Next, we set the controller to erase mode. We don't want to erase the main memory segments, so we only need the ERASE bit. We then clear the LOCK bit to allow changing the flash memory. When the controller is configured this way, we initiate the erase cycle by writing to any address within the segment. There's nothing magic about what value we write, but it is absolutely crucial that you have your address pointer pointing to somewhere in the segment you actually want to erase!</div>
<div>
<br /></div>
<div>
Once it has erased, we reconfigure the controller to write mode (not block write), and then proceed to write the values we need, either as bytes or words. If you preserved the prior contents, a better method is to change the contents to the new values and use block write mode. For this tutorial, we'll keep it simple.</div>
<div>
<br /></div>
<div>
Finally, when everything is written, we clean up, clearing the write mode bit and locking the flash memory.</div>
<span class="Apple-style-span" style="font-family: inherit;"><br /></span><br />
<div style="margin-bottom: 0in;">
<span class="Apple-style-span" style="font-family: inherit;"><span style="color: black;">The
code I wrote for writing the UART DCO calibration to Segment B using the TLV format is found in <a href="https://sites.google.com/site/mspscifiles/tutorials/uartcalG2231.c?attredirects=0&d=1">uartcalG2231.c</a>. Remember, if you run
this, it requires the watch crystal. If there is anything already
stored in SegB, </span><span style="color: black;"><i>it
will be lost</i></span><span style="color: black;">.
If you'd rather store it in Segment C (0x1040 to 0x107F) or Segment D
(0x1000 to 0x103F), change the address in the assignment instructions
accordingly. If you choose to put in in Segment A (0x10C0 to 0x10FF),
look in the Family User's Guide for instructions on unlocking the
Segment. I do not recommend this, however, since we're calibrating a
non-standard DCO frequency. Wherever you choose to store it, you'll
need to remember the address. If you use my method here, that address
is 0x10BE for the CALBC1 value and 0x10BF for the CALDCO value. Next time we'll write code that pulls these calibration
values from memory and configures the DCO and TimerA to start setting
up our software UART.</span></span>
<br />
<span class="Apple-style-span" style="font-family: inherit;"><span style="color: black;"><br /></span></span><br />
<span class="Apple-style-span" style="color: #0b5394; font-family: inherit;"><i>This tool will be very useful to you; keep in mind that though the Value Line devices only have one DCO calibration and don't appear to have any calibrations for the ADC, there is space for them! This process is not too difficult, you can upgrade your devices to have better calibrations, essential for accurate scientific work. For more information on the ADC calibration values that are often included in MSP430 devices, see the TLV chapter of the Family User's Guide.</i></span></div>Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-8853820565211362966.post-74062010740480000912011-11-02T14:18:00.000-04:002011-11-03T17:33:05.665-04:00Tutorial 16d: Flash Memory and TLVI've used a number of resources in preparing this tutorial; in addition to the Family User's Guide and the device datasheets, one of the most helpful was an application report titled <a href="http://www.ti.com/lit/an/slaa334a/slaa334a.pdf">MSP430 Flash Memory Characteristics</a>.<br />
<br />
We'd really like to hold onto our DCO calibration for two reasons. We'd like to be able to use it later, but we'd also like to do so without using up our limited code space with self-calibration. In addition, we may need it in an application where we don't have a crystal connected to the device. (Rather, we could program the calibrations in our LaunchPad with a crystal connected, and then transfer the chip to our intended application while retaining the calibration values in memory.) Programming the flash memory in the MSP430 is not difficult, but there are a few things that have to be done properly to protect your device. The steps done in programming make more sense when we understand how flash memory works.<br />
<br />
<b><span class="Apple-style-span" style="font-size: large;">The Science in Flash</span></b><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI_SNbk_-IV5E6QNFp1hjPY8GkgyBvGRF7TYuV2ogAPG_w-UBercu3qbrdvVSD-eCl2BH-uOX5LDf5agVeplKfxyfnjM00kWyaBbbK-9vwXbu40Ue4wq671E9U5iLJCiLOZKE8oHiTzunA/s1600/NOR-flash.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI_SNbk_-IV5E6QNFp1hjPY8GkgyBvGRF7TYuV2ogAPG_w-UBercu3qbrdvVSD-eCl2BH-uOX5LDf5agVeplKfxyfnjM00kWyaBbbK-9vwXbu40Ue4wq671E9U5iLJCiLOZKE8oHiTzunA/s320/NOR-flash.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A NOR flash bit is a transistor with a little pocket for storing charge.</td></tr>
</tbody></table>
The flash memory inside the MSP430 is what we call NOR flash (as opposed to the NAND flash that makes up your typical USB flash drive, for example). It works by trapping charge in an isolated region called a floating gate. The charge in the floating gate changes the transistor's threshold, and determines what value is read from the bit. Acting much like a switch, a positive charge in the floating gate makes it so the read voltage "closes" the switch, and we read a logic 1. A negative charge keeps the switch open, and we read a logic 0.<br />
<br />
When we erase flash memory, each bit is put in a state where the floating gate is positively charged. Thus, a "blank" bit will always read 1; a byte of erased flash will read 0xFF. We can program the bit to a 0 by applying a high voltage to the control gate, which allows the floating gate to push charges through the drain. The resulting negative charge in the floating gate is retained, barring physical effects like quantum tunneling that take 100's of years to de-program the bit. (At 25°C, the typical lifetime of a NOR flash bit is on the order of 1,000 years!) Fortunately, the ability to generate this high voltage is built into the Flash Controller peripheral of the MSP430, so we are able to program the flash memory as long as the operating voltage on our microcontroller is at least 2.2 V.<br />
<br />
The flash memory in the MSP430 is organized in chunks called segments. The Main Memory portion is divided into segments of 512 bytes. (That means there are 4 segments in the main memory of both the G2211 and G2231, which each have 2 kB available.) Each segment is further subdivided into blocks of 64 bytes. Additionally, an extra 512 bytes is included in each MSP430 device, divided into segments of either 64 or 128 bytes (one or two blocks, respectively). These segments make up the Information Memory portion of the device. The Value Line devices that come with the LaunchPad have four information memory segments of 64 bytes each, called Segment A–D.<br />
<br />
The physical structure of the flash cells is important to us, as it has direct bearing on how we deal with it. First of all, when we erase flash memory, we can only erase an entire segment at a time! This means any time we need to change a 0 in a single cell to a 1, every cell in the segment has to be erased. When we program flash memory, the high voltage is applied across an entire block at a time, even if we are only programming one cell. This voltage causes stress to the flash cells, and so we cannot exceed a specified "cumulative programming time", typically 10 ms. Erasing releases the stress, and essentially resets our cumulative timer to 0. These stipulations affect our programming speeds and how often the block needs to be erased.<br />
<br />
At the typical speeds we can use (between 257 and 476 kHz), we can program an entire block twice. (Note that in this case, "program" doesn't mean we can write any value-- we can change 1's to 0's twice. To change 0's to 1's, we are forced to erase the entire segment.) We cannot, however, program the same byte multiple times, even if we don't program any other bytes in the block. The rule of thumb, then, is if you reach 10 ms of programming time or write to the same byte twice, the block (and thus the entire segment for main memory) must be erased. This is a real pain, especially if we want to use main memory to store our calibrations. Because of this, I would recommend using the Information segments for storing anything you want to retain outside of the actual program in the device. The process you would use would be to read the entire contents of the block to a buffer (in RAM), erase the Information Segment, change anything necessary in the buffer value, and re-write the buffer to the segment. (It seems like a lot more to do than you're used to when using a flash drive, but in reality the same thing is happening there. Your computer just does a fantastic job of hiding it so you don't have to worry about it.)<br />
<br />
<b><span class="Apple-style-span" style="font-size: large;">Information Memory</span></b><br />
A quick word about the various segments: segments B–D are each alike in dignity. However, SegmentA is set aside specifically by TI to store information that should be retained regardless of any programming changes. The factory calibrations, for example, are stored in this segment. As a result, this segment is locked, and you will have to set a particular bit before any instructions erasing or programming within this segment. In addition, to ensure integrity of the data stored in SegA, one word (two bytes) of it is set aside as a checksum, which I'll explain shortly. If you change anything in the segment, a checksum calculation will fail. Some programs rely on this, so if you really must change something in this segment, be prepared to deal with the consequences. Well, at least be prepared to recalculate what the checksum value will be. For our purposes in this tutorial, we'll use SegmentB instead, but we will program it in much the same way that SegmentA is structured. That way, if you choose, you can use SegA to store your calibrations, since that is where they are intended to be.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPVjLDATkmKUgHjN8rrmLt9RhZG1oCIg-umB1ggk0PQ4haCl0r0guivlYvAyRq9XgosPp8x87d_U3R2CHdLiRzSV4gV3dG4jZcGF4gCOnbJ0Fie6LLkuZqMhZBge30COSeyTd1iOOIp4bp/s1600/save-btn.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPVjLDATkmKUgHjN8rrmLt9RhZG1oCIg-umB1ggk0PQ4haCl0r0guivlYvAyRq9XgosPp8x87d_U3R2CHdLiRzSV4gV3dG4jZcGF4gCOnbJ0Fie6LLkuZqMhZBge30COSeyTd1iOOIp4bp/s1600/save-btn.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Memory Save<br />
Button</td></tr>
</tbody></table>
Let's take a look at how SegA is put together. Fire up the debugger in CCS; it doesn't matter what code you're using, as we won't even be running the program. We just want to enter the debug mode. Once there, the default view has a panel on the right side with tabs for the code disassembly and memory; the memory tab shows the contents of the flash memory of the device. (If this window is not visible for you, you can find it by selecting Window → Show View → Memory.) The information memory is located (as specified in the datasheet) from address 0x1000 to 0x10FF. SegA starts at 0x10C0 and ends at 0x10FF. You can use this window to browse that region, or you can save a particular region of memory to a text file. To do this, click the save button, navigate to a file you want to save the data to, and enter a Start Address of 0x10C0 and a length of 0x20. (Note it specifies to give the length in words; a word is 16 bits in the MSP430, so there are 0x20 (32) words in the 64-byte segment.)<br />
<br />
An example of the output from my G2231 device is <a href="https://sites.google.com/site/mspscifiles/tutorials/G2231-SegA.dat?attredirects=0&d=1">here</a>. The first line specifies where in the memory the dump comes from. The first line has one word–it comes from the two bytes at addresses 0x10C0 and 0x10C1. Take note that the lower address of the word refers to the least significant byte (LSB, as opposed to lower case lsb for least significant bit), while the higher address refers to the most significant byte (MSB). The last line has the 32nd word–from addresses 0x10FE (LSB) and 0x10FF (MSB). Most of the memory in SegA is obviously blank, as most of the entries are 0xFFFF. (Remember, when flash is erased it reads logic 1.) There are a few programmed words, however, so let's see what each one means. In the x2xx Family User's Guide (current revision as of this writing is <a href="http://www.ti.com/lit/ug/slau144h/slau144h.pdf">slau144h</a>), turn to the chapter on TLV, chapter 24. TLV stands for Tag-Length-Value. This is the format TI uses in SegA to store information. Basically, one word is dedicated to specifying the length of memory allocated to a specific type of data and what type of data is stored there. Table 24-1 gives an example of how this is done. The first word in the segment stores a checksum value. Note that it specifies the checksum as the two's complement of the bitwise XOR. If you start with the next word and XOR it with the third word, that result with the fourth word, and so on, then add it to the checksum value, it will add up to zero. Something like this:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;">int</span> chksum = 0;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;">char</span> passed;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;">int</span> *i;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;">for</span> (i=(int *)0x10C2; i<(int *)0x1100; i++) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> chksum ^= *i;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;">if</span> (chksum + *(int *)0x10C0 == 0)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> passed = 1;</span><br />
<span class="Apple-style-span" style="color: purple; font-family: 'Courier New', Courier, monospace;">else</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> passed = 0;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="color: #76a5af; font-family: 'Courier New', Courier, monospace; font-size: x-small;"><i>Note: I'm not completely familiar with pointers just yet; I'm working that out in preparation for the next tutorial. If there's an error in this code, I'll correct it then. For now, think of it more as pseudocode.</i></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: inherit;">Now that we understand how the segment memory is organized, let's look at what's inside it. Using my device, I have a checksum value of 0xB22C. The next word is 0x26FE. This means that the next 0x26 entries (38 bytes) are of type 0xFE. Looking in Table 24-2, we see that 0xFE refers to "TAG_EMPTY", meaning the next 38 bytes (or 19 words) are unused. Sure enough, the next 19 lines are all 0xFFFF. The next line gives the next Tag-Length entry: 0x1010. The next 0x10 entries (16 bytes or 8 words) are of type 0x10, which isn't specified in the Family User's Guide. I've submitted a question to TI support to find out about this. In any case, each entry is blank. The next Tag-Length listed is 0x0201, which means there's one word of type "TAG_DCO_30". Here we have the DCO calibration values at room temperature and 3 V. (Note that the Vcc value of the LaunchPad itself is 3.3 V, and remember that different voltage has a significant impact on the DCO!) There's only one entry, which we know has the values for CALBC1_1MHZ (0x86 on mine) and CALDCO_1MHZ (0xC4 on mine) as per the G2231 datasheet.</span><br />
<span class="Apple-style-span" style="font-family: inherit;"><br /></span><br />
<span class="Apple-style-span" style="color: #990000; font-family: inherit; font-size: x-small;">Reader Exercise: Using either your memory dump or mine, calculate the checksum of SegA and compare it to the value at address 0x10C0. Hint: xor'ing 0xFFFF twice has no effect; an even number of these lines can be ignored. When added to the stored checksum value, do you get zero? Calculate the two's complement by ~chksum + 1 and compare it to the stored value.</span><br />
<span class="Apple-style-span" style="font-family: inherit;"><br /></span><br />
<span class="Apple-style-span" style="font-family: inherit; font-size: large;"><b>Preparing the Custom DCO Calibration</b></span><br />
<span class="Apple-style-span" style="font-family: inherit;">Here's the plan for our code: we'll find a custom calibration value for 7.3278 MHz and store it in SegB using the standard TLV coding set by TI. (You could do this in SegA if you wish, but since we're using a non-standard DCO frequency, I've opted to keep it out for now.) SegB is found in the memory range 0x1080 to 0x10BF. The organization of what we'll be writing will then be like this:</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijcm7LlbD-CbdK8BwnQlvVOJShJWrVR6irNxyQTg7CZTWPbZJvlsQ74XPrXBfcpRldGqbJmt8fO9KMcksKVGGxJsT5vyKGJpGlSmBvBv2Prwt4NkH_mmIg-1tIw_APu5Qs8Nd6oEo5p1wl/s1600/SegB.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="164" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijcm7LlbD-CbdK8BwnQlvVOJShJWrVR6irNxyQTg7CZTWPbZJvlsQ74XPrXBfcpRldGqbJmt8fO9KMcksKVGGxJsT5vyKGJpGlSmBvBv2Prwt4NkH_mmIg-1tIw_APu5Qs8Nd6oEo5p1wl/s320/SegB.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">There's a typo here.. should be 0x38FE. I will fix the image soon.</td></tr>
</tbody></table>
<span class="Apple-style-span" style="font-family: inherit;"><br /></span><br />
<span class="Apple-style-span" style="font-family: inherit;">We use the crystal to find the calibration value for 7.3278 MHz. This value is put into the above table to calculate the checksum: </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">chksum = 0x38FE ^ 0x0201 ^ {calibration values}</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> = 0x3AFF ^ {calibration values}</span><br />
<span class="Apple-style-span" style="font-family: inherit;">The value stored at 0x1080 is then ~chksum + 1.</span><br />
<span class="Apple-style-span" style="font-family: inherit;"><br /></span><br />
<span class="Apple-style-span" style="font-family: inherit;">Next, we erase SegB, and write in the following addresses:</span><br />
<br />
<ul>
<li>0x1080: two's complement of chksum</li>
<li>0x1082: 0x37FE</li>
<li>0x10BC: 0x0201</li>
<li>0x10BE: 0x{CALBC1}{CALDCO}</li>
</ul>
<div>
This tutorial has ended up pretty long, so we'll end the discussion here. The next tutorial will review the registers in the MSP430 Flash Memory controller and describe how to program this information to the Information Memory.</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="color: #990000; font-size: x-small;">Reader Exercise: The Value Line devices do not come with the other standard calibrations: 8 MHz, 12 MHz, and 16 MHz. In the chapter on TLV of the Family User's Guide, we see that the locations of these calibrations are standardized for SegA to appear in the order TLV Tag, CAL_16MHz, CAL_12MHz, CAL_8MHz, CAL_1MHz. The data in SegA for the Value Line devices doesn't leave room for these with the other three calibrations left blank, so the entire segment structure needs to be shifted. If we want to add these without loosing any of the remaining structure, how will the segment be set up? Draw up a table similar to that used in this tutorial to map out the segment structure, and show how to calculate the new checksum value based on this table.</span></div>Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-8853820565211362966.post-4083015778813907812011-10-23T20:44:00.000-04:002011-10-23T21:53:27.302-04:00Tutorial 16c: Accurate Clocks<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVYCheHwfdrGZWipzXwMSm25_QCVMESpAEBhFSKphUzdaJ8kCMRHQG32zN9UJ3eU960tr0mOo0enPM9Q2981R1286i8y84OLmOBATJNTvZUzR7xg-ryxwblEH6Pw2D4Ov1JnzgIGHWYgtI/s1600/perfect+clock.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVYCheHwfdrGZWipzXwMSm25_QCVMESpAEBhFSKphUzdaJ8kCMRHQG32zN9UJ3eU960tr0mOo0enPM9Q2981R1286i8y84OLmOBATJNTvZUzR7xg-ryxwblEH6Pw2D4Ov1JnzgIGHWYgtI/s320/perfect+clock.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Perfectly synchronized clocks can measure the bits<br />
anywhere in the middle.</td></tr>
</tbody></table>
<b><span class="Apple-style-span" style="font-size: large;">Clock Accuracy</span></b><br />
So just how accurate does a clock need to be for UART? First, consider a perfect pair of clocks, each operating at exactly the same frequency. In this case, it doesn't much matter where we measure the incoming bits, as long as we measure after the bit has changed to the next one. If there's some error, however, a slow clock will measure later and later, until it missed a bit completely. A fast clock will likewise measure earlier and earlier, until it measures the same bit twice. Not knowing before hand if your clock is fast or slow, it makes sense to shoot for the middle of the bit; that way you have as much time as possible before the error takes over and you make a mistake.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPi6vJanMxGeI-DYu9tM8EuELPtQj4XeBMOlsNdCALsR3qCu078b_CrYrvgiZvctGzLH_WTi8JwKjaO_cpiQKsmdVRdfmBgMcozJAdW_DH_FZvsQwczlyt91Q7bzwOsXNB2bA7bGnAfUQR/s1600/real+clocks.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPi6vJanMxGeI-DYu9tM8EuELPtQj4XeBMOlsNdCALsR3qCu078b_CrYrvgiZvctGzLH_WTi8JwKjaO_cpiQKsmdVRdfmBgMcozJAdW_DH_FZvsQwczlyt91Q7bzwOsXNB2bA7bGnAfUQR/s320/real+clocks.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Errors can add up very quickly when there are<br />
small differences between clocks.</td></tr>
</tbody></table>
Looking at the plot on the left, using clocks that are off by only 6%, notice where the first problem occurs: bit 9 is completely skipped! Using these clocks, we can't even reliably send one frame of data! (By the way, this type of transmission error is classified as a frame error.) My choice of 6% for this plot isn't arbitrary; the error quoted in the datasheets for the MSP430 give the calibrated DCO frequencies (calibrated, mind you; not just the DCO in general) a tolerance over temperature and any other changes of ±3%. If we use calibrated DCO as the clock for both sender and receiver, we could feasibly have as much as a 6% error, making UART transmission completely unreliable!<br />
<br />
In reality, not all hope is lost; this is a worst case scenario, and likely the clock in your computer (assuming you want to communicate with it instead of another MSP430) is more accurate than that. A total of 5% tolerance between the clocks would, on average, encounter a frame error on bit 11, and 4% tolerance on bit 13, so as long as we have better tolerance than that and have a short gap between frames to "resynchronize", we <i>should</i> be able to communicate. Note this does mean we are limited in the frame sizes we can use reliably, and our actual data transmission rate will be a little less since we need a recovery between frames. If you want fast communication, you need an accurate clock!<br />
<br />
<b><span class="Apple-style-span" style="font-size: large;">Crystal Oscillators</span></b><br />
The best option for accuracy in a clock is to use a crystal. The watch crystal that comes with the LaunchPad is accurate over a wide temperature range to 20 ppm (0.002%)! There are some disadvantages, however: crystals take a while to stabilize, and use more power. In addition, at 32,768 Hz, we can't achieve very high transmission rates. Typically we want at least 16 clock cycles per bit to accurately catch the start bit and start sampling at the center of each bit. Using that rule of thumb, the highest bit rate we could attain with the watch crystal is 2048 baud. Larger MSP430 devices allow for high frequency crystals, but the G2xx series that are compatible with the LaunchPad do not. However, if we're careful, getting transmission rates up to 9600 baud with the watch crystal can be done.<br />
<br />
Soldering the crystal to your LaunchPad might seem a daunting task with such a small part, but in reality it's not terribly difficult. A little bit of patience (mostly in the form of a little piece of masking tape) is all you need. Aside from a soldering iron and solder, of course. If you have not already put a crystal on your LaunchPad and would like to, there are a number of good demonstrations of the technique for soldering the crystal to the board available on YouTube. For now, I'll assume you've successfully put it on.<br />
<br />
You may have noticed a couple of empty pads near the crystal for capacitors. For a crystal to oscillate at the right frequency, it needs to see a particular capacitance to ground. If the capacitance is off, the frequency may be off, or the crystal may not oscillate at all. The crystal included in the LaunchPad kit wants to see 12.5 pF. The MSP430 crystal inputs also have user-selectable capacitances internal to the device. The user can select from 1, 6, 10, and 12.5 pF as the effective capacitance seen by the crystal. (The device defaults to 6 pF.) The selection is done by the two XCAPx bits in the BCSCTL3 register.<br />
<br />
If for some reason you need a capacitance other than one of these, you can solder the proper capacitors to the pads on the outside. Unfortunately, it's not as easy as putting 12.5 pF capacitors on the pads; the capacitors you put on will be in parallel with the capacitance from the traces and the chip itself. The formula for calculating the right load capacitors is: C1 = C2 = 2*C_Load - (Cp + Ci). C_Load would be whatever capacitance the crystal expects to see, Cp any parasitic capacitance from traces etc., and Ci the Capacitance of the MSP430 device. The last two terms can be assumed to be whatever is set in XCAPx. So for a crystal that wants 18 pF, you would want to put 30 pF capacitors on the board (using the default 6 pF).<br />
<br />
Most of what's needed to use the watch crystal is set as the default values in the BCS+ module. To use the supplied crystal, you really only need two lines:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">BCSCTL3 |= XCAP_3; <span class="Apple-style-span" style="color: #6aa84f;">// 12.5 pF for LaunchPad crystal</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">__delay_cycles(55000); <span class="Apple-style-span" style="color: #6aa84f;">// let crystal stabilize</span></span><br />
<br />
The first line sets up the proper capacitance for your crystal, and should be changed if you're using a different capacitance value. The second line lets enough time pass for the crystal to stabilize at the operating frequency. How long do you really need to wait? Typically you need a few hundreds of milliseconds. The above code will wait for 55000 clock cycles; at the default DCO of 1.1 MHz, that's about 50 milliseconds. Depending on your application, you can wait longer if necessary.<br />
<br />
<span class="Apple-style-span" style="font-size: large;"><b>Self-Calibrating the DCO</b></span><br />
If you're paying attention, you might have spotted another problem with the watch crystal: how do we get a standard clock rate from the crystal? 9600 baud is 3.41 clock cycles. 1200 baud uses more clock cycles, so is more reliable, but it's still 27.31 clock cycles. The closest division we can get is 27 clock cycles, which would correspond to 1213.6 baud, an error of 1.1%. It seems we've lost a lot of the accuracy we gained using the crystal! There are crystals available that divide perfectly into the standard baud rates. A 7.3728 MHz crystal, for example, has exactly 768 clock cycles per bit for 9600 baud. If you use an MSP430 device that allows for a high frequency crystal, this is an excellent choice for serial communication at standard rates. Another option, however, is to use the DCO.<br />
<br />
But wait, didn't we just argue that the calibrated values of the DCO are too large to be reliable? Yes, but under certain circumstances we can do better. For one, the error margins quoted in the data sheet cover both the entire temperature range where the MSP430 can be used-- from -40° to 85°F-- and the entire range of voltages at which it can operate-- from 1.8 to 3.6 V. Generally we won't be operating in such extremes. Even if we are, as long as the temperature and operating voltage aren't going to change too much during operation, we can <i>recalibrate</i> the DCO to that particular configuration! The accuracy of the clock will be much better than the quoted 3% in this case; the datasheet even specifies that from 0° to 85°F, the calibration is good to 0.5% at 3 V. (Obviously a consistent voltage is the real key to a stable DCO.)<br />
<br />
Calibrating the DCO is quite simple, but it requires a very accurate clock to compare the DCO against; this is a job for a crystal. The idea is simple-- use the crystal to time an accurate interval (say 1 s.) We know how many oscillations should occur for a given frequency in that interval, so we adjust the DCO until we get as close to that as we can get. Then we save the values for DCOCTL and BCSCTL1, and we have an accurate calibration for our clock! Here's how all the magic happens:<br />
<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><span class="Apple-style-span" style="color: purple;">void</span> Set_DCO(<span class="Apple-style-span" style="color: purple;">unsigned int</span> Delta) { <span class="Apple-style-span" style="color: #6aa84f;">// Set DCO to selected</span></span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> // frequency</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">unsigned int</span> Compare, Oldcapture = 0;</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> BCSCTL1 |= DIVA_3; <span class="Apple-style-span" style="color: #6aa84f;">// ACLK = LFXT1CLK/8</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> TACCTL0 = CM_1 + CCIS_1 + CAP; <span class="Apple-style-span" style="color: #6aa84f;">// CAP, ACLK</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> TACTL = TASSEL_2 + MC_2 + TACLR; <span class="Apple-style-span" style="color: #6aa84f;">// SMCLK, cont-mode, clear</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">while</span> (1) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">while</span> (!(CCIFG & TACCTL0)); <span class="Apple-style-span" style="color: #6aa84f;">// Wait until capture </span></span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> // occured</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> TACCTL0 &= ~CCIFG; <span class="Apple-style-span" style="color: #6aa84f;">// Capture occured, clear </span></span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> // flag</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Compare = TACCR0; <span class="Apple-style-span" style="color: #6aa84f;">// Get current captured </span></span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> // SMCLK</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Compare = Compare - Oldcapture; <span class="Apple-style-span" style="color: #6aa84f;">// SMCLK difference</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> Oldcapture = TACCR0; <span class="Apple-style-span" style="color: #6aa84f;">// Save current captured </span></span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> // SMCLK</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">if</span> (Delta == Compare)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">break</span>; <span class="Apple-style-span" style="color: #6aa84f;">// If equal, leave </span></span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> // "while(1)"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">else if</span> (Delta < Compare) {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> DCOCTL--; <span class="Apple-style-span" style="color: #6aa84f;">// DCO is too fast, slow </span></span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> // it down</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">if</span> (DCOCTL == 0xFF) <span class="Apple-style-span" style="color: #6aa84f;">// Did DCO roll under?</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">if</span> (BCSCTL1 & 0x0f)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> BCSCTL1--; <span class="Apple-style-span" style="color: #6aa84f;">// Select lower RSEL</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">else</span> {</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> DCOCTL++; <span class="Apple-style-span" style="color: #6aa84f;">// DCO is too slow, speed </span></span><br />
<span class="Apple-style-span" style="color: #6aa84f; font-family: 'Courier New', Courier, monospace;"> // it up</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">if</span> (DCOCTL == 0x00) <span class="Apple-style-span" style="color: #6aa84f;">// Did DCO roll over?</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> <span class="Apple-style-span" style="color: purple;">if</span> ((BCSCTL1 & 0x0f) != 0x0f)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> BCSCTL1++; <span class="Apple-style-span" style="color: #6aa84f;">// Sel higher RSEL</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> }</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> TACCTL0 = 0; <span class="Apple-style-span" style="color: #6aa84f;">// Stop TACCR0</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> TACTL = 0; <span class="Apple-style-span" style="color: #6aa84f;">// Stop Timer_A</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> BCSCTL1 &= ~DIVA_3; <span class="Apple-style-span" style="color: #6aa84f;">// ACLK = LFXT1CLK</span></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">}</span><br />
<br />
This code comes from the dco_flashcal.c example code available with most of the MSP430 devices. The example code file for the G2xx1 devices seems to not have it; I copied this from the F21x2 examples and changed TACCTL2 to TACCTL0 to be compatible with LaunchPad devices.<br />
<br />
It's a bit of code to sort through, but it turns out to be straightforward. ACLK is configured to use the watch crystal divided by 8-- 4096 Hz. The timer is set to capture mode, triggering off a rising edge of CCI0B. (From the G2231 or G2211 datasheets, this corresponds to ACLK. If these terms seem confusing, review the tutorial on the capacitance meter which used the capture mode.) The timer itself is running off SMCLK, sourced by the DCO. If we want to calibrate 2 MHz, then in one clock cycle of ACLK, we expect 2 MHz/ 4096 Hz = 488.3 SMCLK cycles. We pass the value 488 to this routine, which starts the clocks and timers. When a capture occurs, it checks to see if more or fewer cycles of SMCLK have happened, and adjusts DCO and RSEL accordingly. It repeats this, until it finds the configuration that returns exactly 488 cycles in the interval. The values in DCO and RSEL are then the calibration values we want to save; we just look at the DCOCTL and BCSCTL1 registers and save their values for future use.<br />
<br />
This routine is used in the example code found in <a href="https://sites.google.com/site/mspscifiles/tutorials/DCOcalibrate.c?attredirects=0&d=1">DCOcalibrate.c</a>. Try it out (with the crystal soldered on, of course), and see what values are obtained for DCOCTL and BCSCTL1 for each frequency it calibrates. If you happen to have an oscilloscope, measure them on P1.4 to see if they're right. (The code finishes on the last calibration done; you can modify the code to end with the one you want to measure, or add code to change to the frequency you want to see.) You can write these values down for future reference, but next time we'll look briefly at writing to the flash memory in the MSP430 so you can save it for use later on!<br />
<br />
<span class="Apple-style-span" style="color: #990000; font-size: x-small;">Reader Exercises: How good is the calibration done in the factory? Modify the code to find the calibration values for 1 MHz. How do they compare to the values stored in CALDCO_1MHZ and CALBC1_1MHZ? It seems many have reported that the 1 MHz calibration from the factory, at least for early batch runs of the value line devices, is closer to 980 kHz.</span><br />
<span class="Apple-style-span" style="color: #990000; font-size: x-small;"><br /></span><br />
<span class="Apple-style-span" style="color: #990000; font-size: x-small;">How much does temperature affect the results? Place your LaunchPad somewhere warm for a while (or cold; a freezer might not be best, though-- too much water around!) and re-run the code. How much difference is there in the calibration values?</span><br />
<span class="Apple-style-span" style="color: #990000; font-size: x-small;"><br /></span><br />
<span class="Apple-style-span" style="color: #990000; font-size: x-small;">Could you use a calibrated frequency to go the other direction and measure the crystal frequency? Imagine doing an experiment to see how the four XCAPx settings might affect the crystal. Which ones oscillate? How much does the frequency change if you use 10 pF instead of 12.5 pF? See if you can write some code to find out!</span>Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-8853820565211362966.post-62849775753603883752011-10-23T17:08:00.000-04:002011-10-23T17:08:21.789-04:00Tutorial 16b: UART Definition<span class="Apple-style-span" style="font-size: large;"><b>Universal</b></span><br />
So what exactly makes the Universal Asynchronous Receiver-Transmitter universal? The UART has a long history, starting way back in the 1840's with some of the first telegraph systems. Back then, when the telegraph key was held down, a current would flow in the receiver, pushing a stylus into a strip of paper, leaving a "mark". The Morse code signals sent would then visually display on the paper, making it simple to read the transmitted message. Of course, it didn't take long before the operators got so used to hearing the patterns of clicks that they found they could just as easily listen to the message as write it on a piece of paper, and sounds began being used instead of a mechanical system. Of course, the sounds would turn on when a current was flowing in the receiver, so the signal was still divided into "marks", where current was flowing, and "spaces", where it was not. In other words, the "standard" had changed from a stylus on a paper to listening by ear, but the "protocol" of Morse code stayed the same.<br />
<br />
Morse code was a phenomenal technology change, making it possible to send messages easily over very long distances, particularly when radio was implemented, and wires connecting between the source and destination were no longer needed. While that was happening, someone realized a financial benefit could be obtained by using the technology, and hence was born the first ticker tape machine for the stock market. These machines changed the technique slightly. Instead of using special codes for each character, a series of pulses would be sent to turn a printing wheel from its current position to the next letter to be printed. A special pulse signal would instruct the printer to stamp the current letter onto the tape. As technology improved, rather than a rotary printing wheel the Baudot code was developed as a new protocol, equating particular pulse patterns to particular characters.<br />
<br />
Like telegraphy, the teletype grew with the new technologies of radio and, in particular, the computer. Teletype machines became useful not only as a means of communication between people, but also as an interface to early computers. Instructions could be sent by typing a particular pattern of keys, sending a particular pattern of pulses to the computer. Results would be sent back with a similar pattern of pulses to a printer, which would translate them back to the letters and numbers we needed to understand them. But even though the technologies had taken different paths, both came from the same beginnings with Samuel Morse. As such, some characteristics and naming conventions stuck; in particular the use of "mark" and "space" to designate when current was flowing (logic high) and when it was not (logic low).<br />
<br />
In computers, a change was made from detecting current flow to just measuring a voltage. Some of the conventions continued, which is why in the RS-232 standard has logic high as a negative voltage. The negative voltage originally would open the current of a teletype machine to produce a "mark" signal. A positive voltage would cut off the current, producing a space. As it turns out, different circumstances (and sometimes just different companies) would require a slightly different standard for sending serial data. connectors and voltage levels for mark and space wouldn't be the same, but the protocol (the way of encoding the characters in pulses) used would carry over. In particular, the use of transistors made it easy to create a universal system that could be understood by any computer or device, as long as each device had something to convert the transistor logic (TTL) signals into whatever standard they expected. The protocol was changed as well, using ASCII to encode the data into digital information. Thus was born the Universal Asynchronous Receiver-Transmitter. (Note that TTL uses positive voltage, be it 5 V, 3.3 V, or anything else, for "mark" and 0 V for "space".)<br />
<br />
<span class="Apple-style-span" style="font-size: large;"><b>Asynchronous</b></span><br />
Now that it's clear what makes the UART universal, let's look at what is meant by asynchronous. In radio, you can send a message (by voice, digital code, morse code, or whatever), but the message cannot be received unless someone is listening. For serial communication, it requires more than just a signal saying data is ready to be transmitted, unfortunately. Imagine a system where I'm going to send a message to you by holding up a giant sign. We agree before hand that at 1:32 PM I will put the sign up, and at the designated time you look in my direction, see the sign, and read the message. This would constitute a parallel type of transmission--each letter was visible all at once. Now let's say I just don't have access to a big enough piece of paper to write the whole message, but I can send you one letter at a time. So we agree that every 10 seconds, I'll hold up a new letter. You come at the specified time and see me hold up the first letter, which you record on a piece of paper. Every 10 seconds you look back, and I'm holding a new letter up and you record it. This is serial communication. But what happens if one of us has a bad clock, and it's saying 10 seconds are up when, say, 12 seconds have passed. Eventually the mismatched timing causes you to either record the same letter twice or miss a letter completely, depending on whose clock is faster. In order to ensure the message gets through, our clocks need to be synchronized.<br />
<br />
There are synchronous methods of serial communication, including both SPI and I2C, which we'll address in the future. These methods have synchronized clocks by using the same clock for the sender and the receiver. The disadvantage is that sharing a clock means another wire. It's clear from the history that developed the UART why a clock signal was not included along with the message; instead, both the transmitter and receiver agree before hand at what rate the data will be sent. This allows sender and receiver to have their own clocks, which don't have to be synchronized in terms of when the second hand ticks, but it does require that each person's clock is accurate. Asynchronous communication simplifies the connection by not needing a second signal in parallel with the data, at the cost of needing an accurate way to time intervals between data.<br />
<br />
<span class="Apple-style-span" style="font-size: large;"><b>Receiver-Transmitter</b></span><br />
Enough history; let's look at how the UART actually transmits information. Whatever protocol we may be using, we are able to encode data as a series of 1's and 0's. We can encode a number as its binary representation, or we can encode a character as a particular binary number. In any case, we have a certain number of 1's and 0's to send. In a UART, we also add on at least two extra bits: one to designate the start of a new set of data, and one to designate the end. These start and stop bits with the data bits in between constitute what we call one "frame" of data. Using ASCII encoding, often 7 bits of data are sent. In addition, a 10th bit would be sent between the data and the stop to help determine if the data received was correct or not. If the sender and receiver agree that every frame will have an even number of 1's in it, then this "parity bit" would be 1 or 0, depending on the number of 1's in the rest of the message. The receiver could then look at the 7 data bits and parity bit, add up the number of 1's, and if the total number is even be confident that they received the right message. In 8 and 16 bit systems like a microcontroller, it could also make sense to send data in 8 bit segments instead. Often times no parity bit is included in this case, to keep the total data length of each frame to 10 bits. The compromise is that there is no way to check for errors in the transmission, but generally error checking is only necessary under particular circumstances.<br />
<br />
Let's say we want to encode the letter "D" using 7-bit ASCII encoding and odd parity. The ASCII code for the letter "D" is 0x44, or 0b1000100 in 7-bit binary. Now we face a choice: do we send the least significant bit first, or the most significant bit first? The typical protocol used in UART is what we call "little-endian", meaning we start with the least significant bit. (This makes sense when you think in terms of a shift-register; the SR in a UART pushes bits from high to low, so you send the lowest bit first.)<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBoRdp23M0rly4dUxEMKtQpm7xwSg_AMUX9ZjtEG8AkXF2WGJxq80ZM_hmVe35YQSpj_NE7-HMtDHArG6zuW3appvAKTwpBYBeADWUlIVJStThQjz9mgDCRz4Leh7AsOWGa6ySQrt-VsKS/s1600/ASCII-D.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBoRdp23M0rly4dUxEMKtQpm7xwSg_AMUX9ZjtEG8AkXF2WGJxq80ZM_hmVe35YQSpj_NE7-HMtDHArG6zuW3appvAKTwpBYBeADWUlIVJStThQjz9mgDCRz4Leh7AsOWGa6ySQrt-VsKS/s320/ASCII-D.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Representation of the ASCII character "D" in UART TTL.</td></tr>
</tbody></table>
UART uses logic high as the default (or idle) state. So to start a message, we want to change from high to low. Thus, our start bit will be a 0. Likewise, to stop we want to go back to the default state, so the stop bit is 1. So far, our total encoding is now "00010001x1", where x represents our parity bit. We want odd parity, and there are two 1's in the representation for "D", so we set this bit to 1 to ensure an odd number in the whole message. Our final message is the 10 bit stream "0001000111". (If we do this with 8-bit data and no parity, we would have "0001000101", this time the 2nd to last bit being the most significant bit in the 8-bit code 0b01000100.)<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />Hopefully this gives you a clear picture on how UART works. There are really no limitations on the protocol you use, so long as you have a start and stop bit. As long as the sender and receiver agree on what goes in the middle and at what rate the information comes, it will work. The standard protocols such as those illustrated here are convenient as it's very simple to use a computer to read the data coming from the microcontroller. In addition, keep in mind that there are standard speeds for transmitting bits (bits per second (bps) or baud), which are leftover from the old teletype days. In any case, many systems are limited to using these rates, so it's often a good idea to standardize to them. If you have your own internal system, use what ever baud rate is convenient, but do remember that a lot of computers and devices may expect one of the more conventional rates, like 300, 1200, 4800, 9600, or 115200 baud.<br />
<br />
We've identified one of the key things we'll need for a successful UART: a good clock. Next time we'll look at some options, their limitations, and how to implement them.<br />
<br />
<span class="Apple-style-span" style="color: #990000; font-size: x-small;">Reader Exercises: Using 7 bit encoding with even parity, what would the bit stream look like to send the character "j"? How about the character "k"?</span><br />
<span class="Apple-style-span" style="color: #990000; font-size: x-small;">Using 8 bit encoding with no parity, what would the bit stream look like to send the newline character, "\n"? How about the character "&"?</span>Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-8853820565211362966.post-53663029641934060302011-10-17T17:37:00.000-04:002011-10-17T17:37:36.911-04:00Tutorial 16a: Getting SerialIn order to do an actual scientific experiment using the MSP430, we need one more tool. To be fair, we could do with what we've covered so far, but it requires constant (or at least regular periodic) monitoring of the equipment, and manual recording of the data displayed on the LCM. No, what we need is a way to automatically record the data when it is taken.<br />
<br />
There are two different paths open to us at this point: the MSP430 has on-board flash memory. We could use it to record multiple measurements. The other, more complicated path is to learn how to communicate between the LaunchPad and a computer via USB. There's some elegance in starting with the former, as our focus to this point has been on the LaunchPad itself, but unfortunately we'd need a way to transfer the data from the flash memory to a useable location anyway, which more or less requires connection to a computer. So even though it will delay getting to some of the cooler things we can do with the MSP430, it's time we tackle serial communication. Once we have this piece mastered, we'll start a little science experiment that will take me a few days/weeks to complete. During that time, we'll begin looking at recording to flash, communicating with external peripherals, and how to put all the pieces together for remote data collection. We'll also start looking at alternative power systems, system control, and other great things that will completely open the field of what's possible with a microcontroller. The future looks bright; but first we'll have to tackle this difficult task.<br />
<br />
Well, things aren't really so bleak... serial communication isn't that complicated. In fact, most MSP430 devices have peripherals built in already for that very purpose, making it simple to do. However, of the two devices that come with the LaunchPad, only the G2231 has one of these peripherals, and it only has two modes of operation, conspicuously missing the one we really need first: the Universal Asynchronous Reciever/Transmitter, or UART. So, instead, we are going to turn to learning to implement this functionality in software.<br />
<br />
Fortunately, there's some real advantage to this; a solid understanding of how serial communication works helps us understand how to process and record scientific data. In fact, when we get to the USI/USCI peripherals, looking at other modes of communication such as SPI and I2C, we'll take the time to understand how these methods send data. (The particular implementation of a serial communication system is called a protocol. There are even more protocols available, including Bluetooth, Wi-Fi, and ZigBee, which are cool things we'll tackle some day!)<br />
<br />
You might be asking, "Why are we going to rehash software UART? Lots of people have published articles about it already, and lots of code and examples are available." Well, I'd respond that there are two reasons. The more philosophical reason is that you become a better scientist when you understand how the tools you're using work; Einstein once said you don't really understand anything until you can explain it to your Grandmother. The more practical reason is that none of the articles I've perused give much explanation to why the code is set up the way it is. That's our goal here: by completely dissecting the software UART, we learn how serial communication works, and get a thorough example of using the MSP430 peripherals to our advantage in getting jobs done. We'll also do a very thorough job, starting with just transmission (I guess technically it would be UAT), then moving to just reception (likewise UAR), then designing a full-on UART transceiver. Along the way, we'll talk a bit about crystals as well as learn about calibrating our DCO. We'll even talk about saving DCO calibration to the flash memory, and introduce the concept of a checksum. (So we'll see a little bit about writing to flash memory soon after all!)<br />
<br />
If that sounds like a lot to cover, it is. I'll do my best to keep the posts coming regularly and quickly, so that we can move on to more advanced ideas soon. There is motivation for approaching this topic in this way at this time, however. These tutorials have always been designed as notes from my own learning. As a result, sometimes the methods/styles have been a little disjointed, but one of the goals of this blog was to put together a curriculum that could be used to teach science students in a one-semester course on microcontrollers. (After graduation, I'll gather, edit, and format these tutorials into a book that can be downloaded for just such a purpose.) I think the material we've covered to this point fits about a one semester course very well, so think of this tutorial as the final project for the course. It's a bigger concept that will take a while, but will draw on our knowledge from the other peripherals and skills we've learned. The fact that we'll introduce some new ideas along the way will add to the sum total of knowledge taken away from this course. So strap in; we're going to start the final for MSP430 101!Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-8853820565211362966.post-49990226404505988372011-10-16T23:59:00.002-04:002011-10-17T00:00:22.996-04:00Question for ReadersWe're fast approaching the end of what I would call the 'basic tutorials', and the point where I'll move on to interfacing with the real world and other cool toys and devices. Unfortunately, that means I need some cool toys and devices. In brainstorming ways to help fund this little hobby, a friend suggested to me that I might consider using Google AdSense on this blog. If I were to do so, I would want it to be unobtrusive, as my intent is not to sell things for other people. How would you feel if I were to do this? Would I be better off thinking of another way to raise a little hobby revenue?Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-8853820565211362966.post-41126020607314479072011-10-16T23:46:00.001-04:002013-05-31T12:57:13.123-04:00Tutorial 15b: Using ADC10Like the Comparator_A+ peripheral, ADC10 has a wide range of operating modes and features. It can also integrate with other peripherals (such as Timer_A, of course), making it a very powerful tool in scientific measurements. Today we'll look at basic configuration of ADC10 in preparation to do a full scientific experiment using the MSP430. Keep in mind that this tutorial requires a device with the ADC10 peripheral, such as the G2231. The G2211 chip that comes with the LaunchPad will not work in this tutorial.<br />
<br />
First, let's examine some of the features of the ADC10 peripheral. ADC10 of course requires a clock, and can source from any of the three clocks in the MSP430 (and subsequently from a crystal, DCO, or VLO). In addition, ADC10 comes with its own internal clock that can be used independently of the system clocks. This clock is typically in the 5 MHz range, but is uncalibrated and thus varies from chip to chip, as well as with operating voltage and temperature. A major advantage to the internal oscillator is that it can remain in operation even when other clocks are powered down in an LPM.<br />
<br />
ADC 10 can connect to up to 16 different inputs. Typically, 8 of these are external inputs, 4 are internal inputs, and 4 are other references (on some devices, they are extra external inputs for a total of 12). The G2231 device has 8 external inputs (on each of the pins in P1), and also has a temperature sensor built into the chip as an internal input (in addition to the other three internal inputs, which have to do with voltage reference comparisons).<br />
<br />
Like the Comparator_A+, ADC10 requires a reference voltage for operation. In fact, it can operate with two reference voltages for the upper and lower bounds of conversion. The upper reference can be anywhere between 1.4 V and Vcc (up to 3.6 V). The lower reference can be between 0 and 1.2 V. There are two references available inside ADC10 at 1.5 V and 2.5 V, and it can also use Vcc in addition to an external reference.<br />
<br />
Finally, the ADC10 also has 4 operating modes. Two of these modes sample only a single channel. The other two modes cycle through a specified set of the 16 possible inputs. Each single/sequence type can be done only once, or repeated. <i style="color: #999999;">(Note: in a sequence mode, you must use the inputs in order. If you want to sample 3 inputs, you must use A0, A1, and A2. Unfortunately, the only way to sample arbitrary inputs is to use single channel mode and change channels in software.)</i><br />
<br />
That covers the bulk of the dizzying ways to configure the ADC10; it's a lot to sort through, so keep in mind that we're only going over it to have the different things you can do in the back of your mind. The best way to learn how to use all of the features is by example, so let's look at a simple example by modifying the capacitance meter project to a voltage meter. This meter will be restrictively useful, as it will only be able to measure voltages between 0 and 3.3 V, but it will illustrate the idea. We'll display the output on the LCD as before, but the code can easily be modified to pause in the debugger to find the result as was done at first with Comp_A+ if you don't have an LCD. For the LCD display, we'll use the single-channel mode and repeat the measurement in software, which makes it easier to use the debugger to see the result.<br />
<div style="text-align: right;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT-tjnS6yDWLH1Y0pXVsw_A91eWimTni-5kRvNwYq9t_-yZAXQMYUKrZlEPckMao7eP0uGoOz9bujIVvFmUYuBfbUeX4tayArSM1YSj3eqiR6qh3d9oH19ZKtQPRMtAhIWOreqetXu3W1h/s1600/x2xx-22-7.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT-tjnS6yDWLH1Y0pXVsw_A91eWimTni-5kRvNwYq9t_-yZAXQMYUKrZlEPckMao7eP0uGoOz9bujIVvFmUYuBfbUeX4tayArSM1YSj3eqiR6qh3d9oH19ZKtQPRMtAhIWOreqetXu3W1h/s320/x2xx-22-7.png" width="288" /></a></div>
<br />
The x2xx User's Guide gives a set of diagrams to explain the process used in each of the four modes. For example, in the repeat single-channel mode, the peripheral is turned on and enabled. The ADC10 is triggered to start a conversion, which is stored upon completion. If interrupts are being used, the flag is set, and the ADC10 returns to on of three steps, depending on just how we set it up. Keep in mind this all happens within the ADC10 module itself, leaving the MSP430 free to perform any other actions it needs to. You can use the ADC10 interrupts to do something with the code after samples are taken.<br />
<br />
Our code will instead use single-channel mode, which is very similar to the repeat single-channel mode, but without the repeat part. =) I've chosen this mode because I won't be using a low power mode, and it's easier to coordinate timing so that a sample isn't taken and finished while waiting for the LCD to update. While a new conversion is occurring, the code will trap in a loop before writing the measured sample to the LCD for display. Once that's finished, a new conversion will be started to update the measurement. <br />
<br />
There are 8 registers associated with ADC10; of these, 4 are used to configure the peripheral. One is used to store the individual samples, and three are used to control transferring the sample data for storage. (More on this later; for now we're going to keep it simple and only worry about 5 registers!)<br />
<br />
While this sounds like a lot of configuration, fortunately two of the registers are used solely for configuring the inputs. ADC10AE0 enables the ADC function of the external pins being used. (This is necessary, because P1SEL changes the operation of those pins to a function other than ADC; since it's a binary value, P1SEL can only configure two different operations. This register frees up those pins for uses other than just ADC!) ADC10AE1 performs a similar function, but only for devices with more than 8 analog inputs.<br />
<br />
We'll look at the other two configuration registers in more detail. ADC10CTL0 handles some of the base configurations of the peripheral-- the voltage references, sampling time and rate, and handling power and interrupts for the ADC. ADC10CTL1 controls the inputs, clock, mode, and data formatting. Here are the essential pieces for each register (we won't cover all of them today):<br />
ADC10CTL0<br />
<ul>
<li>SREFx (Bits 15-13): These select one of 8 different configurations for the upper and lower references for the ADC.</li>
<li>SHTx (Bits 12-11): These select 4 different sampling times for the ADC. The voltage is held constant during conversion by charging a capacitor; these control the amount of time you allow for charging. Obviously more time ensures a more accurate sample, but limits the sampling rate achievable by the device and risks having the voltage being measured change during the sampling time. You can select 4, 8, 16, or 64 clock cycles (of ADC10CLK).</li>
<li>REF2_5V, REFON (Bits 6,5): Selects between 1.5 V and 2.5 V references and turns the reference on/off.</li>
<li> ADC10ON, ENC,ADC10SC (Bits 4,1,0): Turns on the ADC, Enables Conversion, and Starts Conversion respectively.</li>
<li>ADC10IE, ADC10IFG (Bits 3,2): Interrupt enable and flag.</li>
</ul>
ADC10CTL1<br />
<ul>
<li> INCHx (Bits 15-12): In single channel mode, selects the channel to sample. In sequence mode, selects the highest channel to sample.</li>
<li>ADC10DF (Bit 9): change between straight binary data and 2's complement data.</li>
<li>SSELx, DIVx (Bits 4-3,7-5): chose the clock source and divide the clock frequency by 1-8.</li>
<li>CONSEQx (Bits 2-1): Select the sequence mode.</li>
<li>BUSY (Bit 0): a read-only flag that indicates when the ADC is in the middle of a sample/conversion cycle.</li>
</ul>
That's a very brief overview; we can't cover all of the features in detail in a reasonable introductory tutorial, so we'll examine more advanced features in the future as they come up. In the mean time, read the User's Guide and documentation to understand more of what all of these do.<br />
<br />
Last of all, we'll mention the ADC10MEM register. When conversion takes place, the value is stored and read from here. If ADC10DF is cleared (value 0), we can read this straight away: 0x00 is equivalent to the lower reference, 0x3FF is equivalent to the upper reference, and the intermediate values are a line between the two points. If ADC10DF is set (value 1), the value is stored in 2's complement. This can be useful for transferring data in some configurations, but we'll not need it today.<br />
<br />
That does it for a brief (but long!) summary of the basics. Let's look at the simple volt meter now. The code for this project can be seen in <a href="https://sites.google.com/site/mspscifiles/tutorials/VMeterG2231.c?attredirects=0&d=1">VMeterG2231.c</a>. It will require the simple_LCM library from the <a href="http://mspsci.blogspot.com/2011/09/tutorial-14b-adding-new-library.html">previous tutorial</a>. There's very little that's new here, and the code should be clear by itself. It uses input A1 on P1.1. You can test the code by using a potentiometer connected between Vcc and ground and connecting the wiper to P1.1. When you turn the potentiometer, you should see the corresponding value change on the LCD.<br />
<br />
So much for the basics; next time we'll look at how to store the data for later analysis and start working on an actual experiment.<br />
<br />
<span style="color: #990000; font-size: x-small;">Reader Exercise: How can you use this same code to measure a larger voltage range? Hint: a simple way to do it uses only two passive components. A trickier task is to be able to measure positive and negative voltages; can you think of a way to do this even though the MSP430 can't use a negative voltage reference? Hint: an op amp might help.</span>Unknownnoreply@blogger.com10