PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : EEPROM - ausgelesener Wert ist ungenau



basteluwe
07.10.2018, 16:40
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:

/************************************************** ************************
* 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

shedepe
07.10.2018, 18:01
Könntest du ungenau mal genauer spezifizieren. Habe deinen Code jetzt nicht gelesen. Aber prinzipiell: Wenn du einen Wert im EEPROM speicherst und nicht total viel Pech hast und dir kosmische Strahlung ein Bit kippen lässt, dann wirst du diesen Wert auch wieder auslesen.

Dein Problem könnte eventuell daran liegen, dass du die Frequenz als int ausliest, die EEPROM library aber nur bytes erwartet.
EEPROM.read() gibt aufjedenfall nur einzelne Bytes zurück.

PICture
07.10.2018, 20:52
Hallo!

@ basteluwe

Ich vermute, dass der Fehler durch AFC ("Automatic Frequency Control" -> automatische steuerung der Frequenz) verursacht ist. Auf der "page 9" des Dateblattes habe ich im Register 04H das AFCD "default=0" gefunden. Das bedeutet, dass AFC aktiv ist und du könntest probieren das AFCD "disable=1" programmieren und schauen ob das hilft. ;)

basteluwe
07.10.2018, 21:28
Könntest du ungenau mal genauer spezifizieren. Habe deinen Code jetzt nicht gelesen. Aber prinzipiell: Wenn du einen Wert im EEPROM speicherst und nicht total viel Pech hast und dir kosmische Strahlung ein Bit kippen lässt, dann wirst du diesen Wert auch wieder auslesen.

Dein Problem könnte eventuell daran liegen, dass du die Frequenz als int ausliest, die EEPROM library aber nur bytes erwartet.
EEPROM.read() gibt aufjedenfall nur einzelne Bytes zurück.

Mit Ungenauigkeit meinte ich, ich speichere z.B. den Wert 8820, was 88,20 MHz bedeutet. Bei Neustart tuned der chip aber z.B. auf 8800, also 88,0 MHz. Diese Abweichung ist nicht konstant! Manchmal stimmt der Wert auch.
Ich weiß, dass EEPROM.read() bzw. EEPROM.write() nur bytes akzeptieren. Ich verstehe diese Seite (https://www.arduino.cc/en/Reference/EEPROMPut) aber so, das EEPROM.put() bzw. EEPROM.get() auch int oder float akzeptieren. Oder nicht?

Gruß Uwe

hardware.bas
08.10.2018, 08:02
Ich hatte mal ein Problem mit einem AVR, wollte mit der Zuweisung EEPROMVARIABLE = WERT abspeicher. Da musste ich
feststellen, dass die erste EEPROM-Zelle warscheinlich nicht funktioniert. Nachdem ich eine EEPROM-Dummyfunktion im
Quelltext davor reingeschrieben habe, lief es.
VG Micha

oberallgeier
08.10.2018, 09:05
.. musste ich feststellen, dass die erste EEPROM-Zelle warscheinlich nicht funktioniert. Nachdem ich eine EEPROM-Dummyfunktion ..Es ist sicher fünf Jahre her, da hatte ich dasselbe Problem festgestellt und etliche Hinweise auf ne Dummyfunktion gefunden. Seit der Zeit definiere*) ich wie folgt und habe keine Probleme:

// ###>>> Byte-Array, das erste Byte des EEPROMS gilt als nicht vertrauenswürdig
uint8_t firstByteDummy EEMEM;
uint8_t eeDUMMYbytes [100] EEMEM; // Dummies
uint8_t eeBytes [12] EEMEM; // ?? Allzweckbytes ??
uint16_t eeSrvoff[12] EEMEM; // Servo-Position - Offset
u8 SVT[12][11] EEMEM; // Servotexte

*) vgl. Kernighan-Ritchie, Vorwort zur deutschen Ausgabe
Definition: Eigenschaft und Speicherplatz
Deklaration: nur Eigenschaft

shedepe
08.10.2018, 09:28
Dass es einzelne Zellen defekt sind, kann passieren. Jedoch konnte ich keine Hinweise finden, dass Zelle 0 per se kaputt ist.



fas EEPROM.put() bzw. EEPROM.get() auch int oder float akzeptieren

Stimmt. Da Arduino C++ ist, kann man die Funktionen natürlich entsprechend überladen.

Ein Dummy Read Write auf den EEPROM ist eine gute Idee.
Ein weiterer Punkt auf den das Datenblatt hinweist, ist dass der EEPROM Inhalt korrumpiert werden kann, wenn man versucht bei zu geringer Versorgungsspannung zu lesen oder zu schreiben. Das ist in diesem Fall aber denke ich auszuschließen. (Nur der Vollständigkeit halber erwähnt)

oberallgeier
08.10.2018, 09:36
.. Ein Dummy Read Write auf den EEPROM ist eine gute Idee ..Danke. So weit ich mich erinnern kann stammt die Idee aus diesem Forum (oder aus mikrocontroller.net).


.. Ein weiterer Punkt auf den das Datenblatt hinweist, ist dass der EEPROM Inhalt korrumpiert werden kann ..Genau das hatte ich vorhin vergessen zu erwähnen und wollte es eben nachtragen. Auf dem Arduino Pro Mini dürfte ja ein mega328p drauf sein, zu dem schreib t das Datenblatt :

..
12.4.2. Preventing EEPROM Corruption
During periods of low VCC, the EEPROM data can be corrupted because the supply voltage is too low for
the CPU and the EEPROM to operate properly. These issues are the same as for board level systems
using EEPROM, and the same design solutions should be applied.
An EEPROM data corruption can be caused by two situations when the voltage is too low. First, a regular
write sequence to the EEPROM requires a minimum voltage to operate correctly. Secondly, the CPU itself
can execute instructions incorrectly, if the supply voltage is too low.
EEPROM data corruption can easily be avoided by following this design recommendation:
Keep the AVR RESET active (low) during periods of insufficient power supply voltage. This can be done
by enabling the internal Brown-out Detector (BOD). If the detection level of the internal BOD does not
match the needed detection level, an external low VCC reset Protection circuit can be used. If a reset
occurs while a write operation is in progress, the write operation will be completed provided that the
power supply voltage is sufficient.
..

Insgesamt lohnt es sich alle Abschnitte des Datenblatts zum EEPROM-schreiben-und-lesen wenigstens einmal ordentlich durchzulesen.

Ich komme auch wirklich nicht klar wie Du Deine Umrechnung ".. ich speichere z.B. den Wert 8820, was 88,20 MHz .." machst und in welchem Format die erfolgt. Kannst Du uns bitte darüber auch aufklären?

HaWe
08.10.2018, 12:56
Mit Ungenauigkeit meinte ich, ich speichere z.B. den Wert 8820, was 88,20 MHz bedeutet. Bei Neustart tuned der chip aber z.B. auf 8800, also 88,0 MHz. Diese Abweichung ist nicht konstant! Manchmal stimmt der Wert auch.

tbh, ich habe den vagen Verdacht, dass der "Abrundungsfehler" nicht durch Datenkorruption zu erklären ist, denn 8820 und 8800 liegen nicht um 1 (gelöschtes) Low-Byte auseinander.
8800 dez = 00100010 01100000 bin
8820 dez = 00100010 01110100 bin

wäre es der Datenverlust bei LSB, ergäbe sich dezimal
00100010 00000000 bin = 8704 dez

Ich vermute daher den Fehler woanders, z.B. in einem Rundungsfehler in Soft- oder Firmware (z.B. Division Integer durch Integer).
Auch sonst müsste man bei zufälliger Datenkorruption "veränderte" Werte finden können, die nicht durch glattes Abrunden der letzten beiden Dezimalstellen, sondern eher auch durch randomisierte Vergößerung/Verkleinerung der Ausgangswerte auffallen, dazu wären auch mal einige (10-20) andere solcher beobachteter "Datenveränderungen" interessant zu sehen.

basteluwe
08.10.2018, 14:32
Ich denke, ich bin inzwischen etwas weiter:
Ich frage die Frequenz nach jedem Scan-Durchlauf ab und schreibe sie in den EEPROM. Das Problem liegt nun offensichtlich nicht in der EEPROM-Routine sondern im Wert, den der Radio-Chip auf den Befehl "radio.getFrequency()" zurück gibt. Der stimmt nämlich nicht in jedem Fall mit der aktuellen Frequenz überein. Ich lasse im Moment das Terminal mitlaufen und sehe daher die Abweichung zwischen der neu eingestellten Frequenz und dem nach dem Scan gemeldeten und gespeicherten Wert. Warum das so ist, weiß ich allerdings noch nicht.

Gruß Uwe

oberallgeier
08.10.2018, 15:07
.. bin inzwischen etwas weiter: Ich frage die Frequenz nach jedem Scan-Durchlauf ab und schreibe sie in den EEPROM. Das Problem liegt nun offensichtlich nicht in der EEPROM-Routine ..Hallo Uwe,
prima dass Du die Lösung gefunden hast. Du denkst aber (auch) schon an das Datenblatt ? ? ?

..
• High Endurance Non-volatile Memory Segments
. . .
– Write/Erase Cycles: 10,000 Flash/100,000 EEPROM
..Na ja, jede Stunde ein Scandurchlauf (mal un?sinnigerweise 24/7) ergibt knapp 9000. Bis 100000 sinds dann zehn Jahre . . . ok, das kann passen. Aber evtl. ist ein durchtickern des Adressraums bei den Schreib-/Lesevorgängen trotzdem sinnvoll (und seis nur deswegen, damit man das mal gemacht hat *gg*)?

basteluwe
08.10.2018, 16:04
Ich denke so geht es jetzt:
Die EPROM-Bibliothek hat den update-Befehl, der nur in die Adresse schreibt, wenn der Inhalt abweicht. Allerdings geht das tatsächlich nur mit byte, nicht integer! Daher hab ich nun eine Umrechnung eingebaut. Die möglichen Frequenzwerte sind ja begrenzt und lassen das zum Glück zu. Ich habe diese Routine nun in der Hauptschleife drin, nicht mehr nach dem einzelnen Scannen. Alle 5 Sekunden läuft das und bisher hat er bei jedem Neustart (egal ob kalt oder Reset) die vorherige Frequenz exakt geladen.

if (millis() - previousMillis > 5000)
{
previousMillis = millis();
frequency = (radio.getFrequency()); // Frequenz aus Radio auslesen
storeFreq = (frequency/10)-825; // Frequenz in Speicherwert für EEPROM (byte) umrechnen
EEPROM.update(0, storeFreq); // falls neuer Speicherwert abweicht, dann update EEPROM
}

Gruß Uwe

- - - Aktualisiert - - -


Hallo Uwe,
prima dass Du die Lösung gefunden hast. Du denkst aber (auch) schon an das Datenblatt ? ? ?
Na ja, jede Stunde ein Scandurchlauf (mal un?sinnigerweise 24/7) ergibt knapp 9000. Bis 100000 sinds dann zehn Jahre . . . ok, das kann passen. Aber evtl. ist ein durchtickern des Adressraums bei den Schreib-/Lesevorgängen trotzdem sinnvoll (und seis nur deswegen, damit man das mal gemacht hat *gg*)?

Ja das mit den 100.000 Durchgängen ist mir bewust. Das ist aber nicht wirklich ein Problem, denke ich. So oft, wird der Sender nicht gewechselt und täglich benutzt wird das Ding sicher nicht (schon gar nicht 24/7). Trotzdem Danke für den Hinweis :cool:

Uwe

HaWe
08.10.2018, 19:00
frequency = (radio.getFrequency());
storeFreq = (frequency/10)-825;

ist frequency vom Typ int oder float?
welchen Typ liefert radio.getFrequency() zurück?
Und welchen Typ hat storeFreq ?
nenne vlt auch mal ein paar typische Werte, die radio.getFrequency() zurückiefert!

hier teilst du durch 10 (int), bei float/int macht das nichts, aber bei int/int schon, das gibt besagte Rundungsfehler. Gibt es noch mehr solche Stellen mit ähnlichen Berechnungen?

macht er jetzt noch Fehler beim Speichern und Zurücklesen?

Immerhin bestätigt es ja auch meine Vermutung, dass es nicht an Datenkorruption im EPROM liegt.

basteluwe
08.10.2018, 20:50
...
ist frequency vom Typ int oder float?
welchen Typ liefert radio.getFrequency() zurück?
Und welchen Typ hat storeFreq ?
nenne vlt auch mal ein paar typische Werte, die radio.getFrequency() zurückiefert!
...
macht er jetzt noch Fehler beim Speichern und Zurücklesen?


- frequency ist int
- ich denke, radio.getFrequency() liefert ebenfalls int (typischer Werte ist 8760 als Entsprechung für 87,60 MHz oder 10330 für 103,30 MHz
- storeFreq ist byte, weil nur byte von EEPROM.update akzeptiert wird

Nein, in der letzten Variante tritt der Fehler bisher nicht mehr auf.

Hier der Code, der (bis) jetzt problemlos läuft:

/************************************************** ************************
* 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 ist 3 (0-15 möglich)

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;
byte storeFreq; // Speichervariable für EEPROM
char vol[2];
float volts;
char akku[4];
unsigned long previousMillis = 0;

/*** setup ***/
void setup()
{
radio.init(); // Radio initialisieren
radio.setBand(FIX_BAND); // Radio auf FM setzen
storeFreq = EEPROM.read(0); // Speicherwert für Frequenz aus EEPROM holen
frequency = (storeFreq + 825)*10; // Frequenzwert zu int für Radio-Chip wandeln
radio.setFrequency(frequency); // Radio auf gespeicherte Frequenz setzen
radio.setVolume(volume); // Radio auf gespeicherte Lautstärke setzen

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

if (millis() - previousMillis > 5000){ // Prüfung alle 5 Sek ob update der gespeicherten Station nötig ???
previousMillis = millis();
frequency = (radio.getFrequency()); // Frequenz aus Radio auslesen
storeFreq = (frequency/10)-825; // Frequenz in Speicherwert (byte) umrechnen
EEPROM.update(0, storeFreq); // falls neuer Speicherwert abweicht, dann update EEPROM
}

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);
}

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);
}
}
Uwe

