Ich habe mir ein Programm zusammengestellt, das mir den Wert eines
Potentiometers mittels ADC ausliest und auf einem 16x2 LCD wiedergibt.
Für die Anzeige habe ich die Fleury-Lib genommen.
Die Schaltung besteht aus einem ATmega8-16PU der mit 4MHz internen Takt,
getaktet wird.
1
#include<stdlib.h>
2
#include<avr/io.h>
3
#include<stdint.h>
4
#include"lcd.h"
5
6
volatileuint8_tADCvalue;// Global variable, set to volatile if used with ISR
7
8
intADCsingleREAD(uint8_tadctouse)
9
{
10
intADCval;
11
12
ADMUX=adctouse;// use #1 ADC
13
ADMUX|=(1<<REFS0);// use AVcc as the reference
14
ADMUX&=~(1<<ADLAR);// clear for 10 bit resolution
15
16
ADCSRA|=(1<<ADPS2)|(1<<ADPS1);// 64 prescale for 4Mhz
17
ADCSRA|=(1<<ADEN);// Enable the ADC
18
ADCSRA|=(1<<ADSC);// Start the ADC conversion
19
20
while(ADCSRA&(1<<ADSC));// Thanks T, this line waits for the ADC to finish
21
22
ADCval=ADCL;
23
ADCval=(ADCH<<8)+ADCval;// ADCH is read so ADC can be updated again
24
25
returnADCval;
26
}
27
28
intmain(void)
29
{
30
charbuffer[7];
31
intADCvalue;
32
33
for(;;)
34
{
35
ADCvalue=ADCsingleREAD(1)/12.9;
36
37
/* initialize display, cursor off */
38
lcd_init(LCD_DISP_ON);
39
40
// clear display and home cursor
41
lcd_clrscr();
42
43
// convert interger into string
44
itoa(ADCvalue,buffer,10);
45
46
// put converted string to display
47
lcd_puts(buffer);
48
}
49
}
Vom Prinzip her läuft das ganze soweit. Das einzige was mir auffällt
ist, das wenn man schräg auf das LCD sieht, man erkennen kann wie die
Anzeige durchläuft, sprich aktualisiert wird.
Liegt das am LCD selbst oder an dem Programm?
gleich mit rausschmeissen.
Positionier den Cursor, und gib immer an dieselbe Stelle aus. Aber pro
Sekunde 300 mal das LCD löschen und wieder was hinmalen, flackert wie
Sau. Das heisst, sofern man überhaupt was sieht.
Das ist ne Idee und läuft flüssiger. Dann muß ich aber trotzdem die
restlichen Stellen wenn die Zahl beim runterdrehen wieder einstellig
wird wieder mittels abfrage löschen.
Lokus Pokus schrieb:> Das ist ne Idee und läuft flüssiger. Dann muß ich aber trotzdem die> restlichen Stellen wenn die Zahl beim runterdrehen wieder einstellig> wird wieder mittels abfrage löschen.
Kannst du.
Du kannst aber auch in der Ausgabe etwas intelligenter vorgehen. Auf
jeden Fall ist das alles wesentlich besser als das LCD zu löschen. LCD
löschen dauert im Vergleich relativ lange. D.h in deinem Beispiel ist
das LCD über die Zeit gemittelt zu mehr als vielleicht 60% im Zustand
'leer'. Logisch, dass du dann die Dinge nicht vernünftig siehst.
Da dein Programm in keinster Weise zeitkritisch ist und du
wahrscheinlich auch genug Flash hast, so dass du dir ein sprintf leisten
kannst:
vergleichen mit dem Rest deiner Schleife braucht. Eventuell hast du da
zwischen löschen und neu beschreiben des LCD zu viel Zeit. Soll heißen:
1
// clear display and home cursor
2
lcd_clrscr();
3
4
// convert interger into string
5
itoa(ADCvalue, buffer, 10);
6
7
// put converted string to display
8
lcd_puts(buffer);
->
1
// convert interger into string
2
itoa(ADCvalue, buffer, 10);
3
4
// clear display and home cursor
5
lcd_clrscr();
6
7
8
// put converted string to display
9
lcd_puts(buffer);
Ansonsten musst du das Display auch nicht öfter refreshen, als das
menschliche Auge wahrnehmen kann. Also eventuell eine Verzögerung in die
Schleife bzw einen Timer, der bspw alle 20 ms refresht (50 Hz).
Perfekt, das funktioniert sogar mit führenden Nullen:
1
sprintf(buffer,"%02d",ADCvalue);
sprintf sorgt hier zwar häufig für Unmut und bläht das Programm gleich
um das 3-fache auf, aber momentan muß ich mich noch nicht mit
Platzproblemen herumschlagen.
danke!
@ M. S. (elpaco)
>Ansonsten musst du das Display auch nicht öfter refreshen, als das>menschliche Auge wahrnehmen kann.
Jain.
> Also eventuell eine Verzögerung in die>Schleife bzw einen Timer, der bspw alle 20 ms refresht (50 Hz).
Viel zu schnell! 2-5 Hz reichen locker! Die flimmerfreie Anzeige macht
das LCD selber!
@ Lokus Pokus (derschatten)
>Perfekt, das funktioniert sogar mit führenden Nullen:>sprintf(buffer, "%02d", ADCvalue);
Das geht auch mit führenden Leerzeichen
sprintf(buffer, "% 2d", ADCvalue);
Ich hab mein Programm nun etwas erweitert, dabei werden 2 ADC
ausgewertet.
Die Funktion ist wie folgt:
Über ADC1 wird mittels Poti ein Wert eingestellt.
Auf ADC2 hängt ein Lichtsensor der mir die aktuelle Helligkeit ausgibt.
Wird der Potiwert überschritten leuchtet eine rote LED. Bei Mittelwert
die Gelbe, etc...
Jetzt belegt das doch recht kleine Programm schon die hälfte des
AVR-Flash. Was ja doch recht fett ist.
Liegt das nun alleine an dem sprintf?
Was noch dazu kommt ist, das ich den LCD Kontrast fast bis zum Anschlag
aufdrehen muß damit die Anzeige gut zu lesen ist. Was zu Beginn, wo ich
nur mal die LCD-Lib ausgetestet habe um festzustellen ob das LCD
überhaupt etwas anzeigt, nicht der Fall war. Da war bereits unter der
Hälfte des 5k Kontrastpotis die Anzeige gut lesbar.
@ Lokus Pokus (derschatten)
>Jetzt belegt das doch recht kleine Programm schon die hälfte des>AVR-Flash. Was ja doch recht fett ist.>Liegt das nun alleine an dem sprintf?
Nein, auch an dem Rechnen mit Fließkommazahlen. Für solche einfachen
Rechnungen ist Festkommaarithmetik deutlich sparsamer und
ausreichend.
>Was noch dazu kommt ist, das ich den LCD Kontrast fast bis zum Anschlag>aufdrehen muß damit die Anzeige gut zu lesen ist. Was zu Beginn, wo ich>nur mal die LCD-Lib ausgetestet habe um festzustellen ob das LCD>überhaupt etwas anzeigt, nicht der Fall war. Da war bereits unter der>Hälfte des 5k Kontrastpotis die Anzeige gut lesbar.
Schaltplan? Bild vom Aufbau?
Mach mal ne Bremse in deine Hauptschleife, deine CPU gibt mit wer weiß
wieviel kHz immer neue Daten auf dein LCD. Das könnte Kontrastprobleme
erklären. Einfach am Ende des Durchlaufs _delay_ms(100);
Ach ja, dein Auslesefunktion für den ADC ist auch Quark. Man muss den
ADC nur EINMAL zum Programmstart initialisieren, danach muss man nur
noch den Kanal einstellen und die Wandlung starten. Siehe AVR Tutorial,
dort ist das richtig drin!
der buffer ist auf jeden Fall zu kurz wer 3 Zeichen braucht muß 4
reservieren, das 4te Zeichen buffer[3] ist die String Ende 0
lcd_puts("Schwellwert \n");
sollte
lcd_puts_P("Schwellwert \n"); aus dem flash werden, spart RAM
Falk Brunner schrieb:> Man muss den> ADC nur EINMAL zum Programmstart initialisieren,
es kann aber von Vorteil sein den ab und an auch abzuschalten
auf jeden Fall nach dem Einschalten ein dummyread zu machen
mehrere Messungen können das auch noch genauer darstellen
1
#ifndef READS
2
#define READS 5
3
#endif
4
charanalog_str[5];
5
chardigit_str[5];
6
7
uint16_tadc;
8
uint8_tacnt;
9
10
.....
11
adc=0;
12
acnt=(1<<READS);
13
while(acnt>0)
14
{adc+=analogRead(A8);
15
acnt--;
16
}
17
adc>>=READS;
18
19
itoa((uint16_t)(adc*2520L/1023L),analog_str,10);
ich rechne mit Ganzzahlen, geht schneller, spart Platz, hier ist die
AREF 2520mV
mit READS 5 werden 2^5 = 32 Werte aufaddiert und gemittelt durch
Rechtsshift statt Division
es gehen alle Potenzen die aufaddiert in uint16_t 64K passen und da der
höchste ADC Wert nur 1023 sein kann würde auch noch READS 6
funktionieren.
Nur die Ausgabe der Werte ist noch nicht so wie ich es mir gerne
vorstelle. Bei Maximalanschlag soll der Wert 100 erreicht werden.
In dem Fall ist der Höchstwert 2517. Und beim runterdrehen erscheint bei
Anschlag immer ein anderer "Zufallswert".
Diese Berechnung von ADCintegerREAD(1)*2520L/1023L ist mir daher auch
nicht so ganz klar.
Lokus Pokus schrieb:> Diese Berechnung von ADCintegerREAD(1)*2520L/1023L ist mir daher auch> nicht so ganz klar.
ich hatte die inerten 2,56V Ref vom mega2560 gewählt imd das waren
gemessene 2,52V also 2520L mV
ADMUX |= (1 << REFS0); // use AVcc as the reference
du nutzt ja diese, wieviel das in Wirklichkeit ist musst du am AREF Pin
messen und kannst das ja einsetzen
ich tippe mal 5V oder 3,3V ? aber nachmessen und den richtigen Wert
einsetzen wäre nicht doof !
dein Buffer ist immer noch zu kurz 1023 wäre das maximale Ergebnis also
muss der Stringbuffer 4 Stellen + ENDE 0 aufnehmen
char buffer1[5];
char buffer2[5];
Lokus Pokus schrieb:> Bei Maximalanschlag soll der Wert 100 erreicht werden.
aha du willst % nicht Volt
ADCintegerREAD(1)*100L/1023L
wobei das aber so nicht richtig ist
der ADC misst ja eine Spannung und die ist 100% wenn der ADC Wert 1023 *
AREF /1023 ist
und das wäre dann die ADC Spannung, wenn diese dann 100% zeigen soll
teilen wir durch die AREF und multiplizieren mit 100
So ganz passt es noch nicht.
Ich habe das ganze einfach mal durch 10.2 dividiert.
1
ADCvalue1=ADCintegerREAD(1)/10.2L;
2
3
itoa((uint16_t)(ADCvalue1),buffer1,10);
dadurch erchalte ich bei maximalanschlag den Wert 100. Also so, wie ich
es vorhin in meinem Programm berechnet hatte.
Nun hätte ich gerne wieder meine führenden Nullen.
Und was mir noch aufgefallen ist, wenn ich den Regler schnell auf den
Anschlag zurückstelle, bekomme ich anstelle von 0 immer irgendeinen
Zufallswert angezeigt.
Lokus Pokus schrieb:> So ganz passt es noch nicht.> Ich habe das ganze einfach mal durch 10.2 dividiert.> ADCvalue1 = ADCintegerREAD(1)/10.2L;
ich dachte wir wollten weg von der Fliesskommabrechnung aus Platz und
Geschwindigkeitsgründen ?
was soll dann dein . in der Zahl ?
kann es sein das du 1234L noch nicht verstanden hast ?
1234 ist eine Integerzahl und das L castet es zur Berechnung in ein long
um es am Ende der Berechnung wenn es passt mit einem cast wieder in die
richtige Größe bringt.
> itoa((uint16_t)(ADCvalue1), buffer1, 10);> dadurch erchalte ich bei maximalanschlag den Wert 100. Also so, wie ich> es vorhin in meinem Programm berechnet hatte.
und es ist trotzdem falsch, es bezieht sich auf die VCC als AREF
ADMUX |= (1 << REFS0); // use AVcc as the reference
und die nimmt an das es 5V oder 3,3V sind......
wenn dem nicht stimmt misst du eh falsch !
klar kann man den ADC mit irgendwas multiplizieren was 100 ergibt, aber
ist das sinnvoll ?
Ich könnte genausogut annehmen mein Kontostand ist 1 Million und mir
deswegen sofort einen Ferrari bestellen, klingt doof ist auch so.
> Nun hätte ich gerne wieder meine führenden Nullen.
wer hindert dich einen String mit 8 Stellen zu reservieren,
char hilfsstring[9]={0}; // 8 Stellen mit NULL ENDE
den mit 4 Nullen zu füllen
strcpy(hilfsstring, "0000");
und den gewandelten ADC (0-1023) als String strcat() anzuhängen.
strcat(hilfsstring, itoa((uint16_t)(ADCvalue1), buffer1, 10));
Danach werden nur die letzten 4 Stellen kopiert
war der ADC 0 so hat der String "0000" + "0" ist er "00000"
die letzten 4 brauche ich ja also kopiere ich die über den Umweg
Stringpointer
char *str_ptr;
str_ptr = hilfsstring; // oder &hilfsstring[0] Adresse vom Nullten
Element
ich will alles ab Stelle2 die 4 Nullen
also:
str_ptr += strlen(buffer1);
mit -------str_ptr + Länge // 1
und nun kopiere ich mit strcpy(buffer1, str_ptr); die letzten 4 Zeichen
die letzen 4 Stellen mit führenden Nullen in buffer1
und nun für 100
war der ADC 100 so hat der hilfsstring "0000" + "100" ist er "0000100"
die letzten 4 brauche ich ja also kopiere ich die über den Umweg
Stringpointer
char *str_ptr;
str_ptr = hilfsstring; // oder &hilfsstring[0] Adresse vom Nullten
Element
ich will alles ab Stelle4 die 4 Ziffern
also:
str_ptr += strlen(buffer1);
mit -------str_ptr + Länge // 3
und nun kopiere ich mit strcpy(buffer1, str_ptr); die letzten 4 Zeichen
die letzen 4 Stellen mit führenden Nullen in buffer1
gibt "0100" mit führender 0
> Und was mir noch aufgefallen ist, wenn ich den Regler schnell auf den> Anschlag zurückstelle, bekomme ich anstelle von 0 immer irgendeinen> Zufallswert angezeigt.
ja bei READS 6 -> 2^6 = 64 ADC Wandlungen kann das schon langsamer sein
als du drehst ! kommt halt auf dein Wandeltempo an das muss natürlich zu
deinem Messwunsch passen !
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); // 64 prescale for 4Mhz
ist ja auch lahm, wolltest du das ?
Ja, AVcc und VCC sind 5V bei meiner Schaltung.
"64 prescale for 4Mhz", reicht eigentlich für meine Zwecke.
Trotzdem muß das Beispiel von dir nun anders drauf reagieren.
Den mit der sprintf-Auswertung ging alles wunderbar exakt. Keine
Verzögerung etc.
Inzwischen funktioniert nun nicht mal mehr der beeper, der parallel zur
Roten LED auch was von sich geben soll.
Lokus Pokus schrieb:> "64 prescale for 4Mhz", reicht eigentlich für meine Zwecke.
ja OK aber nicht mit der Mittelwertbildung aus 64 Messwerte !
Ich schlage vor du überlegst dir was du eher brauchst und wie du es
erreichen willst, entweder wenig langsame Messungen bis runter auf eine
mit entsprechende Fehler oder viele schnelle gemittelt mit entsprechend
andere Fehler.
Ich komme bei 2^5 -> 32 Messungen gemittelt, nachweislich auf fast +-1
Promille am Skalenendwert (mit 6,5 Stellen DVM Fluke, Agilent
kontrolliert, Teiler abgeglichen auf besser 0,1 % und mit
Gradengleichung bei linearen Funktionen -> y = m * x + b wobei x mein
Digiwert ist, b der Offsetanteil und y die ermittelte Spannung)
Die Genauigkeit der Messwerte ist mir eigentlich egal.
Wichtiger ist das hier keine sichtbare Verzögerung stattfindet.
Warum gibt's denn mit "sprintf" keine Verzögerung?
Lokus Pokus schrieb:> Die Genauigkeit der Messwerte ist mir eigentlich egal.> Wichtiger ist das hier keine sichtbare Verzögerung stattfindet.
Was du unter sichtbar verstehst und was ein µC unter sichtbar versteht,
das sind 2 komplett verschiedene Welten.
Aus Sicht eines µC ist alles was du (oder irgendein anderen Mensch)
macht, extremste Super-Super-Zeitlupe.
In der Zeit, in der du einen Wimpernschlag machst, langweilt sich ein µC
zu Tode, löst 5 Kreuzworträtsel, berechnet nur so zum Spass die Lösung
von 20 quadratischen Gleichungen und macht ein Nickerchen.
Dir scheint nicht klar zu sein, dass ein zb. mit 16Mhz getakteter AVR in
1 Sekunde rund 14 Millionen Befehle (aus einem üblichen Befehlsmix)
abarbeitet. 14 Million!
Lokus Pokus schrieb:> Warum gibt's denn mit "sprintf" keine Verzögerung?
Es gibt weder mit sprintf noch mit itoa eine für einen Menschen
wahrnehmbare Verzögerung.
Wenn du also die Möglichkeiten, die dir sprintf bietet, sinnvoll
einsetzen kannst UND du die Resourcen dazu hast, dann spricht nichts
dagegen. Gegenüber einer itoa Lösung ist sprintf als eierlegende
Wollmilchsau natürlich größer und auch langsamer. Das ist aber nicht die
Frage, die dir stellen musst. Die richtige Frage lautet: kann ich mir
das leisten? Brauche ich den Flash-Speicherplatz? Brauche ich die
Laufzeit?
Ob du das Flash deines µC zu 87% ausfällst oder ob du es nur zu 32%
ausfüllst, interessiert in Wirklichkeit keinen. Wichtig ist, das dein
Programm rein passt. Wenn das Flash voll wird kann man immer noch
schauen, wo man was einsparen kann. Denn für nicht verbrauchten Speicher
kriegst du von Atmel kein Geld zurück. Wohl aber kostet dich (als
Industrieprogrammierer) jede Minute Entwicklungszeit Geld.
Einen µC kannst du nicht mit menschlicher Reaktionszeit in Bedrängnis
bringen. Bedrängnis kommt bei einem AVR auf, wenn Zeiten im µs Bereich
gefragt sind. Aber alles was über Millisekunden liegt, löst bei einem
AVR (und den typischen, mathematisch nicht aufwändigen
Aufgabenstellungen) maximal ein mitleidiges Gähnen aus (wenn er Gähnen
könnte). Das heißt nicht, dass man deswegen hirnverbrannt kompliziert
programmieren muss. Das heißt nur, dass man diesen Faktor auch nicht
überbewerten soll.
Joachim B. schrieb:>> Nun hätte ich gerne wieder meine führenden Nullen.>> wer hindert dich einen String mit 8 Stellen zu reservieren,
Mann ist das kompliziert.
itoa treibt Aufwand um die führenden 0-en loszuwerden und du treibst
einen horrenden Aufwand um sie wieder reinzukriegen.
Da ist es ja noch einfacher, sich eine itoa Version zu schreiben, die
gar nicht erst versucht, führende 0-en loszuwerden.
Lokus Pokus schrieb:> Nur die Ausgabe der Werte ist noch nicht so wie ich es mir gerne> vorstelle.
Dann würde ich vorstellen, du gehst das ganz mal ganz in Ruhe und
systematisch an.
Dei Funktion hier
1
intADCintegerREAD(uint8_tadcport)
2
{
3
adc=0;
4
acnt=(1<<READS);
5
while(acnt>0)
6
{
7
adc+=ADCsingleREAD(adcport);
8
acnt--;
9
}
10
adc>>=READS;
11
12
returnadc;
13
}
die muss ja nicht so kryptisch sein.
Zum einen ist es immer eine gute Idee, wenn man Variablen lokal in den
Funktionen hält. Eine Ausnahme machen große Arrays, aber bei ein paar
Zwischenwerten ist das kein Problem. Das hat nämlich den Vorteil, dass
man nicht ständig wegen der Kontrolle der Datentypen durch den halben
Code scrollen muss.
Nichts gegen deine while Schleife, aber wenn ich überlegen muss, wie oft
die Schleife durchlaufen wird, dann mach ich was falsch. Denn dafür gibt
eine for-Schleife.
Weiters: Halte deinen Compilerbauer nicht für einen Volltrottel. Wenn es
möglich ist, eine Berechnung durch eine Schiebeoperation zu ersetzen,
dann erkennt das der Compiler schon. Das brauchst du ihm nicht vorkauen.
Generell: Jeden einzelnen dieser 'cleveren Tricks' die du in den
nächsten 2 Jahren benutzen wirst, sind für Compilerbauer bzw. Compiler
ein alter Hut und du kannst davon ausgehen, dass der Compiler die alle
beherrscht. Besser noch: er weiß sogar unter welchen Umständen man
diesen Trick nicht einsetzen kann und berücksichtigt das auch.
1
#define NR_SAMPLES 64
2
3
intADCintegerREAD(uint8_tadcport)
4
{
5
intadc=0;
6
uint8_ti;
7
8
for(i=0;i<NR_SAMPLES;i++)
9
adc+=ADCsingleREAD(adcport);
10
11
returnadc/NR_SAMPLES;
12
}
als nächstes schauen wir da mal bezüglich der Datentypen drüber. Du
verwendest hier int. int hat zwar 16 Bit, da aber 1 Bit fürs Vorzeichen
draufgeht, bleiben nur 15 Bit für die Zahl selber. Ein int hat einen
Wertebereich von -32768 bis +32767
Auf der anderen Seite liefert ADCsingleREAD einen Maximalwert von 1023.
Und es liefert nie etwas, was kleiner als 0 ist. Die Summe von 64 Werten
kann also im Bereich von 0 bis 65472 liegen. 65472, das ist aber größer
als die in einem int maximal möglichen 32767 sein. Daher:
Overflow-Gefahr.
So gehts also nicht.
Da wir allerdings wissen, dass die Summe nie negativ sein kann, können
wir die Forderung nach einem Vorzeichen für die Summe aufgeben. Ein
uint16_t ist also insofern besser geeignet, solange sicher gestellt ist,
das NR_SAMPLES nicht größer als 64 ist.
Jetzt gibt es 2 Ansatzmöglichkeiten: entweder wir vertrauen dem
Programmierer, das er das auch nie macht, zumindest sollten wir aber
einen entsprechenden Hinweis als Kommentar im Programm hinterlassen.
1
// darf nicht größer als 64 sein!
2
#define NR_SAMPLES 64
3
4
intADCintegerREAD(uint8_tadcport)
5
{
6
uint16_tadc=0;
7
uint8_ti;
8
9
for(i=0;i<NR_SAMPLES;i++)
10
adc+=ADCsingleREAD(adcport);
11
12
returnadc/NR_SAMPLES;
13
}
oder aber, wir schaffen ein wenig Platz, indem wir einen größeren
Datentyp benutzen
1
#define NR_SAMPLES 64
2
3
intADCintegerREAD(uint8_tadcport)
4
{
5
uint32_tadc=0;
6
uint8_ti;
7
8
for(i=0;i<NR_SAMPLES;i++)
9
adc+=ADCsingleREAD(adcport);
10
11
returnadc/NR_SAMPLES;
12
}
den uint32_t kann man machen, weil das im Programm nicht so zeitkritisch
ist aber im Grunde hat man damit das Problem nur hinausgezögert. Egal
wie man es dreht und wendet, auch hier gibt es natürlich eine
Beschränkung von NR_SAMPLES. Allerdings kaann man natürlich vom
Vertrauensgrundsatz ausgehen und annehmen, dass wohl keiner so doof sein
wird, die Anzahl in derartige Höhen zu schrauben.
WEiters entwickeln wir Programme schrittweise. Ehe da in main() groß
rumgerechnet wird, überzeugen wir uns erst mal davon, dass vom
ADCintegerREAD auch wirklich die Werte geliefert werden, die wir
erwarten.
D.h. die erste Version von main sieht so aus
1
intmain()
2
{
3
intwert;
4
charbuffer[10];// nicht kleckern, klotzen!
5
// für nicht verbrauchtes SRAM gibts kein Geld zurück
6
// mit 10 haben wir erstmal ein wenig Ruhe, selbst
7
// wenn der zu konvertierende Wert nicht im erwarteten
8
// Bereich ist
9
.....
10
11
for(;;)
12
{
13
wert=ADCintegerREAD(1);
14
sprintf(buffer,"%04d",wert);
15
lcd_gotoxy(12,0);
16
lcd_puts(buffer);
17
18
wert=ADCintegerREAD(2);
19
sprintf(buffer,"%04d",wert);
20
lcd_gotoxy(12,1);
21
lcd_puts(buffer);
22
}
23
}
und dann wird das mal getestet, ob am LCD die erwarteten Werte
aufscheinen.
Und erst wenn die stimmen, wird die Verarbeitung der Werte ins Programm
eingebaut.
Karl Heinz schrieb:> itoa treibt Aufwand um die führenden 0-en loszuwerden und du treibst> einen horrenden Aufwand um sie wieder reinzukriegen.Karl Heinz schrieb:> // Zahl in Ziffern zerlegen.> // 4 Stellen, führende 0-en sind gewollt.> for( uint8_t i = 0; i < 4; i++ ) {> buffer[3-i] = ( value % 10 ) + '0';> value /= 10;> }> buffer[4] = '\0';>> return buffer;
ich bin halt kein softie aber ich bemühe mich und finde halt Lösungen
die für mich funktionieren und die ich nachvollziehen kann, gerne lasse
ich meine Programme von dir schreiben so das sie dir gefallen,
einverstanden ?
noch ne Lösung von mir die auch andere Zeichen als Null im "Vorlauf"
erlaubt, z.B. SPACE, nützlich bei LCD Ausgaben immer an der selben
Stelle mit Zeichen löschen und überschreiben.
LG jar
Joachim B. schrieb:> ich bin halt kein softie aber ich bemühe mich und finde halt Lösungen> die für mich funktionieren und die ich nachvollziehen kann, gerne lasse> ich meine Programme von dir schreiben so das sie dir gefallen,> einverstanden ?
Darum gehts doch gar nicht.
Aber wenn du was vorschlägst, dann solls auch vernünftig sein. WEnn du
ihm sprintf ausreden willst, und dann mit einer Lösung daher kommst, die
mit einem strcat und einem strlen arbeitet, dann treibst du den Teufel
mit dem Belzebub aus.
Karl Heinz schrieb:> Joachim B. schrieb:> Darum gehts doch gar nicht.> Aber wenn du was vorschlägst, dann solls auch vernünftig sein. WEnn du> ihm sprintf ausreden willst, und dann mit einer Lösung daher kommst, die> mit einem strcat und einem strlen arbeitet, dann treibst du den Teufel> mit dem Belzebub aus.
OK ich kann es nicht besser, wenn du verbesserst, lernen min. 2 davon !
Lokus Pokus schrieb:> lcd_init(LCD_DISP_ON);> lcd_clrscr();
Das sind die beiden einzigen Zeitfresser, die sichtbare Störungen
erzeugen.
Den 1. macht man nur einmal, den 2. läßt man ganz weg und schon ist
alles in Butter.
Ob float oder int, ist wurscht, so schnell kann der Mensch keine neuen
Werte erfassen.
Und sprintf ist bequemer als itoa, weil Du dann nicht führende
Leerzeichen umständlich einfügen mußt.
Peter Dannegger schrieb:> Ob float oder int, ist wurscht, so schnell kann der Mensch keine neuen> Werte erfassen.
aber die float LIB schluckt Platz ohne Ende oder ?
> Und sprintf ist bequemer als itoa, weil Du dann nicht führende> Leerzeichen umständlich einfügen mußt.
ich dachte die sprintf braucht mehr Zeit
aber ich lerne gerne von dir deine bullet prof Entprell Routine ist mein
Standard geworden uvam.
Joachim B. schrieb:> aber die float LIB schluckt Platz ohne Ende oder ?
Beim AVR sind das einmalig 1kB.
Sprintf mit float kostet auch etwas.
Im ATmega8 Pinout kannst Du bis zum ATmega328P upgraden.
Lokus Pokus schrieb:> Der AVR ist fast zu 100% voll (6548 von 8192 Byte). Wie optimiere ich> jetzt? Welche Variante?> Der eine schreibt so der andere so...
Worum geht es dir dabei genau? Ich sags mal so: Dein Programm läuft und
das ist auch gut so. Wenn du jetzt nur zu Lehrzwecken wissen willst wie
man optimiert, da gibts ein paar Tricks.
1. Werte, die sich nicht ändern (z.B. Strings) wirft man ins EPROM statt
ins Flash wie es bei dir grade ist. Grund hierfür: Das EPROM ist idR
viel größer als das SRAM/Flash. Du hast ein 8 kByte, das EPROM des
Atmega8 ist aber, ich glaub, 256 kByte groß. OK, da kommt bei dir nicht
viel rum aber nur mal so grundsätzlich halt ;)
2. sprintf frisst auch Platz ohne Ende. Auch hier gibt es
platzsparendere Methoden. Als Gegenleistung musst du dann halt selber an
der Formatierung des auszugebenden Strings (hier der ADCValue) arbeiten.
Ich denke grade durch Punkt 2. wirst du einiges an Platz sparen können.
Hi
>1. Werte, die sich nicht ändern (z.B. Strings) wirft man ins EPROM statt>ins Flash wie es bei dir grade ist. Grund hierfür: Das EPROM ist idR>viel größer als das SRAM/Flash. Du hast ein 8 kByte, das EPROM des>Atmega8 ist aber, ich glaub, 256 kByte groß. OK, da kommt bei dir nicht>viel rum aber nur mal so grundsätzlich halt ;)
Wie kommst du auf das schmale Brett? Bei AVRs ist der EEPROM im allg.
der kleinste Speicher. Der ATMega8 hat 512 Byte EEPROM.
MfG Spess
spess53 schrieb:> Wie kommst du auf das schmale Brett?
Weils so auf der ATMEL-Seite steht siehe Bild, ist dann wohl ein
Tippfehler da ;)
EDIT: Verdammt, warum hat der jetzt beide Bilder genommen? Ach egal, das
große kann gelöscht werden.
Lokus Pokus schrieb:> Der AVR ist fast zu 100% voll (6548 von 8192 Byte).
Das ist erheblich zuviel für so ein kleines Progrämmchen, da ist ja
nichtmal float drin.
Wenn Du mal das komplette Projekt zipst, könnte man es compilieren.
Ohne die LCD-Lib kann man das nicht.
Peter Dannegger schrieb:> Das ist erheblich zuviel für so ein kleines Progrämmchen, da ist ja> nichtmal float drin.
Naja, da ist schon Float drin:
Lokus Pokus schrieb:>…> ADCvalue1 = ADCsingleREAD(1) / 10.2;>…> ADCvalue2 = ADCsingleREAD(2) / 10.2;>…
und das sprintf frisst doch bestimmt auch noch was und klar, die LCD-Lib
wäre eventuell auch noch spannend.
Aber sicher finde ich auch 6k Code etwas viel für die drei Zeilen.
@Lokus
Ich seh grad noch ne Division durch 2. Das Register zu schieben hätte
aber denselben Effekt, ist dafür aber schneller und platzsparender.
Einfach mal merken: Multiplikationen und Divisionen mit einem Faktor 2^n
(mit n ∈ N) können durch Links- bzw. Rechtsshift schneller und
platzsparender durchgeführt werden. ;)
Michael Köhler schrieb:> @Lokus>> Ich seh grad noch ne Division durch 2. Das Register zu schieben hätte> aber denselben Effekt, ist dafür aber schneller und platzsparender.> Einfach mal merken: Multiplikationen und Divisionen mit einem Faktor 2^n> (mit n ∈ N) können durch Links- bzw. Rechtsshift schneller und> platzsparender durchgeführt werden. ;)
das hatte ich schon weiter oben geschrieben,
shift statt Division oder Multiplikation mit 2
String Bearbeitung und Ganzzahlen und Verzicht auf sprintf
aber ich rede ja nur und sowas kommt eben raus......
Michael Köhler schrieb:> Ich seh grad noch ne Division durch 2. Das Register zu schieben hätte> aber denselben Effekt, ist dafür aber schneller und platzsparender.> Einfach mal merken: Multiplikationen und Divisionen mit einem Faktor 2^n> (mit n ∈ N) können durch Links- bzw. Rechtsshift schneller und> platzsparender durchgeführt werden. ;)
so was macht der Compiler schon von selber, da muss man nicht von Hand
dran rumbasteln.
chris schrieb:> so was macht der Compiler schon von selber, da muss man nicht von Hand> dran rumbasteln.
egal wie -os -o1 -o2 -o2 ?
komisch ich muss im Studio die Optimierung vorgeben und nun lese ich das
der das selber macht ?
Lokus Pokus schrieb:> _delay_ms(dauer); // Wait x Milliseconds
delay.h:
\note In order for these functions to work as intended, compiler
optimizations <em>must</em> be enabled, and the delay time
<em>must</em> be an expression that is a known constant at
compile-time. If these requirements are not met, the resulting
delay will be much longer (and basically unpredictable), and
applications that otherwise do not use floating-point calculations
will experience severe code bloat by the floating-point library
routines linked into the application.
Außerdem benutzt Du noch die veraltete float-Lib.
Mit WINAVR 2010:
Joachim B. schrieb:> das hatte ich schon weiter oben geschrieben,
Hab ich gezielt überlesen, sorry :D
chris schrieb:> so was macht der Compiler schon von selber, da muss man nicht von Hand> dran rumbasteln.
Hä? Welcher Compiler macht bitte schön aus
1
temp=variable_23*2.0;
ein
1
temp=variable_23<<1;
? Also das ist mir neu, dass das ein Compiler alleine macht.
Michael Köhler schrieb:> temp = variable_23 * 2.0;
daraus natürlich nicht, weil du ihn hier zwingst, sinnloserweise in
float zu rechnen.
aber aus
1
temp=variable_23*2;
eine Shift-Operation zu machen, ist ja wohl eine der trivialsten
Optimierungen, die es gibt.
Wenn der Compiler das nicht macht, schmeiß ihn in die Tonne ;)
lg
Chris
chris schrieb:> erzeugter Code für die Multiplikation ist hier allerdings kein Bitshift,> sondern: test = tmp * 2;> 54: 8a 81 ldd r24, Y+2 ; 0x02> 56: 88 0f add r24, r24
Instruction set:
LSL – Logical Shift Left
(see ADD Rd,Rd)
Ich hatte mal wieder Zeit daran herumzubasteln, und habe das sprinf
durch die selbstgebastelte itoa von Karl Heinz ersetzt.
Dadurch konnte ich einiges an Speicher einsparen.
Was jetzt den Code trotzdem noch auf knapp 5k aufbläßt ist das
"_delay_ms"
Ohne dieser Funktion reduziert sich das Programm gleich mal auf winzige
1K!
Wieso das denn?
Peter meinte ich verwende noch die veraltete float-lib. Woran erkennt
mand as? Und wie lautet die neue?
Hier nochmal das ganze Programm:
@ Lokus Pokus (derschatten)
>Was jetzt den Code trotzdem noch auf knapp 5k aufbläßt ist das>"_delay_ms">Ohne dieser Funktion reduziert sich das Programm gleich mal auf winzige>1K!>Wieso das denn?
Weil man die Funktion nur mit konstanten Werten und eingeschalteter
Compileroptimierung benutzen darf.
_delay_ms(dauer);
MÖÖÖP! FALSCH!
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29
Wenn es variabel sein soll, dann so.
bei 4 MHz braucht ein Takt 250ns, 1ms (was dein delay_ms(1) ist) sind
4000 nop's
ich würde delay_ms rauswerfen und ne Schleife NOPs bauen wenn du keinen
Timer nutzen willst.
+- einige ns-µs kommts ja bei 1ms nicht an....
@ Joachim B. (jar)
>bei 4 MHz braucht ein Takt 250ns, 1ms (was dein delay_ms(1) ist) sind>4000 nop's
WOW, eine Rechenkünstler!
>ich würde delay_ms rauswerfen und ne Schleife NOPs bauen
Ich nicht. Denn _delay_ms() nimmt mir die Rechung incl der NOP-Schleife
ab und macht, was es verspricht, wenn man weiß, wie es geht. Siehe oben.
Weil man _delay_ms nur mit festen Werten verwenden sollte, du verwendest
es aber mit variablen Werten ("dauer").
" \note In order for these functions to work as intended, compiler
optimizations <em>must</em> be enabled, and the delay time
<em>must</em> be an expression that is a known constant at
compile-time. If these requirements are not met, the resulting
delay will be much longer (and basically unpredictable), and
applications that otherwise do not use floating-point calculations
will experience severe code bloat by the floating-point library
routines linked into the application."
Falk Brunner schrieb:> WOW, eine Rechenkünstler!
ja um diese Zeit, ich staune manchmal über mich selbst, Kopfrechnen ist
ja heute nicht mehr so angesagt da versagen manche schon.
Noch eine Unschönheit besteht jedoch.
Wenn ich nur mit Ganzzahlen rechne erreiche ich bei Vollwert nie genau
100.
Der Zähler zuckt dann ständig zwischen 99 und 100 hin und her.
Mit Kommazahlen darf ich ja nicht rechnen.
Wie komme ich sonst auf meine genauen 100?
Lokus Pokus schrieb:> Der Zähler zuckt dann ständig zwischen 99 und 100 hin und her
ich glaube ich hatte es schon mal geschrieben
mehrere Werte aufmitteln, notfalls schneller !
1
ADCSRA|=(1<<ADPS2)|(1<<ADPS1);// 64 prescale for 4Mhz
könnte man ja auf kleineren prescale setzen
http://www.mikrocontroller.net/articles/AVR-Tutorial:_ADC
Beispiel
8 MHz Prozessortakt: 8.000.000Hz / 200.000Hz = 40
Da mit 200kHz gerechnet wurde(Maximale Frequenz), nimmt man den nächst
höheren Wert, also 64.
8.000.000 Hz / 64 = 125.000Hz = 125kHz So erhält man bei 8 MHz einen
Prescaler von 64 und eine Frequenz von 125kHz.
also bietet sich bei 4MHz ja 32 oder 16 an
mit 16 kannst du bei gleichem Tempo 4 Werte aufmitteln und besser
aufrunden
und wenn der Mittelwert mehr zu 100 als zu 99 tendiert eben auf 100
aufrunden, so schwer ist das doch nicht
also alles was größer 1016 im ADC ist wird 100
also alles was größer 1012 und kleiner 1017 im ADC ist wird 99
Super, das Beispiel bringt mir den Fehler:
../main.c:77: warning: comparison is always true due to limited range of
data type
Bei der Zeile: for(i=(digit-1); i>=0; i--)
könnte am
uint8_t i;
liegen versuchs mal mit
int8_t i;
manche Fehler sind doch leicht mit etwas nachdenken zu finden
./main.c:77: warning: comparison is always true due to limited range of
data type
always true: wie soll ein uint <0 werden ? das MUSS doch immer true
sein