Go Down

Topic: Neuling hat ne Frage Projekt Leuchtturm-Karte (Read 5966 times) previous topic - next topic

twoll065

Sketch von jurs drauf und 2-7 blinken wie sie sollen
Sketch von Udo Klein geändert, drauf 6 und 7 glimmen nur

twoll065

Habe es selber gefunden
erste Zeile

const uint8_t pins = 6;
geändert in
const uint8_t pins = 8;

Udo Klein

Quote
Also die kompilierte Programmdatei von meinem Programm ist dann DEUTLICH kleiner als die von Deinem. Mit auskommentierter Serial-Initialisierung hat mein Programm unter Arduino 1.0.1 nur eine angezeigte Binäre Sketchgröße von 1254 Bytes.


Ich hätte statt Speicherverbrauch sagen sollen "Hauptspeicherverbrauch".
Check out my experiments http://blog.blinkenlight.net

Udo Klein

#33
Feb 02, 2013, 07:15 am Last Edit: Feb 02, 2013, 08:14 am by Udo Klein Reason: 1
Nachtrag: um die Pinzahl nicht mehr explizit setzen zu müssen hier noch eine kleine Änderung (die Flashbedarf erhöht aber dafür die Benutzung bequemer macht):

Code: [Select]

//
//  www.blinkenlight.net
//
//  Copyright 2013 Udo Klein
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see http://www.gnu.org/licenses/

#include <MsTimer2.h>

uint8_t setOutput(uint8_t pin) {
   pinMode(pin, OUTPUT);
   return 0;
}

template <uint8_t led, uint16_t d1, uint16_t d2, uint16_t d3, uint16_t d4,
                      uint16_t d5, uint16_t d6, uint16_t d7, uint16_t d8>
void light_my_fire() {  
   static uint16_t phase = setOutput(led);
   
   phase = phase < d1+d2+d3+d4+d5+d6+d7+d8-1? phase+1: 0;

   digitalWrite(led, phase < d1                  ? HIGH:
                     phase < d1+d2               ? LOW:
                     phase < d1+d2+d3            ? HIGH:
                     phase < d1+d2+d3+d4         ? LOW:
                     phase < d1+d2+d3+d4+d5      ? HIGH:
                     phase < d1+d2+d3+d4+d5+d6   ? LOW:
                     phase < d1+d2+d3+d4+d5+d6+d7? HIGH:
                                                   LOW);                                                  
}

void blink() {
   light_my_fire<0,  200, 2800,  200, 2800,  200, 5801,    0,    0>();
   light_my_fire<1, 3000, 3000, 3000, 3000,    0, 8501,    0,    0>();
   light_my_fire<2,  700, 2300,  700, 2300,  700, 2300,  700, 5301>();
   light_my_fire<3, 6000, 6001,    0,    0,    0,    0,    0,    0>();
   light_my_fire<4, 6000, 3001,    0,    0,    0,    0,    0,    0>();
   light_my_fire<5, 6000, 1001,    0,    0,    0,    0,    0,    0>();
}

void setup() {
   MsTimer2::set(1, blink);
   MsTimer2::start();
}

void loop() {}
Check out my experiments http://blog.blinkenlight.net

Udo Klein

Quote

Der Phasenzustand der Leuchttürme wird doch in Deinem wie auch in meinem Sketch nur im LED-Pin gespeichert und vor dem Setzen des Pins jedesmal zur Laufzeit neu ermittelt: In meinem Sketch in den Zeilen unmittelbar vor dem Setzen des Zustands mit digitalWrite, und bei Deinem Sketch direkt im digitalWrite-Funktionsaufruf per Verzweigungslogik.


Falsche Antwort. Der Zustand wird dorthin ausgegeben. Aber die Phase, d.h. die Zeit die er bisher in der Periode ist steht woanders.

Hinweis: was macht eigentlich "static"? Und warum funktioniert das hier korrekt?
Check out my experiments http://blog.blinkenlight.net

jurs


Aber die Phase, d.h. die Zeit die er bisher in der Periode ist steht woanders.

