Tuesday, August 7, 2012

Checking out the new Cortex-M4 ARM core

I've been using the Luminary Micro Cortex-M3 core for awhile now. It's pretty cool, and has some nice power to it for when you're running more advanced stacks. I also started developing on TI's new Cortex-M4F Stellaris processors. The benefits of the new processors:
* Faster - standard up to 80MHz (though single-cycle flash only up to 40MHz)
* Floating Point processing
* Same or lower cost than the comparable Cortex-M3 processor
* Lower power - uses 65nm process instead of the 250nm process used on the M3 line

The best part of Stellaris (both old and new) isn't the chip, it's the software. StellarisWare is a hardware abstraction library for these processors and makes firmware development MUCH faster. It is pretty comprehensive (very few things you can't do in stellarisWare) and reasonably well documented.

The downsides of the Cortex-M4F:
* No Ethernet support
* Weird low power mode - "hibernation" but it doesn't keep the RAM alive, so you have to save your application variables
* Higher power consumption than MSP430.

Monday, November 21, 2011

Wireless Module vs. Custom Implementation

Interested in adding wireless functionality to your project?

There's two main ways of doing this - with a premanufactured Module or a custom implementation. I've used both in the past and right now I'm working on a project right now where I need to use both approaches. There's a lot of hype out there with people arguing that one approach is better than the other but I thought it would be good to record exactly what I encounter with both approaches on this project. The technology used for both of these implementations is nearly the same which makes for a good comparison and both required internal antennas.

Custom Radio Implementation

Design

  • Figure out best antenna for the application: this depends on required performance as well as the enclosure used for the product.
  • Design matching circuit: While most RFICs and antennas have a 50 ohm impedance, some may differ and a small matching circuit is required. This needs to be done by an RF engineer. For most applications the RFIC manufacturer provides a reference design. For some reason that eludes me these always contain rare components that are a pain to source, and the reference design exhorts you not to change anything or else it will screw up your performance
  • Layout the PCB with the RFIC, antenna, and matching circuit. Most EDA packages have an option for automatically setting the width of the trace based on the desired impedance, as long as the PCBs are manufactured consistently each time. Heh.
  • It's a good idea to get the RF circuit reviewed by someone else, to help prevent stupid errors. And because it's "black magic" to most of us. This can be done by the RFIC manufacturer or the antenna manufacturer. Antenova (Antenna vendor) has a nice service whereby you give them some money and they design the matching circuit for you.
  • Firmware is lots of fun (sarcasm here) because you get to have to test that any change to your application may adversely affect the network stack. For simpler protocols this may not be much of an issue but if you're using more complex protocols (Zigbee, WirelessHART, etc.) this can get extremely time consuming.
 So now the PCB has been designed. What usually happens is that you go and fabricate the first few PCBs and find that performance doesn't match the reference board, so a few rounds of fixing is required. Next is manufacturing.

Manufacturing
  • Usually the reference designs use a 4-layer PCB, to get a good ground plane for RF performance. And as I mentioned above, life is hell if you try to modify the reference design, so you're pretty much stuck.
  • Impedance Control - Life is sweet as long as the PCBs are manufactured consistently. However, that's never the case due to all kinds of process variations in the PCB manufacturing process. So this is that little checkbox you see when you get PCB quotes that makes everything more expensive, because the PCB manufacturer has to test and verify the characteristic impedance of each PCB panel, and if it's out of spec then they have to toss them out.
  • Source exotic RF components - as I mentioned previously, the reference designs often use components in the RF signal change that are extremely difficult to find. This becomes oh so much fun when you start production, as the CM will either complain or tell you that your lead time is very long (20+ weeks).
  • Design RF test fixture - Rarely if ever will a variation in the electronics manufacturing process produce better RF performance, almost always RF performance will suffer if something wasn't assembled correctly. So, you'd better test each device for RF performance. Fairly straightforward if the device has a connector but if it has an internal antenna then much thought needs to go into the RF test fixture as it needs to be shielded to prevent interfering with other devices under test. And you'd better design a system to record the RF performance of each device being tested, too, for SPC.
  • It's a minor issue, but if you're doing a cost analysis be sure that you look at what the cost of PCBs, manufacturing, components etc. is based on your actual volume. The module companies get components in 1M quantity discounts whereas you or I are stuck with whatever our actual volume is, usually lower. This can be an ugly surprise and make a custom radio implementation much more expensive down the road.
Certification
This is fairly straightforward if you're only doing FCC but can get expensive if you're targeting multiple countries. For FCC certification you should create a special version of your firmware that controls the radio so that their life is easier. I was working on one product that chirped out a small (20mSec) message every few minutes. That drove the test house crazy because it was very difficult to measure radiated emissions. So we modified the firmware to continuously send RF data. And there's nothing that firmware guys love to do more than stop writing code for The Next Best Thing so that they can create a custom spin of firmware just for RF testing. (sarcasm here). I've found that test houses always need to be "babysat" with someone from your company while the test is happening, that way it's easier to ensure that things are being done correctly, and that you can fix any problem that may arise. Getting FCC certification for a simple RF product usually costs around $9k in direct expenses (e.g. test house fees, FCC fees, etc.) and another $10k in indirect expenses (paperwork, creating test firmware, babysitting the test house etc.).

Module Implementation

Design

  • Figure out best module for the application: this depends on which protocol you're using and even more importantly, how easy it will be to integrate with the rest of the application. Examples are key here, as I'd rather take 10 good examples than 100 pages of specifications.
  • Writing the firmware is an order of magnitude easier with a module, because changes to your application can't break the network stack since the network stack is running on its own processor inside the module. Also makes for less code to maintain in my experience. (20kB vs. 240kB)
  • Since the module has all the RF stuff on it (RFIC, antenna, matching circuit, etc.) you just need to ensure that you pay attention to any ground plane requirements for the module. 
Manufacturing
  • With a module your application will drive the PCB requirements, whereas with a custom radio implementation the RF section will limit your PCB options. So, for simple applications you can get by with a 2-layer non-impedance controlled PCB. Much cheaper and faster to prototype too.
  • You will need to ensure that you can source the module as needed. It's best to find a module that is carried by a few distributors so if one is out of stock then you can get it from someone else.
Certification
If you are using a pre-certified module then certification requirements are much less. Here's a good article about it from Digi (I don't like their modules but they have good information):
http://www.digi.com/technology/rf-tips/2007/11

