Hallo ihr lieben Leut ! Weis jemand, wie man eine int Variable auf schnellste Weise in zB 5 BCD Zahlen umwandelt (in C, für zB. eine Ausgabe in ein LCD-Display) ? Schöne Grüße Josef
puhh in c keine ahnung, aber in asm kann ichs dir sagen =) du nimst die zahl, subtrahierst so lannge 10, bis diese kleiner 10 ist also 34 -10 - 10 - 10 = 4 beim teilen hast du 3 schleifendurchgaennge byte1=3 und den rest byte0=4 ich meine in C gibts da aber eine funktion fuer um sowas zu machen Gruss Jens
Hi am schnellsten? Look-Up-Tabelle. Dann sind aber ~ 300k Speicher weg :-) Ansonsten halt immer wieder durch 10 Teilen und den Rest der Divisionen speichern. Matthias
Ich glaube mit dem % (Modulus) Operator. 34 % 10 = 4; 4 ist der ganzzahlige Rest der Division. 34 / 10 = 3; So müßte es gehen. Michael
Hier mal für 2 Stellen, hab ich irgendwo mal im Internet gefunden #define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf)) #define TOBCD(x) (((x) / 10 * 16) + ((x) % 10))
http://www.mikrocontroller.net/forum/read-4-46127.html#new Noch schneller wäre gewesen, selber in der Codesammlung nachzusehen. Peter
Hi ich persönlcih finde /** Zahl dezimal ausgeben d: auszugebende Zahl n: Anzahl der Stellen (von hinten) v: 0=ohne Vornullenunterdrückung */ void rs232_printd(unsigned long d, unsigned char n, unsigned char v) { unsigned char s[10]; unsigned char m; //Erstmal Array mit 10 Stellen erzeugen for(m=0;m<10;m++) { s[m]=(d%10); d/=10; } m=0; do{ n--; if((!v)||(m)||(s[n])) { rs232_putc(s[n]+0x30); m=1; } }while(n); } schöner. Compiliert (mit dem AVRGCC) sogar kleiner als Peters Variante und unterstützt auch long-Zahlen. Allerdings keine vorzeichenbehafteten Zahlen und ist ähnlich unleserlich :-) Der Compiler (bzw. der Entwickler der Bibliothek) weiß oftmals besser wie man dividiert als wenn man das selber implementiert. Matthias
Hi, ich hattes es mal so gemacht, war für einen PCF8563 bestimmt: unsigned char bcd_to_dec(unsigned char value) { unsigned char dummy = 0; unsigned char dummy1 = 0; unsigned char dummy2 = 0; unsigned char output = 0; dummy = value & 0x0F; dummy1 = value & 0x70; dummy2 = dummy1 >> 4; output = (dummy2 * 10) + dummy; return (output); } unsigned char dec_to_bcd (unsigned char value) { unsigned char dummy = 0; unsigned char dummy1 = 0; unsigned char dummy2 = 0; unsigned char save_val = value; unsigned char output = 0; dummy = save_val %= 10; dummy1 = value / 10; dummy2 = dummy1 << 4; output = dummy | dummy2; return (output); }
Vielen Dank für die Antworten. Ich habe es so gemacht, aber das ist mit 0,25 ms zu lang. //*******************int Zahl in BCD umrechnen und in Ram Array schr. void Convert_Int_Bcd ( unsigned int Zahl) { unsigned int Help; Help = Zahl /10000 Seg_Dat[4] = Convert_Seg (Help) ; Zahl = Zahl - (Help * 10000); Help = Zahl / 1000; Seg_Dat[3] = Convert_Seg (Help) ; Zahl = Zahl - (Help * 1000); Help = Zahl / 100; Seg_Dat[2] = Convert_Seg (Help); Zahl = Zahl - (Help * 100); Help = Zahl / 10; Seg_Dat[1] = Convert_Seg (Help); Zahl = Zahl - (Help * 10); Help = Zahl; Seg_Dat[0] = Convert_Seg (Help); } Schöne Grüße Josef
An Michael: Eine schöne Lösung - aber Modulo ist leider noch langsamer.Habe es probiert: Counter = 1234; Temp = Counter % 10; Counter /= 10; Temp = Counter % 10; Counter /= 10; Temp = Counter % 10; Counter /= 10; Temp = Counter % 10; Funktioniert gut - aber eben langsamer (0,24 ms)als mein umständlich aussehender Code (0,19ms). Vordefinierte Funktion gibt es für diese Funktion nicht (Codevision). Dafür Unmengen an string Operationen. Die Einzige - die ich fand war bcd_t_bin und bin_t_bcd. Die gehen aber nur bis 99. Schöne Grüße Josef
Je nachdem, in welchem Bereich die Zahlen meistens liegen, lohnt sich von den ersten "Zahl = " noch ein "if (Help)". Dann ist Convert_Seg () hoffentlich ein Makro, sonst kannst Du alles andere Gefeile vergessen! (-: Dann weiß ich nicht wie intelligent der Compiler ist, aber auf einem 8-Bitter würde ich Help als unsigned char definieren (Vorsicht dann bei "Help * 100") Falls Du Platz hast und jede µs zählt, ist der LookUp-Table ürigens gar nicht so abwegig, aber Byte-weise: unsigned char High =(unsigned char) (Zahl >> 8); unsigned char Low = (unsigned char) Zahl; // LookUpHighx[] enthält ASCII-Ziffern // LookUpLowx[] enthält Bytes von 0-9 Ziffer[0] = LookUpHigh0[High] + LookUpLow0[Low]; Ziffer[1] = LookUpHigh1[High] + LookUpLow1[Low]; Ziffer[2] = LookUpHigh2[High] + LookUpLow2[Low]; Ziffer[3] = LookUpHigh3[High]; Ziffer[4] = LookUpHigh4[High]; if (Ziffer[0] > '9') { Ziffer[0] -= 10; Ziffer[1]++; } if (Ziffer[1] > '9') { Ziffer[1] -= 10; Ziffer[2]++; } if (Ziffer[2] > '9') { Ziffer[2] -= 10; Ziffer[3]++; if (Ziffer[3] > '9') { Ziffer[3] -= 10; Ziffer[4]++; } } Das Feld ist dann (5 + 3) * 256 Bytes = 2 kB und die Routine wohl auf den meisten kleinen µC deutlich schneller.
Hier allerdings in ASM convert: ldi temp3, 0x00; 1te Stelle (1X) convert_11: cpi temp1, 0x0a brsh convert_1 mov temp2, temp1; 2te Stelle (X) ret convert_1: ldi temp, 0x0a sub temp1, temp inc temp3 jmp convert_11 Ohne Funktionen, die teilen muessen etc.. sollte sehr schnell gehen und laesst sich erweitern
Hi wozu braucht man denn BIN->BCD? Wohl nur für irgendeine Displayausgabe die der Mensch lesen soll. Intern macht diese Art der Zahlendarstellung eigentlich keinen Sinn. Und wie oft kann ein Mensch eine neu Zahl aufnehmen? Alle 0,1s? Wenn die ganze Routine dann 1ms zur Konvertierung braucht sind das 1% der CPU-Zeit. Matthias
"Vielen Dank für die Antworten. Ich habe es so gemacht, aber das ist mit 0,25 ms zu lang." Und warum probierst Du dann nicht die Subtraktionsmethode, die ich Dir angegeben habe ??? Peter
Lieber Mathias ! Mann brauch so eine schnelle Umrechnung, wenn man ZB. ein Siebensegmentdisplay mit 6 Stellen ansteuern will, dazu einen Drehgeber auswerten muß und noch viele andere Sachen dazu. Ich bekomme pro sec. 3000 Impulse auf 4 Kanälen die 90 Grad phasenverschoben sind. Jetzt gilt es diese Impulse zu zählen, zu dividieren (Fließkomma) und dann anzuzeigen. Parametereingaben ,Datensicherung bei Stromausfall, PWM Ausgänge, Helligkeitsregelung, 4 Leds und noch 4 Taster sind auszuwerten. Vernetzung der Zähler obligat. Der ATMega läuft mit Volldampf, tut es gerade noch. LG Josef
Ich habe es jetzt so probiert: #include <stdio.h> char buffer[32]; sprintf(buffer,"%ld",long_var); lcd_puts(buffer); Josef
@Josef: denk nochmal über Matthias seine Antwort nach - er hat schon Recht. Wenn Du die BCD-Zahl wirklich nur für die Anzeige brauchst, dann musst Du sie nicht so oft rechnen, dass diese Rechnung die cpu nennenswert belastet. Du nanntest 0,25ms pro rechnung - das mal 10-20 mal Refresh pro Sekunde ergibt nicht mehr als 0,5% Deiner cpu-Leistung. Du musst Dich nur von der Vorstellung lösen, jeden neuen Wert subito aufs Display zu bringen. So schnell sind unsere Augen nicht ... Stefan
Hi @Josef selbst 10 mal pro Sekunden sind zu schnell. Stell mal eine Zahl dar deren hintere Stelle sich mit 10Hz ändert. Das kannst du kaum noch erfassen. Ich habe schon so einige Steuerungen gebaut. Dort habe ich anfangs auch mit Refreshraten für 7-Seg. Anzeigen von 0,1s gearbeitet. Für jemanden der das ablesen soll oder die ganze Zeit im Augenwinkel hat (weil in KfZ eingebaut) ist das extrem nervig. Mitlerweile haben sich Refreshraten von 0,5s - 1s als brauchbar herausgestellt. Für eine schnelle Tendenzanzeig sind (LED) Balckenanzeigen geeigneter. Wenn man natürlich die Umwandlung so auslegt das sie alle anderen Prozesse blockiert muß man optimieren. Aber dann sollte man sich erstmal Gedanken über sein Konzept machen. Und wenn ich lese das du Fließkomma auf einem ATMega machst wundert es mich überhaupt nicht mehr das du Geschwindigkeitsprobleme bekommst. Es gibt nur sehr wenige Anwendungen die Flieskomma auf Maschinen die das nicht Hardwaremäßig können rechtfertigen. Matthias
Da hat Matthias vollkommen recht. Schau einfach mal auf Dein Multimeter, das macht etwa 2 ... 5 Meßwerte je Sekunde. Aber nicht, weil es nicht schneller geht, sondern weil es ergonomisch ist. Und 5 mal je Sekunde ein bischen Floating Point und Dezimalanzeige lastet einen ATMega noch lange nicht aus. Da ist also etwas grundlegend falsch in Deinem Programmablaufplan. Peter
Wäre froh, wenn jemand eine bessere Idee hätte ! Aufgabenstellung zur Software: Von einem Drehgeber kommen 2 (4) phasenverschobene Impulse. Zählung dieser Impulse und Darstellung im 5-stelligen 7SEG. Die Anzeige soll die zurückgelegete Wegstrecke permanent anzeigen. Also: wenn der Schlitten fährt muß auch die Anzeige permanent mitlaufen, damit der Anwender weiß, wo der Schlitten ist. Max 3000 Impulse /sec. Drehrichtungserkennung über die phasenverschobenen Drehgebersignale (Softentprellung), bei Stromausfall Speicherung der akuellen Position. Die gezählten Impulse müßen über einen einstellbaren Parameter (zB 22.8) geteilt werden, bis sie angezeigt werden dürfen. Dimmung der Anzeige und Vernetzung. Kosten pro Einheit ab 1000 Stück: 15 !. Eigentlich alles gelöst, bis auf unser diskutiertes Problem. Bevor jemand gute Tips gibt, bitte genau überlegen ! SG Josef
Man kann die Impulse im Interrupt zählen, aber sollte gerade ein Timer Interrupt ablaufen (Segmentrefresh)hast du Impulsverluste (AVRs haben keine Interruptpriorität, was sie eigentlich unbrauchbar macht - 8051er haben das schon seit der 1.Stunde (ca. 22 Jahre)) und die Anzeige zeigt falsche Werte an. Nur so zur Anregung ;-) SG Josef
Wobei ihr Recht habt mit der Refreshrate......aber wann soll ich umrechnen - und was passiert derweil...hm ? Josef
Hi Mit welcher Frequenz läuft der AVR? Wenns nicht grade 1MHz ist kannst du bei 3kHz doch per Timer-ISR (~30kHz bei C, mehr bei ASM) problemlos überabtasten und die Erfassung per Software machen. Solcherlei Drehgeber prellen eigentlich kaum und wenn doch so schnell das man das per RC wegbügeln kann. Displayrefresh und anderes machst du dann bequem im Mainloop evtl. synchronisiert über ein Flag aus der Abtast-ISR. Division durch 22,8 kann man einfach, je nach erforderlicher Genauigkeit, mit Fixkomma erschlagen. z.B. Multiplikation mit 2874 und anschließender "Divison" durch 256. Bleibt ein x.8 Fixkommaformat. Integer-Multiplikation geht dank Hardware sehr schnell und Division durch 2^n ist trivial. Sollte mit einem AVR bei 8MHz keine große Sache sein. Matthias
Danke Matthias. Der AVR läuft mit 8 Mhz. Habe vergessen zu erwähnen, dass das Ding mit Batteriebetrieb läuft und min. 6 Monate halten muß. Schalte die Taktung im Betrieb herunter (je nach Bedarf). SG Josef
Du glaubst doch nicht im Ernst, daß ein Mensch 3000 Zahlen/s ablesen kann !!! Also einfach die Impulse im Interrupt zählen und dann bequem im Hauptprogramm ausgeben. Der besseren Ablesbarkeit halber würde ich aber noch mindestens 100ms warten, damit nicht schneller als 10 Werte/s ausgegeben wird, sonst siehst Du beim untersten Digit nur noch flimmernde Achten. Peter
Peter - du hast recht. Das Problem ist, das während der Umrechnung und des Segmentrefreshes Impulse verloren gehen. Deswegen wäre ich mit der Umrechnung nach Abschluß eines Impulses schon gerne fertig um den nächsten Impuls zu pollen und die Drehrichtung zu erkennen. Josef
@Josef, ja, Du must natürlich im Interrupt zählen. In der Codesammlung ist ein Beispiel von mir. Für max 3000 Pulse dürfte ein Timerinterrupt von 10Khz dicke ausreichen (800 Zyklen). Da hat also der AVR noch reichlich Zeit für anderes. Peter
hab gerade noch mal in alten Sachen gewühlt, im schlimmsten Fall (59999) kann man mit 300 Takten (bei 8MHz=38µs) auskommen. Was natürlich nichts dran ändert, dass man nicht jeden Wert anzeigen muss oder sollte.
Hi @Peter Für die Drehgeber reichen die 10kHz Abtastrate nicht. Da die zwei Signale 90° phasenverschoben sind muß man mindestens 4-fach überabtasten um keinen Flankenwechsel zu verlieren. Also mindestens mit 12kHz. Ich persönlich gehe dann lieber auf Nummer sicher und drehe die Abtastfrequenz so hoch wie möglich. Bei einem 4MHz AVR hab ich hier zwei Statemachines zur Drehgeber-Auswertung und zwei Zeitgeber im 20kHz Timer-INT. Im Timer-INT wird mit int-Zahlen gerechnet (alles in C). Braucht, ohne das ich besonders auf Geschwindigkeit optimiert habe, etwa 50% CPU-Zeit. Da bleibt für den Rest noch genug übrig und die Drehgeber werden sicher ausgewertet. Deshalb auch mein Vorschlag mit den 30kHz. Sollte bei einem 8MHz AVR kein Thema sein. Matthias
Warum so etwas im Timerinterrupt? Ext. Int für eines der beiden Signale, ein ordinärer Portpin für das um 90° Phasenversetzte. Im Interrupt den Portpin abfragen und schon hast du die Richtung. Oder übersehe ich was wichtiges? Michael
Externe Interrupts sind störanfälliger, obwohl ich meine eigene Auswertung auch mit zwei externen Interrupts realisiert habe. Die Sache mit dem Timer habe ich noch nicht ganz durchblickt, und ich will das selbst lösen und nicht einfach abkupfern. Thorsten
Hi beim externen INT bekommst du Probleme wenn der Drehgeber auf einer Position steht und er etwas "zappelt" (z.B. immer hin und her durch Viberationen) Dann macht dir dein externer INT den Controller dicht weil er schneller aufgerufen wird als du ihn abarbeiten kannst. Der Timer-INT wirkt da sozusagen als Tiefpass -> Entprellung Matthias
[Josef:] > Das Problem ist, das während der Umrechnung > und des Segmentrefreshes Impulse verloren gehen. Nicht so einfallslos, bitte! Du mußt die Umrechnung nicht am Stück machen. Wenn Du die Umrechnung im main-loop machen willst, dann etwa so: bei Zyklus 1 wird der Counter-Wert gelatcht, bei 2 rechnest Du die erste Stelle, bei 3 die zweite usw., nach der letzten kannst Du mit dem Segmentrefresh anfangen, dann kannst Du x Zyklen auch gar nichts tun, damit die Anzeige ablesbar bleibt. Auf die Art kannst Du praktisch jedes Codebeispiel umbauen.
Neues Problem : Mein Kunde benützt Billigst-Drehgeber mit Hall-Sensoren. Die Dinger schicken Störungen daß einem fad wird (auch bei Stillstand) Ex.Int scheidet nun aus. CPU läuft Amok. Nun kommen RC Glieder ins Spiel. Dimensionieren der RC Glieder sehr kritisch. @Philipp : guter Ansatz -danke´! Josef
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.