Notes

I’ve been using the Teensy 3.0 for an RGB LED bike wheel POV project and I am really impressed with the capabilities of this little board. For the POV to work properly, you need to know when to update the LED columns as they rotate around at different speeds. I’m using a simple hall effect sensor to accomplish that, similar to how the Adafruit SpokePOV works. As the wheel turns, the hall effect sensor detects the south pole of a magnet attached to the bike frame and generates an output pulse. The duration between these pulses are counted in the microcontroller to determine the wheel speed and thus the timing for updating the LEDs, which is important for images and patterns to remain in the same place as the wheel speed changes.

Most modern microcontrollers, including the Freescale K20 ARM Cortex-M4 on the Teensy 3.0, have hardware timers that can be used for a variety of purposes. They can trigger periodic events, keep track of elapsed times, generate precise delays, and create PWM outputs, as some examples. For this project one particularly useful feature is the Input Capture function. Basically the timer counts at a programmed clock rate up to a certain number, typically something like 65,535 for a 16-bit counter. When using input capture, the timer value is stored when an event occurs on an input, such as a falling edge (logic state high -> low). By looking at the captured timer value, you can determine the elapsed time between input conditions. In my case, the hall effect sensor output is normally at a logic high state unless it is in the presence of a magnetic south pole at which time it transitions to a logic low state. That falling edge triggers the input capture function on the Teensy and some simple math is used to calculate the time for one wheel rotation.

There is a more brute-force way of accomplishing more-or-less the same thing. I could continuously poll the state of the hall output and when it goes low note the time (the Arduino and Teensyduino environments have a function called millis() that keeps track of elapsed time in milliseconds since the program started). Then when the next pulse occurs, I note the time again and subtract the difference from the previous time.  Simple, right? So why bother with the Timer Input Capture? The reason is that all this polling and keeping track of system time takes up CPU time that could be better spent updating LEDs or doing some other function. The hardware timer can keep track of the time between pulses independently from the CPU and typically with greater precision.

For the 8-bit Atmel-based Arduino and Teensy boards, libraries exist for Teensyduino to accomplish such frequency measurement. However, since the timer hardware is so different on the Teensy 3.0, these functions had not (yet) been ported. Also, there existed very few examples (one here) of using the Input Capture function on the Teensy 3.0. So what’s a guy to do? Well, the >1,200 page (!) user manual does include a chapter on this function (Chapter 35) but unfortunately doesn’t provide any code examples. Between the manual and the example I found on GitHub, I managed to figure out how to use the FlexTimer Input Capture on the Teensy 3.0 (code examples below).

In my application, I wanted to be able to measure elapsed time between hall effect sensor pulses up to about a second. This is because a 24″ bike wheel rotates at about 1 revolution per second at about 5mph. For the POV to work well, the wheel needs to be rotating quickly and the faster the better. I’m using two LED strips 180 degrees apart on the wheel so the ideal speed is >10mph but it will still look cool at slower speeds. To measure 1 second on a 16-bit timer without overflow (meaning the counter counts up the max and then starts over at zero, effectively preventing you from determining the elapsed time), the clock to the timer must be slower than 65.535kHz.

On the Teensy 3.0, the FlexTimer module can be connected to the system clock which runs at 48MHz, an external clock source, or what’s called the Fixed Frequency Clock. The Teensy 3.0 is setup such that the FLL input is the 16MHz crystal oscillator on the board divided by 512. This FLL input branches off and is also called the Fixed Frequency Clock (MCGFFCLK).  See Chapter 24 of the manual for more info on the Multipurpose Clock Generator or page 142 for a nice block diagram. Since it runs at 31.250kHz (16M/512), using that as the clock source for the timer means that the timer would not roll over until after 2 seconds. Otherwise, with the 48MHz input, the timer would provide finer time measurement but would roll over after only 1.3ms.  A pre-scaler (up to 128) can be used to reduce the system clock input down to 375kHz, but that’s still too fast for what I need. Therefore, in my example below, I have the FlexTimer module configured to use the MCGFFCLK.

Another advantage to using the hardware timer that I have not mentioned yet is its interrupt capability. You can either poll the captured value register periodically to get your elapsed time or just let it run and have it generate an interrupt when the capture event occurs.  Depending on your application, this interrupt may or may not be useful but when implemented, keeping track of time is truly happening in the background.