Conclusion - Which is the best approach?
The answer probably comes as no surprise - it depends on a number of factors.

Cost
For low volume products (less than 10k per year) the module solution is almost always less expensive due to the high up-front costs of developing a custom radio solution. For higher volume products then you'll need to do a valid cost comparison. If it is unsure whether the product will become high volume then I recommend the module approach, as it will be much faster and less expensive up-front. Then later if it turns out that the product line is indeed high volume then you can do a cost comparison. However, if you're going high volume then oftentimes the module manufacturers will work with you on pricing so that it becomes a wash whether to continue using a module vs. re-engineering a custom solution.

Expertise
If your company lacks RF experience then you're probably better off going with the module approach, as it will be much easier than trying to learn RF. Most companies that choose the custom radio approach will end up outsourcing most of the RF work but your company will still need to learn about RF so that you can solve the inevitable manufacturing issues.

Time to Market
One aspect of time-to-market that oftentimes gets overlooked when comparing a modular approach with a custom radio is firmware development time. If the module comes with lots of relevant examples then life is good since you will be able to get up and going quickly and you won't have to learn the intricacies of the network stack. If you're developing a custom radio solution then you'll get to learn the network stack in-depth, which will obviously take much longer.

Friday, August 5, 2011

Reading the supply voltage using the MSP430's internal Analog to Digital Converter

Many times you need to measure the supply voltage (Vdd) of the MSP430. The MSP430 has an ADC input channel which is connected to Vdd/2. The divide by two is required because the MSP430's internal reference is 2.5V, so Vdd must be divided by two for full resolution.

Two simple routines for reading the supply voltage of the MSP430 family are shown below. Some MSP430 devices use the 10-bit ADC, e.g. the MSP430F2274, and some use the 12-bit ADC, e.g. the MSP430F248. Refer to the datasheet for your part to determine which is used. 

Using the ADC10:
/** Reads the MSP430 supply voltage using the Analog to Digital Converter (ADC).
On ez430 boards, this is approx. 3600mV
@return Vcc supply voltage, in millivolts
*/
unsigned int getVcc3()
{
ADC10CTL0 = SREF_1 + REFON + REF2_5V + ADC10ON + ADC10SHT_3;  // use internal ref, turn on 2.5V ref, set samp time = 64 cycles
ADC10CTL1 = INCH_11;                        
delayMs(1);                                     // Allow internal reference to stabilize
ADC10CTL0 |= ENC + ADC10SC;                     // Enable conversions
while (!(ADC10CTL0 & ADC10IFG));                // Conversion done?
unsigned long temp = (ADC10MEM * 5000l);        // Convert raw ADC value to millivolts
return ((unsigned int) (temp / 1024l));
}

