I have learnt thing or two after developing applications with a number of microcontrollers. The following points mention information that I wish I knew before spending ages to figure out why something was not working.
- Investing time in setting up a debugger - it is worth-while. The cycle of compile-flash-test to test code squanders time, particularly if you programmer is slow or your firmware is large.
- It is always a good idea to have at least one LED that your micro can toggle. This is usually the first step I perform when checking the hardware of the device I've just created - can it execute code to make it flash an LED? Flashing LEDs can only convey so much information. I suggest to allocate a UART port for printing out debug messages. Ideally this should not be dedicated to debugging.
- I suggest that you disable global interrupts around the initial setup code. This is important, particularly if your micro is connected to a sensor that is always generating interrupts. I've come across a situation when the code locked up the microcontroller due to a race condition. Because an interrupt was being received, but the code to setup the interrupt handler had not yet been executed.
- Develop the application with the architecture of the CPU in mind. If you are doing 32-bit math on an 8-bit processor, bear in mind the single line of code that just does an add is not going to be an atomic operation. Appropriate care needs to be taken.
- Unless you absolutely have to, don't develop with newly released or next generation chips. Odds are you will be helping the manufacturer fill out their errata sheets!
- Linux file devices are buffered, i.e., /dev/ttyUSB0 will only send its contents when it receives a new line character. This is also true the other way - it will only send a string to your program after receiving a new line char. Either append a '\n' to your string that you send, or change the settings of the computer's serial port.
- Typically I2C slave addresses are 7-bit, and the LSB is the read/write bit. Pay attention with the I2C libraries you might be using in your project. Does the library accept a 7-bit address, and then internally shift this by one bit to the left? Or do you need to do this yourself, and OR the result with 0x01 bit if you are issuing a read instruction?
- Pay attention to the suffix of the device. For instance, an ATmega168P-20AN is not the same as a ATmega168-20AU! I had some really weird issues porting code over to use the newer devices (not that I intentially went the newer models, I just blindly selected the cheapest price). Perhaps the toolchain was too old and did not handle the newer devices? Anyway, after messing about it was just easier to order the chips I actually wanted.
- When debugging a UART receive handler interrupt, you should be printing out debug messages out of another UART peripheral. Otherwise, I found that it would lock up my micro completely!
- ATmega32u4 is a fantastic chip, however, be aware that the WDT is enabled by default, so during development work you need to disable it. This can be done in software.
- Instead of setting register values to configure the oscillator, I recommend that you call the function 'SYSTEMConfig' PIC32 library function instead. This not only sets the speed of the clock, but also sets up the cache and memory wait states to get the best performance for your specified clock rate. Otherwise, your PIC32 is likely to perform poorly!
- You need to enable RCC clock before making changes to the things like GPIO etc. This caught me out when I was developing an I2C slave driver.
- You must assign an handler function that deals with spurious interrupts. Otherwise, every so often you system may appear to reset for no good reason! By default, the AIC_SPU register is 0x0, which is the address of the reset vector. Unlike most of the interrupt sources, the spurious interrupt is always enabled.
- There are very few interrupt sources capable of waking up the SAM7 from a deep sleep (MCLK is static). Therefore, be wise about the pin allocation. An external interrupt can wake up the CPU, whereas a pin change from the PIO controller does not!
- Configuring a GPIO pin as an output does not automatically turn off its pull-up resistor (on by default, at startup). This was tracked down when I was reducing the quiescent current of a device.
- Read the datasheet to find out to how a peripheral is clocked. I had a problem where, once the CPU was in an idle state, something would cause the SAM7 to draw a lot more current after a certain time. As it turned out, the PIT timer is not clocked like the rest of the timers on the device, and its clock was still active despite issuing an instruction to disable the clock to all peripherals.