Scott Zitek - Fab Academy 2014

Output devices

Week 12 Assignment

The assignment for this week is add an output device to a microcontroller board you've designed and program it to do something. We covered examples of RGB color changing LEDs, Charlieplexing an array of LEDs, LCD displays, video, sound, DC motors, servo motors, stepper motors and more. I was most curious about the Charlieplexing so I decided to make a circuit that used only 5 pins to control a linear array of 20 LEDs.


Designing the circuit

  1. I started out by researching Charlieplexing and reverse engineering the hello.array.44 example circuit.

  2. I color coded the hello.array.44 example to better visualize the different circuit paths.
  3. hello.array.44 example with the various circuit paths color coded.


  4. That didn't help much, so I made up ladder diagram with 20 different combinations.
  5. ladder diagram of 20 Charlieplexed LEDs


  6. I then realized that if I kept the LEDs on my circuit board, in the same linear array, it would be a good example of how Charlieplexing works. I was able to arrange the LEDs so that there were only four points in the circuit that overlapped and I could just bridge those with the LEDs.
  7. ladder diagram with red dots where circuit points overlap.


  8. I used Eagle CAD to draw the circuit from scratch. Once again this took much longer than expected. Partly because there were so many LEDs. It also didn't help that Eagle CAD does not have built in align and distribute capabilities. When I was just about done, I did something to break the connection between the schematic and the board layout and had to start over.
  9. Circuit traces



Making the circuit board

  1. As usual, I cut the circuit board using Fab Modules and the Modela mill, however this time the board was not usable.
  2. Some of the gaps between traces were too small for the 1/64" endmill to fit. Note the hole to the left side was 1/64"



    There were three areas that did not cut the way I needed them to.


  3. So I went back to Eagle CAD and added a zero ohm jumper resistor and re-routed the ground trace to the voltage regulator.
  4. Just a few modifications solved the problems. I also made most of the traces wider.


  5. I was able to cut the revised circuit board without any problems.

  6. Soldering the circuit board went pretty fast considering the number of components involved. I just had to pay special attention to make sure that each LED was facing the correct way.
  7. Notice alternating orientation of each LED. This matches the ladder diagram.



Programming

  1. Based upon my research on Charliplexing and the ATTiny44 Data Sheet, I was able to reverse engineer the hello.array.44 example program and modify it to control my circuit board configuration. I used the same pins as in the example but my LEDs were configured very differently.

  2. I was glad to see that all 20 LEDs worked and the program sequenced the LEDs exactly as I intended on the very first try.

  3. Click this link if embedded video is not working