Now for some code examples.  I’ve commented the lines of code to show what is being set and why but please refer to the manual for additional information about the registers and their functions. The following was implemented using Teensyduino.

const int ledPin = 13;
const int hallPin = 22;
uint16_t FTM0Count = 0;
int timerOverflow = 0;

// Setup function for FlexTimer0
void setupFTM0() {
  // Input filter to help prevent glitches from triggering the capture
  // 4+4×val clock cycles, 48MHz = 4+4*7 = 32 clock cycles = 0.75us
  FTM0_FILTER = 0x07;

  // Must set the Write-protect disable (WPDIS) bit to allow modifying other registers
  // The enable (FTMEN) bit is also set to enable the FlexTimer0 module
  // FAULTIE=0, FAULTM=00, CAPTEST=0, PWMSYNC=0, WPDIS=1, INIT=0, FTMEN=1
  FTM0_MODE = 0x05;

  // FLEXTimer0 configuration
  // Clock source is Fixed Frequency Clock running at 31.25kHz (FLL Clock input = MCGFFCLK)
  // Dividing that by 2 would have the counter roll over about every 4 seconds
  FTM0_SC = 0x00; // Set this to zero before changing the modulus
  FTM0_CNT = 0x0000; // Reset the count to zero
  FTM0_MOD = 0xFFFF; // max modulus = 65535
  FTM0_SC = 0x11; // TOF=0 TOIE=0 CPWMS=0 CLKS=10 (FF clock) PS=001 (divide by 2)
  FTM0_C0SC = 0x48; // CHF=0 CHIE=1 (enable interrupt) MSB=0 MSA=0 ELSB=1 (input capture) ELSA=0 DMA=0

  // Enable FTM0 interrupt inside NVIC
  NVIC_ENABLE_IRQ(IRQ_FTM0);

  /* Pins that can be used for Input Capture:
     Teensy Port  Name  CPU Pin  Signal
     9            PTC3  46       FTM0_CH2
     10           PTC4  49       FTM0_CH3
     22           PTC1  44       FTM0_CH0
     23           PTC2  45       FTM0_CH1
  */

  // PIN configuration, alternative function 4 on Pin 44 (Teensy 22) (FTM0_CH0)
  PORTC_PCR1 |= 0x400;
}

// Interrupt Service Routine for FlexTimer0 Module
extern "C" void ftm0_isr(void) {
  // Reset count value
  FTM0_CNT = 0x0000;

  // Save current captured count value
  FTM0Count = FTM0_C0V;

  // Keep track of overflow condition
  // Read the timer overflow flag (TOF) in the status and control register (FTM0_SC)
  if ((FTM0_SC&FTM_SC_TOF) != 0) {
    timerOverflow = 1;
    // Clear overflow flag 
    FTM0_SC &= ~FTM_SC_TOF;
  }
  else
    timerOverflow = 0;
}

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
  setupFTM0();
}

void loop() {
  if ((FTM0_C0SC&0x80) != 0) { // Look for channel interrupt flag
    // Clear channel interrupt flag
    FTM0_C0SC &= ~0x80;
    digitalWrite(ledPin, HIGH);
    Serial.print("Count: ");
    Serial.print(FTM0Count/(31250.0/2)); // Captured time of wheel revolution
    Serial.print("s");
    Serial.print(" Overflow: ");
    Serial.println(timerOverflow); // Overflow happens after ~ 4.2s
    delay(100);
  }
  else {
    digitalWrite(ledPin, LOW);
  }
}

When the magnet passes by the hall effect sensor, the timer detects the falling edge of the pulse, the interrupt routine runs which saves the captured time between sensor pulses, and the overflow condition is checked which tells me if the wheel is turning too slowly.  All that happens outside the loop(). Within the loop I check to see if the interrupt occurred and then print out the computed time between pulses and the overflow condition.  Really, I don’t even need to check if the interrupt occurred – I could just directly use the stored captured count value at any time and it would be automatically updated each rotation thanks to the interrupt routine. The same goes for the overflow flag – I plan on using that to tell me if I should be updating the LEDs for the POV effect or go into a different mode if the wheel is stationary or turning slowly.