Using the ADC12:
/** Private helper method to setup ADC for one-shot conversion and read out value according to registers.
Inserts a delay before beginning conversion if REFON
@return the raw ADC value with the specified commands.
@todo move the VREF warmup to startup and leave on to avoid 17mSec blocking delay each time?
*/
unsigned int getAnalogInput(unsigned int adc12ctl0, unsigned int adc12ctl1, unsigned char adc12mctl0)
{
#define ADC_VREF_DELAY_MS 17   
ADC12CTL0 = adc12ctl0;
ADC12CTL1 = adc12ctl1;
ADC12MCTL0 = adc12mctl0;
if (adc12ctl0 & REFON)                    // if internal reference is used...
delayMs(ADC_VREF_DELAY_MS);           // 17mSec delay required to Vref capacitors
ADC12CTL0 |= ENC;                         // Enable conversions
ADC12CTL0 |= ADC12SC;                     // Start conversions
while (!(ADC12IFG & 0x01));               // Conversion done?
return ADC12MEM0;    // Read out 1st ADC value
}


/** Measures Vcc to the MSP430, nominally 3.3V
- ADC measures VCC/2 compared to 2.5V reference
- If Vcc = 3.3V, ADC output should be (1.65/2.5)*4095 = 2703
- Therefore (halfVcc/2.5)*4095 = ADC reading and (Vcc/2.5)*4095 = 2*ADC
- So Vcc*4096 = 5*ADC and VCC=5*ADC/4095

@return Vcc in millivolts
*/
unsigned int getVcc3()
{
unsigned int ctl0 = REFON + REF2_5V + ADC12ON + SHT0_15;  // turn on 2.5V ref, set samp time=1024 cycles
unsigned int ctl1 = SHP;                                  // Use sampling timer, internal ADC12OSC
unsigned char mctl0 = SREF_1 + INCH_11;                   // Channel A10, Vcc/2
unsigned long vcc = (unsigned long) getAnalogInput(ctl0, ctl1, mctl0);
unsigned long mult = vcc * 5000l;
return ((unsigned int)(mult / 4096l));
}

Saturday, March 12, 2011

Making Development Boards That Don't Suck

Lately I've had to use a few microcontroller development kits from manufacturers that, well, suck. From unlabeled connectors/components to not bringing out the unused pins, I'm convinced that many designers of development boards never actually use them to design anything.

When I'm trying out a new design, I frequently make my own development board. By "development board" I mean a schematic and layout that is the same circuit as the end product but optimized for firmware development and debugging. This allows a few things:
  • Firmware development can start sooner - this is probably the most important since FW typically takes an order of magnitude longer than HW design
  • Verify PCB footprints for components - you can spend a month checking and double checking a PCB footprint, or just build it and verify it on a real PCB in a week.
  • Power Measurement
  • Fast layout. Since the point of a development board is to optimize for development not layout, you can make things a little larger and not worry about squeezing everything in.
  • Related to the previous, a development board helps you get a feel for how small you can make the design once everything is working.

Here are the a few rules for how to design development boards that don't suck:
  • Expose all the pins of the components on test points or at least SMT pads. Soldering wires to fine pitch QFNs is a pain in the ass, and forget trying to do anything with BGAs. I recently helped out a major IC manufacturer and soldered some of their dev kits together because they didn't expose the unused pins.
  • Include a serial port (RS-232 is my preference).
  • Related to the above, bring out all signals that you can on easy 0.100" headers. This makes development much easier since you can attach logic analyzers, scopes, etc. Label them all with their signal name (more on this below).
  • Label liberally. Silkscreen is free, so on a development board label all components, as well as the signals on 0.100 headers, etc. Taking 5 minutes to label the signals on the PCB is faster than taking 5 minutes every time to look it up in documentation.
  • If it is a RF design with an onboard antenna, make an option for a connector. Can be SMA, U.FL or whatever, but something that you can connect to directly. I like to do this with a component in the signal chain that can be populated one way for the onboard antenna, or rotated 90 degrees to go to the connector instead.
  • Include a power connector and LDO to allow powering from a wall-wart. Makes it much easier to hand over to the firmware engineer and reduces unnecessary thought.
  • Enable power measurement: add a 0.100" header between LDO output and power input. This will have a shunt that can be removed and an ammeter attached instead. For extra credit, do this for the major power subsystems of the circuit.
  • Use full-sized JTAG connectors, and used shrouded (polarized) headers to prevent having to think about it.

