Hey Leute,
ich benötige mal eine kleine Hilfe bzgl. der Ansteuerung einer 7
Segmentanzeige.
Konkret möchte ich den Wert der vom ADC (0-1023) eingelesen wird auf 4
7-Segmentanzeigen ausgeben.
Jetzt habe ich Beispiele gesehen, wo der eingelesene Wert mittelels
Modulodivision in Tausender-, Hunderter-, Zehner- und Einerstellen
zerlegt wird und diese dann auf an Display gesendet werden.
Dies kostet allerdings Rechenleistung und Zeit
Daher meine Frage:
Könnte man nicht einfach den ADC Wert in ein Array einlesen und dann
einfach die ensprechenden Felder des Arrays an das Display senden und
sich die ganze rechnerei sparen oder habe ich da einen Denkfehler?
Und, wie müsste man dies programmieren. Über ein Beispiel wäre ich sehr
Dankbar, da sich meine Programierfähigkeiten noch in Grenzen halten.
Falls ich auf dem falschen Dampfer bin, hat jemand vielleicht einen
anderen Lösungsansatz parat, der die ganze Rechnerei überflüssig macht?
Gruß Martin
Das braucht nur lächerlich wenig Rechenleistung.
Und wenn du dir Umrechnerei im MC zu aufwändig ist (binär und dezimal
passen nun mal nicht gut zusammen) bau dir eine Hex-Anzeige und rechne
im Kopf um :-).
3E8 sind übrigends 1000 :-)
Und wie willst du die einzelnen Zahlen trennen, um sie in ein Array zu
schreiben?
Dass die Modulo-Operationen zu langsam sind und zu viel Speicher
verbrauchen glaube ich dir nicht.
Du musst ja nur alle XXX Millisekunden mal ein Update des Displays
machen - und nur dann musst du die Zahlen umrechnen.
Einerseits sagst du, dass du vom Programmieren wenig Ahnung hast,
andererseits meinst du beurteilen zu können, ob diese Lösung zu
rechenintensiv ist - irgendwie passt das nicht. Wenn, dann liegt die
Ursache für Probleme garantiert woanders in deinem Code.
"
Daher meine Frage:
Könnte man nicht einfach den ADC Wert in ein Array einlesen und dann
einfach die ensprechenden Felder des Arrays an das Display senden und
sich die ganze rechnerei sparen oder habe ich da einen Denkfehler?"
Tjo, dann tust du den Integer in ein Array:
adc_wandlungen[0] = ADC; //Vom Typ uint16
und das sind dann 10 Bits die entweder 0 oder 1 sein können.
Wie du jetzt ohne Umwandlung vom Binär- auf das Dezimalsystem kommen
willst sodass man diese Zahl dann auf den 7seg Anzeigen lesen kann: Der
Vorgang nennt sich Magie.
Binär und Dezimal sind halt komplett unterschiedliche Dinge, daher
braucht man eine Umwandlung.
Du kannst alternativ auch boolesche Algebra bemühen wenn dir Modulo
Aufrufe zu teuer sind. Ob das dann weniger Rechenleistung kostet ist
fraglich, mit ziemlicher Sicherheit wird sich das simple Problem dann
von wenigen Zeilen auf mehrere DINA4 Seiten aufblähen.
Im Prinzip ist da schon was dran. Frühere Generationen hatten sich mit
BCD-Code rumgeärgert, um die Division einzusparen. Ist man von
abgekommen. Bei den Hardwarepreisen lohnt sich der Aufwand nicht mehr.
Ausserdem sind die Compiler inzwischen so gut; für Divisionen mit
Konstanten puzzlen die den optimalen Maschinencode zusammen.
Martin schrieb:> Dies kostet allerdings Rechenleistung und Zeit
Oje oje, wie schnell kannst du denn gucken ?
Erkennt dein Auge mehr als 32000 Zahlen pro Sekunde ?
> Daher meine Frage:> Könnte man nicht einfach den ADC Wert in ein Array einlesen
Einlesen, was soll hier einlesen ? Ein Indexwert ist es, also
indizieren.
> und dann> einfach die ensprechenden Felder des Arrays an das Display senden und> sich die ganze rechnerei sparen oder habe ich da einen Denkfehler?
Klar kann man, wenn man einen uC mit 4k Speicher hat.
Der ewige space vs. speed tradeoff, man tauscht Rechenzeit durch
vorausgerechnete Tabellenwerte.
1
charbcd[1024][4]=
2
{
3
{0,0,0,0},
4
{0,0,0,1},
5
:
6
{1,0,2,4}
7
};
8
9
x=AnalogRead(0);
10
digit3=bcd[x][0];
11
digit2=bcd[x][1];
12
digit1=bcd[x][2];
13
digit0=bcd[x][3];
Sinnvoll ist das sicher nicht, bei der langsamen Beobachtungsgabe hat
der uC 1 Million Befehle übrig um den Analogwert in Ziffern zu wandeln,
da kann man sogar gnadenlos zählen statt dividieren.
1
x=AnalogRead(0);
2
digit[0]=digit[1]=digit[2]=digit[3]=0;
3
while(x-->0)for(i=0;++digit[i]>9;i++)digit[i]=0;
Das dürfte dann der Code mit dem geringsten Speicherbedarf sein.
Martin schrieb:> OK, dann war der Gedanke halt falsch und ich mach es halt doch mit> Division. Wollte ja nur wissen, ob es eine bessere Lösung gibt.
Die gibt es tatsächlich. Im Prinzip geht es um eine Wandlung der rein
binär codierten Darstellung einer Zahl in eine BCD-Darstellung. Dafür
gibt es hochoptimierte Asm-Routinen, die das in einem klitzekleinen
Bruchteil der Zeit einer dümmlichen Implementierung in Plain-C
erledigen.
Das "Problem" ist nur: Das ist für deine Anwendung überhaupt nicht
relevant. So schnell, wie du das Ergebnis vom Display ablesen kannst,
kann das der Controller mit Links und 40 Fieber die Wandlung in eine
BCD-Darstellung erledigen, selbst wenn er nur mit 32kHz Takt läuft (z.B.
mit einem Uhrenquarz als Taktgeber) und die dümmstmögliche
C-Implementierung des Algorithmus ackert...
Im Gegenteil: Deine nächste Frage würde dann sowieso sein: Wie sorge ich
dafür, daß es in der letzten Stelle nicht so schrecklich wackelt, das
Geflimmere kann ja keiner ablesen...
Sprich: du darfst das Pferd nicht vom Schwanz aufzäumen. Sinnvolle
Aktualisierungsraten eines 7-Segment-Displays liegen so bei etwa 2Hz,
wesentlich schneller kann nämlich kein Mensch das angezeigte Zeug
erfassen. Die Wandlung in das Anzeigeformat muss also höchstens zwei mal
pro Sekunde erfolgen und nicht so oft, wie du den Messwert gewinnst.
Wobei dann sowieso noch zu klären wäre, ob die gewählte Samplerate für
die Messung überhaupt sinnvoll ist, aber das ist schon wieder ein ganz
anderes Thema...
>Dafür>gibt es hochoptimierte Asm-Routinen, die das in einem klitzekleinen>Bruchteil der Zeit einer dümmlichen Implementierung in Plain-C>erledigen.
Dürfte wohl nur wenige Programmierer geben, die so eine trickreich
optimierte Division hinbekommen:
https://blog.fefe.de/?ts=a922623d
Martin schrieb:> Könnte man nicht einfach den ADC Wert in ein Array einlesen und dann> einfach die ensprechenden Felder des Arrays an das Display senden und> sich die ganze rechnerei sparen oder habe ich da einen Denkfehler?
Kein Denkfehler, das kann man so machen.
Allerdings ist bei kleinen Controllern der Speicherplatz ohnehin meist
knapp. Aber wenn du davon genug hast, warum nicht?
Man kann theoretisch sogar direkt die 7-Segment Darstellung ins Array
packen 3x7 + die Eins = 22Bit pro Feld. Praktisch eher 3 x 8 Bit und die
dann direkt auf 3 Ports raus, wenn man diese denn frei hat.
Schön! Kein Multiplexgedöns, keine schwierige Rechnerei, alles schön
glatt und übersichtlich. Bau das doch so, egal was hier noch für "Profis
auftauchen".
Man muß auch mal etwas anderes tun als nur dem Mainstream zu folgen.
Jeder programmiert so wie er es für richtig hält und am schnellsten ans
Ziel kommt, einzig das Ergebnis zählt.
Auch lustig ist natürlich die Idee mit dem Zählen von Laberkopp...
H.Joachim S. schrieb:> das dürfte sich auch mit damit> https://de.wikipedia.org/wiki/Brainfuck> gut programmieren lassen
Jaja, mein Post war sooo ironisch nicht gemeint... Habe etwas in der art
nämlich vor vielen vielen Jahren in Hardware zusammengetüddelt.
Seinerzeit ist mir ein wirklich wunderschönes ic BURR-BROWN ADC80
zugelaufen dem ich mangels anderer Ideen unbedingt ne 7-Segmet Anzeige
spendieren wollte. Also die 12 Bit in 3 74193 laden, bis 0 abwärts
zählen und mit gleichen Takt 4 74192 aufwärts zählen lassen...et voilà!
Daher meine Sympathie für exentrische Ideen;-)
Damit rennste bei mir offene Türen ein...
Aber es ist sicher nicht die richtige Idee für jemanden, der Angst davor
hat, dass 3 läppische Divisionen die performance seines MC ins
unterirdische abgleiten lassen .
Wenn man die Division nicht mag, kann man die Wandlung auch mit
Multiplikationen machen:
Erst den ADC wert passend skalieren, das der Wert für die oberste Ziffer
oben steht (z.B. oberstes byte, obere 4 Bits) und dann für die weiteren
Stellen jeweils die oberen Bits Löschen und den Rest mit 10
Multiplizieren, so dass dann die nächste Stelle nach oben kommt.
Bei der ersten Multiplikation gibt es ggf. einen kleinen Rundungsfehler,
der aber auch nicht größer ist als bei einer normalen Skalierung mit
einem krummen Wert, etwa von der Umrechnung der Ref. Spannung oder
Taktfrequenz.
Das kann vor allem dann hilfreich sein, wenn der µC Hardware
Multiplikation kann, oder eine Multiplikations-routine sowieso schon da
ist.
Man kann die Wandlung auch ohne Division erledigen, indem man
wiederholte Subtraktionen ausführt. Ein Beispiel für eine max.
6-stellige Zahl:
// Wandelroutine bin -> String
void long2a(int32_t l, int8_t *s, int8_t nachkomma)
{
uint32_t tab[] = {100000, 10000, 1000, 100, 10, 1};
uint32_t temp;
int8_t i = 0, j , n = (sizeof(tab)/sizeof(tab[0])-nachkomma);
if(l < 0) {
l = -l;
*s++ = '-';
}
while((i < n-1) && (tab[i] > l)) {
i++;
}
do {
if(i == n) {*s++ = '.';}
temp = tab[i];
j = '0';
while(l >= temp) {
j++;
l -= temp;
}
i++;
*s++ = j;
} while(temp > 1);
*s = 0; // String abschließen
}
c-hater schrieb:> Sinnvolle> Aktualisierungsraten eines 7-Segment-Displays liegen so bei etwa 2Hz,> wesentlich schneller kann nämlich kein Mensch das angezeigte Zeug> erfassen.
Zum einen kann ein Mensch deutlich schneller ablesen und zum anderen
bestimmen die Anforderungen, was sinnvoll ist.
Kürzlich wollte jemand bei einer Hochgeschwindigkeitskamera mit bis zu
400 Bildern/s die Zeit nebst Bildnummer (frame) einblenden. Da muß die
Wandlung erheblich schneller passieren. Gleiches gilt, wenn die
BCD-Daten z.B. an einen PC übertragen werden sollen.
Michael B. schrieb:> da kann man sogar gnadenlos zählen statt dividieren.
Das könnte man sogar in Hardware ganz ohne uC machen:
Ein Binärzähler wird mit dem AD-Wert geladen und zählt
mit einem festen Takt bis Null. Mit diesen Impulsen
lässt man einen BCD-Zähler mit 7-Segment-Ausgang hoch-
zählen. Das könnte man z.B. mit 4000-ICs mehrmals in
einer Sekunde machen
m.n. schrieb:> Harald W. schrieb:>> Das könnte man z.B. mit 4000-ICs mehrmals in>> einer Sekunde machen>> 4000? Das sind aber ganz schön viele ;-)
Naja, damit kann man eine 1000-stellige 7-Segment-Anzeige bauen.
In C ist das sicherlich nicht der erste Weg. Aber in C würde ich die
Frage auch gar nicht stellen.
Für Assembler ist das die Lösung:
https://en.wikipedia.org/wiki/Double_dabble
Gruß
Jobst
Time to Live schrieb:> Man muß auch mal etwas anderes tun als nur dem Mainstream zu folgen.
Wie wärs damit:
Ein 10-bit ADC mit parallelem Ausgang wird an Adresse A2 - A12 eines
EPROM/Flash Speichers gelegt, in dem die passenden 7-Segment Muster als
4er Gruppen programmiert wurden, und an A0-A1 kommt ein Zähler bis 4
(0b00 - 0b11) der die 4 Digits durchklappert und die Digits
durchschaltet. Der Datenausgang des EPROM geht an die 7 Segmente der
Multiplex Anzeige. Sollte recht einfach aufzubauen sein und braucht auch
keinen Assembler :-P
Der Gehirnschmalz liegt in den EPROM Mustern, das sollte aber zu
automatisieren sein.
Allerdings sind 16kB EPROMs/Flashs nicht üblich, also einen 32k nehmen
und A13 als Umschalter auf einen andere Darstellung nehmen oder
totlegen.
Martin schrieb:> Dies kostet allerdings Rechenleistung und Zeit
Ein AVR läuft ab Werk mit 1MHz, da ist das bissel Rechnen nur wenige
Promille CPU-Last.
Selbst wenn Du alles in float rechnest, wirst Du nicht in der Lage sein,
es so schnell abzulesen.
Du mußt absichtlich noch Delays einfügen, damit es nicht flackert. Ich
nehme dafür 200ms Delay (= 5 Messungen/s).
Matthias S. schrieb:> Allerdings sind 16kB EPROMs/Flashs nicht üblich, also einen 32k nehmen> und A13 als Umschalter auf einen andere Darstellung nehmen oder> totlegen.
Oder statt BCD gleich 7-Segment ausgeben.
Matthias S. schrieb:> Wie wärs damit:> Ein 10-bit ADC mit parallelem Ausgang
....
Ich glaube, Du bist so jung, daß Du das nie machen mußtest.
10 Bit ADC mit Parallelausgang? Ein Ferranti ZN432? Oder womöglich ein
Flash-Wandler mit 10 ns?
Bitte nicht!
Dann schon lieber ein ATtiny25/45/85 mit serieller Datenausgabe auf
Schieberegister.
Matthias S. schrieb:> Habe ich doch geschrieben - ins EPROM/Flash kommen direkt die 7-Segment> Daten, nix BCD oder so.
Genau so.
Ich habe das vor Jahrzehnten schon für mehrere Temperaturanzeigen in
einer Galvanik so gemacht. Die Eproms mit ihren Adressleitungen auf den
AD-Wandler und auf den Datenleitungen direkt 7-Segment Code ausgegeben.
Dann noch ein paar Leitungstreiber dran, weil die Eproms nicht "bullig"
genug waren, die Anzeigen selber anzusteueren. Der Einzige, der rechnen
mußte, war ich (vorher, für die Tabelle in den Eproms)
MfG Paul
Matthias S. schrieb:> Und es ging ja darum, das mit so wenig Rechenleistung wie möglich zu> machen - voila, das ist die Lösung ganz ohne MC.
Du meinst also den ICL7106/07.
> Hier ein paar aktuelle 10-Bit ADC mit Parallelausgang:
'aktuelle' bitte durch 'teure' ersetzen ;-)
m.n. schrieb:> Du meinst also den ICL7106/07.
Nein, den meine ich nicht. Mit der alten Schüssel kannst du mir nicht
mehr kommen. Steinzeit, langsam und keine 4 Stellen. Du denkst eben
einfach zu konventionell und es ging ja um 'Denken ausserhalb der
Kiste'.
Nur, weil du keine Parallelwandler einsetzt, heisst das nicht, das der
Rest der Welt es auch nicht tut.
Matthias S. schrieb:> Und es ging ja darum, das mit so wenig Rechenleistung wie möglich zu> machen
Nö, es ging darum, daß der OP fälschlicher Weise dachte, das kleine
bischen Rechnen würde eine merkbare CPU-Last ausmachen.
Matthias S. schrieb:> Nur, weil du keine Parallelwandler einsetzt, ...
... biete ich zu Versandkosten einige AD574AKN und AD667JN an.
Die sind nach 30 Jahren gut gereift ;-)
Aber das ist auch die uneffizienteste Lösung, printf() ist ein Monster.
Andererseits, wenn du ohnehin auch an anderen Stellen printf gebrauchen
kannst, ist es unterm Strich betrachtet gar nicht mal so verkehrt.
Abgesehen davon: Scheiß auf Effizienz, solange das Programm tut was es
soll und in den Speicher passt. Die gesparte Zeit kann man woanders
gewinnbringender einsetzen.
Ich muss jeden Tag in Java programmieren, mit SOAP und dreifachen
Datenbank-Abstraktionslayern. Da habe ich mir das feilschen um Effizienz
abgewöhnt. Ich müsste meine Werke sonst ununterbrochen beweinen.
Ansonsten: utoa() ist eventuell ein heißer Tip.
Martin (M.) schrieb:> Sind nicht die LEDs deutlich langsamer als die modulo Berechnung?
Eher nicht. Die Schaltzeit der LEDs dürfte im sub-µs-Bereich liegen. ;-)
Wie lange die Modulo Operation dauert, hängt sicher davon ab, welcher µC
dafür eingesetzt wird, insbesondere, ob sie durch die Hardware
unterstütz wird.
Allerdings ist die Modulo-Berechnung u.U. ganz überflüssig, wenn man
statt dessen mit Subtraktionen arbeiten und sich von den Tausendern zu
den Einern durcharbeitet.
Stefan U. schrieb:> Da habe ich mir das feilschen um Effizienz> abgewöhnt. Ich müsste meine Werke sonst ununterbrochen beweinen
Na ja, so ist es die Kundschaft, die jeden Tag erneut die grottenlahmen
Programme beweint.
Kollektive übereinstimmende Menung gerade beim grossen Essenstisch: alle
jammerten daß WebSeiten von Onlinezeitschriften und Smartphone Apps auch
von Google wie z.B. Google Maps inzwischen allesamt eine Zumutung sind
und wegen Langsamkeit weitgehend unbrauchbar.
Leider durfte ich in 30 Jahren nur einmal die gute Qualität abliefern,
die ich mochte.
In der Realität zählt eher: Schnell gemacht und möglichst billig. Je
billiger du eine Software entwickelst, umso mehr wir deine Leistung vom
Geldgeber gewürdigt - sofern sie denn die Anforderungen erfüllt.
Qualität bezahlt kaum jemand. Wenn du mal einen Murks-Code refactorn
willst, musst du es entweder einfach heimlich machen (und einen riesen
Anschiss riskieren) oder den Geldgeber mühsam vom Sinn der Aktion
überzeugen. "Läuft doch schon" ist schwer, mit Argumenten zu überbieten.
Das Problem trifft Handwerker sicher ebenso.
Martin schrieb:> Jetzt habe ich Beispiele gesehen, wo der eingelesene Wert mittelels> Modulodivision in Tausender-, Hunderter-, Zehner- und Einerstellen> zerlegt wird und diese dann auf an Display gesendet werden.
In der vagen Hoffnung, daß nach all den Diskussionen der TO hier
überhaupt noh mitliest, sag ich man folgendes:
Die Rechnerei mit Modulo geht extrem gut bei Architekturen, die über
eine Hardware-Division verfügen. Ist dem nicht so, wie z.B. auf allen
üblichen 8 Bit Architekturen, macht man es eben auf die Weise, die wir
mal alle in der Schule gelernt haben.
Also: so oft 1000 abziehen, bis Zahl eben noch nicht kleiner als 0 ist,
Anzahl merken. Das ist die 1000er Stelle.
Dann dasselbe mit 100, dann mit 10, dann der Rest ist die Einerstelle.
ist doch simpel und kommt mit ein paar einfachen Subtraktionen und
Additionen aus.
Das Umsetzen so einer Stelle in ein Bitmuster für die 7 Segment-Anzeige
macht man mit einer kleinen Tabelle. Wie die aufgebaut ist, hängt von
der Beschaltung ab.
So. und jammert nicht, wenn ihr bei eurem System kein sprintf findet.
Ist in 99.9% aller Fälle überflüssig. Ich halte nichts von Leuten, die
ohne vorgekaute printf Bibliotheken auf dem Schlauche stehen und selber
ne einfache Konvertierung nicht zuwege bringen.
W.S.
Stefan U. schrieb:> "Läuft doch schon" ist schwer, mit Argumenten zu überbieten.>> Das Problem trifft Handwerker sicher ebenso.
Insbesondere Automechaniker, die dürfen die Bremse immer nur an einem
Rad reparieren: "Bremst doch schon!"
W.S. schrieb:> ist doch simpel und kommt mit ein paar einfachen Subtraktionen und> Additionen aus.
Programm siehe hier:
Beitrag "Re: ADC-Wert auf 4 7-Segmentanzeigen darstellen ohne viel Rechenleistung"
Guten Morgen!
eProfi schrieb:> Ich habe vor 8 Jahren so etwas mit Hilfe von Tabellen gemacht:
Wenn man 100 Byte für eine Tabelle mit den Werten 0x00 ... 0x99 übrig
hat, reicht bei vierstelligen Zahlen eine einzige Division: Zahl/100.
Das Ergebnis (Quotient) liefert den Index auf die Tabelle für die beiden
oberen Stellen. Für die beiden unteren Stellen nimmt man den Rest als
Index.
Beispiel für 0x2694 und 0x64 (100) als Divisor:
0x2694/0x64 ergibt 0x62 mit Rest 0x4c.
0x62 als Index liefert aus der Tabelle '98' und 0x4c den Wert '76'.
Als BCD-Ergebnis erhält man: '9876'.
Bei µCs, die 32/16 Bit per Hardware rechnen können, kann man gleiches
Verfahren für achtstellige Ergebnisse verwenden, indem zunächst
Zahl/10000 ausgeführt wird. Quotient und Rest werden dann nochmals wie
gezeigt /100 geteilt. Effizient programmiert man soetwas am besten in
Assembler ;-)
In this case I have replaced three mods with three subtractions and three
2
multiplications. Thus although I have replaced a single operator (%) with
3
two operations (- *) I still expect an increase in speed because the
4
modulus operator is actually three operators in one (- * /). Thus
5
effectively I have eliminated three divisions and so I expected a
6
significant improvement in speed.
Modulo liefert den Rest, der bei einer Division ohnehin anfällt. Der
Aufwand für Division (/) und Modulo (%) ist also normalerweise gleich.
Was er gemacht hat, ist den Aufwand für 3 Divisionen durch 3
Multiplikationen und 3 Subtraktionen zu ersetzen.
1
The results however were a little surprising:
Kein Wunder, wenn man von falschen Voraussetzungen ausgeht.
Stefan U. schrieb:> Das Problem trifft Handwerker sicher ebenso.
Natürlich, was denn sonst. Programieren ist doch letztlich genauso ein
Handwerk wie jedes andere auch, nur dass die Werkstücke keine Entitäten
der pysischen Welt sind.
Aber ansonsten ist alles ziemlich gleich: grober Pfusch fällt sofort
auf, gemäßigter Pfusch etwas später. Und das optimale Ergebnis ist nur
mit überproportional hohem Aufwand zu erzielen. Und Erfahrung ist durch
nichts zu ersetzen.