www.mikrocontroller.net

Forum: GCC Tipps für Lichteffektsteuerung mit ATtiny13 gesucht

Autor: Paul Hamacher (powl)
Datum: 12.05.2008 22:28

Hi!

Aus besonderen Anlässen habe ich mir eine LED-Kette gebastelt ;-) Diese
besteht aus jeweils zwei gegenüberliegend angeordneten LEDs in Rot,
Grün, Gelb und Blau wovon ich jeweils immer eine Farbe per Portpin
ansteuern kann.

Zum Einsatz kam ein ATtiny13. Da ich das Ding gerne in C programmieren
würde, ergibt sich nun ein Dickes Problem mit der Codegröße.

Wenn mein Programm aus lauter Einzelanweisungen à la

PORTB = (0 << yellow) | (0 << green) | (1 << red) | (0 << blue);
_delay_ms(100);

besteht, wächst schon nach zwei Effekten der Programmcode auf 500bytes
an was schon 50% des Speichers vollmachen. Ich hätte jedoch gerne mehr
verschiedene Effekte. Darunter eventuell auch mal Fading-Effekte. Das
meiste werden jedoch nur 1/0-Folgen sein die dann wie ein Film ein paar
mal wiederholt hintereinander ablaufen.

Da ich nicht so der C-Geek bin und mit µCs 2 Monate Pause gemacht hab,
wollte ich mal Fragen ob jemand eine Idee hat, wie man die
Programmierung von solchen Folgen mit knappem Code realisieren kann und
ob das in C überhaupt so möglich ist oder ob ich assembler verwenden
muss.

Ach ja, es sei noch zu erwähnen dass es da noch einen Taster gibt mit
dem man z.B. ins nächste Programm springen oder die Geschwindigkeit der
Programmabfolge bestimmen können soll.

lg PoWl
Autor: Christian L. (lorio)
Datum: 13.05.2008 00:03

Wenn mit der Delay-Bibliothek gearbeitet wird:
Die Optimierung einschalten.
Sonst werden die .hex mal schnell seeeeehr groß ^^

Aber besser noch: Interrupts verwenden.
Autor: Paul Hamacher (powl)
Datum: 13.05.2008 00:21

Danke, die Optimierung hat leider keine Codereduktion erbraucht. Mir ist
das grad was eingefallen

Am besten wäre wohl eine Funktion die ich mit Daten folgender Struktur
füttern kann:

Jeweils ein Byte

Rot | Grün | Blau | Gelb | 4-Bit Delay

Dann kann ich die ganzen Effekte einfach in einer einfachen Liste
speichern, das was da vorher 300 Byte lang war wär somit nur noch 20
byte lang :-) Fragt sich nur wie viel Overhead das produziert. Muss sich
mal testen

Wenn man fürs Delay nur zwei oder drei Bit einplant könnte man ja noch
so dinge wie ein/ausfaden mit einplanen :-)

lg PoWl
Autor: Sebastian Mazur (izaseba)
Datum: 13.05.2008 00:35

Leg Dir doch mal eine Tabelle im Flash an:
const uint8_t Animation[] PROGMEM = { 0x01,0x02,0x04,0x08 };

in einem Timerinterrupt gibst Du das nächste Element auf PORTB aus

ISR (TIMER0_OVF_vect) {
static uint8_t folge = 0;

PORTB = pgm_read_byte(&Animation[folge]);
folge++;
if ( folge >=4 )
   folge = 0;
}

Du kannst ein 2 Dimensionales Array anlegen, für verschiedene Effekte.
Die Erste Dimension läuft im Timer Durch, die Zweite schaltest Du mit
Deinem Taster um.

Gruß Sebastian
Autor: Paul Hamacher (powl)
Datum: 13.05.2008 00:43

ahh danke das werde ich mal so auhsprobieren :)
Autor: AVRFan (Gast)
Datum: 13.05.2008 00:48

Statt den Flash (= CSEG) zum Speichern von Datenmengen zu verwenden,
kann man diese auch ins EEPROM (= ESEG) auslagern.  Eine Routine, die
ein Byte aus der übergebenen EEPROM-Adresse ausliest, ist fix
geschrieben, und benötigt nur wenig Platz.
Autor: Paul Hamacher (powl)
Datum: 13.05.2008 01:13

AVRFan wrote:
> Statt den Flash (= CSEG) zum Speichern von Datenmengen zu verwenden,
> kann man diese auch ins EEPROM (= ESEG) auslagern.  Eine Routine, die
> ein Byte aus der übergebenen EEPROM-Adresse ausliest, ist fix
> geschrieben, und benötigt nur wenig Platz.

