EEPROM - ausgelesener Wert ist ungenau
Hallo Forum,
ich bin mal wieder an einem kleinen Arduino FM-Radio dran, diesmal mit RDA5807M, Arduino Pro Mini und OLED 128x32.
Ich speichere die aus dem RDA ausgelesene Frequenz im EEPROM, um bei Neustart bei der selben Station zu beginnen.
Mein Problem ist die Genauigkeit. Manchmal stimmt die Startfrequenz genau, oft liegt sie aber nach Reset oder Neustart mehr oder weniger neben der vorherigen. Ich hab versuchsweise kleine Pausen vor und nach dem EPROM-Schreiben eingefügt. Hat nichts gebracht. Die sind zwar immer in der Nähe des Sollwerts aber selten genau wo sie sollen.
Hier der Code:
Code:
/**************************************************************************
* FM-Radio mit RDA5708M, Arduino Pro Mini 3.3V und OLED 128x32 *
* Steuerung über vier Drucktaster: *
* button 1 - volume down *
* button 2 - volume up *
* button 3 - scan frequency down *
* button 4 - scan frequency up *
* Das OLED-display zeigt zwei Zeilen: *
* 1- "Empfangsfrequenz" *
* 2- "Volume level" + "Batteriespannung" *
* alternativ bei Unterschreiten von 3,3V "CHARGE BATTERY" *
* Die Empfangsfrequenz wird im EEPROM gespeichert. *
* *
* U.Galepp 2018 *
**************************************************************************/
#include <Arduino.h>
#include <Wire.h>
#include <radio.h>
#include <RDA5807M.h>
#include "U8glib.h"
#include <EEPROM.h>
U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE); // Auswahl des OLED Typs
#define FIX_BAND RADIO_BAND_FM // ausgewähltes Band ist FM
#define FIX_VOLUME 3 // Lautstärke beim Start
RDA5807M radio; // create an instance of class for RDA5807M chip
int volume = FIX_VOLUME;
int button_1 = 4; // Taster für volume up an D4
int button_2 = 5; // Taster für volume down an D5
int button_3 = 6; // Taster für scan up an D6
int button_4 = 7; // Taster für scan down an D7
int buttonState_1 = 0;
int buttonState_2 = 0;
int buttonState_3 = 0;
int buttonState_4 = 0;
int frequency;
char vol[2];
float volts;
char akku[4];
/*** setup ***/
void setup()
{
Serial.begin(57600);
Serial.println("Radio...");
delay(200);
radio.init(); // Radio initialisieren
radio.setBand(FIX_BAND);
EEPROM.get(0, frequency);
delay(100);
radio.setFrequency(frequency);
delay(100);
radio.setVolume(volume);
pinMode(button_1, INPUT); // Taster-Pins als Eingang definieren
pinMode(button_2, INPUT); // -||-
pinMode(button_3, INPUT); // -||-
pinMode(button_4, INPUT); // -||-
digitalWrite(button_1, HIGH); // Pull-Up Widerstände aktivieren
digitalWrite(button_2, HIGH); // -||-
digitalWrite(button_3, HIGH); // -||-
digitalWrite(button_4, HIGH); // -||-
}
/*** Subroutine Spannungsmessung ***/
void measureVolt()
{ analogReference(INTERNAL);
volts = (float)analogRead(A0)*6.1/1000;}
/*** Subroutine Datenanzeige auf OLED ***/
void displayData()
{
u8g.firstPage();
do {
u8g.setFont(u8g_font_helvB14);
char s[12];
radio.formatFrequency(s, sizeof(s));
u8g.drawStr( 10, 15, s);
u8g.setFont(u8g_font_unifont);
if(volts > 3.3) // wenn Vcc > 3.3V zeige "Volume" & "Volt"
{
u8g.drawStr( 0, 32, "Vol:");
sprintf (vol, "%d", volume); // Umwandlung int "volume" zu char "vol"
u8g.drawStr( 35, 32, vol);
u8g.drawStr( 60, 32, "Bat:");
dtostrf(volts, 2, 1, akku); // Umwandlung float "volts" zu char "akku"
u8g.drawStr( 95, 32, akku);
u8g.drawStr( 119, 32, "V");
}
else // wenn VCC >= 3.3V zeige "CHARGE BATTERY"
{
u8g.drawStr(8, 32,"CHARGE BATTERY");
}
} while( u8g.nextPage() );
}
/*** main loop ***/
void loop()
{
measureVolt(); // Batteriespannung messen
displayData(); // alle Daten auf OLED anzeigen
/* frequency = (radio.getFrequency()); // nur zum Test was gespeichert ist
Serial.print("Frequenz: ");
Serial.println(frequency);
*/
if(!digitalRead(button_1) ) { // wenn Taster 1 gedrückt, Volume einen Schritt runter
while(!digitalRead(button_1) ) // warten auf Taster loslassen == HIGH
{delay(10);}
volume --;
if (volume < 0) volume = 0;
radio.setVolume(volume);
delay(100);
}
if(!digitalRead(button_2) ) { // wenn Taster 2 gedrückt, Volume einen Schritt hoch
while(!digitalRead(button_2) ) // warten auf Taster loslassen == HIGH
{delay(10);}
volume ++;
if (volume == 16) volume = 15;
radio.setVolume(volume);
delay(100);
}
if(!digitalRead(button_3) ) { // wenn Taster 3 gedrückt, abwärts scannen
while(!digitalRead(button_3) ) // warten auf Taster loslassen == HIGH
{delay(10);}
radio.seekDown();
delay(100);
frequency = (radio.getFrequency()); // neue Frequenz aus RDA5807M auslesen
delay(100);
EEPROM.put(0, frequency); // neue Frequenz in EEPROM speichern
delay(100);
}
if(!digitalRead(button_4) ) { // wenn Taster 4 gedrückt, aufwärts scannen
while(!digitalRead(button_4) ) // warten auf Taster loslassen == HIGH
{delay(10);}
radio.seekUp();
delay(100);
frequency = (radio.getFrequency()); // neue Frequenz aus RDA5807M auslesen
delay(100);
EEPROM.put(0, frequency); // neue Frequenz in EEPROM speichern
delay(100);
}
}
Ich habe heute erst mit EEPROM begonnen, wahrscheinlich habe ich einen Denkfehler aber wo?
Für Tips wäre ich dankbar.
Gruß Uwe
Liste der Anhänge anzeigen (Anzahl: 1)
@Moppi
Zitat:
Zitat von
Moppi
wenn radio.getFrequency() nicht immer ein stabiles Ergebnis liefert, hättest Du auch so lange radio.getFrequency() aufrufen und den Wert vergleichen können, bis der stabil ist und erst dann speichern. Irgendwann muss der stabil sein. Jetzt machst Du es anders, da passiert in etwa dasselbe, indem Du alle 5 Sekunden den Wert liest und mit dem im EEPROM abgleichen lässt, der wird im EEPROM solange geändert, bis der sich nicht mehr ändert. Blöd ist dabei, dass Du unnötiger Weise ins EEPROM schreibst. Anders herum ist es eine konsequente Lösung. Wenn Du das andauernd so machst, speichert das Programm immer den letzten Sender, wann immer er verstellt wird. Aber nur solang bis die Schreibzugriffe dem EEPROM zu viel waren :-) Das ist dann auch konsequent. ;-) Geplante Obsoleszenz...
Das hat mir dann doch keine Ruhe gelassen, also hab ich eine Zählvariable eingebaut, die im Terminal anzeigt, wann und wie oft in den EPROM geschrieben wird.
Anhang 33695
Das Bild zeigt das Ergebnis von 30 Betriebsminuten, in denen ich fünf mal die Scan-Funktion aufgerufen habe. Da wird also tatsächlich nur einmal je angefordertem Frequenzwechsel in den EEPROM geschrieben.
Genau genommen könnte ich die Zeitschleife von 5 Sekunden auch auf eine halbe Minute oder mehr vergrössern. Alles was ich erreichen will, ist ja nur der Neustart mit der gleichen Frequenz, wie beim Ausschalten. Solange ich nicht ausschalte, bevor die Schleife seit dem letzten Scan einmal durch ist, ist alles OK. Das würde sämtliche Scanbefehle dazwischen ignorieren und Schreibvorgänge im EPROM sparen. Wiederholtes Scannen innerhalb der Zeitspanne triggert ja kein neues Schreiben in den EPROM. Die Dauer der Schleife ist dann Ermessensfrage.
Perfekt wäre nur einmaliges Schreiben direkt beim Ausschalten. Das ginge aber wohl nur durch softwaremäßiges Ein- und Ausschalten.
Liste der Anhänge anzeigen (Anzahl: 1)
OK, ich versuch es mal aus meiner Sicht als interessierter Laie:
Die Befehle radio.seekDown(), radio.seekUp() und radio.getFrequency funktionieren jeder für sich ohne jedes Problem! Das ist Fakt!
Es ist die falsche Verwendung/Kombination, die Probleme macht.
Beim Schreiben des ursprünglichen Codes war ich von der Annahme ausgegangen, das das Radio die erhaltenen I2C Befehle der Reihenfolge nach abarbeitet und den zweiten Befehl erst nachdem der erste fertig ist, also den get.Frequency erst nachdem seek.Up/Down fertig ist. So ist es aber nicht!
Code:
if(!digitalRead(button_3) ) { // wenn Taster 3 gedrückt, abwärts scannen
while(!digitalRead(button_3) ) // warten auf Taster loslassen == HIGH
{delay(10);}
radio.seekDown();
delay(100);
frequency = (radio.getFrequency()); // neue Frequenz aus RDA5807M auslesen
delay(100);
EEPROM.put(0, frequency); // neue Frequenz in EEPROM speichern
delay(100);
}
Im Code seht ihr, dass ich nach seek... jeweils nur 100 millis gewartet hatte, bis ich die neue Frequenz abgefragt habe. Es braucht aber durchaus länger, bis er eine neue Frequenz eingestellt hat! Das kann schon eine Sekunde oder länger dauern. Wie gesagt, dachte ich, er würde warten mit dem Senden der neuen Frequenz, bis er mit seek... fertig ist. Ganz offensichtlich tut er das nicht, sondern beantwortet getFrequency sobald er gefragt wird, unabhängig ob seek... schon fertig ist oder nicht! Daher passiert es, das ein zu frühes getFrequency sozusagen ein "Zwischenergebnis" anzeigt, nicht die endgültig neue Frequenz.
Um das zu beweisen, habe ich in den ursprünglichen (fehlerhaften) Code eine zweite Abfrage erst nach 1000 millis eingefügt.
Hier das Ergebnis im Terminal:
Anhang 33697
Das Zwischenergebnis ist nach 100 millis gemessen, das Endergebnis nach 1000. Bei sechs Abfragen sind zwei Fehlergebnisse dabei.
Für mich ist damit das Problem gelöst. Es war kein Problem in irgendeinem Befehl oder Library, sondern einfach nur dumm programmiert! Man muss dem Radio genügend Zeit geben, die neue Frequenz einzustellen, bevor man sie abfragt. So einfach ist das.
Gruß Uwe