My AltStick rocket altimeter is designed around a digital absolute pressure sensor from Freescale – MPL3115A.  I chose this part because 1. it’s small 2. it’s low power and 3. it outputs readings in height (meters) or pressure.  The previous generation part, MPL115A, only provided pressure outputs.  They are both the same size and about the same power, but I really liked the fact that I didn’t have to do any additional calculations in my software to get a height reading.  It wouldn’t have been difficult but it just comes for free with the MPL3115A. The data sheet states that the accuracy could be as good as 0.3 meter or about 1 foot, so I wanted to see if that could be achieved.  Unfortunately, I was never able to get close to that – readings were all over the place most of the time – so I thought I was using it wrong.  Then a discovery was made today…

I was at the Sensors Expo and Freescale had a booth there.  They are featuring the MPL3115A on just about every development kit and eval board now.  I noticed that one of these boards had black fabric over the top.  I thought it might have had to do with this pressure sensor and I was right but for the wrong reason.  Absolute pressure sensors like the ones from Freescale and other suppliers typically have a small port or opening in the top of the device where the sensor element can sense the ambient air.  I had seen people use foam or other material over the top of such sensors to help prevent erroneous readings from turbulent air, say on a quadcopter, for example.  However, when I asked about the fabric I was told it was instead to keep the light out of the sensor. “What does light have to do with it?” I asked.  Well, apparently the sensor die is light sensitive as are those for other sensors like accelerometers, I was told.  The difference is that the pressure sensors have a hole to let air/light in while the accelerometers are completely encapsulated. Now, this would not normally be an issue since they expect the sensor to be embedded within an end-item, say a cell phone.  However, in my case, I have be experimenting and testing with the board out in the open, mainly so I can access the buttons easily. I was assured that this was a well-known characteristic of these types of sensors but I’ve read the MPL3115A datasheet back and forth many many times and I know I didn’t see any mentions about light.

After the show I dug out the datasheet and searched for the word “light” – nothing.  It does refer to an App Note which talks about handling and soldering their pressure sensors but it had really no relevance to this part and again had no mention about precautions with light exposure.  I then went to the other similar part – MPL115A – and searched through that datasheet.  Lo and behold it did mention the light precaution: The sensor die is sensitive to light exposure. Direct light exposure through the port hole can lead to varied accuracy of pressure measurement. Avoid such exposure to the port during normal operation. So it is true!  However, my complaint is that, had I never known about that other part or had the helpful people at the Freescale booth not informed me of this, how would I know about the light sensitivity? On top of that, for design engineers who are using Freescale’s Xtrinsic Freedom development platforms that feature the MPL3115A (totally exposed, by the way), they may not be aware of this.  If you are wondering, I have already mentioned this to the Freescale reps and they are going to see about updating the documentation.

Now that I am aware of the light-sensitive nature, I did find another (brief) mention of it in a presentation about this sensor (a really good read, by the way, if you are interested in this device, along with the Pressure Altimetry App Note).  I also went ahead and covered my sensor on the AltStick with a small piece of foam as you can see in the picture below.  It sufficiently blocks out light without preventing the sensor from measuring the ambient pressure.  Again, this really wouldn’t be an issue when the board is enclosed or contained within a rocket. I had planned on using heat shrink or some other material before using it in a rocket anyway to protect it against hot ejection gasses and landing impact.  The great news is that, after over a dozen ground-based tests, I am now reliably getting +/- 1 foot accuracy – achievement unlocked! Thank you Freescale for making such a great part! I think it is finally time for me to do some test launches to learn more about how this board holds up and what sort of measurements I can get from a much more dynamic environment.

Foam placed on pressure sensor to keep out light

Foam placed on pressure sensor to keep out light

Reading through the recent comments on my ColorNode project, it is clear that I wasn’t clear enough with how I used the hardware from a software perspective.  Most of the software is based on the RF12 examples from JeeLabs.  I took the RF12 Demo example and used/modified that for serial console control on both the controller and the string nodes.  It implemented a nice menu system and I expanded that to send the different ColorNode commands as defined by my command packet structure.   During development, I used that with a JeeNode attached to my laptop via a serial-to-USB converter as the controller and running the controller software.  This also allowed me to print different debug info to the serial console so I knew if different nodes were receiving my commands or not.  I eventually implemented an automatic pattern/sequence loop on the controller to cycle through different effects, as can be seen in the video.  Other users may want to keep the controller connected to a PC and send it serial commands to sequence according to some other software sequencers, which could enable synchronizing the lights to music.  I didn’t get there this year but maybe next year.