An sich ne gute Idee aber das EEPROM umfasst beim Tiny13 nur 64 Byte,
ist leider etwas wenig, das reicht vielleicht für 2-3 Animationen.

Wenn die Ausgaberoutine 200-500Byte braucht hab ich noch 500 Byte nur
für Animationen, das langt dicke :-)

Bin noch nicht dazu gekommen was zu schreiben, mach ich morgen mal.

das hintere nibble eines solchen Animationsdatenbytes kann man ja
benutzen um den Delay vorzugeben in 16 Stufen (4 Bit) mit jeweils
meinetwegen 25ms.

Mit dem Button kann man ja dann noch auswählen wie viel ms ein delay
wirklich umfasst und die Animation somit schneller und langsamer machen
:-)

lg PoWl
Autor: Sebastian Mazur (izaseba)
Datum: 13.05.2008 01:14

@AVRFan,

Genau, EEPROM ist auch ein guter Ablageort.

> Eine Routine, die
>ein Byte aus der übergebenen EEPROM-Adresse ausliest, ist fix
>geschrieben, und benötigt nur wenig Platz.

Das ist nicht nötig,
ersetze PROGMEM durch EEMEM
und pgm_read_byte durch eeprom_read_byte

fertig.

Gruß Sebastian
Autor: Paul Hamacher (powl)
Datum: 14.05.2008 14:27

Hi, danke nochmal für die Hilfe! Mein Akutelles Programm sieht so aus
und funktioniert auch recht ordentlich :-)

Wie kann ich denn nun mehrdimensionale Arrays erstellen? Im
lightshow-Array sollen nun mehrere Arrays mit den einzelnen Szenen
eingespeichert sein. Um die szene automatisch neu beginnen zu lassen
wenn sie fertig durchgelaufen ist brauch ich eine Endemarke 0x00 oder?

lg PoWl
#include <avr/pgmspace.h>
#include <util/delay.h> 

#define    yellow  PB3
#define    green  PB1
#define    red    PB2
#define    blue  PB4

#define    button  PB0

const unsigned char lightshow[] PROGMEM = { 
(0b1000 << 4) | 10,
(0b0100 << 4) | 10,
(0b0010 << 4) | 10,
(0b0001 << 4) | 10
};

int main(void)
{
  PORTB = (1 << button);
  DDRB = (1 << yellow) | (1 << green) | (1 << red) | (1 << blue);

  unsigned char temp, count, lights, delay;

  while(1)
  {
    for(count=0; count<4; count++)
    {
      temp = pgm_read_byte(&lightshow[count]);

      lights = (temp >> 4);
      delay = temp & ~(0xF0);

      if(bit_is_set(lights, 3))
      {
        PORTB |= (1 << yellow);
      }
      else
      {
        PORTB &= ~(1 << yellow);
      }

      if(bit_is_set(lights, 2))
      {
        PORTB |= (1 << green);
      }
      else
      {
        PORTB &= ~(1 << green);
      }

      if(bit_is_set(lights, 1))
      {
        PORTB |= (1 << red);
      }
      else
      {
        PORTB &= ~(1 << red);
      }

      if(bit_is_set(lights, 0))
      {
        PORTB |= (1 << blue);
      }
      else
      {
        PORTB &= ~(1 << blue);
      }

      while(delay--)
      {
        _delay_ms(20);
      }
    }
  }
}
Autor: Matthias Lipinsky (lippy)
Datum: 14.05.2008 19:31

> Paul Hamacher schrieb:
#define    yellow  PB3
#define    green  PB1
#define    red    PB2
#define    blue  PB4
..
if(bit_is_set(lights, 3))
=> yellow

if(bit_is_set(lights, 2))
=> green
Warum machst du das nicht so. Sortiere doch einfach deine "Farbbits"
passend zur Anschlussbelegung. Das Delay würde ich durch einen Timer
ersetzen...
ISR (timer overflow)  // alle 20msek
{
  static uint8_t count;
  static uint8_t time;
         uint8_t lights;
  temp   =   pgm_read_byte(&lightshow[count]);
  if ( ++time == (temp & 0x0F) )
  {
    lights =   (temp >> 4);
    PORTB  |=  ( (PORTB & 0x0F) | lights );
    if ( ++count >= 4 )  count = 0;
  }
}
Autor: Paul Hamacher (powl)
Datum: 14.05.2008 20:33