Hinweis: was macht eigentlich "static"? Und warum funktioniert das hier korrekt?


static uint16_t phase = 0;
phase = phase < d1+d2+d3+d4+d5+d6+d7+d8-1? phase+1: 0;

Der Interrupt in Deinem Sketch wird einmal pro Millisekunde aufgerufen und "phase+1" zählt die Anzahl der Millisekunden bei jedem Aufruf hoch und weist das Ergebnis an phase zu, solange die Bedingung (Anzahl der Millisekunden kleiner als die Summe Einzel-Phasen) erfüllt ist. Sobald die Bedingung nicht mehr erfüllt ist, wird phase wieder auf null gesetzt. Das Speichern der Phase für jeden Turm einzeln funktioniert, weil die Funktion als Template-Funktion tatsächlich so oft vorhanden ist, wie es verschiedene Türme gibt, d.h. "phase" gibt es als Variable für jeden Turm einzeln und kann daher einzeln hochzählen und auf null resettet werden. Als static Variable behält die Variable ihren zugewiesenen Wert zwischen den Funktionsaufrufen.

BTW Systemauslastung in meinem Sketch:
Da alle Einzelphasen Vielfache von 100 Millisekunden sind, brauche ich in meinem Sketch natürlich nicht tatsächlich jede Millisekunde (und schon gar nicht mehrmals pro Millisekunde) auf eine Änderung des Turmstatus zu prüfen, sondern nur zehnmal pro Sekunde.

Falls Systemleistung für zusätzliche Aufgaben benötigt wird, lassen sich mit einer kleinen Änderung der loop-Funktion in meinem Sketch zehnmal pro Sekunde Zeitscheiben von 99 Millisekunden Dauer freischaufeln, in denen andere "langlaufende" Aufgaben erledigt werden können, sogar kurze Delays wären dort möglich, etwa zum Entprellen von Tasten oder um den seriellen Empfangspuffer mit empfangenen Daten zu füllen, oder um langsame Sensoren auszulesen.

Reduzierung der Systemauslastung für meinen Sketch, liefert 10 mal pro Sekunde frei nutzbare Zeitscheiben von 99 ms:

Code: [Select]

void loop() {
  static long nextLoopTime;
  if (millis()>=nextLoopTime)
  {
    nextLoopTime=((millis()+100)/100)*100;
    TurmStatus(Norderney_timing);
    TurmStatus(Pilsum_timing);
    TurmStatus(Campen_timing);
    TurmStatus(Schillig_timing);
    TurmStatus(Voslapp_timing);
    TurmStatus(Tossens_timing);
    // "lange" bis zu 99ms laufenden Code hier rein
  }
  // "kurz" bis zu 1 ms laufenden Code hier rein
}


Vielleicht soll die Landkarte ja sogar noch zu einer "interaktiven Landkarte" ausgebaut werden, etwa mit einem Bedienpult mit sechs Knöpfen und einem LCD-Display. Und wenn man am Bedienpult einen Taster drückt, kann man den Tastendruck mit einem kurzen Delay entprellen und das LCD-Display zeigt Zusatzinfos wie die Höhe und das Baujahr des Leuchtturms an. Nur mal so als Idee.

Udo Klein

Quote

Das Speichern der Phase für jeden Turm einzeln funktioniert, weil die Funktion als Template-Funktion tatsächlich so oft vorhanden ist, wie es verschiedene Türme gibt


Yes!!!
Check out my experiments http://blog.blinkenlight.net

Udo Klein

Quote

Reduzierung der Systemauslastung für meinen Sketch, liefert 10 mal pro Sekunde frei nutzbare Zeitscheiben von 99 ms:


Der Haken ist nur: Du brauchst kooperativen Code der nie länger als 99ms unterwegs ist. Meine Lösung braucht nur, daß der Code keine Interrupts sperrt.
Check out my experiments http://blog.blinkenlight.net

MKy_KB

Hallo Leute,