Moppi
09.10.2018, 07:28
- frequency ist int
- ich denke, radio.getFrequency() liefert ebenfalls int

der Typ ist uint16_t, also Integer (unsigned, wenn ich nicht irre)


Auszug aus radio.h:

/// Frequency data type.
/// Only 16 bits are used for any frequency value (not the real one)
typedef uint16_t RADIO_FREQ;

Deine Typdefinition ist: int frequency;
Darin lassen sich Frequenzen von 0 bis 32767 speichern, bzw. 327,67MHz. Wegen dem Vorzeichen.
Geliefert wird aus der Bilbliothek ohne Vorzeichen.
Besser wäre, um Folgefehler und Denkakrobatik zu vermeiden: unsigned int frequency;

Solang Du über die 327MHz nicht hinaus kommst, sollte es aber kein Problem damit geben.


Wenn Du so machst:

storeFreq = EEPROM.read(0); // Speicherwert für Frequenz aus EEPROM holen
frequency = (storeFreq + 825)*10; // Frequenzwert zu int für Radio-Chip wandeln

kommst Du auf Frequenzen bis 108,00MHz.


Hier sollte dann aber eine Warnung kommen: radio.setFrequency(frequency);
Weil der von Dir verwendete Datentyp nicht übereinstimmt. Macht aber nichts, solang das Vorzeichenbit nicht gesetzt ist.


