Der rotierende Weihnachtsbaum

von Hans Eckardt
ELO 2009
Elektronik-Labor  Labortagebuch  ELO  


Als erste eigene Anwendung auf dem Pong-Board entstand dieser Weihnachtsbaum. Es dient vor allem dazu, als kleine Spielerei das Multiplexen der 120 LEDs auszutesten. Das Multiplexen erfolgt in der ISR, die bei Zähler 0 Überlauf ausgeführt wird.

Die Anoden (10) werden durchgeschaltet, und die Kathoden (12) werden mit dem Bitmuster, welches in die Schieberegister geschoben wird, angesteuert. Um eine zweistufige Helligkeit zu erreichen, wird die Ansteuerung in einen kurzen und einem langen Zyklus unterteilt. Dazu wird Zähler 0 mit entsprechenden Werten in der ISR geladen.

Das Hauptprogramm initialisiert den „Grafikspeicher" mit den Werten des Umrisses (volle Helligkeit) und führt dann in der Hauptschleife das Tauschen der vertikalen Linien im Inneren mit gedimmter Helligkeit aus. Das erfolgt mit einer bestimmten Geschwindigkeit, und so scheint sich der Baum mal rechts und mal linksrum zu drehen. Entwickelt wurde das Programm mit WinAVR (GCC).

Als Stromversorgung nach dem Flashen dient ein MP3-Player Ladenetzteil, das dem Board über den abgebildeten Adapter 5V zur Verfügung stellt.

 

 

Download: C-Quelltext und Hexfile

 

// app to show an xmas tree with "rotating" effect
// using ATMEGA8 on the Conrad Pong PCB

// This code is released to the Public Domain. No warranty for any purpose is given.

// revision (newest first)
// 2009-12-13: finished xmas tree code
// 2009-12-01: initial



// This define is for Visual Studio intellisense, type before any stystem header includes
// It must match the MCU name (MCU = atmega8) in the makefile else intellisense can take you to the wrong definition
#ifndef __GNUC__   // only for the Visual Studio, GCC generates it from MCU name
#define __AVR_ATmega8__
#endif
// Intellisense can be triggered update by ctr+space, or Save all, or at last deleting prj.ncb file and restarting Visual Studio

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

#include <util/delay.h>

#include <stdbool.h>

#include "my_avr_defs.h"

// display orientation landscape if defined, else portrait
//#define LANDSCAPE

#ifdef LANDSCAPE
// width of display (LED count)
#define WIDTH 12
// height of display (LED count)
#define HEIGHT 10
#else
// width of display (LED count)
#define WIDTH 10
// height of display (LED count)
#define HEIGHT 12
#endif

#define CYCLE1_TIME 1  // // cycle 1 is very little of the time of cycle 2, so the brightness is way lower
#define CYCLE2_TIME 32

// cycle 1 is 0.008 millisec
// Time = prescale * counter range / clock frequency
// 0.064 ms = 64 * 1 (CYCLE1_TIME) / 8 000 000 Hz

// cycle 2 is 0.25 millisec
// Time = prescale * counter range / clock frequency
// 2.048 ms = 64 * 32 (CYCLE2_TIME) / 8 000 000 Hz



// global Pixel buffer (always anode, cathode order as this is optimal for the multiplexer)
static byte pixbuf[10][12];

// v is the brightness code, use only 0, 1, and 2, while 2 be the brightest
static inline void SetPixel(byte x, byte y, byte v)
{
#ifdef LANDSCAPE
    pixbuf[y][x]= v;
#else
    pixbuf[x][y]= v;
#endif
}
static inline byte GetPixel(byte x, byte y)
{
#ifdef LANDSCAPE
    return pixbuf[y][x];
#else
    return pixbuf[x][y];
#endif
}

// the xmas tree bit pattern
static const uint PROGMEM pics[12] = {
     //0123456789
   0b0000110000, //0
   0b0000110000, //1
   0b0001001000, //2
   0b0001001000, //3
   0b0010000100, //4
   0b0100000010, //5
   0b0100000010, //6
   0b1000000001, //7
   0b0100000010, //8
   0b1000000001, //9
   0b1111111111, //10
   0b0000110000, //11
};
// where the y lines with inside tree area lie
#define INSIDE_BEGIN  2
#define INSIDE_END   10