mit großem Interesse habe ich die Lösungen um die Leuchtfeuerkarte gelesen. Sketches funktionieren sehr gut. Ich denke hier wird auch keiner wegen einigen Millisekunden Abweichung schauen wollen.
Das Problem mit einer Leuchtfeuerkarte habe ich nun schon fast 20 Jahre vor mir her geschoben, leider hatte ich bisher noch keinen richtigen "Drive" gehabt es umzusetzen.
Anfang des Jahres einen ARDUINO Uno besorgt und etws rumgespielt. Da kam die Frage von TWOLL065 gerade recht. Allerdings will ich das Ding etwas aufbohren - (50 - 100 Leuchtfeuer).
Deswegen habe ich etwas mit Schieberegistern gespielt. Mehrere 595 zusammengeschaltet und verschiedene Sketche probiert. Man kann zwar Counter rauf und runter zählen, aber leider habe ich noch nicht den richtigen Ansatz um den Leuchtturm Sketch mit einem Shift Out Sketch zu verbinden. D.h. die nun als LED Out deklarierten Bits zu einem, bzw. mehreren Byte(s) zusammen zu packen und dann an das Schieberegister zu senden.
Gibt es hierzu eine einfache Lösung?

Grüße MKy

jurs


leider habe ich noch nicht den richtigen Ansatz um den Leuchtturm Sketch mit einem Shift Out Sketch zu verbinden. D.h. die nun als LED Out deklarierten Bits zu einem, bzw. mehreren Byte(s) zusammen zu packen und dann an das Schieberegister zu senden.
Gibt es hierzu eine einfache Lösung?


Einfach ist ja immer relativ.

Und da ich mit Schieberegistern noch nichts praktisch gemacht habe, habe ich eher eine theoretische Vorstellung darüber. Davon und von dem von mir auf der ersten Seite von mir geposteten Sketch ausgehend, würde ich es wie folgt machen:

1. Da die Leuchttürme bei der Lösung mit Schieberegistern nicht mehr bestimmten Arduino-Pins zugeordnet sind, wird das erste Feld der Turmdeklaration aus meinem Sketch nicht benötigt. Benötigt wird allerdings eine Variable, um sich den Zustand des Turms zu merken, also ob seine LED/Lampe gerade an oder aus ist. Dies wird benötigt, weil der Zustand aller Türme am Ende in das/die Schieberegister geschoben werden muß und das geht erst, wenn alle Turmzustände eines 100ms Intervalls zuvor ermittelt wurden und bekannt sind. Also verändere ich die Datenstruktur und recycele das erste Feld, statt des LED-Pins wird sich dort der LED-Status des Turms gemerkt (0 aus, 1 an), ich setze anfänglich mal 0 ein.

Code: [Select]

// LEDState - 0 bei LED aus, 1 bei LED an
// gesamt= Gesamtlänge aller Timings zusammen für einen Zyklus des Leuchtturms
// n= Anzahl der nachfolgenden ein/aus Timings
//                      LED gesamt  n   ein   aus   ein   aus   ein   aus   ein   aus
int Norderney_timing[]={ 0, 12000,  6,  200, 2800,  200, 2800, 200 , 5800};
int Pilsum_timing[]   ={ 0, 20500,  4, 3000, 3000, 3000,11500};
int Campen_timing[]   ={ 0, 15000,  8,  700, 2300,  700, 2300,  700, 2300,  700, 5300};
int Schillig_timing[] ={ 0, 12000,  2, 6000, 6000};
int Voslapp_timing[]  ={ 0,  9000,  2, 6000, 3000};
int Tossens_timing[]  ={ 0,  7000,  2, 6000, 1000};


Zweite Änderung: Den Türmen sind keine Arduino-Pins mehr zugewiesen daher diesen Codeblock löschen:
Code: [Select]

  pinMode(Norderney_timing[0], OUTPUT);
  pinMode(Pilsum_timing[0], OUTPUT);
  pinMode(Campen_timing[0], OUTPUT);
  pinMode(Schillig_timing[0], OUTPUT);
  pinMode(Voslapp_timing[0], OUTPUT);
  pinMode(Tossens_timing[0], OUTPUT);