Hier erzeugst Du ein Problem: storeFreq = (frequency/10)-825; // Frequenz in Speicherwert (byte) umrechnen
Wenn Du fiktiv 10625 durch 10 teilst, bleiben 1062 übrig, dann -825 = 237. Du speicherst damit letztlich 106,20MHz statt 106,25MHz.

MfG
Moppi

HaWe
09.10.2018, 09:01
zu moppis Post:
stimmt, genau das mit dem dividieren eines int durch (int) 10 ergibt den resultierenden Rundungsfehler bei Hin- und Rückrechnung, den ich oben ansprach.
Zumindest Datenkorruption auf dem EPROM kann man ja jetzt wohl doch ausschließen.

Immerhin scheint es aber jetzt ja auf magische Weise nicht mehr relevant zu sein, daher würde mich schon interessieren, wie was vorher berechnet, gespeichert und zurückgerechnet wurde, als der Fehler beim Zurücklesen/rechnen des Speicherwertes noch auftrat.

was int, int16_t , unsigned int und uint16_t angeht:
ich würde hier ebenfalls zur originalen Typ Definition uint16_t raten, NICHT unsigned int und auch nichts anderes.

Grund: Vielleicht nicht hier und nicht hier bei diesem Programm und auf auf dieser AVR Plattform, aber C++ KENNT den generellen Unterschied zwischen dem Plattform-abhängigen unsigned int von nicht exakt definierter Größe und dem expliziten Datentyp uint16_t aus der <stdint> lib.