Also, if this will be a commercially available development board (not just an intermediate step towards a final design) then there are a few more recommendations:
  • Use larger packages to make it easy to assemble and modify. I like to use 0805s for all the non-RF passives, and use the largest package I can get for the micro. And label everything!
  • If the board has onboard peripherals (LEDs, buttons, etc.) then make them able to be disabled (disableable?). My favorite way to do this is with "Cuttable Jumpers", basically an 0805 footprint with a trace connecting the two pads. That way a user can cut the trace to disable the peripheral but can also re-enable it by soldering on a zero-ohm resistor across the pads.
  • Be sure that you include firmware drivers for all the peripherals. You have to develop them to test the board anyway, might as well provide them.
The best development boards I've seen (besides the ones that I've designed of course) are the Stellaris development boards from Texas Instruments. Sadly, that company also makes some of the worst development boards too.

Thursday, June 3, 2010

Calibrating the MSP430 Very Low Power Oscillator

The MSP430F2xxx contains an internal 12kHz very low power low frequency oscillator (VLO). The frequency varies by part, temperature, and supply voltage. On an MSP430F248 the VLO runs at approx. 9.4kHz and on an MSP430F2274 it's approx. 12kHz.
On the MSP430F2274 datasheet (page 38) the base frequency varies between 4kHz-20kHz and has a temp drift of 0.5%/degC and a supply voltage drift of 4%/V.

So in order to make it useful we need to calibrate it. The method below will calibrate the VLO against the main oscillator. The accuracy of this calibration routine will only be as accurate as the main oscillator. If you're using one of the calibrated DCO frequencies (+/- 1%) then the end result will be the VLO within about 2% of actual. This will be more accurate if the main oscillator is sourced from a crystal.

This calibration routine uses Timer A in the capture mode to capture the number of main clock cycles between subsequent ACLK cycles. This routine only counts one pulse, for more consistency you may want to count multiple ACLK cycles and average them.


/** Calibrate VLO. Once this is done, the VLO can be used semi-accurately for timers etc.
Once calibrated, VLO is within ~2% of actual when using a 1% calibrated DCO frequency and temperature and supply voltage remain unchanged.
@return VLO frequency (number of VLO counts in 1sec)
@pre SMCLK is 4MHz
@pre MCLK is 8MHz
@pre ACLK sourced by VLO (BCSCTL3 = LFXT1S_2; in MSP430F2xxx)
@note Calibration is only as good as MCLK source. Obviously, if using the internal DCO (+/- 1%) then this value will only be as good as +/- 1%. YMMV.
@note On MSP430F248 or MSP430F22x2 or MSP430F22x4, must use TACCR2. On MSP430F20x2, must use TACCR0.
Check device-specific datasheet to see which module block has ACLK as a compare input.
For example, see page 23 of the MSP430F24x datasheet or page 17 of the MSP430F20x2 datasheet, or page 18 of the MSP430F22x4 datasheet.
@note If application will require accuracy over change in temperature or supply voltage, recommend calibrating VLO more often.
@post Timer A settings changed
@post ACLK divide by 8 bit cleared
*/
unsigned int calibrateVlo()
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
delayMs(1000);

BCSCTL1 |= DIVA_3; // Divide ACLK by 8
TACCTL2 = CM_1 + CCIS_1 + CAP; // Capture on ACLK
TACTL = TASSEL_2 + MC_2 + TACLR; // Start TA, SMCLK(DCO), Continuous
while ((TACCTL0 & CCIFG) == 0); // Wait until capture

TACCR2 = 0; // Ignore first capture
TACCTL2 &= ~CCIFG; // Clear CCIFG

while ((TACCTL2 & CCIFG) == 0); // Wait for next capture
unsigned int firstCapture = TACCR2; // Save first capture
TACCTL2 &= ~CCIFG; // Clear CCIFG

while ((TACCTL2 & CCIFG) ==0); // Wait for next capture

unsigned long counts = (TACCR2 - firstCapture); // # of VLO clocks in 8Mhz
BCSCTL1 &= ~DIVA_3; // Clear ACLK/8 settings

vloFrequency = ((unsigned int) (32000000l / counts));
return vloFrequency;
}

Tuesday, September 15, 2009

Using Cyclical Redundancy Codes (CRC-16, CRC-32 etc.) on microcontrollers

Oftentimes it is useful to get a checksum or hash of the data, either for verifying there are no errors, or for determining whether something has changed. A Cyclical Redundancy Code (CRC-16, CRC-32 etc.) is commonly used. It is not a cryptographically secure hash (like SHA-1 or the now-obsolete MD-5) but it is useful for checksums and is a little better than a simple checksum or parity bit.