Anstelle des Codes zum Initialisieren der Arduino-Pins als OUTPUT muss stattdessen der Code zum Initialisieren der Schieberegister eingefügt werden.

Die dritte Änderung ist, dass nach dem Berechnen des Turmstatus nicht direkt die LED geschaltet werden kann, sondern dass sich stattdessen der ermittelte Wert gemerkt werden muss. Also
statt:
Code: [Select]

digitalWrite(Turmdata[0], blinkStatusOn);

setze:
Code: [Select]

// im ersten Feld der Turmdaten merken, ob die LED gerade an oder aus sein soll
if (blinkStatusOn) Turmdata[0]=1; else Turmdata[0]=0;


Die vierte und letzte Änderung ist dann, dass wo in der loop von mir der Platzhalter
Code: [Select]

// "lange" bis zu 99ms laufenden Code hier rein

eingefügt wurde (aus dem nachgebesserten loop-Code von Seite 3 dieses Threads), dann der Code hinkommt, der die nun bekannten Turmzustände (Norderney_timing[0], Pilsum_timing[0], Campen_timing[0], etc.) an das/die Schieberegister rausschiebt und aktiviert.

Da sehe ich eigentlich kein größeres softwaretechnisches Änderungsproblem bei der von mir geposteten Programmlogik.



MKy_KB

Hallo jurs,

danke für die schnelle Antwort. Gedacht war z.B. folgenden Sketch aus dem ShiftOut Tutorial zu nehmen.
Diesen habe ich mal auf 6 Reihen erweitert und verschiedene 0b, 0x Werte oder Variablen eingetragen. Teilweise negiert weil ich die LED's an den SH Register von 5V zum SH Ausgang gelegt habe.

Diese Bytes müßten dann wohl halt mit den Blinkergebnissen gefüllt werden. Nur halt die Frage wie? Mit Byte read oder einem Array?

Code: [Select]

//**************************************************************//
//  Name    : shiftOutCode, Dual Binary Counters                 //
//  Author  : Carlyn Maw, Tom Igoe                               //
//  Date    : 25 Oct, 2006                                       //
//  Version : 1.0                                                //
//  Notes   : Code for using a 74HC595 Shift Register            //
//          : to count from 0 to 255                             //
//**************************************************************//



//Pin connected to ST_CP of 74HC595 Pin 12
int latchPin = 8;
//Pin connected to SH_CP of 74HC595 Pin 11
int clockPin = 12;
////Pin connected to DS of 74HC595 Pin 14
int dataPin = 11;



void setup() {
  //Start Serial for debuging purposes
  Serial.begin(9600);
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);

}

void loop() {
 
  //count up routine
  for (int j = 0; j < 256; j++) {
    //ground latchPin and hold low for as long as you are transmitting
    digitalWrite(latchPin, 0);

    shiftOut(dataPin, clockPin, LSBFIRST, 0x55);           //6. Reihe links nach rechts
   
    shiftOut(dataPin, clockPin, LSBFIRST, ~0b00110011);    //5. Reihe links nach rechts
   
    shiftOut(dataPin, clockPin, LSBFIRST, ~0b00001111);    //4. Reihe links nach rechts
   
    shiftOut(dataPin, clockPin, LSBFIRST, ~0xFF);          //3. Reihe links nach rechts
   
    shiftOut(dataPin, clockPin, ~j);                       //2. Reihe
   
    shiftOut(dataPin, clockPin, LSBFIRST, ~0b00110111);    //1. Reihe links nach rechts
    //return the latch pin high to signal chip that it
    //no longer needs to listen for information
    digitalWrite(latchPin, 1);
    delay(100);
  }
}


void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
  // This shifts 8 bits out MSB first,
  //on the rising edge of the clock,
  //clock idles low

//internal function setup
  int i=0;
  int pinState;
  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, OUTPUT);