Programming the pushbutton

  1. I had added a pushbutton to the design because I wanted to use it to select between several different lighting sequences. So I modified my program.
  2. //
    //
    // linear.array.44.pbv1.c
    //
    // Charlieplex LED array hello-world
    //
    // Neil Gershenfeld
    // 11/13/10
    //
    // (c) Massachusetts Institute of Technology 2010
    // Permission granted for experimental and personal use;
    // license for commercial sale available from MIT.
    //
    
    // Modified 4/22/2014 by Scott Zitek
    
    #include < avr/io.h >
    #include < util/delay.h >
    
    #define output(directions,pin) (directions |= pin) // set port direction for output
    #define input(directions,pin) (directions &= (~pin)) // set port direction for input 
    #define set(port,pin) (port |= pin) // set port pin
    #define clear(port,pin) (port &= (~pin)) // clear port pin
    #define pin_test(pins,pin) (pins & pin) // test for port pin
    #define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
    
    #define input_port PORTB
    #define input_direction DDRB
    #define input_pin (1 << PB2)
    #define input_pins PINB
    
    #define led_delay() _delay_ms(1) // LED delay
    
    #define led_port PORTA
    #define led_direction DDRA
    
    #define A (1 << PA1) // row 1
    #define B (1 << PA2) // row 2
    #define C (1 << PA3) // row 3
    #define D (1 << PA4) // row 4
    #define E (1 << PA5) // row 5
    
     uint8_t  pbstate;   //button state 
     uint8_t  pblast;  //button last pass 
     uint8_t  pboneshot; //button pressed oneshot 
     uint8_t  mode; //0-5 
    
    void flash(uint8_t from, uint8_t to, uint8_t delay) {
       //
       // source from, sink to, flash
       //
       static uint8_t i;
       set(led_port,from);
       clear(led_port,to);
       output(led_direction,from);
       output(led_direction,to);
       for (i = 0; i < delay; ++i)
           led_delay();
       input(led_direction,from);
       input(led_direction,to);
       }
    
    void led_cycle(uint8_t number, uint8_t delay) {
       //
       // cycle through LEDs
       //
       uint8_t i;
       for (i = 0; i < number; ++i) {
    
    	 // LEDs 20 to 1
          flash(D,E,delay);   // LED20
          flash(E,D,delay);   // LED19
          flash(C,D,delay);   // LED18
          flash(D,C,delay);   // LED17
          flash(B,C,delay);   // LED16
          flash(C,B,delay);   // LED15
          flash(A,B,delay);   // LED14
          flash(B,A,delay);   // LED13
          flash(C,E,delay);   // LED12
          flash(E,C,delay);   // LED11
          flash(A,C,delay);   // LED10
          flash(C,A,delay);   // LED9
          flash(B,D,delay);   // LED8
          flash(D,B,delay);   // LED7
          flash(B,E,delay);   // LED6
          flash(E,B,delay);   // LED5
          flash(A,D,delay);   // LED4
          flash(D,A,delay);   // LED3
          flash(A,E,delay);   // LED2
          flash(E,A,delay);   // LED1
    
          // LEDs 1 to 20
          flash(E,A,delay);   // LED1
          flash(A,E,delay);   // LED2
          flash(D,A,delay);   // LED3
          flash(A,D,delay);   // LED4
          flash(E,B,delay);   // LED5
          flash(B,E,delay);   // LED6
          flash(D,B,delay);   // LED7
          flash(B,D,delay);   // LED8
          flash(C,A,delay);   // LED9
          flash(A,C,delay);   // LED10
          flash(E,C,delay);   // LED11
          flash(C,E,delay);   // LED12
          flash(B,A,delay);   // LED13
          flash(A,B,delay);   // LED14
          flash(C,B,delay);   // LED15
          flash(B,C,delay);   // LED16
          flash(D,C,delay);   // LED17
          flash(C,D,delay);   // LED18
          flash(E,D,delay);   // LED19
          flash(D,E,delay);   // LED20
          }
       }
    
    void led_cycle2(uint8_t number, uint8_t delay) {
       //
       // cycle through LEDs
       //
       uint8_t i;
       for (i = 0; i < number; ++i) {
    
     	 // LEDs alternate sides towards center
          flash(D,E,delay);   // LED20
          flash(E,A,delay);   // LED1
          flash(E,D,delay);   // LED19
          flash(A,E,delay);   // LED2
          flash(C,D,delay);   // LED18
          flash(D,A,delay);   // LED3
          flash(D,C,delay);   // LED17
          flash(A,D,delay);   // LED4
          flash(B,C,delay);   // LED16
          flash(E,B,delay);   // LED5
          flash(C,B,delay);   // LED15
          flash(B,E,delay);   // LED6
          flash(A,B,delay);   // LED14
          flash(D,B,delay);   // LED7
          flash(B,A,delay);   // LED13
          flash(B,D,delay);   // LED8
          flash(C,E,delay);   // LED12
          flash(C,A,delay);   // LED9
          flash(E,C,delay);   // LED11
          flash(A,C,delay);   // LED10
          
     	 // LEDs alternate sides towards outside
          flash(A,C,delay);   // LED10
          flash(E,C,delay);   // LED11
          flash(C,A,delay);   // LED9
          flash(C,E,delay);   // LED12
          flash(B,D,delay);   // LED8
          flash(B,A,delay);   // LED13
          flash(D,B,delay);   // LED7
          flash(A,B,delay);   // LED14
          flash(B,E,delay);   // LED6
          flash(C,B,delay);   // LED15
          flash(E,B,delay);   // LED5
          flash(B,C,delay);   // LED16
          flash(A,D,delay);   // LED4
          flash(D,C,delay);   // LED17
          flash(D,A,delay);   // LED3
          flash(C,D,delay);   // LED18
          flash(A,E,delay);   // LED2
          flash(E,D,delay);   // LED19
          flash(E,A,delay);   // LED1
          flash(D,E,delay);   // LED20
          }
       }
    
    void led_cycle3(uint8_t number, uint8_t delay) {
       //
       // cycle through LEDs
       //
       uint8_t i;
       for (i = 0; i < number; ++i) {
    
    	 // LEDs 20 to 1
          flash(D,E,delay);   // LED20
          flash(E,D,delay);   // LED19
          flash(C,D,delay);   // LED18
          flash(D,C,delay);   // LED17
          flash(B,C,delay);   // LED16
          flash(C,B,delay);   // LED15
          flash(A,B,delay);   // LED14
          flash(B,A,delay);   // LED13
          flash(C,E,delay);   // LED12
          flash(E,C,delay);   // LED11
          flash(A,C,delay);   // LED10
          flash(C,A,delay);   // LED9
          flash(B,D,delay);   // LED8
          flash(D,B,delay);   // LED7
          flash(B,E,delay);   // LED6
          flash(E,B,delay);   // LED5
          flash(A,D,delay);   // LED4
          flash(D,A,delay);   // LED3
          flash(A,E,delay);   // LED2
          flash(E,A,delay);   // LED1
    
          // LEDs 1 to 20
          flash(E,A,delay);   // LED1
          flash(A,E,delay);   // LED2
          flash(D,A,delay);   // LED3
          flash(A,D,delay);   // LED4
          flash(E,B,delay);   // LED5
          flash(B,E,delay);   // LED6
          flash(D,B,delay);   // LED7
          flash(B,D,delay);   // LED8
          flash(C,A,delay);   // LED9
          flash(A,C,delay);   // LED10
          flash(E,C,delay);   // LED11
          flash(C,E,delay);   // LED12
          flash(B,A,delay);   // LED13
          flash(A,B,delay);   // LED14
          flash(C,B,delay);   // LED15
          flash(B,C,delay);   // LED16
          flash(D,C,delay);   // LED17
          flash(C,D,delay);   // LED18
          flash(E,D,delay);   // LED19
          flash(D,E,delay);   // LED20
    
     	 // LEDs alternate sides towards center
          flash(D,E,delay);   // LED20
          flash(E,A,delay);   // LED1
          flash(E,D,delay);   // LED19
          flash(A,E,delay);   // LED2
          flash(C,D,delay);   // LED18
          flash(D,A,delay);   // LED3
          flash(D,C,delay);   // LED17
          flash(A,D,delay);   // LED4
          flash(B,C,delay);   // LED16
          flash(E,B,delay);   // LED5
          flash(C,B,delay);   // LED15
          flash(B,E,delay);   // LED6
          flash(A,B,delay);   // LED14
          flash(D,B,delay);   // LED7
          flash(B,A,delay);   // LED13
          flash(B,D,delay);   // LED8
          flash(C,E,delay);   // LED12
          flash(C,A,delay);   // LED9
          flash(E,C,delay);   // LED11
          flash(A,C,delay);   // LED10
          
     	 // LEDs alternate sides towards outside
          flash(A,C,delay);   // LED10
          flash(E,C,delay);   // LED11
          flash(C,A,delay);   // LED9
          flash(C,E,delay);   // LED12
          flash(B,D,delay);   // LED8
          flash(B,A,delay);   // LED13
          flash(D,B,delay);   // LED7
          flash(A,B,delay);   // LED14
          flash(B,E,delay);   // LED6
          flash(C,B,delay);   // LED15
          flash(E,B,delay);   // LED5
          flash(B,C,delay);   // LED16
          flash(A,D,delay);   // LED4
          flash(D,C,delay);   // LED17
          flash(D,A,delay);   // LED3
          flash(C,D,delay);   // LED18
          flash(A,E,delay);   // LED2
          flash(E,D,delay);   // LED19
          flash(E,A,delay);   // LED1
          flash(D,E,delay);   // LED20
          }
       }
    
    int main(void) {
       //
       // set clock divider to /1
       //
       CLKPR = (1 << CLKPCE);
       CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
    
       //
       // initialize pins
       //
       input(input_direction, input_pin);
    
       //
       // main loop 
       //
       while (1) {
    
         pbstate=pin_test(input_pins,input_pin);//read button & store
         pboneshot=pbstate && !pblast; //oneshot 
         pblast=pbstate;               //remember last pass result  
         if(pboneshot){ 
         mode++; 
         if(mode > 11) mode=0; 
       } 
         switch(mode){ 
            case 0: led_cycle(1,100); break; 
            case 1: led_cycle(3,20); break;
            case 2: led_cycle(5,10); break;
            case 3: led_cycle(10,1); break;
            case 4: led_cycle2(1,100); break;
            case 5: led_cycle2(3,20); break;
            case 6: led_cycle2(5,10); break;
            case 7: led_cycle2(10,1); break;
            case 8: led_cycle3(1,100); break;
            case 9: led_cycle3(3,20); break;
            case 10: led_cycle3(5,10); break;
            case 11: led_cycle3(10,1); break;
    
       } 
            _delay_ms(400); // Debouncing - delay longer than bounce time     
          }
       }
    
  3. This program kind of worked. I was able to press the button to change modes but I think it only detected a change when it was between sequences. In other words, it didn't matter if you pressed the button if the LEDs were flashing a sequence. You had to wait until it got to the small part of the program where it examined the pushbutton status. I wanted it to switch to the next sequence option immediately whenever I pressed the button.

  4. I looked into it and decided that I needed to use an interrupt to get the behavior I desired. I just randomly had wired the pushbutton to pin 5 of the ATtiny44. This is the PB2 (PCINT10/INT0...) pin. The INT0 turned out to be a special hardware interrupt pin that can be used to trigger at low power, leading edge, and/or trailing edge change of states.

  5. This is where my progress stopped. I spent at least 8 hours trying to get the INT0 interrupt function to work. Biggest problem is that this type of thing either works or it doesn't. In my case no matter what I tried it would not work and there were no clues as to what I was doing wrong or what the program was actually doing.

  6. Usually the program would either do nothing, completely ignore the button, or run the default sequence once and then stop. I searched the web for example programs and tried many different things. Usually my persistence pays off. This time it did not. It is frustrating because the problem with the program is probably something simple that an experienced program would be able to identify and fix easily.


Status update:

I did eventually figure out how to use pushbuttons to select various modes as part of my final project.

For the "interface and applications programming" assignment I created a GUI program to display values from a FTDI interface. This made it much easier to understand what the microcontroller program was doing in situations like these. This interface made it easy to view in near real time how the program modified certain variables. So it very useful in testing and debugging.

I also realized that occasionally when I flashed a program to the microcontroller, it didn't always "take". The program did not update for some reason and microcontroller continues to run the previous program. I have witnessed this happen a few times since when the results of the program change would be obvious. If so, this would explain some of the problems I initially had with this assignment.


Project files


Back to index