Ich habe ein seltsames Problem mit dem Wert eines Potentiometers, der die Lautstärke eines DAC steuern soll. Meine Schaltung besteht aus einem Atmega168, der den DAC (ES9028Pro) via I2C ansteuert. Das Poti bildet mit 3.3V und GND einen Spannungsteiler, dessen Augang an PC0 geht.
Der Bereich von 0 bis 1023 wird in einen Bereich von 0 bis 60 umgewandelt (siehe Code unten). Beim Debuggen sehen alle Werte korrekt aus, aber wenn ich 0 vom Potentiometer an die Funktion „setAllDACVolumes()“ übergebe, erhalte ich keine Dämpfung von 0, sondern kein Audio. Wenn ich aber der Funktion direkt eine "0" gebe, erhalte ich null Dämpfung. Oder auch wenn ich direkt eine "10" gebe, erhalte ich die korrekte Dämpung von 5dB.
1
intcurrentVolume=ADC_read(0);
2
if(currentVolume!=lastVolume){
3
uint8_tatt_db=((currentVolume*60.0f)/1023.0f);
4
setAllDACVolumes(att_db*2);
5
lastVolume=currentVolume;
6
}
Die setAllDACVolumes()-Funktion sieht so aus:
1
voidsetAllDACVolumes(uint8_tValue)
2
{
3
inti;
4
for(i=0;i<8;i++)
5
{
6
SendCtrlReg2(DAC_REG_VOLUME_BASE+i,Value);// set DAC Volumes, 0x00 is default
7
}
8
}
Die Funktion SendCtrlReg2 schreibt den Wert, der der Funktion übergeben wird, in das Register des DAC (ES9028Pro). In meinem Fall sind das die Register 16 bis 23. Der Standardwert für die Lautstärke ist 0x00. Diese Funktion läuft korrekt; sie schreibt auch in andere Register.
Ich code mit Microchip Studio und debugge mit einem Atmel ICE.
Als 0 x 60.0 und dann / 1023.0 und bei Übergabe nochmal x 2
Du verwendest float, und float ist nie genau. Allerdings sillte das Virzeichen stimmen und nicht -0.0001 zu -1 und dann 255 werden, daher kann ich den Effekt nicht erklären.
Man könnte die maximal 1023 von (klugerweuse uint16_t) currentVolume auch mit 60U multiplizieren, ergibt 61380 und passt noch in einen uint16_t und dann durch 1023U teilen zu 59 und dann x 2 macht maximal 118. Ich hätte erwartet du programmierst 0-255. Aber eigentlich hast du ein zusätzliches bit mit 18dB, also 0-291.
Was passiert bei currentVolume == lastVolume ?
Wenn Du das Poti auf 0 stellst, wird das nach dem ersten Mal so sein.
Bei anderen Werten dithert der ADC Wert so rum, da tritt der Fall selten auf. Möglicherweise steckt das Problem an einer ganz anderen Stelle als Du vermutest.
Ist es so, dass die Einstellung via ADC für alle Werte funktioniert, nur speziell 0 nicht? Oder funktioniert die Einstellung über das Poti generell nicht, aber mit jedem hartkodierten Wert doch?
Ist es so, dass die Einstellung via ADC für alle Werte funktioniert, nur
speziell 0 nicht? Oder funktioniert die Einstellung über das Poti
generell nicht, aber mit jedem hartkodierten Wert doch?
Die ADC funktioniert für alle Werte. Selbst wenn ich 0 nicht einstellen könnte, würde der Wert 2 oder 3 in einer sehr kleinen Attenuation resultieren, und damit ein klar hörbares Audiosignal durchkommen würde.
Der Bereich von 0 bis 1023 wird in einen Bereich von 0 bis 60
In Arduino gibt es die Funktion map(). Wird sicher etwas Ähnliches in C/C++ geben. Vielleicht geht es damit. Vor allem, damit kannst du unten oder oben was weg x_en.
Ich weiß nicht, ob das auch da rein spielen könnte. Lautstärkepotis sind oft nicht linear.
In Arduino gibt es die Funktion map(). Wird sicher etwas Ähnliches in
C/C++ geben.
Früher (tm) nannte man das Dreisatz, genauer Geradengleichung in der Zwei-Punkte-Form. Und natürlich lässt sich die auch in C/C++ formulieren, wenn man nicht die Arduino Quellen danach durchsuchen möchte.
Früher (tm) nannte man das Dreisatz, genauer Geradengleichung in der
Zwei-Punkte-Form.
Auch heute noch.
Aber vielleicht sind solche Funktionen schon optimiert und funktionieren besser?
Ich weiß es nicht. Oder meinst du, dass alle zu doof zum Rechnen sind, dass man so was implementieren muss?
Aber vielleicht sind solche Funktionen schon optimiert und funktionieren
besser?
Da gilt es erstmal die Bewertungskriterien für "besser" in einem bestimmten Szenario zu definieren.
Wie die Arduino Funktion map() sich grob benimmt, ist in der Duko so zusammengefasst:
"Die map() - Funktion verwendet Ganzzahl-Mathematik, so dass keine Brüche generiert werden, wenn die Mathematik möglicherweise darauf hinweist, dass dies der Fall ist. Bruchreste werden abgeschnitten und nicht gerundet oder gemittelt."
Auf dem Bild seht Ihr den Debug-Mode. Und die Variable att_db, die an die Funktion setAllDAC Volumes() übergeben wird ist 0. Aber ich bekomme keine 0 Attenuation und damit auch kein Audio. Das ist für mich nicht erklärbar..
Auf dem Bild seht Ihr den Debug-Mode. Und die Variable att_db, die an
die Funktion setAllDAC Volumes() übergeben wird ist 0. Aber ich bekomme
keine 0 Attenuation und damit auch kein Audio. Das ist für mich nicht
erklärbar..
Auf dem Bild seht Ihr den Debug-Mode. Und die Variable att_db, die an
die Funktion setAllDAC Volumes() übergeben wird ist 0. Aber ich bekomme
keine 0 Attenuation und damit auch kein Audio. Das ist für mich nicht
erklärbar..
Er meinte sicher das von mir im Zitat hervorgehobene. Was passiert, wenn
du an Stelle des Ausdrucks "att_db*2" einfach "0" hinschreibst?
Wie bereits erwähnt bekomme ich 0dB Dämpfung und damit volles Volumen,
wenn ich eine "0" anstelle von "att_db*2" setze
OK, nächster Schritt: was passiert, wenn du das mit der 0 so läßt, aber zusätzlich eine Zeile höher vor die Variablendeklaration noch ein "volatile" schreibst?
Der Sinn der Sache ist, die Ausführung des Gleitkommacode-Gedöhns zu erzwingen, auch wenn das Ergebnis der Berechnung dann doch nicht benutzt wird.
Was muss an diese Funktion normalerweise übergeben werden, um kein Audio
auszugeben. Also um das selbe zu erreichen, wie im Fehlerfall?
Eine hohe Dämpfung, in meinem Fall ist die maximale Dämpfung 60dB. Die wird erreicht, wenn currentVolume den Wert 1023 hat. att_db wird mit Faktor 2 multipliziert, da in 0,5dB-Schritten attenuiert wird.
Hat jetzt nicht mit dem eigentlichen Problem zu tun, aber die "*2" in dem Funktionsaufruf ist arg ungeschickt. Dadurch lassen sich die 0.5dB-Schritte nicht einstellen. Warum packst du die 2 nicht in die Float-Rechnung, bevor der Wert auf uint8_t konvertiert wird, d.h. skalierst currentVolume direkt auf 0..120?
Hat jetzt nicht mit dem eigentlichen Problem zu tun, aber die "*2" in
dem Funktionsaufruf ist arg ungeschickt.
Nicht unbedingt.
Wenn es beispielsweise eins der Designziele war (aus welchen Gründen auch immer), nur volle dB-Steps zu ermöglichen, dann passt das schon. Offensichtlich wird ja auch der mögliche Bereich (ganz absichtlich) nicht ausgenutzt.
Nö, wenn da was zu optimieren wäre, dann wäre die erste Maßnahme, den float-Scheiß rauszuwerfen. Das ist, was hier auf jeden Fall kein Mensch braucht (und möglicherweise sogar indirekt über den Resourcenverbrauch der störende Knackpunkt ist).
Nö, wenn da was zu optimieren wäre, dann wäre die erste Maßnahme, den
float-Scheiß rauszuwerfen.
wenn ich weiß daß ich float nicht brauche wäre das meine erste Maßnahme und im µC brauche ich für ADC und DAC nie float.
Float brauche ich allenfalls für eine Ausgabe zu Menschen und kurz davor kann ich zu float wandeln wenns sein muß und das sogar ohne float Library, ich rechne z.B. in CentiVolt als uint_16 und setze zur Ausgabe nur das Komma per Textersetzung in den String.
wenn ich weiß daß ich float nicht brauche wäre das meine erste Maßnahme
und im µC brauche ich für ADC und DAC nie float.
Float brauche ich allenfalls für eine Ausgabe zu Menschen und kurz davor
kann ich zu float wandeln wenns sein muß und das sogar ohne float
Library, ich rechne z.B. in CentiVolt als uint_16 und setze zur Ausgabe
nur das Komma per Textersetzung in den String.
Ich persönlich brauche in fast allen µC-Fällen auch keine Fließkommazahlenberechnungen und mache das so ähnlich wie beschrieben. In dem vorliegenden Fall müsste man den Wert an der entsprechenden Stelle auch casten, es wird aber alles sehr zäh und schwierig, wenn jemand nicht einmal in der Lage ist, seine fünf Zeilen Code systematisch so durchzudebuggen, dass er die Ursache für seine falschen Werte eingrenzen und auf diese Weise finden kann. Bei so etwas gibt es bei mir kein Mitleid, denn das kann man relativ schnell lernen und Methoden zum Debuggen gibt es viele; und diese eigenen Hausaufgaben sollte man auch so schnell wie möglich erledigen. Fertigcode gibt es von mir auch nicht, denn das sollte derjenige schon selbst hier so weit korrigieren und anpassen, bis es passt, sonst wird der Lernprozess nie in Gang gesetzt und in ein paar Wochen oder Monaten wird ein neuer Thread mit ähnlicher Problematik eröffnet... und wieder darauf gewartet, dass einem ein fertiges Kotelett serviert wird.
Float brauche ich allenfalls für eine Ausgabe zu Menschen ...
Gerade der Mensch will eine Darstellung mit Mantisse und Exponent eigentlich nicht habe, sondern kann beim Ablesen besser mit einem Komma an fester Position umgehen, falls es sich nicht um Daten mit extrem großem Dynamikbereich handelt. Dafür eignen sich dann oft logarithmische Angaben besser z.B. in dB oder nach der Richter Skala - je nach Kontext.
Wenn es beispielsweise eins der Designziele war (aus welchen Gründen
auch immer), nur volle dB-Steps zu ermöglichen, dann passt das schon.
Offensichtlich wird ja auch der mögliche Bereich (ganz absichtlich)
nicht ausgenutzt.
Sag mir einen vernünftigen Grund, warum man bei Einsatz eines Potis als Geber für die Lautstärkeeinstellung diese Lautstärke der Einstellung nicht möglichst kontinuierlich folgen soll.
So, wie sich der TO mit dem Debuggen schwer tut, vermute ich erstmal keine ernste Absicht hinter dem Verhalten, sondern eher krampfhaftes Festhalten an einer 1dB-Teilung.
So, wie sich der TO mit dem Debuggen schwer tut, vermute ich erstmal
keine ernste Absicht hinter dem Verhalten, sondern eher krampfhaftes
Festhalten an einer 1dB-Teilung.
Also so wie bei 98% aller Hobbyprojekten?!
Nenn mir einen vernünftigen Grund, warum dich dieser Thread über haut interessiert? Ernste Absichten scheinst du ja nicht zu haben!
Statt der float-Herumrechnerei könnte man auch einfach mal einen Blick auf Zweierpotenzen werfen.
Den ADC-Wert, der zwischen 0 und 1023 liegt, teilt man durch 16. Dann erhält man einen Wert zwischen 0 und 63.
Teilen durch eine Zweierpotenz ist einfach nur schieben nach rechts.
Und jetzt macht man noch 'ne simple Bereichsbegrenzung, ist der Wert größer als 60, wird er auf 60 begrenzt.
Daß da potentiell ein bisschen Drehwinkel des Potis kurz vor dem Anschlag verloren geht, sollte man verkraften können, das ist eh' alles analog und Potis vor allem sind nicht irrwitzig linear und genau schon gar nicht.
1
uint8_tatt_db=currentVolume/16;
2
if(att_db>60)
3
att_db=60;
Wenn das Ziel tatsächlich ist, auf 60 dB zu kommen, sollte man nicht durch 16, sondern nur durch 8 teilen, denn die Lautstärkenregister arbeiten mit 0.5-dB-Schritten.
Auf die Bereichsbegrenzung kann man auch verzichten, denn die Lautstärkenregister sind 8 Bit breit, können also Werte von 0..255 annehmen.
Und wenn man den vollen Wertebereich der Lautstärkenregister nutzen will, statt bei 60 (oder 63) dB aufzuhören, teilt man den ADC-Wert durch 4.
wenn ich 0 vom Potentiometer an die Funktion „setAllDACVolumes()“
übergebe, erhalte ich keine Dämpfung von 0, sondern kein Audio. Wenn ich
aber der Funktion direkt eine "0" gebe, erhalte ich null Dämpfung.
Deine Funktion erwartet einen Integer als Argument. Dort ist die 0 eindeutig definiert, egal woher sie kommt. Es kann nicht sein, dass die Funktion sich unterschiedlich verhält, je nach dem woher die 0 kommt.
Also solltest du diese Annahme nochmal doppelt und dreifach überprüfen, wahlweise mit einem Unterbrechungspunkt innerhalb der Funktion oder mit einer seriellen Text-Ausgabe. Letzteres würde ich bevorzugen.
Wenn das keine neue Erkenntnis bringt dann bedenke, dass Timing eine Rolle spielen kann. Und bedenke, dass ganz andere scheinbar intakte Teile des Programms möglicherweise Fehlfunktionen auslösen, z.B. indem sie einen Heap/Stack Überlauf auslösen, über Array-Grenzen hinaus laufen, oder mit nicht initialisierten Zeigern arbeiten.
Manchmal hilft es, das Programm schrittweise zu verkleinern (Teile auskommentieren, bzw. durch Dummies ersetzen), bus der Fehler verschwindet. Oder anders herum mit einem minimalen Programm neu anfangen und Schrittweise erweitern, bis der Fehler wieder kommt.
Statt der float-Herumrechnerei könnte man auch einfach mal einen Blick
auf Zweierpotenzen werfen.
Statt mit Float, rechnet man mit kleinstmöglichen Integern – in diesem Fall wäre das z.B. 16-Bit ohne Vorzeichen, weil man auf knapp über 60 Tausend kommen möchte, da man die Multiplikation in der Regel immer zuerst machen muss. Wenn es nicht ausreicht oder besser mit noch anderen Integern im Konzept passt, nimmt man z.B. 32-Bit – die Integerberechnungen (Multiplikationen und Divisionen) werden auf einem AVR höchstwahrscheinlich immer noch schneller als Floatberechnungen sein. Bei Addition und Subtraktion wird es auf jeden Fall so sein – wenn es ganz schnell gehen muss, kann man manche Multiplikationen oder Divisionen auch durch Bitverschiebung und/oder Addition ersetzen (z.B. 2a+a=3a oder 4a+a=5a), diese Akrobatik ist in diesem Fall aber gar nicht nötig. Ferner kann man Wertebereiche auch an Zweierpotenzen ausrichten – z.B. wenn man Dezimationen mit ADCs machen möchte und es schnell gehen soll, braucht man dann am Ende nicht 'krumm' teilen, sondern verschiebt das Ergebnis um so viele Bitstellen wie nötig.
Schreibt man dagegen ein Programm für einen PC, sind Floatberechnungen manchmal die erste Wahl – bei Unity (schreiben eines Spiels) oder WPF-Anwendungen z.B. nimmt die grafische Oberfläche in userem Code von vorherein nur Doubles an, also sogar gleich in zweifacher Genauigkeit, weil das von der GPU der Grafikkarte eh alles schneller berechnet werden kann und beim Rendering, Skalierung oder Transformationen (z.B. Drehung von Objekten auf dem Bildschirm) die Integer nur Probleme verursachen würden bzw. völlig unbrauchbar wären. Falls man die Werte gerundet und in einfacher Form haben möchte, um sie beispielsweise als String in seiner ConfigDatei abspeichern zu können, kann man sie vorher für diesen Zweck entsprechend konvertieren – später beim Auslesen macht man es dann umgekehrt und bildet daraus wieder Doubles, damit die Objekte der Grafik es überhaupt annehmen können.
Daß da potentiell ein bisschen Drehwinkel des Potis kurz vor dem
Anschlag verloren geht, sollte man verkraften können, das ist eh' alles
analog und Potis vor allem sind nicht irrwitzig linear und genau schon
gar nicht.
Die Randproblematik des Potis, die übrigens real ist, kann man auch ganz einfach lösen, indem man den Wertebereich etwas verschiebt, ausdehnt oder kürzt und anschließend mit einer oder zwei IF-Zeilen absichert. Dieser Feinarbeit oder Nachjustierung sollte man sich aber grundsätzlich erst danach widmen, also nachdem unsere Hauptroutinen das tun, was sie sollten, um nicht noch mehr Unbekannte ins Spiel zu bringen. Die Regel der Systematik ist ganz einfach: man macht den Code am Anfang extrem einfach und wenn dieser das tut, was er soll, baut man ihn weiter ein wenig aus und prüft wieder. Beim Debuggen von sehr merkwürdigen Fehlern so vorgehen: die verdächtigen Bereiche/Blöcke stupide Zeile für Zeile debuggen, also sich die Werte anschauen oder ausgeben lassen, denn irgendwo muss der Fehler bzw. müssen die falschen Werte ja auftauchen, manchmal ist der Fehler auch schon früher passiert, weil man einen Denkfehler gemacht hat, aber auch das sieht man bei dieser Art des Debuggens sofort. Und Leute: wir reden hier von 5-10 zeilen Code, um den Zonk dingfest zu machen! Bei ein paar Tausend Zeilen, wie bei mir oft der Fall ist, funktioniert das Prinzip des systematischen Debuggens aber genauso gut.
Statt der float-Herumrechnerei könnte man auch einfach mal einen Blick
auf Zweierpotenzen werfen.
Statt mit Float, rechnet man mit kleinstmöglichen Integern
[...]
Jetzt ist es endgültig sicher: Gregor J. ist eine KI. Die typischen Anzeichen waren ja schon länger da (ausschweifendes Werfen von Wattebällchen).
Aber hier hat es konkret auf auf das Posting von Harald geantwortet, dabei aber offenbart, das es von dem semantischen Inhalt des Postings rein garnichts verstanden hat.
Also: entweder KI oder menschliches semmeldummes Pförtnerkind. Da letztere aber normalerweise zu faul und zu dumm sind, solch ausschweifende Postings in fast fehlerfreiem Deutsch zu verfassen, bleibt nur: KI.