//clear everything out just in case to
//prepare shift register for bit shifting
  digitalWrite(myDataPin, 0);
  digitalWrite(myClockPin, 0);

  //for each bit in the byte myDataOut?
  //NOTICE THAT WE ARE COUNTING DOWN in our for loop
  //This means that %00000001 or "1" will go through such
  //that it will be pin Q0 that lights.
  for (i=7; i>=0; i--)  {
    digitalWrite(myClockPin, 0);

    //if the value passed to myDataOut and a bitmask result
    // true then... so if we are at i=6 and our value is
    // %11010100 it would the code compares it to %01000000
    // and proceeds to set pinState to 1.
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {
      pinState= 0;
    }

    //Sets the pin to HIGH or LOW depending on pinState
    digitalWrite(myDataPin, pinState);
    //register shifts bits on upstroke of clock pin 
    digitalWrite(myClockPin, 1);
    //zero the data pin after shift to prevent bleed through
    digitalWrite(myDataPin, 0);
  }

  //stop shifting
  digitalWrite(myClockPin, 0);
}


Udo Klein

Entweder mit Shift Registern oder Du kaufst Dir zwei billige Megas in der Bucht. Die kosten pro Stück eh nur noch so um die 16-17 Euro mit Versand und haben jeweils 54 IO Pins. Das ist zwar teurer, aber dafür ist der Code einfacher und Du must nicht mit Schieberegistern basteln.
Check out my experiments http://blog.blinkenlight.net

jurs

#42
Feb 11, 2013, 07:40 pm Last Edit: Feb 11, 2013, 09:13 pm by jurs Reason: 1

Diese Bytes müßten dann wohl halt mit den Blinkergebnissen gefüllt werden. Nur halt die Frage wie? Mit Byte read oder einem Array?


Wie gesagt, ich habe keine praktischen Erfahrungen mit den Shift-Registern und kenne auch nur das Shiftout-Tutorial.

Wenn es so ist, dass die Werte byteweise rausgeschiftet werden müssen, also mit der Funktion
void shiftOut(int myDataPin, int myClockPin, byte myDataOut)
über das gesetzte byte "myDataOut", dann muß dieses Byte natürlich vor dem Funktionsaufruf aus dem Status von je 8 Türmen zusammengebastelt werden.

Codeausschnitt zum Setzen der 8 Bits mit anschließendem Shiftout:

Code: [Select]

bitWrite(myDataOut, 0, Norderney_timing[0]);
bitWrite(myDataOut, 1, Pilsum_timing[0]);
bitWrite(myDataOut, 2, Campen_timing[0]);
bitWrite(myDataOut, 3, Schillig_timing[0]);
bitWrite(myDataOut, 4, Voslapp_timing[0]);
bitWrite(myDataOut, 5, Tossens_timing[0]);
bitWrite(myDataOut, 6, Turm_XXX_timing[0]);
bitWrite(myDataOut, 7, Turm_YYY_timing[0]);
shiftOut(dataPin, clockPin, myDataOut);


Und das dann für immer acht Türme wiederholen.

Obwohl ich mir nicht sicher bin: Kann man durch eine Reihe kaskadierter Shiftregister nicht gleich alle Bits in einem Rutsch durchshiften, muß das wirklich immer 8-bitweise gemacht werden wie es die Beispiele im Shiftout-Tutorial zeigen?

Zu Udos Vorschlag ist natürlich zu sagen: Ein MEGA hat zwar 54 Pins, aber das Board darf trotzdem an allen Pins zusammen nicht zu viel Strom ziehen:
> Absolute Maximum ratings:
> DC Current VCC and GND Pins......... 200.0 mA
Selbst wenn man den Eigenverbrauch des Board mal außen vor läßt: Schließt man 54 LEDs an, die wenigstens kurzfristig mal alle zusammen leuchten können, dann ist die maximal pro LED erlaubte Stromstärke nur 200/54= 3,7mA.

Das ergibt natürlich recht flau blinkende LEDs. Also kommt man um das Basteln nicht herum.

Die einfachen 74HC595 Schieberegister dürfen wohl mit 70 mA belastet werden, das wären 70/8 = 8,75 mA. Auch noch nicht viel. Irgendwo habe ich mal was von einer Trickschaltung gelesen, dass man mit inverser Anschaltung am Schieberegister auch den doppelten Strom in der Summe bekommen kann, ohne einzelne Pins zu überlasten, das wären ja immerhin 17,5 mA.