Auf esp8266 oder Arduino Zero/Due z.B. ist int kein 16bit- sondern ein 32bit-Integer, also eher int32_t.

Und auch wenn C++ (gpp) es oft durchrutschen lässt, so erzeugt es dennoch manchmal Compilierungsfehler, wenn es zum impliziten Casting kommt, z.B. bei Funktionsparametern.

Also wenn die Lib uint16_t definiert, dann auch uint16_t verwenden, nicht unsigned int
- und keinesfalls int und auch nicht int16_t , und das wegen des komplett abweichenden Zahlen-Definitionsbereichs!

Moppi
09.10.2018, 09:21
Wenn man die Frequenztabellen ansieht, enden die meisten Sender eh auf "0" statt "5".
Das mit den Datentypen führt meist zu Problemen, die sich kaum finden lassen, wenn man darauf kein Augenmerk legt und sich dessen nicht bewusst ist.
Das ist natürlich richtig, dass bei unterschiedlichen CPUs die Datentypen unterschiedliche Bitbreite haben können. Deswegen wichtig: Datentypen kennen.

MfG

HaWe
09.10.2018, 09:28
Wenn man die Frequenztabellen ansieht, enden die meisten Sender eh auf "0" statt "5".
Das mit den Datentypen führt meist zu Problemen, die sich kaum finden lassen, wenn man darauf kein Augenmerk legt und sich dessen nicht bewusst ist.
Das ist natürlich richtig, dass bei unterschiedlichen CPUs die Datentypen unterschiedliche Bitbreite haben können. Deswegen wichtig: Datentypen kennen.

MfG

jain,
die Probleme mit uint16_t versus unsigned int treten auch bei identischen Plattformen im selben Programm auf, nicht nur bei verschiedenen, und auch wenn die Datengröße übereinstimmt, daher IMMER genau identisch verwenden - oder jedes Mal explizit casten!
Das gleiche gilt für long versus int32_t/int64_t, und ganz besonders auch für char, signed char, unsigned char, int8_t, uint8_t und byte (gerade bei char kann man sich bei Arduino vor Verzweiflung die Haare raufen!).

Da das Programm aber jetzt ja angeblich funktioniert, ist die Rundung um 0,5 ja offenbar nicht mehr das Thema, doch wie ich schrieb,

daher würde mich schon interessieren, wie was vorher berechnet, gespeichert und zurückgerechnet wurde, als der Fehler beim Zurücklesen/rechnen des Speicherwertes noch auftrat.

Moppi
09.10.2018, 09:53
Das Problem lag wohl hier:


1. EEPROM.put(0, frequency); // neue Frequenz in EEPROM speichern
2. EEPROM.get(0, frequency);