There are two ways of implementing a CRC algorithm - one that uses a table, one that does not. The table is stored in the nonvolatile memory of the microcontroller and makes computing the CRC a little faster. First I'll show the table method, then the table-less method.

3. CRC-16 With Table
To begin with, you need the table:
unsigned int const crc16table[256] = { 
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, 
};

Next, you simply run the following function over the data you want to check.
unsigned int computeCrc16(unsigned char* start, unsigned int length)
{
  unsigned int crc = 0;
  unsigned char* bytePtr;
  bytePtr = start;
  for (unsigned int i =0; i> 8) ^ crc16table[(crc ^ *bytePtr) & 0xff];
    bytePtr++;
  }
  return crc;
}
Finally, a simple example:
unsigned char test[] = {0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};
printf("CRC-16=%02X\r\n", computeCrc16(test, 9));
This should give a result of 0xBB3D.

3. CRC-32 With Table
This is very similar for CRC-32 but the numbers just get a little larger:

unsigned long const crc32table[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, };

Now, the function:
unsigned long computeCrc32(unsigned char* start, unsigned int length)

{ unsigned long crc = 0xffffffff; unsigned char* bytePtr; bytePtr = start; for (unsigned int i =0; i { crc = (crc >> 8) ^ crc32table[(crc ^ *bytePtr) & 0xff]; bytePtr++; } crc = crc ^ 0xffffffff; //flip bits return crc; }
Finally, a simple example:

unsigned char test[] = {0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39}; printf("CRC-32=%lX \r\n", computeCrc32(test, 9));
This should give a result of 0xCBF43926.

3. CRC-16 (CRC-CCITT) Without Table
The table-less version is very similar, but crunches through the math without the benefit of the table. Running this on {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39} results in 0x31C3.
Here's the code (remember, there is no table):
unsigned int crcCCITT;

void initCrcCcitt(void)
{
  crcCCITT = 0x0000;
}

void processCrcCcitt(unsigned char x)
{
unsigned crc_new = (unsigned char)(crcCCITT >> 8) | (crcCCITT << 8);
crc_new ^= x;
crc_new ^= (unsigned char)(crc_new & 0xff) >> 4;
crc_new ^= crc_new << 12;
crc_new ^= (crc_new & 0xff) << 5;
crcCCITT = crc_new;
}

unsigned int getCrcCcitt(void)
{
return crcCCITT;
}

And here's a simple example:

  initCrcCcitt();
  for (int i=0; i<9; i++)
      processCrcCcitt(test[i]);
  printf("%04X\r\n", getCrcCcitt());
This should output 0x31C3.

Using Quicksort (Qsort) on microcontrollers

Need to sort an array of numbers? I needed to do this on a microcontroller and Quicksort did the trick, very easily. Here's how:

1. Need to include stdlib:
#include //for qsort
2. Need to make a comparison function. For 2-byte integers it's pretty easy:
int comp(const void * a, const void * b)
{
int* aa = (int*) a;
int* bb = (int*) b;
if (*aa==*bb)
return 0;
else
if (*aa < *bb)
return -1;
else
return 1;
}
3. The arguments for qsort are:
a) the array to sort
b) The number of elements to sort (starting from the element at index==0 in the array)
c) The size (in bytes) of what you are sorting
d) the comparison function you wish to use.
See the following example:
void testSort()
{
int numbers[]={1892,45,200,-98,-4,5,-123,107,88,-1000};
printf("Before sorting: ");
for (int i=0;i<9;i++)
printf(" %d ",numbers[ i ]) ;
qsort(numbers,10,sizeof(int),comp) ;
printf("\r\nAfter sorting: ");
for (int i=0;i<9;i++)
printf(" %d ",numbers[ i ]) ;
printf("\r\n");
}
For more information, see http://cplus.about.com/od/learningc/ss/pointers2_8.htm

Saturday, June 13, 2009

Version Control for Dummies

Version Control is one of those things that once you have it you wondered how you ever lived without it. A version control system (like CVS, Subversion, Perforce, etc.) runs on a server and holds all the versions of the files you put into it.

Daily Development with version control works like this:
First, you do an update of the files you're working on. This will get the latest version of each file from the server and copy it onto your local hard drive. Obviously, if you're the only developer you probably won't need to do it very often.
Then, when you come to a good stopping point (usually when you get something working or make a substantial change) you check-in the files to the server.
Now, let's say you're working on something and you screw something up, can't get the code to compile, etc. With a version control system in place, you can just revert the file back to the last good version.