Matthias Lipinsky wrote:
> Warum machst du das nicht so. Sortiere doch einfach deine "Farbbits"
> passend zur Anschlussbelegung.

Das ist nur für mich so, weil Die Kette so aufgebaut ist: Gelb Grün Rot
Blau und ich das gerne auch in der Reihenfolge so programmieren würde
:-)

> Das Delay würde ich durch einen Timer
> ersetzen...

Danke, aber wozu, das wird ja mega kompliziert. Ich möchte doch die
delay-Zeit durch die Angabe im jeweiligen Szenenschritt bestimmen.

Wie erstellt man denn nun mehrdimensionale Arrays? Mein kleines
Experiment scheint nicht zu funktionieren:
#include <avr/pgmspace.h>
#include <util/delay.h> 

#define    yellow  PB3
#define    green  PB1
#define    red    PB2
#define    blue  PB4

#define    button  PB0

const char scene1[] PROGMEM = {
10,
(0b1000 << 4) | 10,
(0b0100 << 4) | 10,
(0b0010 << 4) | 10,
(0b0001 << 4) | 10,
0};

const char scene2[] PROGMEM = {
20,
(0b0000 << 4) | 10,
(0b1000 << 4) | 9,
(0b1100 << 4) | 8,
(0b1110 << 4) | 7,
(0b1111 << 4) | 6,
(0b0111 << 4) | 7,
(0b0011 << 4) | 8,
(0b0001 << 4) | 9,
0};

const char *lightshow[] PROGMEM = {
  scene1,
  scene2
};

int main(void)
{
  PORTB = (1 << button);
  DDRB = (1 << yellow) | (1 << green) | (1 << red) | (1 << blue);

  char currentScene, currentStep;
  char temp, lights, repetitions, delay;

  while(1)
  {
    for(currentScene=0; currentScene<sizeof(lightshow); currentScene++)
    {
      repetitions = pgm_read_byte(&(lightshow[currentScene])[0]);

      while(repetitions--)
      {
        currentStep = 1;

        while(1)
        {
          temp = pgm_read_byte(&lightshow[currentStep]);

          if(temp == 0)
          {
            break;
          }

          [...Portbits setzen...]

          while(delay--)
          {
            _delay_ms(20);
          }
        }
      }
    }
  }
}

lg PoWl
Autor: Matthias Lipinsky (lippy)
Datum: 14.05.2008 22:52

>Danke, aber wozu, das wird ja mega kompliziert
Das ist wohl ein Scherz...

>Ich möchte doch die delay-Zeit durch die Angabe im jeweiligen Szenenschritt
>bestimmen.
Kannst du doch. Beispiel siehe mein Post.

>Mein kleines Experiment scheint nicht zu funktionieren:
Das sind ja auch zwei einzelne Arrays.


>Wie erstellt man denn nun mehrdimensionale Arrays?
char scene[4][2] PROGMEM  = { {1, 2},
                              {3, 4},
                              {5, 6},
                              {7, 8} };
Autor: Paul Hamacher (powl)
Datum: 14.05.2008 23:37

Danke schonmal für deine Posts.

Hast Recht, mit deinem Beispiel geht es. Aber ich verstehe nicht ganz,
was mir das für Vorteile bringen soll? Wozu einen Timer verwenden? Einen
Linearen Programmablauf finde ich übersichtlicher und einfacher zu
handlen für den Anfang.

lightshow dient als zeiger auf die beiden arrays. Demnach müsste
&(lightshow[x]) die Adresse des arrays nummer x haben. Sollte ich die
Eckige klammer danach noch in die Runde mit reinholen?

Mit deiner Methode, mehrdimensionale Arrays zu erstellen, kann man nur
Unterarrays gleicher Länge definieren. Meine Einzelnen Lichtszenen sind
allerdings alle unterschiedlicher Länge.

lg PoWl

Antwort schreiben

Die Angabe einer Email-Adresse ist freiwillig. Wenn Sie automatisch per Email über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Suchfunktion und Betreffsuche benutzen - vielleicht gibt es schon einen ähnlichen Beitrag
  • Aussagekräftigen Betreff wählen
  • Im Betreff angeben um welchen Controllertyp es geht (AVR, PIC, ...)
  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
  • JPEG-Dateien (.jpg) nur für Fotos und Scans verwenden
  • Schaltpläne, Screenshots usw. als PNG oder GIF anhängen

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [pre]vorformatierter Text (z.B. Code in anderen Sprachen)[/pre]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel






webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net