EEPORM.put() wird wohl dann bei dem Wert 10625 nur 129 (die unteren 8 Bit) speichern, wenn die Funktion nur byteweise ins EEPROM schreibt. Damit ist die Frequenz futsch. Kann auch möglich sein dass die put-Funktion zwei Byte ins EEPROM schreibt, nämlich High und Low-Byte, aber nur ein Byte zurückgelesen wird, gäbe auch nichts genaues. Und falls das Folgebyte im EEPROM, an Position #1, für andere Zwecke gebraucht wird, würde das direkt mit überschrieben, falls zwei Byte ab Position #0 geschrieben werden.
Durch den Umweg mit Frequenz/10 minus 825, den basteluwe hinzugefügt hat, funktioniert es dann, er übergibt put() nur noch ein Byte und da ist die Frequenz dann drin. Vorher war sie ja in zwei Byte drin.

HaWe
09.10.2018, 10:09
Das Problem lag wohl hier:


1. EEPROM.put(0, frequency); // neue Frequenz in EEPROM speichern
2. EEPROM.get(0, frequency);


EEPORM.put() wird wohl dann bei dem Wert 10625 nur 129 (die unteren 8 Bit) speichern, wenn die Funktion nur byteweise ins EEPROM schreibt. Damit ist die Frequenz futsch. Kann auch möglich sein dass die put-Funktion zwei Byte ins EEPROM schreibt, nämlich High und Low-Byte, aber nur ein Byte zurückgelesen wird, gäbe auch nichts genaues. Und falls das Folgebyte im EEPROM, an Position #1, für andere Zwecke gebraucht wird, würde das direkt mit überschrieben, falls zwei Byte ab Position #0 geschrieben werden.
Durch den Umweg mit Frequenz/10 minus 825, den basteluwe hinzugefügt hat, funktioniert es dann, er übergibt put() nur noch ein Byte.
danke, aber das erklärt IMO die Abrundung bei 8820 zu 8800 nicht ausreichend (CMIIW).

Daher würden mich auch andere damalige aufgetretene "Fehler" interessieren, und auch der komplette Rechenweg.

Moppi
09.10.2018, 10:39
Aber so, wie es ausschaut, gab es kein anderes Problem. Ich denke, das wars.

@basteluwe weiß mehr, ist sein Projekt.

Gruß

HaWe
09.10.2018, 11:07
Aber so, wie es ausschaut, gab es kein anderes Problem. Ich denke, das wars.
@basteluwe weiß mehr, ist sein Projekt.
Gruß
Das kanns nicht gewesen sein, siehe 8820 -> 8800.
genau deswegen bat ich ja um weitere Informationen von basteluwe.

basteluwe
09.10.2018, 16:13
Oh Ha! Hier ging ja noch richtig die Post ab!
Danke für eure ganzen Beiträge. Ich gebe mal ein Update, um vielleicht etwas Licht in's Dunkel zu bringen.

Das Problem ist tatsächlich für mich gelöst! Der im Post #14 von mir gezeigte Code funktioniert einwandfrei unter den bei mir gegebenen Bedingungen:

Die wichtigste Änderung im neuen Code zum ursprünglichen ist der Punkt an dem ich radio.getFrequency() aufrufe. Ursprünglich habe ich das jedes Mal nach einem Scan-Befehl getan. Zum Debuggen hatte ich dann im Terminal die zurück gemeldete Frequenz anzeigen lassen. Die stimmte sehr oft nicht mit der aktuell wiedergegebenen überein. Pausen vor und nach dem Befehl änderten nichts an der Abweichung.
Schließlich habe ich getFrequency einfach in die Hauptschleife gebaut und über "millis" nur alle 5 Sekunden aufgerufen. Zusätzlich habe ich EEPROM.update entdeckt, das nur bei Änderungen einen neuen Wert in den EPROM schreibt. Mit diesen Änderungen läuft der Code seit Gestern ohne jedes Problem.
Ich habe aber nach wie vor keine Ahnung warum die Abfrage im ursprünglichen Code teilweise falsche Ergebnisse lieferte, aber egal: Problem solved!

Ich sollte noch was zu den Bedingungen sagen, die zu der verwendeten Umrechnung geführt haben:


Sämtliche Sender in unserer Ecke liegen im 100KHz Raster, der frequency-Wert endet also hier IMMER auf 0, niemals auf 5.
Bei einem Frequenzbereich 87,00 - 108,00 MHz ergaben sich also 210 mögliche frequency-Werte.
Um von den richtigen Werten (8700 bis 10800) auf byte-Format zu kommen, das EEPROM.put und EEPROM.get verlangt, habe ich die Formel storeFreq=(frequency/10)-825 "erfunden". Damit liegen die Speicherwerte zwischen 45 (87,0 MHz) und 255 (108,0 MHz).

Mir ist klar, das das nur für meine spezielle Anwendung geht und nicht sehr elegant ist. Es erfüllt aber meinen Zweck, ich bin halt kein Profi-Coder.