I put all my firmware and software into the version control system and many of my PCB design files too. Oftentimes I'll put in other documents too if I'm working with multiple developers.

I use a hosted SVN solution from http://svnrepository.com/ - they have plans starting at $3.95 per month, and they're very good. For client side, I use RapidSVN on the mac which isn't very good but I haven't found anything better yet, or TortoiseSVN in Windows which is a very nice shell extension.

Electrical vs. Mechanical

This week I delivered five electrical prototypes to a world renowned industrial designer. Here's a few lessons I learned:

Get the mechanicals as soon as you can. I have yet to see a product where the first mechanical and electrical prototypes fit together perfectly. Have the mechanical guy there when you first assemble them together to discuss any problems right away.

Mechanical guys like datasheets but they love datasheets plus samples. Give them samples of everything, particularly switches, connectors, displays, light pipes, etc.

Better yet, have the mechanical guy pick out the switches, connectors, displays, etc. based on the requirements you give them.

Good things to have on-hand for modifications to the enclosures: Dremel, hot-glue gun, and strong double-sided tape.

For tight designs, some of the higher-end PCB layout programs (Altium, for example) will produce a 3D model of the PCB from the layout. This is very useful, and then creates a 3D space contract between electrical and mechanical. But be sure that your heights are all correct! Even better, have someone else double-check your design.

When determining how much space you'll need, don't forget to take into account space for wiring etc.

For a prototype, try to get as much space as you can. There will always be surprises.

For components that are very tall, try to find lower height components, even just as a backup. One of the prototypes I just delivered had a speaker that was about 2" in diameter and 1" tall. The mechanical guy screwed up and the space for it was nowhere near large enough, a good 0.2" too short. But, I found a couple other speakers that were similar but thinner, and I swapped one of those in and looked like the hero.

Speaking of Heroes and Villains, at some point both you (the electrical guy) and the mechanical guy will screw up something, so don't be too hard on each other. Be nice when they screw up and chances are they'll be nice to you when you screw up.

Spare, Spares, Spares: if you're working on a product for a demo, get extra enclosures and make extras of everything. On one of my prototypes a stupid little $0.20 battery holder prevented the a system of 3 devices from working. Of course it worked fine in the lab and failed when I got over to the client. Unfortunately I didn't have any spares so I had to troubleshoot it and fix it there on the spot. Doh!

Tuesday, June 9, 2009

Bringing up Baby (or at least a new PCB)

A few words of advice when bringing up a new board.

Idiot Check - I first take a blank PCB and do continuity between Vcc and Gnd as a simple idiot-check. You never know if DRC or ERC missed something, or the PCB vendor screwed up somewhere.

Start with the micro - Next, I put down the bare minimum required to get the microcontroller up and running - usually just the micro, crystal, and JTAG port.

Work outward - Then work your way outward from there, adding peripherals, testing them as you go along. When you power up the board, keep an eye on current consumption - if it inexplicably spikes then you may have a problem.

This being said, if I'm working on a new circuit where I'm not sure if part of the circuit is correct, I'll just populate that part and leave the rest of the PCB unpopulated. For example, a recent project had a DAC feeding an audio output to a speaker; something that I haven't done much before. I populated the audio filter and PA and tested that before I put down anything else.

The art of the prototype

