Introduction: Attiny85 Concurrent Programming or Pumpkin With Multi-Colored Eyes

About: I work as software engineer in one of Bay Area (California) companies. Whenever I have a time I like to program micro controllers, build mechanical toys, and make some home improvement projects.

This project shows how to control two 10mm three-color common anode LEDs (multicolored eyes of Pumpkin Halloween Glitter) with Attiny85 chip. Goal of the project is to introduce reader into the art of concurrent programming and into usage of Adam Dunkels protothreads library. This project assumes that reader knows about AVR 8-bit controllers, can write some C-program and has some experience with Atmel studio.

Project code published on GitHub: https://github.com/jumbleview/pumpkin

Supplies

Before programming one still need to build the circuit. Here are components:

  • Attiny85 controller (any electronic supplier).
  • Two three color 10mm LEDs with common anode. Adafruit LEDs
  • Resistors 100 Ohm, 120 Ohm, 150 Ohm 0.125 or 0.250 Wt (any electronic supplier).
  • Six Pin header for AVR ISP interface. Can be made out of this Adafruit header
  • Some bread board or printed template board. I used this one https://www.adafruit.com/product/589
  • AVR ISP MKII interface and Atmel Studio 6.1 (Later version should work as well).

Step 1: Circut

Design uses five chip pins:

  • Two pins used to control anodes: each LED anode attached to the dedicated pin.
  • Three pins attached (through resistors) to LEDs cathodes (the same color cathode of each led attached to the same pin)

One would ask: why not use all six in/out pins of the chip so LED anodes will be connected directly to +5 v and each cathode will have its dedicated pin? That will make programming straightforward. Alas, there is the problem: pin PB5 (RESET) is a weak pin capable to provide just ~2 mA of the current, while there is need to have ~20 mA.

Of course one can build transistor amplifier for this weak pin but I myself whenever possible prefer to solve problem trough the code.

Step 2: Timing Diagram

Timing diagram helps us to understand what we need to program.

Top two rows on diagram shows voltage change on LED anodes. Voltage on pins connected to LED anodes oscillates with frequency ~ 250 Hz. This voltage oscillation for the left LED is an opposite to the oscillation of right LED. When voltage on anode is high corresponding LED can be bright. When it is low corresponding LED is dark. That means each LED may be bright during 2 milliseconds interval and is dark during another 2 milliseconds. Because human eye has some inertia, 250 Hz blinking is not noticeable by observer.
Bottom three rows on diagram shows change of voltage on pins connected to LEDs cathodes. Let us look on the first diagram column. It shows the case when left LED is in red color and right LED in green color. Here RED cathodes stays low while left anode is high, GREEN cathode stays low while right anode is high, and BLUE cathode stays low all the time. Other columns on diagram shows combinations of cathode and anode voltage for various colors.

As we can see there is interdependency on pins state. Without some framework it would not be easy to resolve. And that's where protothread library comes in handy.

Step 3: Programing. Macros and Definitions

Example in programming steps represent slightly simplified version. Program is shortened, and some symbolic definition replaced with explicit constants.

Let us start from the beginning. Program includes files coming with Atmel Studio as well protothread library header. Next there are two macros to manipulate pins levels and some definitions to give logical names to pin signals. So far nothing special.

Step 4: Programing. Main Loop

Then let us look at the end to see what main procedure contains.

Function main after doing some initialization stays in forever loop. In that loop it makes next steps:

  • Invokes protothread routine for the left LED. It changes some pins voltage.
  • Make two milliseconds delay. There is no change in pin voltage.
  • Invokes protothread for the right LED. It changes some pin voltage.
  • Make 2 MS delay. There is no change in pin voltage.

Step 5: Programming. Auxiliary Functions

Before we start to discuss protothreads we need to look at some helper functions. First there are functions to set particular color. They are straightforward. There are as many such functions as number of supported colors (seven) and one more function to set LED dark (NoColor).

And there is one more function which will be directly invoked by protothread routine. Its name is DoAndCountdown().

Technically speaking usage of such a function is not mandatory but I found it convenient. It has three arguments:

  • Pointer to function setting LED color (like RedColor or GreenColor or etc.)
  • Initial value of reverse counter: number of how many times this function must be invoked at particular protothread stage.
  • Pointer to reverse counter. It is assumed that when there is change in the color that reverse counter is 0, so at first iteration code will assign to that counter initial value. After each iteration counter is decremented.

Function DoAndCountdown() returns value of reverse counter.

Step 6: Programing. Protothread Routines

And here is framework core: protothread routine. For the sake of simplicity example limited only to three steps: for color change to RED, to GREEN, and to BLUE.

Function is invoked with two arguments:

  • Pointer to protothread structure. That structure was initialized by main before main loop started.
  • Pointer to reverse counter. It was set to 0 by main before main loop started.

Function set voltages to make left LED active and then starts protothread segment. This segment is between macros PT_BEGIN and PT_END. Inside there are some code which in our case only repeats macros PT_WAIT_UNTIL. This macros performs next:

  • Invocation of function DoAndCountdown. That sets voltage on LED cathodes to emit particular color.
  • Returned result compared with 0. If condition is 'false' protothread function immediately returns and yields control to the main loop.
  • When protothread is invoked next time it again executes code before PT_BEGIN, then jumps directly inside the PT_WAIT_UNTIL macros from which it returned last time.
  • Such actions repeated until result of DoAndCountdown is 0. In that case there is no return, program stays in protothread and executes next line of the code. In our case it is next PT_WAIT_UNTIL but generally speaking it could be almost any C code.
  • At initial execution of second PT_WAIT_UNTIL reverse counter is 0, so procedure DoAndCountdown() set it to the initial value. Second macros again will be executed 250 times till revers counter reaches 0.
  • State of struct pt is reset as soon as control reaches PT_END macros. When protothread function invoked next time protothread segment starts execute line of the code right after PT_BEGIN.

There is similar protothread routine for the right LED. In our example it just enforces different order of colors, but if we may do it completely differently: there is no tight coupling between left and right LED routine.

Step 7: Internals

Whole program is less then 200 lines of code (with comments and empty lines) and takes less than 20% of Attiny85 code memory. If needed it is possible to utilize here several more protothread routines and assign much more complicated logic to them.

Protothreads library is the simplest form of computer concurrent programming. Concurrent programming is an approach allowing to divide program into logical parts: sometimes they are called coroutines, sometimes thread, sometimes tasks. The principle is that each such task can share the same processor power while keeping code more or less linear and independent of other parts. Tasks from logical point of view may be executed simultaneously.

For advanced systems controls of such tasks performed either by operating system kernel or by language runtime embedded into executable by compiler. But in case of protothreads application programmer controls it manually by using protothreads macros library in tasks routines and invoking such routines (usually out of main loop).

You probably want to know how protothread actually works? Where the magic hidden? Protothreads rely on special C language feature: the fact that C switch case statement may be embedded into if or some other block (like while or for). Details you may find on Adam Dunkels site http://dunkels.com/adam/pt/expansion.html

Electronics internals of this project are very simple. Photo above gives you some clue. I am sure you can do better.

Pumpkin Challenge

Participated in the
Pumpkin Challenge