As far as libraries and the Arduino IDE is concerned, I slightly modified both the G35 library and the RF12 library and did all my development with Arduino version 22.  When you are ready to download, select one of the ‘328 boards @ 16MHz such as the Arduino Pro. For the G35 library, I used the digitalWriteFast.h macro to enable microsecond-accurate delays for the string protocol.  Unfortunately, I couldn’t figure out how to make that work without hard-coding the ColorNode output pin into the G35 code, so it isn’t very universal for compatibility with other Arduino (or Arduino-compatible) boards unless you always use digital pin 19 or change it to match your configuration.  The plus side is that, with the accurate delays, I get absolutely zero glitches – no stuck/flickering bulbs or wrong colors or random bulbs lighting.  For the RF12 library, I have been using the rf12_easySend() function since it auto-retries.  It is intended to be used with ACKs so it knows to re-send the packet until it receives an ACK from the destination or gives up after 8 retries.  My nodes are programmed to not ACK when they are commanded via a broadcast message (node ID 255 – see examples below) so I hacked the timeouts and retries to be 10ms and 10, respectively, to very quickly re-send the packets and stop after 10 times.  Originally, this was 1000ms and 8, but I found that the radio could be used much faster and if one or more strings didn’t receive the packet the first time, they all would after 10 retries over 100ms.  This meant that all strings were synchronized to 1/10 of a second at worst case. If you are addressing one node at a time, the destination node will send an ACK when the command packet is received. The controller code will only retry 10 times if it never gets an ACK, otherwise it will stop once the ACK is received.

Here are some serial command examples:

If you are connected to a node via serial and enter “3,0,12,15,0,0,204,0,10 l” into the serial monitor (without the “”) this equates to Fade,start at bulb zero, end at bulb 12, red value 15 (max), green value zero, blue value zero, go to max intensity (0xCC in hex or 204 in decimal),use option 0 – fade in, step up the intensity once every 10ms. So, in other words, bulbs 0 through 12 would fade in from black to bright red in about 2 seconds.  The l at the end basically indicates that it is the light command, versus entering just an o which turns off everything. No other parameters are necessary for the off command.

If you are connected to a node/JeeNode programmed as a controller and enter “4,25,49,15,15,15,100,1,100,255 l” this equates to Chase, use bulbs 25, through 49, red max, green max, blue max (results in white), half-intensity, use option 1 – chase down, step through the bulbs every 100ms, send the command to all bulbs (255 = broadcast). So, in other words, the lights will be a dim white and chase down from bulb 49 to bulb 25 in 100ms steps (total time would be about 2.5 seconds) and all nodes listening to the controller would run this command. The l and o commands are the same as in the node software, except you can choose which node (or all) to turn off using the o command – “255 o” would turn all strings off, “5 o” would only turn off string 5. If you want to address one node in particular, you would replace 255 with your node #.  I don’t have a way right now to command anything other than all nodes or only one node.  However, the commands are sent so fast that if the code was written to send a command to one node and then immediately after send it to another node, they would only be off by max 200ms (assuming it took 10 retries for node 1 and another 10 retries for node 2).  If node one ACKed after the first packet and the second node did the same, then they would be synchronous within ~20ms.

One last note about the command structure – since I force retries when addressing the broadcast node (255 = all listening nodes), I have it setup to ignore any subsequent command packet that is exactly the same as the previous.  I did this since the strings would sometimes get glitchy if the same light command was issued over an over again which would be the case if one node successfully heard all 10 retries.  The downside of this is if you want to run the same white chase down command over and over again, it would only work the first time.  The nodes would see that the new packet is the same as before and assume that it is due to retries and ignore it.  In this case, you would want to issue an off command at the end of the sequence before sending the same command.  This limitation only exists when sending commands wirelessly via the controller.  If you program the same chase down sequence into the node software and have it loop, you can send the command to choose that particular Program (mode 1) number, then it can chase over and over again.  I like using the pre-programmed sequences on the nodes better since you have much more control over individual bulb timings which allows you to make much more complex patterns than what can be accomplished via the command modes (Fade, Chase, Random, etc.). You are just kinda stuck with the programs on the nodes once they are programmed, sealed up in the box, and the strings hung up.

