für mein nächstes Hobby-Projekt bin ich auf das o. g. Board umgestiegen und habe zur Einarbeitung mein letztes Projekt angepasst.
Nachdem mir Nemopuk zielsicher die Lösung eines Problem der Datenübertragung genannt hat, stellten sich nach und nach weitere Besonderheiten des Uno R4 heraus, über welche zum Großteil auch andere Benutzer im Netz berichten:
Speicherbedarf: Ein Programm welches auf dem Mega 52kB gross ist, benötigt auf dem R4 mehr als den doppelten Speicher.
Abhilfe: keine bekannt
Compilier-Dauer: Das Compilieren dauert beim R4 mit ca. 30s deutlich länger als beim Mega mit ca. 11s. Abhilfe: keine bekannt
random(): Diese Funktion arbeitet beim R4 extrem langsam, da die Routine bei jedem Aufruf den True Random Number Generator (TRNG) neu initialisiert.
Abhilfe: Wert einmal pro Sekunde abfragen und in einem Ringpuffer speichern. Den Wert aus dem Ringpuffer abfragen.
tone: Diese Funktion scheint beim R4 nicht zum Abspielen von Melodien geeignet zu sein - es treten diverse störende Effekte auf.
Abhilfe: Verwendung einer alternativen Bibliothek (z. B. TimerFreeTone)
EEPROM: um exakt zu sein, gibt es beim R4 kein EEPROM, dieses wird im Flash simuliert - bei jedem Schreiben muss die gesamte Page gelöscht werden.
Abhilfe: Verwendung eines externen EEPROM
Datenübertragung: Einbrechen der Übertragungsgeschwindigkeit bei längeren Übertragungen.
Abhilfe: keine bekannt
Sind euch Abhilfen zu den genannten Besonderheiten bekannt?
vielen Dank für deine Antwort, hatte ich bereits vermutet - deshalb habe ich auch Besonderheit geschrieben.
Wenn aber jemand mit einem grösseren Projekt vom Mega auf den Uno R4 umziehen möchte, wird er Schiffbruch erleiden...
Weitere Besonderheiten:
LED-Matrix: recht dunkel, wird diese mit matrix.play(true) gestartet kommt es zu zyklischer Laufzeiterhöhung (was zu erwarten war)
Abhilfe: für Helligkeit keine bekannt, Matrix.next an geeigneter Stelle einsetzen.
Interrupts: einige Funktionen werden vermutlich durch unerwartete Interrupts beeinflusst.
Abhilfe: keine bekannt
Das Compilieren dauert beim R4 mit ca. 30s deutlich
länger als beim Mega mit ca. 11s.
Da ist eine wesentlich fettere Architektur dahinter, von 8 auf 32 bit und natürlich ist ARM auch unheimlich aufgeblasen, so stark, das es nur mit C zu ertragen ist. In Assembler ein kurzes einfaches Blinkprogramm zum testen geht nicht, weil das nicht oder nur sehr umständlich dokumentiert ist wie man den Chip anspricht.
Diese Funktion scheint beim R4 nicht zum Abspielen von
Melodien geeignet zu sein - es treten diverse störende Effekte auf.
Vollkommen andere Architektur der CPU, die solcherlei Hardwarenahen Krempel halt nicht erlaubt. Daher kann man C-Programme ja auch nie auf eine andere Hardware übertragen, obwohl das ja eins der Hauptargumente für C ist. Aber irgendwann nutzt man eben doch Hardwareeigenheiten, und dann kommt das dabei raus.
einige Funktionen werden vermutlich durch
unerwartete Interrupts beeinflusst.
Nochmal: Falsche Hardware.
Du erwartest ernsthaft, das ein nicht mal im entferntesten ähnlicher Controller hardwarenah identisch funktioniert?
Wie läuft den C64 mit Windows 11? Wo hast du das Userport-TPM und den USB3.2-1541-Adapter gekauft, und wie kann man im C64-Bios Secureboot aktivieren?
Oh stimmt ich hatte nach dem Uno R3 gekuckt.
Das ist ja auch der "Vorvater" des Uno R4, nicht der Mega.
Vielleicht muss man vom Mega (mit ATmega2560) auf einen ESP gehen, der hat 4MByte Flash. Und WLAN. Aber halt wenig RAM. Irgendwas ist ja immer.
Nachdem mir Nemopuk zielsicher die Lösung eines Problem der
Datenübertragung genannt hat,
Wenn die Person hinter Nepomuk die ist, welche ich vermute, denn würde ich das Wort "zielsicher" nicht verwenden wollen ;-)
Speicherbedarf: Ein Programm welches auf dem Mega 52kB gross ist,
benötigt auf dem R4 mehr als den doppelten Speicher.
Abhilfe: keine bekannt
Ignorieren. Denn der R4 hat genügend Flash-Speicher (256k). Wie es scheint, beherrscht die CPU auch keinen Thumb Mode, welcher weniger Speicher braucht.
Compilier-Dauer: Das Compilieren dauert beim R4 mit ca. 30s deutlich
länger als beim Mega mit ca. 11s.
Abhilfe: keine bekannt
Ist halt so. Damit kann und muss man leben.
random(): Diese Funktion arbeitet beim R4 extrem langsam, da die
Routine bei jedem Aufruf den True Random Number Generator (TRNG) neu
initialisiert.
Glaub ich nicht. Wie hast du das gemessen? Was ist "extrem langsam"?
EEPROM: um exakt zu sein, gibt es beim R4 kein EEPROM, dieses wird
im Flash simuliert - bei jedem Schreiben muss die gesamte Page gelöscht
werden.
Abhilfe: Verwendung eines externen EEPROM
auf einen ESP gehen, der
hat 4MByte Flash. Und WLAN. Aber halt wenig RAM.
"Der" ESP kann auch mehr Flash-ROM haben, aber wie kommst Du auf "wenig" RAM? Welchen "ESP" meinst Du überhaupt? Es gibt viele Varianten, die alle unterschiedlich ausgestattet sind. Und obendrein kann da PSRAM angeschlossen werden, und ist es auf den besseren Modulen auch standardmäßig.
Vollkommen andere Architektur der CPU, die solcherlei Hardwarenahen
Krempel halt nicht erlaubt. Daher kann man C-Programme ja auch nie auf
eine andere Hardware übertragen, obwohl das ja eins der Hauptargumente
für C ist. Aber irgendwann nutzt man eben doch Hardwareeigenheiten, und
dann kommt das dabei raus.
Hallo Jens,
es geht um die Funktion tone(), welche kein hardwarenaher Krempel ist.
Du erwartest ernsthaft, das ein nicht mal im entferntesten ähnlicher
Controller hardwarenah identisch funktioniert?
Wie läuft den C64 mit Windows 11? Wo hast du das Userport-TPM und den
USB3.2-1541-Adapter gekauft, und wie kann man im C64-Bios Secureboot
aktivieren?
Nein das erwarte ich gar nicht sondern frage hier, ob ihr Abhilfen zu den aufgetretenen Besonderheiten kennt.
Datenübertragung: Einbrechen der Übertragungsgeschwindigkeit bei
längeren Übertragungen.
Abhilfe: keine bekannt
Unsinn! Ohne genauere Beschreibung der Übertragung und des Quellcodes
kann man da rein GAR NICHTS sagen!
Hallo Jens und Falk,
die Ursache dieses Problemes muss ich noch genauer analysieren - zurzeit ist es so, dass die Übertragung längerer Dateien an Mega und Uno R3 störungsfrei funktioniert und bei R4 nicht.
Schaut euch einfach mal den VW Golf Revision 1 und Revision 7 an.
Was haben die gemeinsam?
Nicht viel!
OK, die Räder sind weiterhin unten, und das Dach meist oben, aber sonst?
Kein relevantes Teil passt.
Nichts, außer vielleicht die ein oder andere M6er Schraube.
Dann nimmst du alle Zeilen, aus der platform.txt welche -g3 beinhalten und kopierst diese in die platform.local.txt.
Dann darin alle -g3 in -g0 ändern
Denn:
Die platform.txt wird nur einmal geladen. Die platform.local.txt bei jedem Compilerlauf.
Einträge in der platform.local.txt überdecken Einträge aus der platform.txt
es geht um die Funktion tone(), welche kein hardwarenaher Krempel ist.
Falsch gedacht.
Tone frickelt an einem Timer rum, um eine Frequenz zu erzeugen. Dazu sind Timer nicht da, d.h. das ist eine sehr hardwarenahe Funktion.
Wenn die Timer des anderen Prozessors anders funktionieren, gibt's halt Stress.
Nein das erwarte ich gar nicht sondern frage hier, ob ihr Abhilfen zu
den aufgetretenen Besonderheiten kennt.
Es gibt zwei Abhilfen: Einen Chip nehmen, der die gewünschten Funktionen nativ unterstützt, oder auf die nicht möglichen Funktionen verzichten.
Eine Emulation kann niemals zu 100% identisch funktionieren.
es geht um die Funktion tone(), welche kein hardwarenaher Krempel ist.
Falsch gedacht.
Tone frickelt an einem Timer rum, um eine Frequenz zu erzeugen. Dazu
sind Timer nicht da, d.h. das ist eine sehr hardwarenahe Funktion.
Wenn die Timer des anderen Prozessors anders funktionieren, gibt's halt
Stress.
Hallo Jens,
danke für deine Antwort - wahrscheinlich habe ich es falsch formuliert:
die Funktion tone() ist klar definiert und sollte daher auf allen Plattformen das selbe Ergebnis produzieren:
die Funktion tone() ist klar definiert und sollte daher auf allen
Plattformen das selbe Ergebnis produzieren:
Das kann sie nicht.
Sie funktioniert, weil Arduino nunmal dazu da ist simpel und kurz zu einem Ergebnis zu kommen.
Was völlig unüblich ist.
Also ist es gerade so ein "geht so", identisch kann es gar nicht sein.
Wenn du da tatsächlich von ausgehst bist du seeehr blauäugig.
Die Implementierungen von tone() sowohl AVR, als auch R4 liegen auf Github offen aus.
Da ist nichts identisch!
Bis auf die Funktionsdeklarationen.
Sonst: Komplett unterschiedlich.
nach einigen Experimenten bezüglich tone() kann ich folgendes berichten:
Die Originalversion aller Melodien (https://github.com/robsoncouto/arduino-songs) funktioniert sowohl auf dem Uno R4 als auch R3 störungsfrei.
Der Sketch-Datei beinhaltet sowohl die Defines der Tonhöhen (pitches) als auch die Noten in Form eines Integer-Arrays für jeweils ein Lied.
Zusammen mit anderen Programmteilen wird bei der Verwendung von tone() anstelle bestimmter Noten (= Frequenzen) ein merkwürdiger Ton ausgegeben - könnte man als Tschilpen beschreiben.
(Der Sketch beinhaltet nur die Abspielroutine. Tonhöhen und Noten sind in Header ausgelagert. Die Noten befinden sich im PROGMEM int melody[].
Wird tone() durch TimerFreeTone() ersetzt, so werden fast alle Melodien korrekt abgespielt. Bei 2 von 25 Melodien gibt es Probleme, diese sind durcheinander und zwar sowohl beim Lesen aus dem PROGMEM als auch aus dem EEPROM.
Werden die Melodien vom Uno R4 EEPROM als Datei ausgelesen und in das EEPROM des Uno R3 eingelesen, so ist die Wiedergabe aller Melodien korrekt. In beiden Controllern befinden sich identische Programmteile.
Ich habe das Programm, als auch die Noten schon mehrfach verglichen bzw. kontrolliert, komme aber nicht auf die Fehlerursache. Daher werde ich jetzt eine Pause einlegen....
vielen Dank für deine Antwort - was bedeutet es exakt, dass ich PROGMEM auf dem ARM vergessen kann?
Die meisten Verwendungen des PROGMEM funktionieren ohne erkennbare Fehler, die Fehler treten in Zusammenhang mit den Melodien auf.
Eventuell habe ich in der Adressberechnung auch einen Fehler? Aber weshalb dann nur für Uno R4 bzw. ARM?
Anbei ein minimierter Sketch, welcher auf dem Uno R3 fehlerfrei funktioniert und auf dem R4 die Melodien mehr oder weniger stark verfremdet.
Am stärksten durcheinander sind Melodie 8. Heilige Nacht und 9. Happy Birthday.
Nachtrag: das weiter oben beschriebene Störgeräusch (Zwitschern bzw. Tschilpen) tritt in diesem Minimal-Sketch nicht auf - hier muss ich noch finden, welche Funktion oder Bibliothek ursächlich ist.
was bedeutet es exakt, dass ich PROGMEM auf dem ARM vergessen kann?
Bei 8 Bit AVR wird der Programmspeicher mit anderen Befehlen angesprochen, als RAM. Adresse 1234 ist nicht eideutig. Damit kann RAM oder Flash gemeint sein.
Bei 32 Bit ARM Controllern ist das anders. Dort werden beide Speicher durch die selben Befehle angesprochen. Die Unterscheidung wird durch eindeutige Adress-Bereiche gemacht. Deswegen hat PROGMEM dort keinen Sinn. Beim STM32 Core ist Progmem einfach als gar nichts definiert, PGM_P ist ein normaler const char* und PSTR(von x) ist auch ein dummy, nämlich x.
Bei 8 Bit AVR wird der Programmspeicher mit anderen Befehlen
angesprochen, als RAM. Adresse 1234 ist nicht eideutig. Damit kann RAM
oder Flash gemeint sein.
Bei 32 Bit ARM Controllern ist das anders. Dort werden beide Speicher
durch die selben Befehle angesprochen.
Vor allem bedeutet es, dass du das ganze pgm_read_xxx Geraffel nicht
brauchst. Anstatttempo = pgm_read_word_near(tempis+(snd-1));
kannst also einfach:tempo = tempis[snd - 1];
nach Anpassung des oben angehangenen Minimal-Sketches werden alle Melodien auch auf dem Uno R4 richtig wiedergegeben. Somit war hier die Verwendung von PROGMEM die Ursache.
Was ich aber noch nicht verstehe ist, dass auch die Wiedergabe aus dem externen EEPROM in meinem Gesamt-Sketch an identischen Stellen wie bei Verwendung von PROGMEM fehlerhaft ist. Klar ist, dass wenn ich das EEPROM mit dem Uno R4 beschreibe die Daten deshalb durcheinander sind, weil die Routine die Melodien mittels PROGMEM ausliest und ins EEPROM schreibt.
Der Fehler tritt aber ebenfalls auf, wenn ich ein mit dem Arduino Mega beschriebenes EEPROM einsetze, welches auf Uno R3 und Mega fehlerfreie Melodien ausgibt.
Speicherbedarf: Ein Programm welches auf dem Mega 52kB gross ist,
benötigt auf dem R4 mehr als den doppelten Speicher.
Wahrscheinlich sind die Arduino-Libraries für den Peripheriezugriff beim ARM einfach komplexer, eben weil die Peripherie komplexer ist. Dafür kann sie auch mehr. Der eigene Code der Anwendung könnte sogar durchaus kleiner sein, hängt vom Einzelfall ab.
ich habe an 2 Stellen -G3 gefunden und diese auf -G0 geändert:
Das bringt gar nichts. Die Debug-Informationen landen nur in der ELF-Datei, welche sich ausschließlich auf dem PC befindet. Der eigentliche Maschinencode der auf dem Controller landet ist durch die -g Option vollkommen unverändert. Wenn also die Festplatte im PC fast voll ist und du auf dem PC jedes einzelne KB sparen musst, kannst du auf -g0 gehen, sonst würde ich Mikrocontroler-Projekte grundsätzlich immer mit -g3 kompilieren, denn für den Controller macht es keinen Unterschied (auch nicht bei der Übertragungszeit). Dies vereinfacht die Fehlersuche dramatisch - wobei die Arduino-Hardware alleine ja sowieso nicht in-circuit debuggen kann, aber für Crash-Dumps oder Disassembly o.ä. kann es trotzdem nützlich sein.
Um den Programmspeicher zu reduzieren musst du i.W. die Arduino-Libraries modifizieren und "entschlacken". Das ist viel Arbeit. Arduino kompiliert anscheinend standardmäßig schon mit "-Os", man kann noch mit LTO und ein paar anderen Optionen experimentieren. Das ist aber was für Leute die sich damit auskennen.
Compilier-Dauer: Das Compilieren dauert beim R4 mit ca. 30s deutlich
länger als beim Mega mit ca. 11s.
Abhilfe: keine bekannt
Die Arduino-IDE ist da nicht besonders clever, und kompiliert immer alles/vieles neu. Wenn du auf eine andere IDE (z.B. VS Code) umsteigst und ninja als Build-Tool nutzt, geht es viel schneller. Aber auch hier gilt dass die komplexere Library eben etwas Zeit braucht, auch beim Linken.
so stark, das es nur
mit C zu ertragen ist. In Assembler ein kurzes einfaches Blinkprogramm
zum testen geht nicht, weil das nicht oder nur sehr umständlich
dokumentiert ist wie man den Chip anspricht.
Die Dokumentation von ARM ist exzellent, wenn Renesas die Peripherie nicht gut dokumentiert liegt das nicht an ARM. Und selbstverständlich kann man problemlos in Assembler für ARM Cortex-M ein einfaches Blinkprogramm hinschreiben, das macht nur normalerweise keiner. Tatsächlich programmiert es sich im ARM-Assembler ziemlich angenehm, dank Offset-Adressierung (gerade auch auf dem Stack), der vielen als Pointer nutzbaren Register, vielfältigen Arithmetik-Instructions inkl. Multiplikation, Barrel-Shifting und FPU, effizienten Load/Store-Multi-Instructions, integrierten Shifts bei diversen Instructions (bei Speicherzugriffen und auch Arithmetik), automatischer Kontext-Sicherung bei Interrupts, und natürlich linearem Adressraums. Etwas nervig ist dass auf Peripherieregister immer als Read-Modify-Write zugegriffen werden muss, aber das ist erträglich (kann man z.B. auch in ein Makro stecken).
Daher kann man C-Programme ja auch nie auf
eine andere Hardware übertragen
Komisch dass eine Unmenge an C-Software super auf ARM-Smartphones/Laptops/Servern, x86- und x86_64 -PCs, MIPS-Routern läuft, sogar ohne Massen an "#ifdef arm" etc.
Die Arduino-IDE ist da nicht besonders clever, und kompiliert immer
alles/vieles neu. Wenn du auf eine andere IDE (z.B. VS Code) umsteigst
und ninja als Build-Tool nutzt, geht es viel schneller. Aber auch hier
gilt dass die komplexere Library eben etwas Zeit braucht, auch beim
Linken.
Mir ging es nicht um das Sparen von Arbeitsspeicher, sonden um das Verkürzen der Compilierdauer.
Mir ging es nicht um das Sparen von Arbeitsspeicher, sonden um das
Verkürzen der Compilierdauer.
Das hatte ich verstanden. Du kannst wie gesagt die Compilierdauer drastisch reduzieren indem du nicht mit der Arduino-IDE kompilierst, sondern über ein Tool das immer nur genau das neu baut, das auch wirklich geändert wurde. Wie eben über Ninja.
Beispielsweise hat ST für die STM32 jetzt Plugins für VS Code welche Ninja nutzen, und damit geht's dann sehr flott. z.B. 1-2 Sekunden wenn nur eine nicht allzu komplexe Datei geändert wurde. Das ist die gleiche Architektur und gleicher Compiler wie für den RA4M1.
i.A. geht kompilieren übrigens unter Linux schneller als unter Windows.
Angeblich kann der RA4M1 bis zu 6.6 MHz auf der seriellen Schnittstelle, die lässt sich mit dem ARM-Prozessorkern auf jeden Fall kontinuierlich bedienen, gerade auch wenn man DMA nutzt. Die Verbindung zum PC wird über USB hergestellt, mit einer theoretischen maximalen Datenrate von 12 Mbps, auch die sollte erzielbar sein. Also wieder eine Frage der Software. Kann natürlich sein dass das Arduino-Framework das nicht schafft, da muss man dann ggf. von Hand ran.
die Übertragung kürzerer Dateien (aktuell 605 Zeichen) funktioniert auch beim Arduino R4 ohne Probleme.
Meine erste Analyse zeigt, dass bei grösseren Dateien die Übertragung zwischendurch stockt (ich lasse mir das Erreichen einer bestimmten Anzahl von Daten über die serielle Schnittstelle ausgeben). Der serielle Puffer verschluckt dann scheinbar Zeichen. Am Ende der Übertragung wurden deutlich weniger Zeichen übertragen, als in der Datei vorhanden sind.
Der serielle
Puffer verschluckt dann scheinbar Zeichen. Am Ende der Übertragung
wurden deutlich weniger Zeichen übertragen, als in der Datei vorhanden
sind.
Was ist das Zielgerät? Hast du mal per Oszilloskop oder Logic-Analyzer geprüft was gesendet wird? Sendest du immer nur einzelne Bytes? Mal per USB-Serial-Adapter den Datenstrom mitgeschnitten? Wie ist die Baudrate? Klingt doch stark nach Software-Problem.
Da das Flashen der Firmware (die ja deutlich über 605 Bytes hat) ebenfalls über exakt die selbe Schnittstelle geht und problemlos klappt (sonst würde das Programm nicht laufen) ist das 100% ein Problem des Codes.
Da das Flashen der Firmware (die ja deutlich über 605 Bytes hat)
ebenfalls über exakt die selbe Schnittstelle geht
Das Flashen geht über natives USB, keine serielle Schnittstelle. Das Board hat keinen USB-Serial-Adapter. Kai nutzt vermutlich die "sekundäre" serielle Schnittstelle zur Kommunikation mit einem anderen Gerät.
Das Flashen geht über natives USB, keine serielle Schnittstelle. Das
Board hat keinen USB-Serial-Adapter. Kai nutzt vermutlich die
"sekundäre" serielle Schnittstelle zur Kommunikation mit einem anderen
Gerät.
Hallo Niklas,
richtig - die Datenübertragung zwischen PC und Arduino erfolgt über die "sekundäre" serielle Schnittstelle (19200 Baud).
jedoch tritt beim Empfangen mit Uno R4 das o. g. Problem auf.
Theoretisch könnte es ein Fehler in der Arduino Library für den R4 sein, aber ich kann mir nicht vorstellen dass so ein elementarer Fall nicht korrekt abgedeckt wird.
Das USB Protokoll enthält eine Prüfung und Flusskontrolle für die Nutzdaten, somit kann auf dem Controller kein Puffer überlaufen und Daten verloren gehen.
Daher ist es höchst wahrscheinlich ein Problem in deinem Code.
naja - der schlechte Code läuft aber auf Mega und Uno R3 seit gut 2
Monaten problemlos....
Dann bleibe halt beim Uno R3 und akzeptiere die Schuld, anstatt über den Uno R4 zu schimpfen. Wenn du die Ratschläge damals angenommen hättest, dann hättest du jetzt kein Problem.
Oder nutze dies als zweite Gelegenheit und Grund, es richtig zu machen. Neue Ratschläge braucht es dazu nicht.
ich habe nicht über den Uno R4 geschimpft, sondern Besonderheiten geschildert und nach Abhilfe gefragt. Aus meiner Sicht können alle Punkte auch für andere Uno R4 Umsteiger hilfreich sein - sei es, um eine Entscheidung für oder gegen Arduino Uno R4 zu treffen oder um nicht mit den selben Besonderheiten kämpfen zu müssen.
Übertragung PC an seriellen Monitor funktioniert nicht - Ursache dank deines Tipps gefunden Abhilfe: DTR + RTS müssen gesetzt werden
Doppelter Speicherbedarf - Ursache dank deines Tipps: ARM-Architektur Abhilfe: keine
random() sehr langsam - Ursache Routine initialisiert TRNG bei jedem Aufruf neu initialisiert (100ms) - Abhilfe dank eines Tipps von Falk gefunden
tone() Melodien fehlerhaft - Ursache PROGMEM Abhilfe: dank Tipps von Arduino F, Johann und dir ist PROGMEM nicht nötig. *)
EEPROM: ggf. schnell verschlissen - Ursache: wird im Flash simuliert Abhilfe: wahrscheinlich besser externes EEPROM verwenden
Datenübertragung: Einbrechen der Übertragungsgeschwindigkeit - Ursache: ggf. mein schlechter Code Abhilfe bisher unklar
LED-Matrix: recht dunkel - Ursache: Charlieplexing Abhilfe: keine bekannt - ist aber auch nicht so wichtig.
Interrupts: unerwartete Interrupts - Ursache: ggf. weitere von mir eingebundene Bibliotheken bzw. Funktionen Abhilfe: keine bekannt.
Nur bei Punkt 6. ist eventuell mein Code ursächlich - die Ursache muss ich noch finden - evtl. schlägt ein Timeout zu, da das Programm ja schneller abgearbeitet wird.
Als weitere Besonderheiten wäre noch zu nennen:
Bluetooth: nur BLE
Viele Grüße
Kai
*) Offen ist noch herauszufinden, welche Bibliotheken oder Funktionen den Ton selber stören.
anbei ein Minimal-Sketch bei dem die oben beschriebenen Störungen auftreten. Dieser schreibt die Daten in ein externes 32k-EEPROM und liest diese anschliessend wieder aus.
Bei allen Melodien treten mehr oder weniger starke Störungen auf. Die meisten sind aber noch erkennbar. Nur 8 und 9 sind vollkommen durcheinander.
Ohne das Einbinden des LCD werden keine I2C-Adressen gefunden. Was könnte dafür die Ursache sein?
Die Pull-Up Widerstände auf den gängigen Display Boards sind außerdem recht hochohmig. Das könnte deine Kommunikation zum EEPROM beeinträchtigen. Ich würde sie immer auf 1-3 mA auslegen, wenn die Leitungen kurz sind.
ich messe 1k6.
LCD und RTC sind auch ohne Einbinden montiert und somit deren Pull-Up aktiv.
Jedoch bekomme ich erst nach dem Einbinden des LCD Adressen angezeigt.
Hmm, darauf kann ich mir keinen Reim machen. Das Display sollte auch ohne Initialisierung nicht den Bus blockieren.
Hast du eventuell noch andere Stellen im Code, die auf das nicht initialisierte Display zuzugreifen versuchen? Wahrscheinlich nicht, das würde wohl nicht compilieren.
Nein - ich habe alle Befehle für das Display erst eingefügt, als ich keine Adressen fand. Zuerst habe ich mit dem LCD angefangen. Sobald ich nur diese 3 Zeilen einfüge:
#include <LiquidCrystal_I2C.h> // I2C-Display
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27
lcd.init();
Bei 32 Bit ARM Controllern ist das anders. Dort werden beide Speicher
durch die selben Befehle angesprochen. Die Unterscheidung wird durch
eindeutige Adress-Bereiche gemacht. Deswegen hat PROGMEM dort keinen
Sinn. Beim STM32 Core ist Progmem einfach als gar nichts definiert,
PGM_P ist ein normaler const char* und PSTR(von x) ist auch ein dummy,
nämlich x.
Hallo Nemopuk,
wie bekomme ich die Daten in den Flash bzw. wie und welche eindeutigen Adress-Bereiche muss ich angeben um beispielsweise:
Wenn ich mich recht erinnere ist Arduino so intelligent, alle Strings ins RAM zu kopieren, damit man die da benutzen kann. Auch wenn da nur ein "print 'hello world'" drinsteht und sich der String nicht ändert wird er beim Start in's RAM kopiert.
Mag aber sein das es sich da um eine ATmega328-Spezialität handelt die vom Compiler abgefrühstückt wird.
Bei Arduino und C wundert mich nix mehr.
Wenn ich mich recht erinnere ist Arduino so intelligent, alle Strings
ins RAM zu kopieren,
Das ist keine spezielle Eigenschaft von Arduino, sondern von C/C++. Um dem entgegen zu wirken, benutzt man auf AVR PROGMEM, PSTR und was sonst noch daran hängt. Arduino fügt noch due F-Strings hinzu. Das ist alles gut verständlich dokumentiert:
Auf ARM benutzt man hingegen const. Wenn dem Compiler klar ist, dass der String nicht änderbar ist, dann wird er nicht ins RAM kopiert. Direkte nur lesende Zugriffe auf den Programmspeicher brauchen bei ARM wie gesagt keiner besonderen Befehle.
Um
dem entgegen zu wirken, benutzt man auf AVR PROGMEM und PSTR. Auf ARM
benutzt man hingegen const.
Oh, also eine vollkommen hirnrissige Vorgehensweise, und Hardwareabhängig dazu?
Muss man die dann alle kennen und in #ifdef einpacken, damit man in 10 Jahren wenn man die Architektur wechselt noch weiß wo man hardwarespezifisch war?
Oder schreibt man dann einfach ein ganzes Programm neu? Ist wahrscheinlich einfacher....
Warum kann ein ach so schlauer Compiler nicht im Quellcode nachsehen, ob es irgendwo eine Zuweisung gibt, und wenn nicht ist das im Flash und wird auch kurz daraus gelesen anstatt dafür RAM zu verblasen?
Oh, also eine vollkommen hirnrissige Vorgehensweise, und
Hardwareabhängig dazu?
Hast du eine bessere Idee? Funktionen, die RAM lesen können auf AVR halt nicht den Programmspeicher lesen. Ich bin auf deine zauberhaft bessere Lösung gedpannt.
Warum kann ein ach so schlauer Compiler nicht im Quellcode nachsehen, ob
es irgendwo eine Zuweisung gibt, und wenn nicht ist das im Flash und
wird auch kurz daraus gelesen anstatt dafür RAM zu verblasen?
Jetzt schreit der Verrückte auch noch die GCC Entwickler an....
Das ist ein allgemeines C-Problem (und das schließt c# und c++ ein), denke ich.
Aber speziell bei Arduino bzw. AVR ist mir aufgefallen, das alle Nase lang Zeug auf den Stack und wieder runter geholt wird, und dabei hat der Controller nicht mal einen, der wird simuliert.
Und diese ganze hin und her schieberei kostet Speicher, Zeit und sorgt dafür das es nicht so einfach geht, den Kram auf einer anderen Architektur zu benutzen.
Ich bin auf deine zauberhaft bessere
Lösung gedpannt.
Anstatt den String beim Start in den Ram zu kopieren, und dann wenn er benutzt wird das Ram auf den Stack zu kopieren um dann die Funktion aufzurufen die den Stack abruft könnte eine kleine Funktion den String direkt auf den Stack schreiben, oder besser noch: direkt in den entsprechenden Puffer kopieren, das spart den Stack (oder dessen Benutzung) komplett ein.
Noch ne Alternative (da wir ja eh hardwarespezifisch sind) wäre auch direktes Lesen des Flash-Arrays. Kann der Bootloader ja auch, geht also.
Aber speziell bei Arduino bzw. AVR ist mir aufgefallen, das alle Nase
lang Zeug auf den Stack und wieder runter geholt wird, und dabei hat
der Controller nicht mal einen, der wird simuliert.
Natürlich haben (fast alle) AVR Stack! Und der wird auch nur so viel genutzt, wie nötig. Ich bezweifle stark, dass du einen vom Compiler optimierten Code noch weiter verbessern kannst.
Anstatt den String beim Start in den Ram zu kopieren, und dann wenn er
benutzt wird das Ram auf den Stack zu kopieren um dann die Funktion
aufzurufen die den Stack abruft ...
Wie man sieht, hast du keine Ahnung, wie C funktioniert. Der Zugriff auf Strings erfolgt immer mittels Zeiger.
puts(welcome)
Kopiert den String nicht auf den Stack, sondern legt höchstens dessen Adresse auf den Stack. Bei so wenigen Argumenten bevorzugt der Compiler allerdings die Nutzung von CPU Registern zur Übergabe. Also keine indirekte Übergabe mittels Stack.
Der andere Punkt ist, dass die Funktion puts() eine Zeiger auf Daten im RAM erwartet. Der Zeiger ist technisch eine Integer Zahl, die Adresse der Zeichenkette im RAM. Der Zeiger enthält keine Information, auf welchen Adressraum er sich bezieht. Wenn du einen String aus dem Programmspeicher ausgeben willst, musst du daher eine andere Funktion aufrufen:
Eine C-Funktion, die beides kann, müsste einen weiteren Parameters haben:
puts(welcome,RAM)
puts(welcome,FLASH)
Damit wäre man aber noch weiter von C Standard weg, als die aktuelle Lösung.
Arduino überlädt C++ Methoden, so dass dort scheinbar eine Methode beides kann. In Wirklichkeit sind es aber mindestens zwei Methoden. Der Compiler wählt die zum Zeiger-Typ passende Methode aus. C++ kann das.
Muss man die dann alle kennen und in #ifdef einpacken
Kann man. Oder man legt für jede Hardware eigene Dateien an, und lässt den Build-Prozessor entscheiden, welche Implementierung eingebunden wird. So wie Arduino das mit den "Cores" macht.
könnte eine kleine Funktion den String ... besser noch:
direkt in den entsprechenden Puffer kopieren
Das wird ja oft so gemacht. Dann übergibst du einen Zeiger auf den vorbereiteten Puffer. Die ganze Standard C Bibliothek ist darauf ausgelegt.
Noch ne Alternative (da wir ja eh hardwarespezifisch sind) wäre auch
direktes Lesen des Flash-Arrays.
Das macht puts_P() ja.
Es macht wenig Sinn, die Programmiersprache C zu kritisieren. Sie wurde 1972 für die damals aktuellen Computer entworfen. Natürlich hatten viele Menschen inzwischen andere (teils bessere) Ideen. Aber dann wäre es kein C mehr, sondern vielleicht Basic, oder Pascal, oder ...
Bei C hast du die Garantie, dass sich solche Sachen nicht ändern. Wenn du es ändern willst: gerne, aber unter einem anderen Namen. Du darfst dich gerne in die endlos lange Reihe der Erfinder stellen, die einen "besseren" Nachfolger von C entworfen haben.
Das würde bedeuten, dass titel, melody und tempi im Flash sind.
Aber was verbraucht dann so viel RAM?
Der Sketch verwendet 81312 Bytes (31%) des Programmspeicherplatzes. Das Maximum sind 262144 Bytes.
Globale Variablen verwenden 9020 Bytes (27%) des dynamischen Speichers, 23748 Bytes für lokale Variablen verbleiben. Das Maximum sind 32768 Bytes.
Der größte Verbraucher sind die Arduino Libraries - "Wire" und die Puffer für die seriellen Schnittstellen etc. Sieht ansonsten auf den ersten Blick unauffällig aus.
Vielleicht hast du "const" vergessen oder an die falsche Stelle gesetzt.
Da es C++ ist sollte man besser constexpr nutzen um ganz sicher zu gehen:
1
constexprintmelody[]={1,2,3};
Ist es sinnvoll den Datentyp "int" zu nutzen? Der ist auf ARM 32bit groß. Wenn deine Zahlen einen kleineren Wertebereich haben, nutze int16_t, int8_t, uint16_t oder uint8_t.
Cortex-M4 (und nicht nur der) hat Maschinenbefehle um gefahrlos und ohne Zeitstrafe auf Bytes (LDRB/STRB), Halfwords (LDRH/STRH) und natürlich volle Words (LDR/STR) zuzugreifen.
Anständig /aligned/ müssen sie aber sein, sonst gibt's Saures.
Mit PROGMEM auf Arduino Uno R3 ergibt sich folgender Speicherverbrauch:
Der Sketch verwendet 15472 Bytes (47%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
Globale Variablen verwenden 226 Bytes (11%) des dynamischen Speichers, 1822 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.
Das ist deutlich weniger RAM - allerdings für die paar Variablen auch recht viel
vielen Dank - aber was genau kann ich mit deinem Sketch erreichen?
Du kannst damit nachvollziehen, dass const(expr) Daten eben nicht im Ram landen.
Nicht mehr, und nicht weniger.
Es wird Zeit für dich, dass du akzeptierst, dass die Flash Geschichte sich grundsätzlich zwischen ARM und AVR unterscheidet.
Dein ganzes mit dem Füßen auf dem Boden rumstampfen hilft dir nichts.
Es setzt keine Fakten außer Kraft.
Tendentiell eher nur bei lokalen/temporären Variablen, aber auch i.W. nur weil die Compiler noch nicht gut genug sind das entsprechend zu optimieren. Bei globalen unsignierten Variablen hat es keinen Nachteil kleinere Datentypen zu nutzen, denn der ARM kann über die LDRB/LDRH -Instruktionen beim Laden die Variable automatisch auf 32bit "ausdehnen" (zero-extend). Bei Signed-Werten muss je ein ganzer Takt für die Sign-Extension aufgewendet werden (SXTH/SXTB). Das dürfe in den allermeisten Fällen ein guter Trade-Off sein.
Doch ich wollte ihn nicht mit optionalen Details überfordern.
Das ist nicht optional, sondern eine Empfehlung/Ansage
Zudem macht es keinen Sinn Nutzer mit falschen Informationen zu füttern, oder sie für zu dumm für die Wahrheit zu erklären.
es ist mir gelungen, auch die .map-Datei für den Uno R3 zu erzeugen, aber das bringt uns ja nicht weiter.
Ich habe jetzt verstanden, dass die Arduino Uno R4-Welt eine ganz andere ist.
Dauert bei mir manchmal etwas länger - hängt wohl mit dem Alter zusammen - auch wenn mich vom allseits beliebten alten Knacker noch ein Jahrzehnt trennt.
bezüglich der Melodien habe ich festgestellt, dass die Art des Zugriffs auf die Daten nicht dafür ursächlich ist, dass Melodie 8 und 9 völlig falsch wiedergegeben werden. Verschiebe ich beispielsweise Melodie 8 an die erste Stelle, so wird diese immer noch falsch wiedergegeben.
Auch habe ich die Noten nochmal aus dem Original kopiert. Auch hiermit ändert sich das Verhalten nicht.
Du trittst auf der Stelle, kommst mit deinen "Diagnosen" keinen Millimeter weiter.
Was würde ich tun, wenn mein Arduino falsche Töne ausgibt?
Ich würde testen, ob wirklich die richtigen Werte in die tone Funktion rein kommen.
Würde prüfen ob in dem Tone Gedönse die Berechnungen korrekt sind.
Ist das alles korrekt, dann:
Mir die Doku/Quellcode zu dem verwendetem Timer zur Gemühte führen.
Test Cases bauen um dessen Funktion ausgiebig zu stressen.
Grundsätzlich:
Irgendwann macht es keinen Sinn mehr an der Oberfläche rum zu prokeln, dann muss man in die Tiefe gehen.
Irgendwann macht es keinen Sinn mehr an der Oberfläche rum zu prokeln,
In dem Fall ist die "Oberfläche" ein Brei aus Schlüsselworten, Variablennamen und Sonderzeichen, mit einem Zuckerguss aus Kommentarspalte.
Keine Erkennbaren Coding-Rules, und Leerzeichen und Zeilenumbruch werden gemieden wie der Teufel das Weihwasser.
Sorry für die harsche Kritik, aber so kann man keinen Überblick über den Code und seine Struktur bekommen.
Was im (nicht-vorhandenen) Coding-Style anfängt, setzt sich in den Datenstrukturen — oder vielmehr Nicht-Strukturen fort. Das Array mit den Noten sind einfach nur aneinandergeklatschte Zahlen, statt Strukturierung in einzelne Melodien, die aus Noten und Pausen bestehen.
Dementsprechend umständlich wird der Code, da helfen auch keine Kommentare.
vielen Dank für die aufbauenden Worte - ich betreibe das nur als Hobby und der Minimal-Sketch war zugegeben auch mit der heißen Nadel gestrickt...
Nun habe ich mir den Tipp von Arduino F zu Herzen genommen und die Parametrierung von note() kontrolliert.
Da war alles in Ordnung - aber der Divider hatte anstelle negativer Werte riesige Zahlen (z. B. 65531). Damit war klar, dass die negativen Werte nicht passen. Fehlerursache war, dass die negativen Werte vom ARM falsch interpretiert werden.
Die Lösung war. dass ich das mit read_integer aus dem EEPROM gelesene int16_t für den ARM in ein int32_t wandeln musste.
Deshalb funktionierte es auch auf dem Uno R3....
dass die negativen Werte vom ARM
falsch interpretiert werden.
Diese Sicht auf die Dinge, ist wenig auf Ziel führend.
Um es unverblümt zu sagen:
Irgendwer ist schlampig mit Datentypen umgegangen.
Sowas führt gerne in ein "Undefined Behavior" oder Kurz: UB
Und in ein solches UB bist du gestolpert.
Bei dir waren es nur schiefe Töne. Aber nächstes mal kann es dir den Rasen mähen, oder ein Regenschauer im Kongo auslösen.
Spielt keine Rolle.
Ob Hobby oder Beruf...
Wir(Ich) wollen, dass unsere Programme fehlerfrei laufen.
Dazu braucht man etwas Wissen, viel Übung und auch Sorgfalt.
Alles keine unerreichbaren Dinge für Leute mit mehr als 2,5 Gehirnwindungen.
das map-File macht mich nicht viel schlauer.....
Kannst du da etwas herauslesen?
Ich bin jetzt wieder zu hause, so dass ich es mir anschauen konnte. Am interessantesten ist die .bss Sektion.
Dein eigener Code belegt nur wenig RAM, siehe Screenshot. Dort kannst du sehen, dass der Compiler die meisten globalen Variablen weg optimiert hat. Für sie wird kein RAM belegt, sondern CPU Register.
1
1 kB für Wire
2
1 kB für Wire1
3
1.2 kB für _UART1_
4
1.2 kB für _UART2_
5
1.2 kB für _UART3_
6
1.3 kB für USB
7
258 Bytes für ADC
8
104 Bytes für PWM
Das sind schon 7 kB. Wenn du Programme mit der STM32 Cube IDE generierst sieht es ähnlich aus. Gewöhne dich dran, oder bleibe bei 8 Bit.
Beim Arduino Uno (ATmega328) entfällt viel davon, weil:
Nur eine Instanz von Wire existiert
Nur eine serielle Schnittstelle existiert
Die Puffer viel kleiner sind
Die USB Schnittstelle in einem separaten Chip implementiert ist
Der ursprüngliche AVR Core ist zwangsläufig stärker auf geringen Speicherbedarf ausgelegt, weil er auf Mikrocontrollern mit nur 1 kB RAM laufen musste.
Das so viel Speicher für unbenutzte Hardware verschwendet wird, stört euch nicht?
Oder werden in dem Dudelding tatsächlich zwei I²C, 3 UARTS, USV und ADC bneutzt?
Das so viel Speicher für unbenutzte Hardware verschwendet wird, stört
euch nicht?
Tja, das Arduino-Framework könnte es so umsetzen dass der Speicher nur dann reserviert wird wenn im Code irgendwo die entsprechende "SerialX" -Instanz genutzt wird, auch dann wenn eine ISR im Spiel ist die vom Interrupt-Vektor referenziert wird und die dann die jeweilige Serial-Instanz referenziert. Wird aber offenbar nicht gemacht...
Tja, das Arduino-Framework könnte es so umsetzen dass der Speicher nur
dann reserviert wird wenn im Code irgendwo die entsprechende "SerialX"
-Instanz genutzt wird, auch dann wenn eine ISR im Spiel ist die vom
Interrupt-Vektor referenziert wird und die dann die jeweilige
Serial-Instanz referenziert. Wird aber offenbar nicht gemacht...
Der Weg dahin ist quasi nur einen Pullrequest entfernt.
Hab wenig Motivation auf OpenSource-Arbeit weil ich keine Muße für das Hoch-Arbeiten und sich-einen-Namen-Verdienen habe. Als unbekannter Contributor braucht man bei vielen Open-Source-Projekten nicht mit komplexeren Refactorings um die Ecke kommen. Die meisten meiner PRs werden abgelehnt bzw. ignoriert, außer die beim ESP-IDF, die Leute da arbeiten zielorientiert. Keine Lust auszuprobieren wie die Arduino-Leute so drauf sind.
Ich meinte nicht "kann man benutzen", sondern "werden benutzt".
Ich hätte gedacht, das der Compiler nur die Komponenten einbindet, die auch z.B. durch ein serial.init() benutzt werden.
Somit sollte für die nicht benutzten I²C und UARTs z.B. einfach ein "Nulltreiber" benutzt werden sofern erforderlich, der die entsprechenden Register auf "aus" mit minimalem Stromverbrauch und ohne Beeinflussung der anderen Resourcen setzt.
Es gibt kein serial.init() sondern ein Serial.begin() und dafür muss Serial vorhanden sein, sonst kein begin().
Und nein, auf keinem Arduino Board muss Serial extra eingebunden werden. Es ist immer vorhanden, wenn das Board die nötige Hardware mitbringt.
Ansonsten:
Schön, dass du was zum nörgeln gefunden hast.
Leider bin ich die falsche Adresse.
Auch du darfst hier
https://github.com/arduino/ArduinoCore-renesas
gerne deine Verbesserungen unterbringen.
Die nicht mal 1% der Anwender jemals ausgenutzt haben!
Wie sollen Arduino-Nutzer das auch jemals nutzen? Die können nur 2-3 libs einbinden und dann jeweils mit 5 Befehlen, die sie per copy&paste ausm Netz haben, ansprechen.
Kein Arduino-Opfer kann kilobyteweise Code selbst schreiben der danach auch irgendwie läuft.
Hallo Nemopuk, Arduino F, Niklas, Norbert, Joachim B und Jens M,
Nemopuk:
Für mich klingt das nach einem nur halb verstandenem Problem. Denn ARM
Controller können durchaus auch 16 Bit Integer korrekt benutzen.
du hast vollkommen recht: mit Zuweisung zu einem int16_t funktioniert es ebenfalls - nur nicht mit einem int.
Joachim B:
naja - der schlechte Code läuft aber auf Mega und Uno R3 seit gut 2
Monaten problemlos....
nutze doch die bedingte Compilierung und berücksichtige die beiden
Zeigergrößen 16bit zu 32bit dann ist der doppelt so große Code kein
Problem
entschuldige - verstehe ich leider nicht - kannst du es bitte genauer erläutern?
Nemopuk:
Ich bin jetzt wieder zu hause, so dass ich es mir anschauen konnte. Am
interessantesten ist die .bss Sektion.
Dein eigener Code belegt nur wenig RAM, siehe Screenshot. Dort kannst du
sehen, dass der Compiler die meisten globalen Variablen weg optimiert
hat. Für sie wird kein RAM belegt, sondern CPU Register.
vielen Dank für deine Analyse - das hilft mir beim Verständnis weiter - ich hatte fälschlicherweise angenommen, dass dies meine Daten sind - hätte ich als erstes ein einfaches Blinkprogramm hochgeladen, wäre mir der erhöhte Speicherbedarf aufgefallen.
Arduino F:
dass die negativen Werte vom ARM
falsch interpretiert werden.
Diese Sicht auf die Dinge, ist wenig auf Ziel führend.
Um es unverblümt zu sagen:
Irgendwer ist schlampig mit Datentypen umgegangen.
korrekt: die Zuweisung zu einem int funktioniert nur auf AVR - für den ARM muss einem int16_t zugewiesen werden.
Sowas führt gerne in ein "Undefined Behavior" oder Kurz: UB
Und in ein solches UB bist du gestolpert.
Bei dir waren es nur schiefe Töne. Aber nächstes mal kann es dir den
Rasen mähen, oder ein Regenschauer im Kongo auslösen.
richtig - aber ein gründlicher Software-Test ist mindestens ebenso wichtig und dabei ist das Problem (UB) aufgefallen....
ich betreibe das nur als Hobby
Spielt keine Rolle.
Ob Hobby oder Beruf...
Wir(Ich) wollen, dass unsere Programme fehlerfrei laufen.
Dazu braucht man etwas Wissen, viel Übung und auch Sorgfalt.
Alles keine unerreichbaren Dinge für Leute mit mehr als 2,5
Gehirnwindungen.
hast ja recht - aber ich habe nicht absichtlich falsch codiert, sondern mangels vollständigen Verständnisses.
entschuldige - verstehe ich leider nicht - kannst du es bitte genauer
erläutern?
Ich übersetze es dir mal:
Ihm hat den Präprozessor halbwegs verstanden, und meint, diesen Hammer auf alles anzuwenden, was ihm an C++ noch nicht verstanden hat.
Völlig unbelastet, von der Tatsache, dass gerade die Vermeidung von Präprozessor Statements eins der Entwicklungsziele in modernerem C++ ist.
danke für deine Antwort - dann ist das kein möglicher Weg?
Eine Frage zu Bluetooth auf dem Arduino Uno R4: ist es wirklich damit nicht möglich, grössere Datenmengen wie z. B. mit dem HC05 an den Controller zu übertragen?
Zurzeit verwende ich ein HC05-Modul am 328P bzw. Uno R3, um die Daten mit dem Gerät im Wohnzimer auszutauschen, ohne es aus dem Gehäuse befreien und in meine Bastelecke transportieren zu müssen, was auch fehlerfrei funktioniert.
Doch, möglich!
So wie man auch mit dem Kopf durch die Wand gehen kann, selbst wenn der Mauerer 2m weiter ein Loch gelassen hat.
Die Frage ist nicht, ob möglich, sondern ob nötig.
danke für deine Antwort. Zurzeit habe ich 3 Varianten des Sketches:
Uno R3 (bzw. 328P)
Mega:
Uno R4
und hatte schon mal überlegt, ob ich diese mit konditionierter Programmierung zu einem zusammenfassen könnte, befürchte aber, dass dieser aufgrund des Overheads nicht mehr lesbar ist.
Eine weitere Frage zum Datentyp long: ich glaube festgestellt zu haben, dass sich dieser zwischen Uno R3 und Uno R4 unterschiedlich verhält. Hast du hierzu Informationen?
danke für den Link - ist es somit richtig, dass ein long sowohl auf AVR als ARM 32 bit hat? Ein unsigned long müsste somit den Wertebereich bis 4.294.967.295 haben.
Ich habe den Verdacht, dass meine CRC-Berechnung ein Problem verursacht. Diese ermittelt die benötigte Anzahl führenden Nullen und hängt daran die Summe aller Daten als CRC an, so dass sich immer die selbe Anzahl an Ziffern am Ende der Datei befindet:
1
void add_crc(unsigned long crc){
2
unsigned long check = 0x10; // Mask
3
for (byte i = 0; i < 7; i++){ // Check all nibbles
4
if(crc < check){Serial.print(F("0"));} // Add leading 0
Diese ermittelt die benötigte Anzahl führenden Nullen und hängt daran
die Summe aller Daten als CRC an, so dass sich immer die selbe Anzahl an
Ziffern am Ende der Datei befindet
Viel Prosa, aber keine Fakten
Wie wäre es mit Beispieldaten.
Was soll raus kommen, wie sieht das Ergebnis stattdessen aus?
mit Zuweisung zu einem int16_t funktioniert es
ebenfalls - nur nicht mit einem int.
Liegt vermutlich daran dass die EEPROM Leseroutine sowas ist wie
1
uint16_teeprom_read_word(constuint16_t*);
Wenn der Rückgabewert einem int zugeweisen wird, dann wird /zuerst/ eine unsigned Erweiterung auf die Bitbreite von int gemacht, und erst dann dem int zugewiesen.
1
intval=(int16_t)eeprom_read_word(addr);
hingegen wird der 16-Bit Wert sign-extended (wobei man da formal u.U. ein signed Overflow hat).
Zu long:
Seit über 25 jahren gibt es uint32_t. Warum nicht das verwenden?
ich habe die Datenübertragung nun mit einem Uno R4 Minima getestet und dabei festgestellt, dass sie hier fehlerfrei funktioniert. Der Sketch ist der selbe, wie für den Uno R4 WiFi, jedoch ohne die LED-Matrix.
Die Daten kommen kontinuierlich und ohne Stocken herein und anschließend ist die CRC in Ordnung und die erwartete Anzahl von Bytes eingetroffen:
1
CRC523E8OKdata-type:melodyall0end:8605bytes:8605
Die angezeigte CRC 523E8 ist also richtig berechnet, da diese mit der von der Übertragung berechneten übereinstimmt.
Anschliessend habe ich diesen Sketch wieder in den Uno R4 WiFi übertragen. Hier stockt die Datenübertragung mehrfach. Die kritische Datenlänge beträgt etwa 5200 Bytes. Bis zu dem Erreichen dieser Anzahl funktioniert die Übertragung. Darüber bricht sie ab.
Arduino UNO R4, als jemand der erfolgreich einen Pull-Request für ArduinoCore-renesas gemacht hat, kann ich Dir sagen, warum der Arduino UNO R4 an manchen Stellen langsam ist.
Da sitzt noch eine Software-Schicht dazwischen, die Arduino Klassen rufen Funktionen der Renesas Flexible Software Package (FSP) auf.
Diese Library liegt im Arduino Core Repository auch nicht als Source-Code vor, sondern als .lib, sonst würde das Compilieren noch viel länger dauern.
Bei meinen ersten Versuchen vor zwei Jahren hat mein Test-Programm für eine SPI Übertragung von 231 Bytes auf dem UNO R4 fünf Mal so lange gebraucht wie das gleiche Programm mit dem gleichen SPI Takt auf einem UNO R3.
Das Problem war dann, das die SPI Klasse für ein SPI.transfer(data); nur eine einzige FSP Funktion aufrufen kann, es gibt keine Alternative dazu.
Und diese FSP Funktion hat jedesmal die SPI Einstellungen geprüft, die SPI Einheit aktiviert, die SPI Einheit konfiguriert, das Byte verschickt und die SPI Einheit wieder deaktiviert.
Darauf habe ich dann die SPI Klasse auf Register-Zugriffe umgeschrieben.
Inklusive so Spaß dabei das es ein notwendiges Register gibt das im User-Manual vom Controller nicht dokumentiert ist (oder war), zwar in den Includes enthalten und von FSP verwendet, aber nicht dokumentiert.
Ach ja, irgendwo in diesem Thread kam ein Spruch DMA zu benutzen.
Wenn es nach mir ginge, dann könnte die UNU R4 SPI Klasse jetzt auch DMA, der Pull-Request wäre damit aber nicht angenommen worden, da die offizielle Arduino API kein DMA unterstützt.
Das gleiche oder ein ähnliches Problem werden die anderen Arduino Klassen auch haben, sofern sich da noch niemand erbarmt hat die zu Überarbeiten.
Das wurde implementiert und auf Funktion geprüft, da hat sich bei Arduino offenbar vor Veröffentlichung niemand die Mühe gemacht einen Logic-Analyzer zu benutzen und Renesas kümmert das offensichtlich auch nicht.
Compilier-Zeit.
Da kann ich mich nur der Empfehlung anschließen Platform-IO zu verwenden.
Das hat dann auch gleich den Vorteil, dass man Build-Flags setzen kann.
Ein Build von meinem Test-Projekt hat hier gerade 2.47s für den UNO R3 und 5.53s für den UNO R4 gebraucht - und das war nur so lange, weil ich vorher ein "Full Clean" gemacht habe.
Größe des Builds.
UNO R3 -Os
RAM: [ ] 3.6% (used 73 bytes from 2048 bytes)
Flash: [=== ] 30.7% (used 9890 bytes from 32256 bytes)
UNO R3 -O2
RAM: [ ] 3.6% (used 73 bytes from 2048 bytes)
Flash: [==== ] 39.1% (used 12598 bytes from 32256 bytes)
UNO R4 Minima -Os
RAM: [== ] 21.1% (used 6912 bytes from 32768 bytes)
Flash: [== ] 18.1% (used 47492 bytes from 262144 bytes)
UNO R4 Minima -O2
RAM: [== ] 21.1% (used 6924 bytes from 32768 bytes)
Flash: [== ] 20.1% (used 52736 bytes from 262144 bytes)
Von weit weg betrachtet immer das gleiche Programm, das läuft auch so mit diesen und noch mehr Controllern.
Im Detail macht jeder Arduino Core seine eigenen Faxen und das sind diverse verschiedene Compiler.
Solange das Programm in den Controller passt macht das ja auch nichts.
Darum nutze ich normalerweise auch -O2 und nicht -Os.
if(crc<check){Serial.print(F("0"));}// Add leading 0
5
check=check<<4;}// Next nibble
6
Serial.print(crc,HEX);}// Add crc
7
}
Deine Code-Formatierung ist unter aller Sau. kein Wunder, dass du stellenweise selbst nicht durchblickst. Benutze doch bitte wenigstens die Auto-Formatierung deiner IDE, wenn du schon so faul bist.
Die macht daraus folgendes:
1
voidadd_crc(unsignedlongcrc){
2
unsignedlongcheck=0x10;// Mask
3
for(bytei=0;i<7;i++){// Check all nibbles
4
if(crc<check){Serial.print(F("0"));}// Add leading 0
5
check=check<<4;
6
}// Next nibble
7
Serial.print(crc,HEX);
8
}// Add crc
9
}
Und schon wird (in der letzten Zeile) offensichtlich, dass da eine Klammer zu viel drin ist.
Da werde ich mich wohl tiefer einlesen müssen oder kennst du eine
einfache Beschreibung, wie man dieses für Arduino einsetzt?
Ich versuchs mal, ich habe schon ewig kein Projekt komplett neu angelegt.
Ich benutze das als Extension in VSCode.
Nach der Installation hat man erstmal PlatformIO auf der Linken Seite in der Leiste.
Und wenn das "PIO Home" nicht direkt aufgeht, dann kann man das über "Open" aus dem "Quick Access" Menü öffnen.
Und auf der "PIO Home" Seite gibt es "New Projekt", "Import Arduino Project" und "Project Example".
Kann sehr gut sein, dass man da per Default direkt nach der Installation noch nicht weit kommt, genau wie die Arduino IDE nach der Installation auch noch nicht alles kennt.
Dann muss man im "Quick Access" Menü oder auf dem "PIO Home" auf "Platforms" drücken und da auf den Reiter "Embedded" wechseln.
Da werden die ganzen Platforms für Controller gelistet und es wird auch jeweils angezeigt welche Frameworks die unterstützen.
Wenn man eine Platform anklickt bekommt man einen "Install" Button.
Wenn man eine Platform im "Installed" Reiter anschaut, dann kann sich dort auch die "Examples" anschauen.
ist es wirklich damit nicht möglich, grössere
Datenmengen ... zu übertragen?
Wer sagt das, du?
Es ist mit Sicherheit möglich, beliebig große Datenmengen zu übertragen.
Der Mikrocontroller schränkt dich dabei nicht ein.
es ging hier um Bluetooth BLE, von welchem ich las, dass dieser nur für das Übertragen kleiner Datenmengen - wie z. B. Sensordaten - geeignet sei. Grössere Mengen müssten in Chunks aufgeteilt werden. Bisher habe ich noch keine Beschreibung gefunden, wie man dieses ähnlich eines HC05 verwenden kann.
Deine Code-Formatierung ist unter aller Sau. kein Wunder, dass du
stellenweise selbst nicht durchblickst. Benutze doch bitte wenigstens
die Auto-Formatierung deiner IDE, wenn du schon so faul bist.
ich glaube, dass ich erst recht nicht mehr durchblicke, wenn ich 297kB Quellcode autoformatieren lasse.....
es sieht so aus, als wenn der Uno R4 Minima egal mit welcher Einstellung des seriellen Monitors immer mit 115200 Baud kommuniziert und der serielle Puffer mindestens 254 Bytes groß ist. Die Übertragung von 8605 Zeichen funktioniert problemlos.
Im Gegensatz dazu arbeitet der Uno R4 WiFi mit der eingestellten Baudrate des seriellen Monitors. Auch hier scheint der Puffer mindestens 255 Bytes groß zu sein. Nach Änderung von Serial.begin auf 115200 Baud ist das Stocken deutlich kürzer, leider wird die Übertragung immer noch abgebrochen - und zwar bereits nach 4500 Zeichen..
Im Gegensatz dazu arbeitet der Uno R4 WiFi mit der eingestellten
Baudrate des seriellen Monitors.
Soweit mir bekannt arbeiten beide mit Serial über natives USB, also CDC.
Da ist die eingestellte Baudrate völlig egal.
Einzig die 1200 Baud haben eine Sonderrolle in der Arduino Welt.
Nein, U2 und U6.
Aber der hat keinen Einfluss auf die Baudrate.
SJ1 ist der Löt-Jumper mit welchem du U2 und U6 umschalten kannst.
Ist dieser offen, so ist der USB mit dem ESP verbunden - dieser arbeitet dann als Bridge zum ARM.
@Arduino F: ich möchte den SJ1 des Uno R4 WiFi noch nicht umlöten, da damit vermutlich auch die WiFi-Funktionalität wegfällt und ggf. auch das unschöne Verhalten des Uno R4 Minima hinzukommt, beim Flashen jedes Mal den USB zu trennen und danach neu zu verbinden.
um das Problem eingrenzen zu können, habe ich einen Minimal-Sketch erstellt und damit das Verhalten der verschiedenen Boards genauer analysiert - geprüft jeweils mit der selben Test-Datei mit einer Länge von 17210 Bytes.
Der Sketch sendet alle 20 Zeichen ein + über den Seriellen Monitor (Damit man das Stocken des Arduino Uno R4 WiFi besser nachvollziehen kann) und am Ende der Übertragung eine Zusammenfassung:
Arduino Mega:
Kontinuierliche Datenübertragung, der Puffer läuft auf max. 9 Bytes hoch, wird zwischendurch leer, alle gesendeten 17210 Bytes werden empfangen.
Arduino Uno R4 Minima:
Kontinuierliche Datenübertragung, der Puffer läuft auf max. 255 Bytes hoch, wird zwischendurch nicht leer, alle gesendeten 17210 Bytes werden empfangen.
Arduino Uno R4 WiFi:
Die Datenübertragung stockt zum ersten Mal nach 255 Bytes, anschließend nach einer größeren Anzahl von Bytes, der Puffer läuft auf max. 9 Bytes hoch, wird zwischendurch leer, es werden max. 11398 Bytes empfangen.
Hat einer von euch eine Erklärung für das abweichende Verhalten des Uno R4 WiFi?
Viele Grüße
Kai
PS: die Testdatei habe ich in den Seriellen Monitor kopiert und damit gesendet
Ich stellte für den Uno R4 Minima fest, dass es exakt so ist, wie du beschreibst - eine Änderung der Einstellung des seriellen Monitors hat keinerlei Auswirkungen - die Daten werden mit einer festen (hohen) Baudrate gesendet und empfangen.
Beim Uno R4 WiFi werden die Empfangs-Daten nicht mehr korrekt angezeigt, wenn ich eine von der Definition des Serial.begin(19200) abweichende Baudrate einstelle. Wie schnell die Sende-Daten ausgegeben werden, konnte ich noch nicht eindeutig ermitteln. Aus der Feststellung, dass der Puffer auf maximal 9 Bytes anwächst, schliesse ich aber, dass die Baudrate ebenso hoch ist, wie beim Mega - nämlich 19200 Baud - da hier der Puffer ebenfalls auf maximal 9 Bytes anwächst.
ist der USB Swich standardmäßig auf den ESP eingestellt. Dieser ist wiederum über UART mit dem Renesas Controller verbunden. Deswegen gibt es eine wirksame Baudrate. Der ESP spielt hier die Rolle des USB-UART Adapters.
Dein Hauptproblem ist immer noch, dass du kein vernünftiges Übertragungsprotokoll hast. Du ballerst den Mikrocontroller einfach rücksichtslos mit Daten zu, in der Hoffnung, dass er schnell genug mit kommt. Im vorherigen Thread wurde dir bereits dringend davon abgeraten.
Gerade bei USB gibt es kein verlässliches Timing mehr. Deswegen taugt Timing alleine nicht als Übertragungsprotoll.
ist der USB Swich standardmäßig auf den ESP eingestellt. Dieser ist
wiederum über UART mit dem Renesas Controller verbunden. Deswegen gibt
es eine wirksame Baudrate. Der ESP spielt hier die Rolle des USB-UART
Adapters.
Dein Hauptproblem ist immer noch, dass du kein vernünftiges
Übertragungsprotokoll hast. Du ballerst den Mikrocontroller einfach
rücksichtslos mit Daten zu, in der Hoffnung, dass er schnell genug mit
kommt. Im vorherigen Thread wurde dir bereits dringend davon abgeraten.
ich verstehe dein Argument vollkommen. Aber welche Abhilfe gibt es?
Da es kein Anzeichen dafür gibt, dass der Controller nicht schnell genug ist, kann ich auch keine Maßnahme dagegen treffen.
Natürlich wäre es möglich, immer nur ein kleines Paket zu senden. Aber wie groß soll es sein?
Meine Feststellung ist, dass beim Arduino R4 WiFi oberhalb einer bestimmten Grenze keine Daten von der UART mehr kommen. Bis dahin hat der Controller immer brav alle Daten abgeholt und dies so schnell geschafft, dass der Puffer nur auf 9 Bytes angewachsen ist. Also hat sich die Hoffnung erfüllt, dass er schnell genug ist....
danke für deine Antwort und den Hinweis auf ZMODEM, welches ich mir jetzt genauer ansehen werde.
Ich habe mir die damalige Diskussion noch einmal komplett durchgelesen. Die Quintessenz ist:
a) Benutze ein gesichtertes Protokoll oder
b) Puffere die Daten im RAM und schreibe dann ins EEPROM.
Damals hatte ich nicht genug RAM, weshalb ich hier
ablehnend reagierte.
Jetzt hätte ich genug RAM, wird aber nichts am Verhalten ändern, da
es identisch zu meinem zuletzt geposteten Minimal-Sketch
funktioniert.
Wozu hat der RS232-Gott eigentlich RTS und CTS erfunden?
Und wenn man die 4 Cent für die Stecker gespart hat (USB-UARTS können das eigentlich alle!) kann man ja auch mit XON/XOFF arbeiten.
Das dürfte einfacher sein als ZModem.
Oder man reduziert die Sache einfach auf "ey, hörst du?" "Jau" "gut dann haste hier 16 Bytes" "ok, danke. kommt da noch was?", ähnlich wie es viele Bootloader machen. Klappt auch 100%.
das Aufteilen der 17212 Bytes großen Datei in 3 Pakete und eine Pause von über 2 Sekunden zwischen dem Senden derselben bringt eine Änderung des Verhaltens:
Das Ergebnis der Datenübertragung ist dann fehlerfrei.
Daraus folgere ich, dass beim Uno R4 die Übertragung bei Überschreitung eines bestimmten Wertes des Sendepuffers gesperrt und erst bei Unterschreitung eines deutlich niedrigeren Wertes wieder freigegeben wird.
Das ist alles Open Source, was sagt denn die Analyse der Arduino Klasse
dazu?
wo findet man diese genau? Hat einer von euch bitte einen Link dazu?
Wahrscheinlich ist das Verhalten wie folgt:
Behauptung: Es gibt für die USB-Interfaces aller Boards eine Beschränkung des Sendepuffers auf ca. 12kB.
Der Unterschied, ob diese zu tragen kommt, ist die Baudrate für das Senden und das Abholen vom USB-Interface.
Arduino Uno R3 und Mega:
Der serielle Monitor sendet und empfängt die Daten an / vom USB-Interface mit der eingestellten Baudrate. Die Daten werden vom Controller genauso schnell abgeholt, wie sie gesendet werden. Der Sendepuffer erreicht seine Grenze nicht.
Arduino Uno R4 Minima:
Der serielle Monitor sendet die Daten mit hoher Baudrate an das USB-Interface, diese werden vom Controller ebenfalls mit identisch hoher Baudrate abgeholt. Der Sendepuffer erreicht seine Grenze nicht.
Arduino Uno R4 Wifi:
Der serielle Monitor sendet die Daten mit hoher Baudrate an das USB-Interface, diese werden vom Controller aber nur mit der im seriellen Monitor eingestellten (deutlich niedrigeren) Baudrate abgeholt. Der Sendepuffer erreicht seine Grenze und sperrt die Übertragung.
Werden die Daten durch die App in Pakete unterhalb der Grenze aufgeteilt und im Abstand von ca. 2s gesendet:
1
if (delayCounter > 0)
2
{
3
string gesamt = textBox1.Text;
4
int Ende = dateiLänge - 12000 -1;
5
string teil1 = gesamt.Substring( 0 , 6000);
6
string teil2 = gesamt.Substring( 6000 , 6000);
7
string teil3 = gesamt.Substring( 12000 , Ende);
8
9
if (delayCounter == 45) { serialPort1.Write(teil1); }
10
if (delayCounter == 22) { serialPort1.Write(teil2); }
11
if (delayCounter == 1) { serialPort1.Write(teil3); }
12
}
erreicht der Sendepuffer seine Begrenzung nicht, da der Sendepuffer immer wieder geleert wird.
Arduino Uno R4 Minima:
Der serielle Monitor sendet die Daten mit hoher Baudrate an das
USB-Interface, diese werden vom Controller ebenfalls mit identisch hoher
Baudrate abgeholt. Der Sendepuffer erreicht seine Grenze nicht.
Nochmal:
Mit nativem USB, klarer CDC, ist es eine virtuelle Serielle.
Die interessiert sich nicht für die Baudrate.
Da kannst du fantasieren wie du willst
Ausnahme: 1200Baud (Arduino Besonderheit)
Arduino Uno R4 Wifi:
Der serielle Monitor sendet die Daten mit hoher Baudrate an das
USB-Interface,
Auch wieder natives USB, also keine Baudrate auf dieser Seite des "USB-Interface".
Wobei das "USB-Interface", in dem Fall, vom ESP32 gestellt wird.
diese werden vom Controller aber nur mit der im seriellen
Monitor eingestellten (deutlich niedrigeren) Baudrate abgeholt. Der
Sendepuffer erreicht seine Grenze und sperrt die Übertragung.
Erst bei der UART Verbindung zwischen ESP32 und RA4 spielt dir deine Baudrate rein.
Und sie kann nicht "niedriger" sein, weil es vorher keine gibt.
Der ESP32 übernimmt die Baudrate aus dem Serial-Monitor.
Der RA4 aus dem Serial.begin();
Sind beiden gleich, dann können sie miteinander quatschen.
So sollte es sein!
Der ESP32 kann Daten schneller vom PC empfangen, als per UART an den Renesas Chip weiter zu leiten. Wenn der Puffer des ESP32 voll ist, sollte er dies dem PC signalisieren und die Übertragung passend ausbremsen.
Vielleicht tut er das nicht, sondern verwirft einen Teil der Daten. Das könnte man gezielt austesten.
Der ESP32 kann Daten schneller vom PC empfangen, als per UART an den
Renesas Chip weiter zu leiten. Wenn der Puffer des ESP32 voll ist,
sollte er dies dem PC signalisieren und die Übertragung passend
ausbremsen.
er bremst den PC leider nicht
Vielleicht tut er das nicht, sondern verwirft einen Teil der Daten. Das
könnte man gezielt austesten.
habe ich ja - er läuft an seinen Anschlag und ignoriert die folgenden Daten. Nur wenn man die Daten vor dem Erreichen desselben abholt, hat man eine Chance.
Ich bin mir sehr sicher, das die USB-Verbindung zum ESP keine Puffer überschreibt.
Das wird erst auf der UART-Seite zum Controller passieren, denn die hat kein Handshaking.
Wozu hat der RS232-Gott eigentlich RTS und CTS erfunden?
Und wenn man die 4 Cent für die Stecker gespart hat (USB-UARTS können
das eigentlich alle!) kann man ja auch mit XON/XOFF arbeiten.
Das dürfte einfacher sein als ZModem.
Oder man reduziert die Sache einfach auf "ey, hörst du?" "Jau" "gut dann
haste hier 16 Bytes" "ok, danke. kommt da noch was?", ähnlich wie es
viele Bootloader machen. Klappt auch 100%.
XON/XOFF hatte ich bereits probiert - brachte keine Verbesserung.
Das ist alles Open Source, was sagt denn die Analyse der Arduino
Klasse dazu?
Ich habe versucht die Quelldateien zu analysieren. Zum Einen die vom Uno R4 Renesas, welche Arduino F freundlicherweise verlinkt hatte und zum anderen die der uno-r4-wifi-usb-bridge und bin teilweise auch fündig geworden.
Der Puffer scheint bei allen 512 Bytes gross zu sein, was sich mit meinen Beobachtungen deckt.
Angenommen die hier angegebenen 8192 Bytes wären die Beschränkung des Schreibpuffers, so deckt sich dies erst einmal nicht mit der Beobachtung, dass ich maximal ca. 12.000 Bytes ohne Pause fehlerfrei empfange.
Berücksichtigt man jedoch, dass wärend der Zeit des Schreibens in den Sendepuffer auch Daten abgeholt werden, so würde es doch passen (beim Uno R4 WiFi hole ich die die Daten mit 19200 BAUD ab - somit wird der Puffer in 1s um 4000 Bytes geleert).
So richtig kann ich mir noch keinen Reim darauf machen....
XON/XOFF hatte ich bereits probiert - brachte keine Verbesserung.
Die XonXoff kommen aber dann vom Zielprozessor, und dessen Puffer läuft nie in den Xoff, oder?
Weil dein Problem die ESP-USB/UART-Bridge ist, die nur wenig RAM hat und dadurch nicht puffern kann. Die könnte den Datenstrom USB-Seitig bremsen, bekommt aber von dem Problem des Ziels nichts mit.
Also solltest du probieren, eine erprobte Größe X zu transferieren und dann immer eine Quittung vom Ziel ganz am Ende der Kette zu bekommen, und evtl. auch X anpassen, quasi adaptives Buffer probing.
Weil Software schneller langsam wird als Hardware schneller wird.
Das sind zwei abartig unterschiedliche Architekturen, da nur die MHz zu vergleichen ist sinnlos.
Mein Lamborghini hat 247kW, kann aber nur 60km/h. Ist halt nen Traktor, ne? ;)
vielen Dank für deine Antwort - nein wollte ich nicht.
Meine Frage zielte eher in die Richtung, dass aus einem bestimmten Grund
nicht mit einer deutlichen Geschwindigkeitszunahme zu rechnen ist.
Das kann viele Gründe haben.
Schlechte Software, welche die CPU ausbremst (delays)
der R4 ist mit seinem ARM ineffizienter als ein R3 mit AVR Kern, d.h. die 48 MHz bedeuten nicht automatisch doppelte Rechenleistung
IO Komponenten und Bibliotheken sind lahm (z.B. die SPI-Implementierung beim RP2040 ist grottig!)
Viele Grüße
Kai
PS: du willst nicht wirklich 298 kB Quelltext analysieren?
Man kann sie aber überfliegen, um möglicherweise grobe Fehler zu erkennen. Wenn deine Software mit delays vollgestopft ist, nützt die schnellste CPU nichts.
Die XonXoff kommen aber dann vom Zielprozessor, und dessen Puffer läuft
nie in den Xoff, oder?
korrekt (schon länger nicht mehr - hatte ich anfangs mal probiert)
Weil dein Problem die ESP-USB/UART-Bridge ist, die nur wenig RAM hat und
dadurch nicht puffern kann. Die könnte den Datenstrom USB-Seitig
bremsen, bekommt aber von dem Problem des Ziels nichts mit.
Also solltest du probieren, eine erprobte Größe X zu transferieren und
dann immer eine Quittung vom Ziel ganz am Ende der Kette zu bekommen,
und evtl. auch X anpassen, quasi adaptives Buffer probing.
exakt so stellt es sich dar - ich glaube in den Sourcen gefunden zu haben, dass der Puffer 8192 Bytes groß ist. Teile ich die Datei in 6kB große Teile und warte zwischendurch mit der Übertragung, so funktioniert es.
Kai schrieb:
Hat dafür jemand eine Erklärung?
Weil Software schneller langsam wird als Hardware schneller wird.
Das sind zwei abartig unterschiedliche Architekturen, da nur die MHz zu
vergleichen ist sinnlos.
Mein Lamborghini hat 247kW, kann aber nur 60km/h. Ist halt nen Traktor,
ne? ;)
ok - gute Antwort - es wird in diversen Artikeln davon geschrieben, dass der Uno R4 aufgrund seiner 48 MHz deutlich mehr Rechenleistung zur Verfügung stellt.
der R4 ist mit seinem ARM ineffizienter als ein R3 mit AVR Kern, d.h.
die 48 MHz bedeuten nicht automatisch doppelte Rechenleistung
IO Komponenten und Bibliotheken sind lahm (z.B. die
SPI-Implementierung beim RP2040 ist grottig!)
ok - klingt einleuchtend
Man kann sie aber überfliegen, um möglicherweise grobe Fehler zu
erkennen. Wenn deine Software mit delays vollgestopft ist, nützt die
schnellste CPU nichts.
dass
der Uno R4 aufgrund seiner 48 MHz deutlich mehr Rechenleistung zur
Verfügung stellt.
Ja das ist ja auch so.
Aber die Software ist halt aufgeblasener und schluckt das wieder weg.
Erst Recht wenn sie auf beiden Systemen läuft und keine Rücksicht auf systemspezifische Krankheiten nimmt.
Mit einem anderen Aufbau deines Programms lässt sich evtl. noch was machen, und mit besseren Drittprogrammen auch noch. Aber du bindest irgendwelche Libraries ein und bist denen ausgeliefert, und wenn die schlecht gebaut sind oder prozessorspezifisches Zeug enthalten das simuliert werden muss knallt's halt.
der R4 ist mit seinem ARM ineffizienter als ein R3 mit AVR Kern, d.h.
die 48 MHz bedeuten nicht automatisch doppelte Rechenleistung
Mit Sicherheit nicht, der ARM Cortex-M ist selbst bei gleicher Taktfrequenz im Durchschnitt deutlich schneller.
Es handelt sich mit Sicherheit um ein Software -Problem. Man könnte jetzt natürlich noch ewig mit den Arduino Frameworks rum basteln, oder doch mal direkt die Low-Level Treiber des Herstellers nutzen oder direkt auf die Peripherie zugreifen. Damit hat man das Problem wahrscheinlich ziemlich fix umgangen und kann Daten per Serial beliebig schnell empfangen.
Mit Sicherheit nicht, der ARM Cortex-M ist selbst bei gleicher
Taktfrequenz im Durchschnitt deutlich schneller.
Sicher? Wo kann man einen Test dazu sehen? Das ist ein echter 32 Bit ARM mit 32 Bit Code, nix Thumb(2). D.h. auch, der Flash muss doppelt mindestens soviel Daten wie der AVR schaufeln. Kann er das? Es gib genügend CPUs, die einen Flaschenhals beim Flash haben (z.B. der PICCOLO) und das nur bedingt durch Cache kompensieren können, vor allem wenn keiner da ist, nur die normale Pipeline.
Es handelt sich mit Sicherheit um ein Software -Problem.
Man könnte
jetzt natürlich noch ewig mit den Arduino Frameworks rum basteln, oder
doch mal direkt die Low-Level Treiber des Herstellers nutzen oder direkt
auf die Peripherie zugreifen. Damit hat man das Problem wahrscheinlich
ziemlich fix umgangen und kann Daten per Serial beliebig schnell
empfangen.
Man könnte auch einfach den Lötjumper durch einen richtigen (oder Micro Schalter) ersetzen.
Das ist ein echter 32 Bit ARM
mit 32 Bit Code, nix Thumb(2)
Hä? Bin ich im falschen Film? Wir reden immer noch vom Arduino R4, ja? Der basiert auf dem Renesas RA4M1, das ist ein ARM Cortex-M4 und der implementiert die ARMv7M-Variante "T32" von Thumb2, also 16/32bit Code gemischt - aber nicht den reinen 32bit-Code "A32", wie man ihn von Cortex-A oder ARM11 kennt. Aber es ist dennoch ein vollwertiger 32bit-Prozessor (es gibt überhaupt keine 8/16bit ARMs).
Die meisten "T32"-Instruktionen (vom Cortex-M4) sind 16bit breit, d.h. genau so breit wie die vom AVR. Die Handvoll 32bit-Instruktionen brauchen i.A. auch nur einen Takt zum Berechnen; das Laden aus dem Flash kann langsamer sein daher implementieren typische Cortex-M4-Mikrocontroller einen Prefetch/Cache. Dafür können die typischen 32bit-Instruktionen halt auch etwas, für das der AVR noch viel mehr Takte brauchen würde.
Die großen ARMs mit "A32" Instruction Set, bei denen alle Instruktionen 32bit groß sind, haben sowieso keinen Flash, sondern führen alles aus dem (externen) SDRAM aus, der vom L1-L3 -Cache gepuffert ist...
Da findet sich sicher einiges im Netz. Aus meinem eigenen Wissen kann ich sagen, dass die Cortex-M (insbesondere M3/4/7) einen ganzen Haufen Instruktionen haben (viele davon 16bit breit), die viele Dinge in 1 Takt machen, die man auf dem AVR in Software mit vielen einzelnen Instruktionen implementieren muss. Ganz offensichtlich insbesondere 32bit-Berechnungen; der AVR braucht für jede Pointer-Berechnung 2 Takte, das kann der ARM in einem. Sobald es um richtiges "Numbercrunching" geht, schlägt der ARM mit der FPU und den DSP-Erweiterungen den AVR um viele Größenordnungen.
Wenn der Flash wirklich der Flaschenhals sein sollte, kann man den Code auch einfach aus dem internen SRAM ausführen. Aber für eine popelige UART-Ansteuerung ist das absolut nicht notwendig. Der Core könnte auch locker mehrere Serialports bei maximaler Datenrate abhandeln.
dabei fällt mit noch die Frage ein, weshalb der Uno R4 mit 48 MHz
Taktfrequenz nicht wesentlich schneller ist, als der Mega oder Uno R3
mit 16MHz?
Ich schrieb ja schon:
Da sitzt noch eine Software-Schicht dazwischen, die Arduino Klassen
rufen Funktionen der Renesas Flexible Software Package (FSP) auf.
Ob Dein Programm davon wirklich betroffen ist kann ich natürlich nicht sagen, kommt eben darauf, was es wirklich macht.
Aber wie sich das auf den SPI ausgewirkt hat, das habe ich dokumentiert.
Die Bilder sind Ausschnitte aus den auch angehängten Logic-Analyzer Mitschnitten.
Das ist der gleiche 5 Byte lange SPI Frame, einmal mit dem UNO R3 gesendet, einmal mit dem UNO R4 gesendet mit der SPI Klasse die Juli 2023 zur Verfügung stand.
Der SPI Takt war für beide 8MHz.
UNO R3: 16.06µs von SPI-Enable auf Low bis SPI-Enable wieder auf High.
UNO R4: 60.4µs von SPI-Enable auf Low bis SPI-Enable wieder auf High.
Faktor 3,7 für den UNO R4 bei dreifachem CPU-Takt.
Der Grund?
uint8_t ArduinoSPI::transfer(uint8_t data)
hat im wesentlichen das hier gemacht:
_write_then_read(&_spi_ctrl, &data, &rxbuf, 1, SPI_BIT_WIDTH_8_BITS);
Und diese FSP Funktion prüft die Parameter, schaltet den SPI ein, konfiguriert den SPI, sendet das Byte, wartet auf die Antwort, schaltet den SPI wieder aus und liefert das Ergebnis.
Also das meiste was die Funktion macht ist komplett überflüssig.
Und so kommt man auf 10,9µs zwischen zwei Bytes für den R4, während der UNO R3 nur 1µs braucht.
In Taktzyklen sind das 16 mit dem AVR und 523 mit dem RA4M1.
Der Controller ist zwar viel schneller, musste durch die Implementierung ein Vielfaches vom Code für die gleiche Aufgabe ausführen.
Die aktuelle R4 SPI Klasse ist meine ich schneller als der R3, aber weder finde ich gerade die Traces dazu, noch den UNO R4 um das noch mal nachzumessen.
Und arduino/Serial.cpp? Da war noch keiner wirklich dran, das nutzt immer noch R_SCI_UART_Write(&uart_ctrl, &c, 1); aus der ursprünglichen Implementierung.
Das sind 100 Zeilen für R_SCI_UART_Write() um 1 Byte zu schreiben.
So Handbremsen sind da bestimmt noch haufenweise in der Software drin.
Beim reinen Rechnen ist der RA4M1 ganz sicher viel schneller als der AVR, erst recht mit 32 Bit Werten, aber in den Klassen Richtung Peripherie verpufft das alles, wenn sich da noch niemand drum gekümmert hat.