Ich persönlich würde für so eine interaktive Landkarte wohl die Lösung mit den zwei MEGA Arduinos machen, und dann in entsprechender Anzahl ULN2803 Treiber hinter die Arduino-Pins schalten, damit jede LED auch ihre 20 mA bekommen kann, ohne dass Bauteile oberhalb ihres Max-Ratings betrieben werden. Man will sich mit sowas ja auch nicht die Bude abfackeln oder riskieren, dass es schon nach kurzer Betriebszeit die ersten Ausfallerscheinungen gibt.

Nachtrag: Auch beim MEGA kann man doch die Analog-Pins als Digital-Pins verwenden? Wenn man sonst nichts anderes an Hardware anschließen möchte, beträgt die Anzahl der ohne Verwendung von Shiftregistern maximal gleichzeitig blinkenden Türme 54+16= 70, weil man insgesamt 70 Pins pro MEGA zur Verfügung hat.

MKy_KB

Ich denke mit SH Register ist wohl die elegantere Lösung, da ja ein paar LED's setzen einen µC nicht gerade voll auslastet. Sicherlich 2 mal den Mega genommen ist evtl. am Anfang einfacher.
Strom der LED's ist normalerweise auch kein Problem, man braucht die nicht mit den vollen 20mA ansteuern, sind dann immer noch hell genug, sonst evtl. wie gesagt einen 2803.
Die SH Platine wir sowieso separat mit 5V versorgt, ist mit dem UNO nur über die 3 SH Leitungen und GND verbunden.

Ich werde auf jedenfall die nächste Tage das mit dem bitWrite versuchen. Evtl. klapp es ja.

Danke

jurs


Ich werde auf jedenfall die nächste Tage das mit dem bitWrite versuchen. Evtl. klapp es ja.


Na dann auf gutes Gelingen!

Ich sehe da softwaretechnisch eigentlich keine größeren Probleme. Außer vielleicht das Problem der LED-Verkabelung und der exakten Zuordnung zwischen einem bestimmten ShiftRegister-Ausgang und einem bestimmten Leuchtturm, da gibt es natürlich diverse Verwechslungsmöglichkeiten beim Schieben der Bits. Und es muß beim Verkabeln dann gut aufgepaßt werden, sonst blinkt leicht ein Turm so wie eigentlich ein anderer blinken sollte.

Anyway, bevor Du jetzt alles fix mit Daten füllst, würde ich auch nochmal prüfen, ob die Datenstruktur und der RAM-Speicherbedarf insgesamt passend ist. Ich hatte meinen Sketch gemacht, da standen die Daten der Türme zuerst fest und RAM-Speicher war wegen der wenigen Daten kein Thema.

Zu prüfen wäre:
1. Reicht eine maximale Zyklusdauer von 32,7 Sekunden, d.h. ist sichergestellt, dass sich das Blinkmuster jedes Turms innerhalb dieser Zeit exakt wiederholt? Wenn nein, wie lang ist die längste vorkommende Zyklus-Gesamtdauer aus Ein- und Aus-Zyklen?

2. Soll das Programm auf einem UNO laufen? Wenn durchschnittlich in der Datenstruktur 14 Bytes pro Turm verbraten wären, macht das 1400 Bytes für 100 Türme.  Das könnte bei einem UNO kritisch werden, der ja nur insgesamt 2kB RAM-Speicher hat. In dem Fall müßte man entweder doch wegen des RAM-Speicherbedarfs auf den MEGA gehen, oder sich eine Datenstruktur überlegen, die weniger RAM-Speicher pro Turm benötigt.

Überlegungen zu den beiden Punkten 1. und 2. würde ich lieber jetzt anstellen, bevor umfangreicher Bitshift-Code dazukommt. Denn wenn man erst ziemlich am Schluß feststellt, dass grundlegende Datenstrukturen noch geändert und angepaßt werden müssen, muß man sicher auch den Bitshift-Code wieder nochmal anfassen.


Go Up