Ich bin wirklich dankbar für Eure Hilfe, das ist schon ein Spitzen-Forum hier!

Uwe

- - - Aktualisiert - - -



Immerhin scheint es aber jetzt ja auf magische Weise nicht mehr relevant zu sein, daher würde mich schon interessieren, wie was vorher berechnet, gespeichert und zurückgerechnet wurde, als der Fehler beim Zurücklesen/rechnen des Speicherwertes noch auftrat.

Sorry, die Frage hatte ich überlesen.
Im ursprünglichen Code gab es keine Umrechnung! Ich hatte get und put für den EEPROM verwendet, weil ich meine, dass die integer handeln können. Also habe ich den Wert aus getFrequency direkt in den EPROM gespeichert.
Im neuen Code verwende ich für den EEPROM update, das leider nur byte akzeptiert. Daher jetzt die Umrechnung storeFreq = (frequency/10)-825;

Uwe

HaWe
09.10.2018, 16:14
Ursprünglich habe ich das jedes Mal nach einem Scan-Befehl getan.
aha, und was macht dieser Scan-Befehl genau, wie lautet und funktioniert er, und warum liefert er falsche oder schwankende Ergebnisse?

Moppi
09.10.2018, 16:31
@basteluwe

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.

Es gibt ja auch sowas wie Frequenzkorrektur, wenn der Sender abhaut, dass der sich durch Suchen den Sender wieder einfängt. So etwas war schon mal hier geschrieben, dass da noch eine Automatik im Spiel ist/sein könnte. - Spekulation.

MfG

HaWe
09.10.2018, 16:42
@moppi:
radio.getFrequency() funktioniert doch wie gewünscht, das Scannen vorher hat nicht funktioniert.
Daher meine Frage:

was macht dieser Scan-Befehl genau, wie lautet und funktioniert er, und warum liefert er falsche oder schwankende Ergebnisse?

Moppi
09.10.2018, 16:48
Wenn er einen Scan-Befehl schickt und ruft radio.getFrequency() auf, solange der Scan nicht beendet ist, wird es von radio.getFrequency() kein stabiles Ergebnis geben.

Gruß

basteluwe
09.10.2018, 17:38
aha, und was macht dieser Scan-Befehl genau, wie lautet und funktioniert er, und warum liefert er falsche oder schwankende Ergebnisse?

Der Befehl heisst radio.seekDown(); bzw. radio.seekUp();.
Was er genau macht und wie er funktioniert kann sicher der Programmierer der radio.h beantworten, ich in jedem Fall nicht. Die falschen Werte liefert nicht der Scan-Befehl sondern der radio.getFrequency() in zusammenhang mit dem Scan.
Ich denke, Moppi hat Recht. Vielleicht war der Scan noch nicht fertig, wenn schon getFrequency beantwortet wurde. Das könnte in der ersten Version den Fehler verursacht haben. Jetzt passiert das ja nicht mehr.
Der nackte Befehl get.Frequency liefert stabile Ergebnisse. Er wird ja in jeder Hauptschleife für die Anzeige im OLED sowieso abgefragt und die Anzeige steht stabil wie sonstwas!

Uwe

HaWe
09.10.2018, 18:07
Der Befehl heisst radio.seekDown(); bzw. radio.seekUp();.
Was er genau macht und wie er funktioniert kann sicher der Programmierer der radio.h beantworten, ich in jedem Fall nicht. Die falschen Werte liefert nicht der Scan-Befehl sondern der radio.getFrequency() in zusammenhang mit dem Scan.
Ich denke, Moppi hat Recht. Vielleicht war der Scan noch nicht fertig, wenn schon getFrequency beantwortet wurde. Das könnte in der ersten Version den Fehler verursacht haben. Jetzt passiert das ja nicht mehr.
Der nackte Befehl get.Frequency liefert stabile Ergebnisse. Er wird ja in jeder Hauptschleife für die Anzeige im OLED sowieso abgefragt und die Anzeige steht stabil wie sonstwas!

Uwe

also, jetzt mal logisch mitgedacht:
wenn der Befehl radio.getFrequency alleine korrekt funktioniert, dann wird er auch in Verbindung mit anderen Befehlen richtig funktionieren.
Also auch mit irgendwelchen dubiosen Scan-Befehlen.

Wenn also bei radio.getFrequency zusammen mit irgendwelchen dubiosen Scan-Befehlen Fehler auftreten, wird es nicht an radio.getFrequency liegen.
Sondern...? Na...? ;)

Also wo wird dann wahrscheinlich oder möglicherweise irgendwas schief gelaufen sein?

basteluwe
10.10.2018, 14:53
@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.
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.

Moppi
10.10.2018, 17:24
Vernünftig ist das. Man muss wissen, was man tut.
Theor. könnte man den Spannungsausfall feststellen und Schaltung bauen, die den Betrieb sicherstellt, bis der Wert geschrieben wäre. Also ca. eine Sekunde(?). Wen der nur einmal pro Frequenzwechsel reinschreibt, ist ja alles gut. Man hätte annehmen können, dass der das pro Scan mehrere Male macht.