// init all stuff of the AVR for safe operation
static void AVR_Init(void)
{
    //cli();  // interrupts are disabled at startup

    // watchdog should reset after 15 millisec (protect the LEDs, although the max current is OK for always on)
    wdt_enable(WDTO_15MS);

    // activate internal pullups to avoid floating input pins
    PORTB= (1u<<6)|(1u<<7);
    PORTC= (1u<<4)|(1u<<5)|(1u<<6)|(1u<<7);
    PORTD= (1u<<0)|(1u<<1)|(1u<<2)|(1u<<3);

    // set output ports
    DDRB= (1u<<0)|(1u<<1)|(1u<<2)|(1u<<3)|(1u<<4);  
    DDRC= (1u<<0)|(1u<<1)|(1u<<2)|(1u<<3);
    DDRD= (1u<<4)|(1u<<5)|(1u<<6)|(1u<<7);

    // 8bit timer0 init, internal clock / 64
    // LED multiplexing occurs on overflow0 Intr
    TCCR0= (1u<<CS01)|(1u<<CS00);
    TIMSK= (1u<<TOIE0); // interrupt on overflow
}


int __attribute__ ((OS_main)) main(void)
{
    // make the outline pic from the xmas tree
    for (byte y= 0; y < HEIGHT; ++y) {
        uint row= pgm_read_word(&pics[y]);
        uint shft= 1;
        for (byte x= 0; x < WIDTH; ++x) {
            if (shft & row)
                SetPixel(x,y,2); // full brightness
            shft*= 2;
        }
    }
    for (byte x= 0; x < WIDTH; ++x)
        SetPixel(x,10,2); // the only full line is set manually as above algorithm screws on that

    AVR_Init();

    sei(); // we want interrupts globally now

    bool even= false;

    for (;;) {

        // slowly swap the interiour lines
        for (byte y= INSIDE_BEGIN; y < INSIDE_END; ++y) {
            uint row= pgm_read_word(&pics[y]);
            uint shft= 1;
            bool inside= false;
            for (byte x= 0; x < WIDTH; ++x) {
                if (shft & row) {
                    if (!inside)
                        inside= true;
                    else
                        break;
                }
                else if (inside)
                    SetPixel(x,y, x % 2 == even ? 0 : 1); // low brightness for interiour (odd columns)
                shft*= 2;
            }
        }
        even = !even;
        _delay_ms(160);
    }

/* simple kind of snake for test
        for (byte y= 0; y < HEIGHT; ++y) {
            for (byte x= 0; x < WIDTH; ++x) {
                wdt_reset(); // keep watchdog watching...
                SetPixel(x,y,1);
                _delay_ms(45);
            }
        }
        for (byte y= 0; y < HEIGHT; ++y) {
            for (byte x= 0; x < WIDTH; ++x) {
                wdt_reset(); // keep watchdog watching...
                SetPixel(x,y,2);
                _delay_ms(45);
            }
        }
        for (byte y= 0; y < HEIGHT; ++y) {
            for (byte x= 0; x < WIDTH; ++x) {
                wdt_reset(); // keep watchdog watching...
                SetPixel(x,y,3);
                _delay_ms(45);
            }
        }
        for (byte y= 0; y < HEIGHT; ++y) {
            for (byte x= 0; x < WIDTH; ++x) {
                wdt_reset(); // keep watchdog watching...
                SetPixel(x,y,0);
                _delay_ms(45);
            }
        }
*/
}


// interrupt routine
// will put the pixel data to the LED matrix
// select actual cathode line by the shift register, then put the anodes with the ports to high for the on pixel
//
// Note:
//   Because CLK connected to MOSI, not SCK, we cannot bitbang with SPI
SIGNAL (SIG_OVERFLOW0)
{
    static bool firstcy;
    static byte anode;
    static byte shift= 1;

    firstcy = !firstcy;
    if (firstcy) {
        TCNT0= 256-CYCLE1_TIME;
        anode++;
        shift*=2;
        if (anode == 8)
            shift= 1;
        else if (anode == 10) {
            anode= 0;
            shift= 1;
        }
    }
    else
        TCNT0= 256-CYCLE2_TIME;

    byte cmp= firstcy ? 1 : 2;

    // bitbang the cathode pattern through the shift register
    for (byte b= 0; b < 12; ++b) {
        if (pixbuf[anode][b] == cmp)
            PORTB &= ~(1u<<4);        // first cathode line put to low, this low is bitbanged through the registers
        else
            PORTB |= (1u<<4);         /// next ones put to high
        // impulses for the shift registers (CLK)
        PORTB |= (1u<<3);
        PORTB &= ~(1u<<3);
    }

    wdt_reset(); // keep watchdog watching...

    // reset all anode lines
    PORTB &= ~((1u<<0)|(1u<<1));
    PORTC &= ~((1u<<0)|(1u<<1)|(1u<<2)|(1u<<3));
    PORTD &= ~((1u<<4)|(1u<<5)|(1u<<6)|(1u<<7));

    // impulses for the shift registers (strobe)
    PORTB |= (1u<<2);
    PORTB &= ~(1u<<2);

    // set according anode line
    if (anode < 4)
        PORTC |= shift;
    else if (anode < 8)
        PORTD |= shift;
    else
        PORTB |= shift;
}




 




Elektronik-Labor  Labortagebuch  ELO