Here's a few things I've learned to make prototype hardware. This mainly applies to low-speed (<100mhz)>
  • Use a processor with more Flash and RAM than you think you need. It's always easier to downgrade later than upgrade now.
  • Give yourself a debug serial port. It doesn't have to be much, and you could bit-bang it if need be, but this will make your life much easier. For complicated designs where I know I'll need to rely on it I'll go ahead and put an RS-232 level converter IC and DB-9 connector to make life easier. If I'm not sure whether or not I'll need it then I'll just leave it as a header with Tx,Rx,Gnd, and Vcc.
  • Don't route traces under ICs, if you can. I learned that the hard way recently. Why? because if you need to cut/jump that trace you now need to remove the IC first. I use both, but through-hole parts (in particular resistors and caps) tend to be more flexible than surface-mount. I like to use through-hole parts for power resistors or large (>100uF) decoupling caps because it makes it much easier to swap parts.
  • Use zero-ohm resistors liberally to give yourself options. On one board I made, I wasn't sure if I would need to power the audio amp from 3V or 6V. So, I put a zero-ohm resistor tying the audio power rail to both 3V and 6V rails. Of course I only populated one of them, but it allows you to change things easily.
  • Give yourself space. If possible, don't bunch components tightly together. The one that's in the middle of them all will be the one you need to replace. Also, I try to use the larger SMT components (0805+) to make it easier to rework.
  • Use "full-size" JTAG connectors, preferably polarized. Make life easy on yourself and prevent mistakes. Nothing's worse than having to replace your processor IC because the firmware guy reversed the JTAG connector, frying it.
  • Use the silkscreen to label liberally. Every connector/switch, etc should be labeled. A little thinking now can save a lot of time later on.
  • Bring out unused pins to headers. I like to use 0.100" single-row headers because it makes it easy to route. On each header be sure to put Vcc & Gnd just in case.
  • Idiot Lights - give yourself a power LED and also connect one LED to an output pin on your micro. Makes life a lot easier when troubleshooting.
  • DIP Switches - if you have space, put on 2 or 4 DIP switches. Very handy for when you have customers who can't make up their mind. I write the firmware to do a couple different things and then I can change things on the fly easily.
  • If you are working on a power-critical project, then run each subsection through a zer0-ohm resistor so you can do a good power analysis. There's nothing worse than populating a board, putting the micro to sleep, and then finding out that the PCB is still consuming a huge amount of current.
  • Use decoupling capacitors liberally. I use a ton of 0.1uF caps, putting them near Vcc pins on ICs. A few designs I've worked on recently controlled very power-hungry devices (high brightness LEDs, speakers) and I used a few large electrolytics.
  • Get extra PCBs made. They're very handy to test out different parts of the circuit, or even just to look at as a visual reference.
  • Speaking of PCBs, if your design is very simple and you're only making 1-2 then you might be able to use a development board and just connect everything point-to-point by soldering wires around. If your design is not trivial or you have to make more than 1-2 of them then by all means make a PCB. They're really inexpensive nowadays.
  • And finally, connectorize. I don't like to have lots of cables soldered directly to the PCB; if possible use connectors instead. They don't have to be fancy; I use a lot of simple 0.100" headers and mating connectors because they're easy to work with.

Thursday, June 4, 2009

The Joy of Bit-Banging, part 1: TX

Need a good bit-bang?

Sometimes you need one more UART than what your microcontroller has. In that case you need to bit-bang it out. "Bit-banging" sounds much more vulgar than it actually is - it just means that instead of using the built-in shift peripheral, you are manually turning on/off the bits. For this example I could implement it using a blocking method, so I just delay the microcontroller inbetween bits. A better implementation would be to use a timer to generate the bit timings.

The most common UART data format is 8N1: 8 start bits, no parity bits, one stop bit. This only tells part of the story. Actually there are a total of 10 bits: (see wikipedia)
  • One start bit (a "1")
  • Eight data bits
  • One stop bit (a "0")
For 9600 baud, bit spacing is 104uSec. To perfect the bit spacing I first just twiddled a bit on and off with the delay loop inbetween until I got exactly 104uSec.

On the hardware side, I ran the output of this through a simple level converter and then into an RS-232 port on a PC to verify that everything was working ok. One level shifter I like is available from sparkfun.com here.

/**
* Bit-Bang UART transmit
* Transmits one byte out the specified pin
* Baud rate is 9600 (104uSec bit timing)
* 8-N-1: 8 data bits, no parity, 1 stop bit
*
* Line is nominally at '1' (high)
* 1 Start bit = '0'
* Data, MSB first, LSB last. '1' = line high, '0' = line low
* 1 Stop bit = '1' (idle, or high)
*
* PRECONDITION: LINE IS high (idle) and port/bit configured for output!
*/

#define BIT_BANG_TX_PORT P4OUT
#define BIT_BANG_TX_BIT BIT6
#define BIT_LENGTH_9600 160
void bitBangOutput(unsigned char byte)
{
//start bit - pull line down
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT;
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

//LSB
if (byte & BIT0)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT1)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT2)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT3)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT4)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT5)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT6)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

//MSB
if (byte & BIT7)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

//Stop bit
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //let line go high
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay
}

For receive, see The Joy of Bit-Banging, part 2: RX

Wednesday, June 3, 2009

PWM on a microcontroller, the Hard Way

On a project I had to implement nine PWM outputs for an LED dimming application. Up to eight is easy on an MSP430 - use TimerB outputs TB 1..6 and TimerA outputs TA1,2. But I needed nine. So, I had to do this the hard way, in code. Here's how I did it.

