Is ja cool! Das spart ja noch mehr und macht weiteren Raum für Spielerei.
Man(n) lernt halt nie aus. Aber das werde ich mir nach meinem Umzug
in Ruhe und genau ansehen.
Besten Dank
Schön, wenn es gefällt!
Noch mehr Raum, geht auch noch.
Aber auf Kosten der Taktgenauigkeit.
const byte ledtakt[] PROGMEM = {60,25,40,75,33,90,57,24,100,68,73,80,37,75,254,97,105};
byte ledtime[sizeof(ledtakt)];
inline void toggle(const uint8_t pin)
{
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
*portInputRegister(port) = bit;
}
byte zeitMerker = 0; // millis() ErsatzStoff
void setup()
{
DDRD = 0b01111111;
DDRA = 0b00000011;
DDRB = 0b11111111;
}
void loop()
{
for( byte LED = 0; LED < 17; LED++ )
{
if(zeitMerker - ledtime[LED] >= pgm_read_byte(&ledtakt[LED]))
{
ledtime[LED] = zeitMerker;
toggle(LED);
//digitalWrite( LED, !digitalRead(LED) );
}
}
_delay_ms(99); // 1 ms für die Verarbeitung
zeitMerker++;
}
ungetestet
Ich schätze mal, hier ist dann so ziemlich Ende der Fahnenstange:
const byte ledtakt[] PROGMEM = {60,25,40,75,33,90,57,24,100,68,73,80,37,75,254,97,105};
byte ledtime[sizeof(ledtakt)];
inline void toggle(const uint8_t pin)
{
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
*portInputRegister(port) = bit;
}
byte zeitMerker = 0; // millis() ErsatzStoff
int main()
{
DDRD = 0b01111111;
DDRA = 0b00000011;
DDRB = 0b11111111;
for(;;)
{
for( byte LED = 0; LED < 17; LED++ )
{
if(zeitMerker - ledtime[LED] >= pgm_read_byte(&ledtakt[LED]))
{
ledtime[LED] = zeitMerker;
toggle(LED);
//digitalWrite( LED, !digitalRead(LED) );
}
}
_delay_ms(99); // 1 ms für die Verarbeitung
zeitMerker++;
}
}
ungetestet
Kompiliert für einen Mega
Der Sketch verwendet 580 Bytes (0%) des Programmspeicherplatzes
Globale Variablen verwenden 18 Bytes (0%) des dynamischen Speichers
Ausgabe der letzten Fassung:
Der Sketch verwendet 260 Bytes (12%) des Programmspeicherplatzes. Das Maximum sind 2048 Bytes.
Globale Variablen verwenden 18 Bytes (14%) des dynamischen Speichers, 110 Bytes für lokale Variablen verbleiben. Das Maximum sind 128 Bytes.
Hmmm, in beiden ungetestet-Versionen fehlte oben jeweils #include <util/delay.h> Nachdem ich das eingebunden hatte übersetzte er es anstandslos. Doch irgendein Problem ist in beiden Fassungen: Er startet und blinkt anfangs nett wie eh und je ... nach einigen Umdrehungen hängt sich der 2313 auf und macht nix mehr. Alles via UNO R3 auf einen 2313 mit 17 LEDs übertragen.
Das reizt mich aber ungemein, die Philosophie dahinter zu verstehen. Muß jedoch (leider) erst den Umzug abwarten...
Wenn ich "zeitmerker" auf unsigned long setze ... hängt er sich zwar nicht mehr auf, aber nach einer gewissen Anzahl Umdrehungen fängt alles an wild zu flackern. Da muss ich mal bei Gelegenheit auf die Suche gehen... interessiert mich nämlch ungemein!
Wenn das #include <util/delay.h> fehlt...
KA, warum er das bei mir nicht anmäckert
Ich weiß auch nicht wo der Haken bei mir sein kann.
Benutzte IDE: 1.8.5 Hourly Build 2017/08/28 06:33
Werde nochmal drüber schauen...
Aber nicht innerhalb der nächsten 36H.
Habe die letzten Versionen mit einer IDE 1.6.12 erstellt.
Daher wohl das unterschiedliche Verhalten beim Include.
Die Durchdreher beim abspielen haben sicherlich was mit der Standard Integer Arithmetik, zu tun.
Wäre zumindest mein erster Ansatz.
Soweit wie möglich die Byte Verarbeitung erzwungen.
#include <Arduino.h>
const uint8_t size = 17;
const uint8_t ledtakt[size] PROGMEM = {60,25,40,75,33,90,57,24,100,68,73,80,37,75,254,97,105};
uint8_t ledtime[size];
/*
inline void toggle(const uint8_t pin)
{
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
*portInputRegister(port) = bit;
}
*/
uint8_t zeitMerker = 0; // millis() ErsatzStoff
int main()
{
DDRD = 0b01111111;
DDRA = 0b00000011;
DDRB = 0b11111111;
// Serial.begin(9600);
while(1)
{
for( uint8_t LED = 0; LED < size ; LED++ )
{
uint8_t diff = zeitMerker - ledtime[LED];
uint8_t takt = pgm_read_byte(&ledtakt[LED]);
if(diff >= takt)
{
ledtime[LED] = zeitMerker;
uint8_t bit = digitalPinToBitMask(LED);
uint8_t port = digitalPinToPort(LED);
*portInputRegister(port) = bit;
}
}
_delay_ms(99); // 1 ms für die Verarbeitung
zeitMerker++;
// Serial.println(zeitMerker,HEX);
}
}
auch quasi ungetestet
// Ich liebe diese Battles "ich kann besser" da lernt man als nicht "C"ler ungemein viel
Danke.
Gruß
DerDani
Nur schade dass ich im Moment viel zu wenig Zeit für diese Dinge habe. Es ist in der Tat eine sehr erfrischende Unterhaltung mit auch für mich hohem Lerneffekt. Die Arduino-Welt inkl. C kenne ich erst seit 3 Jahren - und fast täglich kommt eine Menge Stoff hinzu. combie's Ausführungen rufen ganz laut danach, näher untersucht zu werden. Vor allen Dingen, wenn man (wie ich) gerne mit den kleinen AVRs hantiert und oft jedes Byte 2x umdrehen muss.
Trotz Zeitmangel juckte es in den Fingern:
combie's letzte Fassung ... Minus #include <Arduino.h> ... Minus Kommentar-Block "toggle()" ... Plus #include <util/delay.h>
#include <util/delay.h>
const uint8_t size = 17;
const uint8_t ledtakt[size] PROGMEM = {60,25,40,75,33,90,57,24,100,68,73,80,37,75,254,97,105};
uint8_t ledtime[size];
uint8_t zeitMerker = 0; // millis() ErsatzStoff
int main()
{
DDRD = 0b01111111;
DDRA = 0b00000011;
DDRB = 0b11111111;
while(1)
{
for( uint8_t LED = 0; LED < size ; LED++ )
{
uint8_t diff = zeitMerker - ledtime[LED];
uint8_t takt = pgm_read_byte(&ledtakt[LED]);
if(diff >= takt)
{
ledtime[LED] = zeitMerker;
uint8_t bit = digitalPinToBitMask(LED);
uint8_t port = digitalPinToPort(LED);
*portInputRegister(port) = bit;
}
}
_delay_ms(99); // 1 ms für die Verarbeitung
zeitMerker++;
}
}
Kompliert mit IDE 1.8.5 ... Übertragen via UNO R3 auf Tiny 2313 ...
Der Sketch verwendet 252 Bytes (12%) des Programmspeicherplatzes. Das Maximum sind 2048 Bytes.
Globale Variablen verwenden 18 Bytes (14%) des dynamischen Speichers, 110 Bytes für lokale Variablen verbleiben. Das Maximum sind 128 Bytes.
Und es blinkt seit gut einer Stunde nett, lustig und stabil vor sich hin. Optimal!
Ich schätze mal, hier ist dann so ziemlich Ende der Fahnenstange
Dem stimme ich zu!
Beste Grüße, ich bin dann mal fort und packe weiter meine Kisten...
Rudi
Schön!
Fein, dass es jetzt funktioniert.
Auch ist es ein Zeichen dafür, dass man den Datentypen, bei Berechnungen/Vergleichen, erhebliche Aufmerksamkeit widmen muss. Ins besondere den impliziten Konvertierungen.
Jetzt:
Der Sketch verwendet 252 Bytes (12%) des Programmspeicherplatzes. Das Maximum sind 2048 Bytes.
Globale Variablen verwenden 18 Bytes (14%) des dynamischen Speichers, 110 Bytes für lokale Variablen verbleiben. Das Maximum sind 128 Bytes
Zu Anfang:
Der Sketch verwendet 2738 Bytes (133%) des Programmspeicherplatzes. Das Maximum sind 2048 Bytes.
Globale Variablen verwenden 262 Bytes (204%) des dynamischen Speichers, -134 Bytes für lokale Variablen verbleiben. Das Maximum sind 128 Bytes.
Eingedampft, auf deutlich unter 10%, sowohl Ram, als auch Flash.
Ich möchte mal sagen: Eine gute Teamleistung!
Beim Ram kann nichts mehr zu holen sein.
17 Byte für das Array und 1 Byte für den Zeitzähler.
Beim Flash sitzt noch was drin, wenn man denn Assembler, statt C++ verwenden würde:
- kleinere Optimierungen bei dem Algorithmus
- Verzicht auf die Stack Initialisierung
- Verzicht auf die Interrupt Tabelle
- Weg von den Arduino Pin Nummern.
Zu 3:
Wir nutzen keine ISR, also könnte in dem Bereich auch Code platziert werden.
19 Vektoren sind so einzusparen, 38 Byte (wenn ich mich nicht verrechnet habe)
Zu 4:
Da ist noch erhebliches Potential!
Die 17 Pins, * 2 Tabellen, ca 34Byte
Zusammengenommen, bestenfalls, geschätzte weitere 90Byte mögliche Flash Einsparung.
Floetzinger:
Hilfe!
Wenn ich geahnt hätte, dass ihr alle so viel Hirnschmalz für mein Problem verbraucht, hätte ich niemals nach Lösungsvorsachläge gefragt. Ihr seid ja famos.
Danke euch allen!
Ganz im Gegenteil!
Ich habe dir zu danken!
Als das größte Lob unter Hackern gilt: Spannende Frage!
Und die hast du geliefert!
Eine interessante, spannende, Frage.
Zu 4:
Verzicht auf die Arduinonummerei
Die Pins und Zeiten sind jetzt neu zugeordnet.
Wenn nötig, dann sind die Zeiten neu anzuordnen
#include <util/delay.h>
const uint8_t size = 17; // anzahl LED Pins
const uint8_t ledtakt[size] PROGMEM = { 60, 25, 40, 75, 33, 90, 57, 24, 100, 68, 73, 80, 37, 75, 254, 97, 105};
// Register Pin Zuordnung {PB0 PB1 PB2 - - - - - - - - - PB7 | PD0 PD1 - - - - - - - - PD6 | PA0 PA1}
uint8_t ledtime[size]; // merker für den Zeitablauf
uint8_t zeitMerker = 0; // millis() ErsatzStoff
// Schwellwerte
const byte schwelleB = 8; // 8 Bit in Port B (bit 0 bis 7)
const byte schwelleD = 7; // 7 bit in Port D (bit 8 bis 14)
// der Rest muss in Port A stecken (bit 15 und 16)
int main()
{
DDRB = 0b11111111;
DDRD = 0b01111111;
DDRA = 0b00000011;
while(1)
{
for( uint8_t LED = 0; LED < size ; LED++ )
{
uint8_t diff = zeitMerker - ledtime[LED];
uint8_t takt = pgm_read_byte(&ledtakt[LED]);
if(diff >= takt)
{
ledtime[LED] = zeitMerker;
byte bit = LED; // das Bit, welches es zu verarbeiten gilt
if(bit<schwelleB)
{
PINB = (1<<bit);
continue;
}
bit -= schwelleB;
if(bit<schwelleD)
{
PIND = (1<<bit);
continue;
}
bit -= schwelleD;
PINA = (1<<bit);
}
}
_delay_ms(99); // 1 ms für die Verarbeitung
zeitMerker++;
}
}
auch wieder ungetestet, bin mitten auf dem Acker, keinerlei Arduino zur Hand
Ich möchte mal sagen: Eine gute Teamleistung!
und
Eine interessante, spannende, Frage.
Das sehe ich genau so.
Vor allen Dingen, weil die letzte combie-Version ebenfalls funktionierte und noch mal wieder einiges an FLASH einsparte:
Der Sketch verwendet 244 Bytes (11%) des Programmspeicherplatzes. Das Maximum sind 2048 Bytes.
Globale Variablen verwenden 18 Bytes (14%) des dynamischen Speichers, 110 Bytes für lokale Variablen verbleiben. Das Maximum sind 128 Bytes.
Nun denn, die obigen Hinweise auf Assembler sind genau diejenigen gewesen, die mir auch vorschwebten. Also bin ich hingegangen, habe den *.HEX-Output von meinen Disassembler entflechten lassen und das ganze Programm etwas (sofern es meine Zeit erlaubte) untersucht. Einige Zuordnungen der einzelnen C++-Zeilen konnte ich (noch) nicht genau identifizieren. Mir fehlt einfach die Zeit für nähere Beschäftigung damit. Dennoch zeigt es, dass noch eine gute Hand voll Bytes drin sitzen. Wahrscheinlich würde es noch sparsamer im FLASH werden, wenn man so etwas direkt in Assembler anfängt.
Egal, den Code habe ich hier eingebunden. Sieht wahrscheinlich wie Kauderwelsch aus und ist noch nicht optimal kommentiert:
;***********************************************************
;17 LEDs an AT Tiny 2313 bei 8 MHz
;-----------------------------------------------------------
;Idee: "Floetzinger" (Member Forum Arduino.CC)
;Optimierung: "conbie" & "RudiDL5" (Menber Forum Arduino.CC)
;***********************************************************
;defines
;--------------------------------------------
.equ SREG = 0x3F
.equ RAMSTART = 0x0060
.equ DDRB = 0x17
.equ DDRD = 0x11
.equ DDRA = 0x1A
.equ schwelleB = 8
.equ schwelleD = 7
;datasegment
;--------------------------------------------
.dseg
.org RAMSTART
zeitMerk: .byte 1
ledTime: .byte 17
;codesegment
;--------------------------------------------
.cseg
.org 0
pwron: RJMP resvect
progmem: .dw 0x3C19 ; 60; 25;
.dw 0x284B ; 40; 75;
.dw 0x215A ; 33; 90;
.dw 0x3918 ; 57; 24;
.dw 0x6444 ; 100; 68;
.dw 0x4950 ; 73; 80;
.dw 0x254B ; 37; 75;
.dw 0xFE61 ; 254; 97;
.dw 0x6900 ; 105; 0;
resvect: CLR r1 ; SREG
OUT SREG, r1 ; "
LDI r26, LOW(RAMSTART)
LDI r27, HIGH(RAMSTART)
;main
;--------------------------------------------
SER r24 ; DDRB = 0b11111111
OUT DDRB, r24 ; "
LDI r24, 0x7F ; DDRD = 0b01111111
OUT DDRD, r24 ; "
LDI r24, 0x03 ; DDRA = 0b00000011
OUT DDRA, r24 ; "
;while
;--------------------------------------------
LDI r20, 0x01
LDI r21, 0x00
while1: LDI r26, 0x61
LDI r27, 0x00
LDI r24, 0x00
LDI r25, 0x00
;for
;--------------------------------------------
L_006E: LDS r19, zeitMerk ; diff = zeitMerk - ledTime[LED]
MOV r18, r24 ; "
MOVW r31:r30, r25:r24 ; " (berechnet zeiger auf progmem-
SUBI r30, 0xFE ; " adressen für "Z" OHNE isr-vectoren)
SBCI r31, 0xFF ; "
LPM r30, Z ; takt = pgm_read_byte( &ledTakt[LED] )
LD r22, X ; "
MOV r23, r19 ; "
SUB r23, r22 ; "
CP r23, r30 ; if( diff >= takt )
BRCS next ; ...
ST X, r19 ; if( bit < schwelleB )
CPI r24, schwelleB ; ...
CPC r25, r1 ;
BRCC L_00A0 ;
MOVW r23:r22, r21:r20 ;
MOV r0, r24 ;
RJMP L_0098 ;
L_0094: LSL r22 ;
ROL r23 ;
L_0098: DEC r0 ;
BRPL L_0094 ;
OUT 0x16, r22 ; schreibt BIT nach PORT B
RJMP next ; continue
L_00A0: LDI r19, 0xF8 ; if( bit < schwelleD )
ADD r19, r24 ; ...
CPI r19, schwelleD ;
BRCC L_00B8 ;
MOVW r23:r22, r21:r20 ;
RJMP L_00B0 ;
L_00AC: LSL r22 ;
ROL r23 ;
L_00B0: DEC r19 ;
BRPL L_00AC ;
OUT 0x10, r22 ; schreibt BIT nach PORT D
RJMP next ; continue
L_00B8: SUBI r18, 0x0F ;
MOVW r23:r22, r21:r20 ;
RJMP L_00C2 ;
L_00BE: LSL r22 ;
ROL r23 ;
L_00C2: DEC r18 ;
BRPL L_00BE ;
OUT 0x19, r22 ; schreibt BIT nach Port A
next: ADIW r25:r24, 0x01 ; "next" FOR ?
ADIW r27:r26, 0x01 ; "
CPI r24, 0x11 ; "
CPC r25, r1 ; "
BRNE L_006E ; "
;_delay_ms(99)
;--------------------------------------------
del99: LDI r23, 0xBF
LDI r24, 0x6A
LDI r25, 0x02
del99_1: SUBI r23, 0x01
SBCI r24, 0x00
SBCI r25, 0x00
BRNE del99_1
;zeitMerker++
;--------------------------------------------
LDS r24, zeitMerk
SUBI r24, 0xFF
STS zeitMerk, r24
;end of while(1)
;--------------------------------------------
RJMP while1
Aber was solls? Das Programm wurde noch mal kürzer:
;-------------------------------------
;Alles Okay, keine Fehler vorhanden
;Bytes .CSEG: 178 von max. 2048
;Bytes .DSEG: 18 von max. 128
;Bytes .ESEG: 0 von max. 128
;-------------------------------------
2738 Bytes zu 178 Bytes FLASH
262 Bytes zu 18 Bytes SRAM
So viel zum Thema "Hilfe mein Sketch ist zu groß" ![]()
Beste Grüße
Rudi
Ja...
So wirds gehen.
Um jedes Byte kämpfen...
Wie hasste das denn kompiliert?
(egal, finde ich selber raus...)
Aber jetzt beim Assembler, sind wir eigentlich an dem Punkt, wo ich aus dem Rennen bin.
Habe mich irgendwann entschieden, den C++ Weg zu gehen!
Aber was solls...
Vielleicht ist es ja jetzt an der Zeit mal wieder 131 Befehle (in der Tiefe) zu lernen.
.........
Da ist mir noch einer eingefallen...
Beim Ram kann nichts mehr zu holen sein.
Denn es gibt ja noch:
GPIOR0
GPIOR1
GPIOR2
Also
Statt:
uint8_t zeitMerker = 0; // millis() ErsatzStoff
Dieses einsetzen:
#define zeitMerker GPIOR0
Wieder 1 Byte gespart.
Und noch 2 in Reserve behalten.....
(Gilt natürlich auch für Assembler)
Wie hasste das denn kompiliert?
Zugegeben - auf eine recht schräge Art. Im letzten Frühjahr hatte ich einen Durchhänger und wollte von kleineren Sketches eigentlich "nur mal so" analysieren, was eigentlich im *.HEX-Output "tatsächlich" alles drin ist und wie das ganze letztendlich funktioniert. Aus dieser Idee ist zunächst ein Disassembler entstanden. Immer mehr Blut geleckt ... habe ich den immer weiter verbessert. Der Assembler selbst ist daraus eigentlich "zwangsläufig" entstanden. Alles in DELPHI. Er nimmt auch #includes, .macros und prüft, ob ein einzelnes Kommando für den jeweilgen Controller erlaubt ist. Übertragen werden die Bytes via USBasp. Ebenso können Fuses gelesen/geschrieben werden.
Okay, AVR-Studio kannte ich zwar, aber erstens ist der mir letztes Jahr mit einem PC gestorben und irgendwie ist mir das Ding zu sehr überladen. Ich mag meinen kleinen Dis/Assembler sehr und kann damit alles machen was ich will. Auch wenn er bei weitem nicht an das Studio heranreicht. Aber für meine paar Tiny-Versuche läuft das Teil sehr gut.
So auch das o.g. Programm seit Stunden jetzt brav blinkt.
![]()
Ach, so einer bist du.....
Ja, ja, hätte ich mir ja fast denken können.
Aus meiner Vergangenheit: Forth
Ja ja, so einer bin ich ... ![]()
Dass du Forth machst habe ich schon mal irgendwo aufgeschnappt. Oha, lang lang ist es her, als ich selbst in Forth experimentierte. Ist 'ne interessante Sache, das Buch dazu hatte ich erst vor wenigen Wochen noch in der Hand. Ich erinnere mich gerne an die Stack-Sachen daraus, wenn ich mal so etwas brauche.
Ich sage mal "gute Nacht". Morgen ist wieder "Handwerk" angesagt... ![]()
sorry, Arbeit hat mich aufgehalten.
Unglaublich, was da so zusammen geschrumpft wird.
Ich denk dann mal übetr ein neues zu lösendes problem nach
![]()
War übrignes sehr spannend, wie ihr euch dann mal so nach Vorn gepeitscht habt.
Respekt!