MfG

HaWe
10.10.2018, 18:06
naja, also ehrlich: wenn man gerade die Frequenz frisch gewechselt hat, kann man auch 5 sec warten, bevor man den Stöpsel zieht... ;)

so wie es aussieht, lag dann ja der Fehler also weder an radio.getFrequency() noch an korrumpierten EEPROM Daten, oder?

Moppi
10.10.2018, 19:15
Hatte basteluwe nicht irgendwo geschrieben, dass der Wert, den er von der Funktion liest, nicht der Frequenz entspricht, die eingestellt war? Dass muss man dann zu dem Zeitpunkt mal so annehmen, dass das stimmt. Aber wie sich herausstellt, ist es wohl so nicht gewesen. Der Wert wird nicht gestimmt haben, nachdem er ihn aus dem EEPROM gelesen hat, zum Erneuten Einstellen der Frequenz.

Meine Glaskugel sagt: Irgendwo muss der Fehler bei der ungewollten Umrechnung in irgendwas passiert sein.


Das Problem lag wohl hier:


1. EEPROM.put(0, frequency); // neue Frequenz in EEPROM speichern
2. EEPROM.get(0, frequency);


EEPORM.put() wird wohl dann bei dem Wert 10625 nur 129 (die unteren 8 Bit) speichern, wenn die Funktion nur byteweise ins EEPROM schreibt. Damit ist die Frequenz futsch. Kann auch möglich sein dass die put-Funktion zwei Byte ins EEPROM schreibt, nämlich High und Low-Byte, aber nur ein Byte zurückgelesen wird, gäbe auch nichts genaues. Und falls das Folgebyte im EEPROM, an Position #1, für andere Zwecke gebraucht wird, würde das direkt mit überschrieben, falls zwei Byte ab Position #0 geschrieben werden.
Durch den Umweg mit Frequenz/10 minus 825, den basteluwe hinzugefügt hat, funktioniert es dann, er übergibt put() nur noch ein Byte und da ist die Frequenz dann drin. Vorher war sie ja in zwei Byte drin.

Da es basteluwe ja selbst nicht nachvollziehen kann, können wir das Phänomen nicht ergründen.

Ist auch egal, weil es für ihn keine Rolle mehr spielt. Und letzten Endes sind die verschiedenen Vermutungen auch vorher schon im Thread mal hier und da geäußert worden.

Manchmal führen die einfachsten Fehler zu den merkwürdigsten Problemen, in denen man zeitweise sogar glaubt, eine Regel zu erkennen - hinterher kann man dann nicht sagen, warum Fehler verschwunden sind.
Das menschliche Gehirn trügt auch oft, durch Vereinfachen oder Ersetzen fehlender Informationen. Da muss man keine mathematische Formel mit Herleitung haben, sondern nur die Größe das einzugestehen. ;-)


MfG

HaWe
10.10.2018, 21:35
Nein, es kann NICHT an einzelnen Bytes iiegen, die verloren gehen, denn dadurch wäre der Fehler von 8820 zu 8800 nicht erklärbar!
Denn dann müsste entweder nur der MSB- oder nur der LSB-Wert zurückkommen, also in jedem Fall <=255, was aber nicht der Fall ist.
Auch ist das EEPROM nie "ungenau", wie es noch im Titel heißt und so auf der 1. Seite hier viele vermutet Datenkorruption haben.
EEPORM.put andererseits kann ohne weiteres auch ints schreiben, nicht nur bytes.

basteluwe:
ich fände es wirklich interessant, wenn du den Fehler eingrenzen könntest, nachdem du ja gezeigt hast, dass radio.getFrequency richtig funktioniert.
Was machen also die Scan-Befehle oder sonstige weitere Befehle unmittelbar davor und unmittelbar danach?

Moppi
10.10.2018, 21:42
Also ist das die Möglichtkeit! DOCH kann es NUR an einzelnen Bytes oder Bits liegen, die CPU verarbeitet nichts anderes. :-)

So, gut jetzt ;-)

MfG

HaWe
10.10.2018, 21:58
Also ist das die Möglichtkeit! DOCH kann es NUR an einzelnen Bytes oder Bits liegen, die CPU verarbeitet nichts anderes. :-)

So, gut jetzt ;-)

MfG

nein, das musst du erklären: wie soll es an nur 1 einzelnen verlorenen Byte beim Lesen oder Schreiben im EEPROM liegen, wenn die ursprüngliche Version auf glatte 100er abrundet, z.B. von 8820 auf 8800 ??
Ich habe gereits gezeigt, dass das nicht an einem komplett verloren gegangenen LSB und erst recht nicht an einem verlorenen MSB liegen kann, siehe hier, bereits auf der 1. Seite: https://www.roboternetz.de/community/threads/72518-EEPROM-ausgelesener-Wert-ist-ungenau?p=647127&viewfull=1#post647127
Und auch nicht irgendwie an radio.getFrequency, denn das funktioniert ja IMMER, wie bereits gezeigt.
Und reine (statistische) Datenkorruption muss hingegen eine Gaussche, statistische Fehlerverteilung vom ursprünglichen Wert sowohl nach oben als auch nach unten gleichermaßen führen, was der OP auch nicht bestätigen konnte.
Oder wie erklärst du das Runden auf glatte 100er mit deinen verlorenen LSB oder MSB?!