This example "fades up" an LED:
unsigned int onTime = 0; //periodsPerStep;
unsigned int offTime = 0; //PWM_PERIOD - onTime;

#define PWM_PERIOD 0xFF //period for PWM wavelength - 0xFF = 8kHz or so
#define CROSSFADE_STEPS 0xFF //how many intervals of intensity. 16 looks choppy
#define PERIODS_PER_STEP 0x3F//0x7F //how much time to wait at each intensity interval: 0xFF = 6 secs

unsigned int perStepInterval = PWM_PERIOD / CROSSFADE_STEPS;

for (int step = 0; step < CROSSFADE_STEPS; step++)
{
onTime = perStepInterval * step;
offTime = PWM_PERIOD - onTime;

//PWM periods:
for (int periodCounter = 0; periodCounter < PERIODS_PER_STEP; periodCounter++)
{
P1OUT = 0x01;
for (unsigned int counter = 0; counter < onTime; counter++) ; //on interval

P1OUT = 0x00;
for (unsigned int counter = 0; counter < offTime; counter++) ; //off interval
}
}

White Noise on a Microcontroller

I recently finished a project where I had to make a white noise generator using a microcontroller. It was a lot harder than I thought it would be. What didn't work: wavetable with 100-1000 random numbers. The human ear can detect aural patterns, and at an 8kHz sample rate, the 1000 number pattern repeats itself about 8 times per second which is noticeable. What does work: a Linear Feedback Shift Register. (Wikipedia) Using a 16 bit LFSR you can get a 16*16 = 65535 pseudorandom number sequence. In my tests the pattern could not be detected, which is perfect.

I implemented this on an MSP430F2618 using its internal DAC and the internal timer; though an MSP430F169 could work too. It uses TimerA0 to generate a constant period. When the timer interrupt occurs, the LFSR generates a new value and sends it out via the DAC.

Setting up the DAC:
void setupDac()
{
ADC12CTL0 = REF2_5V + REFON; // Internal 2.5V ref on
// Delay is 17mSec for caps to charge, [from code] 13600 at 1MHz, or 0x1A900 at 8MHz
// At 8000 cycles/Msec, need 0x21340 clock cycles to get 17mSec
// for safety, we'll delay a little longer, or 0x22000 clock cycles
#define SEVENTEEN_MS_AT_8MHZ 0x22000
for (long j = SEVENTEEN_MS_AT_8MHZ; j; j--); // Delay for needed ref start-up.
DAC12_1CTL = DAC12IR + DAC12AMP_5 + DAC12ENC + DAC12OPS; // Int ref gain 1, DAC12OPS = output select for DAC12_1 on P6.5
}
Setting up TimerA0
void setupTimerA()
{
TACCTL0 = CCIE; // TACCR0 interrupt enabled
TACCR0 = 200; //starting interval, gets changed at first interrupt
TACTL = TASSEL_2 + MC_2; // SMCLK, up mode
}
And finally, the TimerA0 interrupt service routine (ISR):
/*
* White Noise Generator
* Galois Linear Feedback Shift Register implementation
*/
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
TACCR0 += 0x3FF; // Timer interval, Add Offset to TACCR0
lsb = lfsr & 1; //Get lsb (i.e., the output bit).
lfsr >>= 1; //Shift register
if(lsb == 1) //Only apply toggle mask if output bit is 1.
lfsr ^= 0xB400u; //apply toggle mask, value has 1 at bits corresponding to taps, 0 else where.
DAC12_1DAT = lfsr;
}
That's it. In your main code, be sure that you enable interrupts, or else it won't work.

Saturday, May 30, 2009

Sound on the MSP430

Started playing with sound on the MSP430. I'm using an MSP430F2618 because it has an internal DAC, although a '169 or using an external DAC would work too. A couple of notes:
  • Output of DAC feeds a Sallen-Key filter which then drives a TI TPA721 300mW amplifier which drives a little toy speaker
  • White noise is harder than it sounds. After several tries, I ended up using a Linear Feedback Shift Register, specifically a Galois LFSR. See WikiPedia.
  • For sine waves, I made a wave table with 72 points (every 5 degrees from 0 to 355 degrees)
  • To get volume levels I ended up making ten wave tables, one for each 10% volume level. You could of course compute this on the fly but it took too many clock cycles for my implementation.
  • To prevent "zippering" at the start of the code I compute intermediate wave tables (5%, 15%, etc)
  • Learned the hard way that the "rail to rail" op amp ain't. Needed to recompute the wave tables with an offset to keep the bottom of the DAC output above the ~100mV floor of the op-amp.