Attached is a .zip file with the code I used this past December for the video.  It includes the Node software, Controller software, and modified libraries.  I am glad that so many people are using the ColorNode hardware/software platform to control the GE Color Effects lights.  Keep the comments and questions coming – I’ll try to address them as I can.  Please post links to project pages or any videos of the lights in action so others can see what cool things people are doing with these lights.  Thanks!

Code file: ColorNode Xmas 2011

I am a big fan of the Arduino environment because of its simplicity, ease of use, and user community.  I like to use it for many of my projects so this means I must design around the Atmel ATmega MCUs to be compatible with the IDE. When you get a new commercial Arduino development board, a bootloader is already programmed on the chip.  This bootloader enables the IDE to download code over the serial port on the device rather than through the SPI interface. When you click the Download Code button on the IDE, the ATmega is briefly reset, and the bootloader code runs looking for indication that new code needs to be programmed.  The IDE then sends the new code to the UART on the ATmega from a serial port on the computer, usually via a USB-to-TTL converter.

If you are making your own Arduino-compatible hardware and you don’t purchase already-programmed chips, then you will need to program the chips with an Arduino serial bootloader first to be able to use the standard IDE.  There are various ways of doing so and they are well documented (for the most part).  The Arduino software installation comes with bootloader code ready to be programmed onto your target device.  The bootloader code has different flavors to match the different variants in ATmega chips, such as flash size and crystal frequencies.  I personally use the Arduino as ISP method to program the bootloader because I can use it with my existing Arduino board and not have to purchase a stand-alone AVR programmer.

I have an older Arduino Diecimila which I use as my programmer and I put the new chips on a breadboard which is on an Adafruit Proto Shield.  I have the necessary pins wired up from the shield headers to the breadboard and also included the necessary crystal and reset resistor (see here: burning the bootloader).  This setup works pretty well and lets me quickly and easily swap out the chip to be programmed.  It’s not as easy as this, but I already had all the necessary hardware.

I recently ran into a couple issues trying to use this Arduino as ISP method while programming the bootloader on chips for my ColorNode project. First, I kept getting a “not in sync” error when it tried to run the bootloader process. After digging around, I found this site which suggested installing ~ 110Ω worth of resistance between the programmer’s Reset line (Arduino board header closest to power jack) and +5V.  The method is also discussed and explained here. Basically, when the IDE connects to download the bootloader code via the Arduino board programmed as the AVR programmer, that board get’s reset due to the way the auto-reset circuitry works.  Pulling the Reset line to +5V with a much lower resistance prevents this from happening and screwing up the bootloader programming process.

Once I resolved that issue I was able to successfully program the bootloader onto some ATmega328P chips (I chose the regular Duemilanove /w ATmega 328 as the board). I had also purchased up some ATmega328 (non-P) chips because they were cheaper and readily available from DigiKey. The main difference is the process technology where the P indicates their PicoPower technology and is best suited for low-power applications. Since I’m not running off batteries in my application, it doesn’t really matter. The code is compatible with either device and the Arduino IDE doesn’t differentiate between the two when doing the serial programming.

When I attempted to use the above process to program the bootloader on the ATmega328 chips, I ran into my second issue. When I ran the bootloader process I got an “Expected signature for ATMEGA328P is 1E 95 0F” error. Although the two chips are nearly identical and, from the code space aspect are identical, they have different part identification signatures. These signatures need to match those setup in the ISP configuration, specifically the avrdude.conf file. Page 302 in the full datasheet shows that the ATmega328 has a device signature of 0x1E 0x95 0x14 while the signature for the ATmega328P is 0x1E 0x95 0x0F. I found this posting on the arduino forums that talks about it more. Once I changed that setting the bootloader programmed correctly and both the P and non-P versions were able to be serially programmed with the IDE using the standard process.