also nochmal

basteluwe:
ich fände es wirklich interessant, wenn du den Fehler eingrenzen könntest, nachdem du ja gezeigt hast, dass radio.getFrequency richtig funktioniert.
Was machen also die Scan-Befehle oder sonstige weitere Befehle unmittelbar davor und unmittelbar danach?


EDIT:
und nochmal ergänzt, hatte ich auch schon öfter erbeten:
wie sahen andere Beispiele damals aus, wo es zu Abweichungen kam? (10 Beispiele mindestens).
Vielleicht waren es ja nicht immer Abrundungen, wie es sich bislang anhörte - oder doch?

Moppi
10.10.2018, 22:23
Schau mal an den Anfang des Threads, da hatte er den Code doch reingestellt.

Gruß

HaWe
10.10.2018, 22:30
ich verstehe nicht die einzenen Funktionen, was die genau mit welcher Konsequenz machen oder machen sollen,
und auch nicht den komplexen Code im Zusammenspiel.

Auch radio.getFrequency verstehe ich in seiner Funktionsweise nicht, ich sehe nur, dass es aus logischen Gründen nicht daran liegen kann, ebensowenig an komplett verloren gegangenen Bytes.

Daher aber auch neben allem anderen: man muss die genauen Fehler mal aufgelistet sehen, um sie analysieren zu können.

basteluwe
11.10.2018, 09:47
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!


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:
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

oberallgeier
11.10.2018, 10:33
.. Man muss dem Radio genügend Zeit geben, die neue Frequenz einzustellen, bevor man sie abfragt. So einfach ist das ..So ein Problem ist im Betrieb von chemisch-physikalischen Laboren bekannt beim Abwägen von Mengen. Dort beschreibt man es mit "Wägen bis zur Gewichtskonstanz". Daher könnte es eine sichere Abfragemethode geben: abfragen1, kurz warten, abfragen 2 - wenn a1==a2 dann (zur Sicherheit vielleicht noch mal) ist der WErt ok, wenn a1 != a2 dann nochmal warten, nochmal abfragen und vergleichen a2 mit a3 usf. Bis zur "Wertekonstanz".

HaWe
11.10.2018, 13:35
@basteluwe:
ahaaa, danke, jetzt ist mir das Problem klar, und so lässt sich auch alles erklären mit den beobachteten Fehlern!

Klebwax
11.10.2018, 22:18
Statt zu warten, bis sich die Frequenz während des Sendersuchlaufs nicht mehr ändert, könnte man auch in das Datenblatt (http://cxem.net/tuner/files/tuner84_RDA5807M_datasheet_v1.pdf) des Radiochips schauen. Dort sind die via I2C erreichbaren Register beschrieben. Man findet dort auch das Bit, das gesetzt werden muß, um einen Seek zu starten. Und man findet ebenfalls, daß es vom Chip zurück gesetzt wird, wenn ein Sender gefunden wurde. Man braucht also nur das passende Register (0x02) zu lesen und Bit 8 auszuwerten. Dann weiß man genau, daß der Empfänger auf einen Sender eingerastet ist. Ich denke mal, so haben sich die Erfinder des Chips das gedacht.

MfG Klebwax

PICture
13.10.2018, 07:37
Statt zu warten, bis sich die Frequenz während des Sendersuchlaufs nicht mehr ändert, könnte man auch in das Datenblatt (http://cxem.net/tuner/files/tuner84_RDA5807M_datasheet_v1.pdf) des Radiochips schauen.

Genau ! Änliches habe ich schon im Beitrag #3 erwähnt. ;)

HaWe
13.10.2018, 19:32
@ basteluwe

Ich vermute, dass der Fehler durch AFC ("Automatic Frequency Control" -> automatische steuerung der Frequenz) verursacht ist. Auf der "page 9" des Dateblattes habe ich im Register 04H das AFCD "default=0" gefunden. Das bedeutet, dass AFC aktiv ist und du könntest probieren das AFCD "disable=1" programmieren und schauen ob das hilft. ;)


Genau ! Änliches habe ich schon im Beitrag #3 erwähnt. ;)

hier sprichst du aber nur von AFC, das betrifft allerdings nicht die Frage, ob er überhaupt mit dem Scan fertig ist, was über das flag bit 8 im byte an 0x02 abgefragt werden kann.

D.h., es ist ganz egal ob automatisch (AFC) oder manuelle Kanalwahl - er kann in beiden Fällen mit dem Scan fertig sein oder eben auch nicht, und wenn nicht, kann es in beiden Fällen zu den beobachteten Scanfrequenz-Abfragefehlern führen.