Forum: Mikrocontroller und Digitale Elektronik 16x2 LCD Langsamer Displayaufbau


von Lokus P. (derschatten)


Lesenswert?

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
volatile uint8_t ADCvalue;          // Global variable, set to volatile if used with ISR
7
8
int ADCsingleREAD(uint8_t adctouse)
9
{
10
  int ADCval;
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
  return ADCval;
26
}
27
28
int main(void)
29
{
30
  char buffer[7];
31
  int ADCvalue;
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?

von M. S. (elpaco)


Lesenswert?

>    /* initialize display, cursor off */
>    lcd_init(LCD_DISP_ON);

Das solltest du aus der Schleife rausnehmen und DAVOR packen.

von Lokus P. (derschatten)


Lesenswert?

Das ist es leider nicht. Dadurch wird das LCD total dunkel.
Obwohl es eigentlich logisch wäre.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

und den hier
1
    // clear display and home cursor
2
    lcd_clrscr();
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.

von Lokus P. (derschatten)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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:
1
int main(void)
2
{
3
  char buffer[7];
4
  int ADCvalue;
5
6
  lcd_init(LCD_DISP_ON);
7
8
  for (;;)
9
  {
10
    ADCvalue = ADCsingleREAD(1) / 12.9;
11
12
    sprintf( buffer, "%3d", ADCvalue );
13
    
14
    lcd_gotoxy( 5, 0 );
15
    lcd_puts(buffer);
16
  }
17
}

von M. S. (elpaco)


Lesenswert?

Ich weiß nicht, wieviel Zeit das
1
itoa(ADCvalue, buffer, 10);
 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).

von Lokus P. (derschatten)


Lesenswert?

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!

von Falk B. (falk)


Lesenswert?

@ 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!

von Falk B. (falk)


Lesenswert?

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

von Lokus P. (derschatten)


Lesenswert?

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.
1
/*************************************************************************
2
Title:    ADC Auswertung auf LCD
3
Autor:    DerSchatten
4
Datei:    AVRtest 01.10.2014 23:59
5
Software:  AVR Studio 4.18
6
Hardware:  HD44780 kompatibles LCD 16x2 Display
7
      ATmega8-20PU mit 4MHz Takt
8
**************************************************************************/
9
#include <stdlib.h>
10
#include <avr/io.h>
11
#include <stdint.h>
12
#include <stdio.h>
13
#include "lcd.h"
14
15
volatile uint8_t ADCvalue1;            // Global variable, set to volatile if used with ISR
16
volatile uint8_t ADCvalue2;            // Global variable, set to volatile if used with ISR
17
18
// charset
19
#define ue          "\xF5"        // ü
20
#define oe          "\xEF"        // ö
21
#define ae          "\xE1"        // ä
22
23
// output
24
#define OUT1_DDR      DDRC
25
#define OUT1_PORT      PORTC
26
#define OUT1_PIN      PINC
27
28
#define OUT2_DDR      DDRB
29
#define OUT2_PORT      PORTB
30
#define OUT2_PIN      PINB
31
32
#define green        0x20        // 0010 0000
33
#define yellow        0x10        // 0001 0000
34
#define red          0x08        // 0000 1000
35
#define off          0x38        // 0011 1000
36
37
#define beeper        0x20        // 0010 0000
38
39
int ADCsingleREAD(uint8_t adctouse)
40
{
41
  int ADCval;
42
43
  ADMUX = adctouse;              // use #1 ADC
44
  ADMUX |= (1 << REFS0);            // use AVcc as the reference
45
  ADMUX &= ~(1 << ADLAR);            // clear for 10 bit resolution
46
47
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1);    // 64 prescale for 4Mhz
48
  ADCSRA |= (1 << ADEN);            // Enable the ADC
49
  ADCSRA |= (1 << ADSC);            // Start the ADC conversion
50
51
  while(ADCSRA & (1 << ADSC));        // this line waits for the ADC to finish 
52
53
  ADCval = ADCL;
54
  ADCval = (ADCH << 8) + ADCval;        // ADCH is read so ADC can be updated again
55
56
  return ADCval;
57
}
58
59
void init(void)
60
{
61
  OUT1_DDR |= (1 << PC3) | (1 << PC4) | (1 << PC5);
62
  OUT2_DDR |= (1 << PC5);
63
}
64
65
int main(void)
66
{
67
  init();
68
  char buffer1[3];
69
  char buffer2[3];
70
  int ADCvalue1;
71
  int ADCvalue2;
72
73
  lcd_init(LCD_DISP_ON);            // initialize display, cursor off
74
75
  lcd_puts("Schwellwert \n");          // put string to display
76
  lcd_puts("Helligkeit  \n");          // put string to display
77
78
  for (;;)
79
  {
80
    ADCvalue1 = ADCsingleREAD(1) / 10.2;  // read ADC Value from Pot
81
    ADCvalue2 = ADCsingleREAD(2) / 10.2;  // read ADC Value from Temp
82
83
    sprintf(buffer1, "%03d", ADCvalue1);  // convert interger into string
84
    lcd_gotoxy(12,0);            // move cursor to position 12 on line 1
85
        lcd_puts(buffer1);            // put converted string to display
86
    sprintf(buffer2, "%03d", ADCvalue2);  // convert interger into string
87
    lcd_gotoxy(12,1);            // move cursor to position 12 on line 2
88
        lcd_puts(buffer2);            // put converted string to display
89
90
    OUT1_PORT &= ~(off);
91
    OUT2_PORT &= ~(beeper);
92
93
    if (ADCvalue2 < ADCvalue1 / 2) OUT1_PORT |= green;
94
    if ((ADCvalue2 >= ADCvalue1 / 2) && (ADCvalue2 < ADCvalue1)) OUT1_PORT |= yellow;
95
    if (ADCvalue2 >= ADCvalue1)
96
    {
97
      OUT1_PORT |= red;
98
      OUT2_PORT |= beeper;
99
    }
100
  }
101
}

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

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!

von Joachim B. (jar)


Lesenswert?

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
char analog_str[5];
5
char digit_str[5];
6
7
uint16_t adc;
8
uint8_t acnt;
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.

von Lokus P. (derschatten)


Lesenswert?

Ich habe dein Beispiel mal in eine Funktion gepackt und eingebaut:
1
/*************************************************************************
2
Title:    ADC Auswertung auf LCD
3
Autor:    DerSchatten
4
Datei:    AVRtest 01.10.2014 23:59
5
Software:  AVR Studio 4.18
6
Hardware:  HD44780 kompatibles LCD 16x2 Display
7
      ATmega8-20PU mit 4MHz Takt
8
**************************************************************************/
9
#include <stdlib.h>
10
#include <avr/io.h>
11
#include <stdint.h>
12
#include <stdio.h>
13
#include "lcd.h"
14
15
volatile uint8_t ADCvalue1;            // Global variable, set to volatile if used with ISR
16
volatile uint8_t ADCvalue2;            // Global variable, set to volatile if used with ISR
17
18
#ifndef READS
19
  #define READS        6
20
#endif
21
22
char analog_str[5];
23
char digit_str[5];
24
25
uint16_t adc;
26
uint8_t acnt;
27
28
// charset
29
#define ue          "\xF5"        // ü
30
#define oe          "\xEF"        // ö
31
#define ae          "\xE1"        // ä
32
33
// output
34
#define OUT1_DDR      DDRC
35
#define OUT1_PORT      PORTC
36
#define OUT1_PIN      PINC
37
38
#define OUT2_DDR      DDRB
39
#define OUT2_PORT      PORTB
40
#define OUT2_PIN      PINB
41
42
#define green        0x20        // 0010 0000
43
#define yellow        0x10        // 0001 0000
44
#define red          0x08        // 0000 1000
45
#define off          0x38        // 0011 1000
46
47
#define beeper        0x20        // 0010 0000
48
49
int ADCsingleREAD(uint8_t adctouse)
50
{
51
  int ADCval;
52
53
  ADMUX = adctouse;              // use #1 ADC
54
  ADMUX |= (1 << REFS0);            // use AVcc as the reference
55
  ADMUX &= ~(1 << ADLAR);            // clear for 10 bit resolution
56
57
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1);    // 64 prescale for 4Mhz
58
  ADCSRA |= (1 << ADEN);            // Enable the ADC
59
  ADCSRA |= (1 << ADSC);            // Start the ADC conversion
60
61
  while(ADCSRA & (1 << ADSC));        // this line waits for the ADC to finish 
62
63
  ADCval = ADCL;
64
  ADCval = (ADCH << 8) + ADCval;        // ADCH is read so ADC can be updated again
65
66
  return ADCval;
67
}
68
69
int ADCintegerREAD(uint8_t adcport)
70
{
71
  adc = 0;
72
  acnt = (1<<READS);
73
  while(acnt>0)
74
  {
75
    adc+=ADCsingleREAD(adcport);
76
    acnt--;
77
  }   
78
  adc >>= READS;
79
80
  return adc;
81
}
82
83
void init(void)
84
{
85
  OUT1_DDR |= (1 << PC3) | (1 << PC4) | (1 << PC5);
86
  OUT2_DDR |= (1 << PC5);
87
}
88
89
int main(void)
90
{
91
  init();
92
  char buffer1[4];
93
  char buffer2[4];
94
95
  lcd_init(LCD_DISP_ON);            // initialize display, cursor off
96
97
  lcd_puts_P("Schwellwert \n");        // put string from program memory to display
98
  lcd_puts_P("Helligkeit  \n");        // put string from program memory to display
99
100
  for (;;)
101
  {
102
103
    itoa((uint16_t)(ADCintegerREAD(1)*2520L/1023L), buffer1, 10);      // convert interger into string
104
    lcd_gotoxy(12,0);            // move cursor to position 12 on line 1
105
        lcd_puts(buffer1);            // put converted string to display
106
    itoa((uint16_t)(ADCintegerREAD(2)*2520L/1023L), buffer2, 10);      // convert interger into string
107
    lcd_gotoxy(12,1);            // move cursor to position 12 on line 2
108
    lcd_puts(buffer2);            // put converted string to display
109
110
    OUT1_PORT &= ~(off);
111
    OUT2_PORT &= ~(beeper);
112
113
//    if (ADCvalue2 < ADCvalue1 / 2) OUT1_PORT |= green;
114
//    if ((ADCvalue2 >= ADCvalue1 / 2) && (ADCvalue2 < ADCvalue1)) OUT1_PORT |= yellow;
115
//    if (ADCvalue2 >= ADCvalue1)
116
//    {
117
//      OUT1_PORT |= red;
118
//      OUT2_PORT |= beeper;
119
//    }
120
  }
121
}

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.

von Joachim B. (jar)


Lesenswert?

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

von Lokus P. (derschatten)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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 ?

: Bearbeitet durch User
von Lokus P. (derschatten)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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)

von Lokus P. (derschatten)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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!

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.
1
char* myItoA( uint16_t value, char* buffer )
2
{
3
  // sicherheitshalber. Wenn keine Gefahr besteht
4
  // kann man das auch weglassen
5
  if( value > 9999 || value < 0 ) {
6
    strcpy( buffer, "----" );
7
    return buffer;
8
  }
9
10
  // Zahl in Ziffern zerlegen.
11
  // 4 Stellen, führende 0-en sind gewollt.
12
  for( uint8_t i = 0; i < 4; i++ ) {
13
    buffer[3-i] = ( value % 10 ) + '0';
14
    value /= 10;
15
  }
16
  buffer[4] = '\0';
17
18
  return buffer;
19
}

von Amateur (Gast)


Lesenswert?

>Ich habe das ganze einfach mal durch 10.2 dividiert.

Das kann man so nicht sagen.

10.2L = 10

von Karl H. (kbuchegg)


Lesenswert?

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
int ADCintegerREAD(uint8_t adcport)
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
  return adc;
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
int ADCintegerREAD(uint8_t adcport)
4
{
5
  int adc = 0;
6
  uint8_t i;
7
8
  for( i = 0; i < NR_SAMPLES; i++ )
9
    adc += ADCsingleREAD(adcport);
10
11
  return adc / 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
int ADCintegerREAD(uint8_t adcport)
5
{
6
  uint16_t adc = 0;
7
  uint8_t i;
8
9
  for( i = 0; i < NR_SAMPLES; i++ )
10
    adc += ADCsingleREAD(adcport);
11
12
  return adc / NR_SAMPLES;
13
}

oder aber, wir schaffen ein wenig Platz, indem wir einen größeren 
Datentyp benutzen
1
#define NR_SAMPLES 64
2
3
int ADCintegerREAD(uint8_t adcport)
4
{
5
  uint32_t adc = 0;
6
  uint8_t i;
7
8
  for( i = 0; i < NR_SAMPLES; i++ )
9
    adc += ADCsingleREAD(adcport);
10
11
  return adc / 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
int main()
2
{
3
  int wert;
4
  char buffer[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.

: Bearbeitet durch User
von Joachim B. (jar)


Lesenswert?

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 ?
1
char *trimm_string(char vorlauf, char *s_string, UBYTE num)
2
{  static char _tmp[MAX_STRING]={0};
3
  memset( _tmp, (int)vorlauf, num);
4
   _tmp[num]=0;
5
   strcat(_tmp, s_string);
6
  return &_tmp[strlen(_tmp)-num];
7
} // char *trimm_string(char vorlauf, char *s_string, UBYTE num)
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

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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 !

von Peter D. (peda)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

Peter Dannegger schrieb:
> Im ATmega8 Pinout kannst Du bis zum ATmega328P upgraden.

habe nix anderes mehr ! (ausser m2560)

: Bearbeitet durch User
von Lokus P. (derschatten)


Lesenswert?

Also, so funktioniert es jetzt mal wie ich es gerne hätte:
1
/*************************************************************************
2
Title:    ADC Auswertung auf LCD
3
Autor:    DerSchatten
4
Datei:    AVRtest 01.10.2014 23:59
5
Software:  AVR Studio 4.18
6
Hardware:  HD44780 kompatibles LCD 16x2 Display
7
      ATmega8-20PU mit 4MHz Takt
8
**************************************************************************/
9
#include <stdlib.h>
10
#include <avr/io.h>
11
#include <stdint.h>
12
#include <stdio.h>
13
#include <util/delay.h>
14
#include "lcd.h"
15
16
volatile uint16_t ADCvalue1;          // Global variable, set to volatile if used with ISR
17
volatile uint16_t ADCvalue2;          // Global variable, set to volatile if used with ISR
18
19
// charset
20
#define ue          "\xF5"        // ü
21
#define oe          "\xEF"        // ö
22
#define ae          "\xE1"        // ä
23
24
// output
25
#define OUT1_DDR      DDRC
26
#define OUT1_PORT      PORTC
27
#define OUT1_PIN      PINC
28
29
#define OUT2_DDR      DDRB
30
#define OUT2_PORT      PORTB
31
#define OUT2_PIN      PINB
32
33
#define green        0x20        // 0010 0000
34
#define yellow        0x10        // 0001 0000
35
#define red          0x08        // 0000 1000
36
#define off          0x38        // 0011 1000
37
38
#define beeper        0x20        // 0010 0000
39
40
int ADCsingleREAD(uint16_t adctouse)
41
{
42
  int ADCval;
43
44
  ADMUX = adctouse;              // use #1 ADC
45
  ADMUX |= (1 << REFS0);            // use AVcc as the reference
46
  ADMUX &= ~(1 << ADLAR);            // clear for 10 bit resolution
47
48
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1);    // 64 prescale for 4Mhz
49
  ADCSRA |= (1 << ADEN);            // Enable the ADC
50
  ADCSRA |= (1 << ADSC);            // Start the ADC conversion
51
52
  while(ADCSRA & (1 << ADSC));        // this line waits for the ADC to finish 
53
54
  ADCval = ADCL;
55
  ADCval = (ADCH << 8) + ADCval;        // ADCH is read so ADC can be updated again
56
57
  return ADCval;
58
}
59
60
void buzzer(uint8_t dauer)
61
{
62
  OUT2_PORT |= beeper;            // Beeper is on
63
  _delay_ms(dauer);              // Wait x Milliseconds
64
  OUT2_PORT &= ~(beeper);            // Beeper is off
65
  _delay_ms(dauer);              // Wait x Milliseconds
66
}
67
68
void init(void)
69
{
70
  OUT1_DDR |= off;              // set LED to output
71
  OUT2_DDR |= beeper;              // set Beeper to output
72
73
  lcd_init(LCD_DISP_ON);            // initialize display, cursor off
74
}
75
76
int main(void)
77
{
78
  init();
79
  char buffer[10];
80
81
  lcd_puts_P("Schwellwert    %\n");      // put string from program memory to display
82
  lcd_puts_P("Helligkeit     %\n");      // put string from program memory to display
83
84
  for (;;)
85
  {
86
    ADCvalue1 = ADCsingleREAD(1) / 10.2;  // read ADC Value from Pot
87
    sprintf(buffer, "%03d", ADCvalue1);    // convert interger into string
88
    lcd_gotoxy(12,0);            // move cursor to position 12 on line 1
89
    lcd_puts(buffer);            // put converted string to display
90
91
    ADCvalue2 = ADCsingleREAD(2) / 10.2;  // read ADC Value from Temp
92
    sprintf(buffer, "%03d", ADCvalue2);    // convert interger into string
93
    lcd_gotoxy(12,1);            // move cursor to position 12 on line 2
94
    lcd_puts(buffer);            // put converted string to display
95
96
    OUT1_PORT &= ~(off);          // LED Port to LO
97
98
    if (ADCvalue2 < ADCvalue1 / 2) OUT1_PORT |= green;
99
100
    if ((ADCvalue2 >= ADCvalue1 / 2) && (ADCvalue2 < ADCvalue1)) OUT1_PORT |= yellow;
101
102
    if (ADCvalue2 >= ADCvalue1)
103
    {
104
      OUT1_PORT |= red;
105
      buzzer(1);
106
    }
107
  }
108
}

Der AVR ist fast zu 100% voll (6548 von 8192 Byte). Wie optimiere ich 
jetzt? Welche Variante?
Der eine schreibt so der andere so...

: Bearbeitet durch User
von oOo_||_oOo (Gast)


Lesenswert?

Lokus Pokus schrieb:
> Der AVR ist fast zu 100% voll (6548 von 8192 Byte).

Lass ihn doch. Du bekommst kein Geld für ungenutzten Speicher zurück.

von M. K. (sylaina)


Lesenswert?

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.

von spess53 (Gast)


Lesenswert?

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

von M. K. (sylaina)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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.

von M. K. (sylaina)


Lesenswert?

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

von Joachim B. (jar)


Lesenswert?

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

von chris (Gast)


Lesenswert?

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.

von Joachim B. (jar)


Angehängte Dateien:

Lesenswert?

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 ?

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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:
1
4.3.3
2
AVR Memory Usage
3
----------------
4
Device: atmega8
5
6
Program:    2816 bytes (34.4% Full)
7
(.text + .data + .bootloader)
8
9
Data:         46 bytes (4.5% Full)
10
(.data + .bss + .noinit)

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

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.

von chris (Gast)


Lesenswert?

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

von chris (Gast)


Lesenswert?

Da mir gerade langweilig ist, hab ich das mal schnell ausprobiert:
1
int main(void)
2
{
3
  volatile uint8_t test;
4
  volatile uint8_t tmp = 123;
5
  
6
  test = tmp * 2;
7
  
8
  test;
9
  
10
    while(1)
11
    {
12
        //TODO:: Please write your application code 
13
    }
14
}

erzeugter Code für die Multiplikation ist hier allerdings kein Bitshift, 
sondern:
1
  test = tmp * 2;
2
  54:  8a 81         ldd  r24, Y+2  ; 0x02
3
  56:  88 0f         add  r24, r24
4
  58:  89 83         std  Y+1, r24  ; 0x01


Für eine Division durch 2 wird allerdings ein Shift erzeugt:
1
  test = tmp / 2;
2
  54:  89 81         ldd  r24, Y+1  ; 0x01
3
  56:  86 95         lsr  r24
4
  58:  8a 83         std  Y+2, r24  ; 0x02

Bei Division durch 4 entstprechend 2 Shifts:
1
  test = tmp / 4;
2
  54:  89 81         ldd  r24, Y+1  ; 0x01
3
  56:  86 95         lsr  r24
4
  58:  86 95         lsr  r24
5
  5a:  8a 83         std  Y+2, r24  ; 0x02

lg
Chris

von Peter D. (peda)


Lesenswert?

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)

von Lokus P. (derschatten)


Lesenswert?

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:
1
/*************************************************************************
2
Title:    ADC Auswertung auf LCD
3
Autor:    DerSchatten
4
Datei:    AVRtest 01.10.2014 23:59
5
Software:  AVR Studio 4.18
6
Hardware:  HD44780 kompatibles LCD 16x2 Display
7
      ATmega8-20PU mit 4MHz Takt
8
**************************************************************************/
9
#include <avr/io.h>
10
#include <stdio.h>
11
#include <util/delay.h>
12
#include "lcd.h"
13
14
volatile uint16_t ADCvalue1;          // Global variable, set to volatile if used with ISR
15
volatile uint16_t ADCvalue2;          // Global variable, set to volatile if used with ISR
16
17
// charset
18
#define ue          "\xF5"        // ü
19
#define oe          "\xEF"        // ö
20
#define ae          "\xE1"        // ä
21
22
// output
23
#define OUT1_DDR      DDRC
24
#define OUT1_PORT      PORTC
25
#define OUT1_PIN      PINC
26
27
#define OUT2_DDR      DDRB
28
#define OUT2_PORT      PORTB
29
#define OUT2_PIN      PINB
30
31
#define green        0x20        // 0010 0000
32
#define yellow        0x10        // 0001 0000
33
#define red          0x08        // 0000 1000
34
#define off          0x38        // 0011 1000
35
36
#define beeper        0x20        // 0010 0000
37
38
int ADCsingleREAD(uint16_t adctouse)
39
{
40
  int ADCval;
41
42
  ADMUX = adctouse;              // use #1 ADC
43
  ADMUX |= (1 << REFS0);            // use AVcc as the reference
44
  ADMUX &= ~(1 << ADLAR);            // clear for 10 bit resolution
45
46
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1);    // 64 prescale for 4Mhz
47
  ADCSRA |= (1 << ADEN);            // Enable the ADC
48
  ADCSRA |= (1 << ADSC);            // Start the ADC conversion
49
50
  while(ADCSRA & (1 << ADSC));        // this line waits for the ADC to finish 
51
52
  ADCval = ADCL;
53
  ADCval = (ADCH << 8) + ADCval;        // ADCH is read so ADC can be updated again
54
55
  return ADCval;
56
}
57
58
char* myItoA( uint16_t value, char* buffer )
59
{
60
  for (uint8_t i = 0; i < 3; i++)
61
  {
62
    buffer[2-i] = (value % 10) + '0';
63
    value /= 10;
64
  }
65
  buffer[3] = '\0';
66
67
  return buffer;
68
}
69
70
void buzzer(uint8_t dauer)
71
{
72
  OUT2_PORT |= beeper;            // Beeper is on
73
  _delay_ms(dauer);              // Wait x Milliseconds
74
  OUT2_PORT &= ~(beeper);            // Beeper is off
75
  _delay_ms(dauer);              // Wait x Milliseconds
76
}
77
78
void init(void)
79
{
80
  OUT1_DDR |= off;              // set LED to output
81
  OUT2_DDR |= beeper;              // set Beeper to output
82
83
  lcd_init(LCD_DISP_ON);            // initialize display, cursor off
84
}
85
86
int main(void)
87
{
88
  init();
89
  char buffer[4];
90
91
  lcd_puts_P("Schwellwert    %\n");      // put string from program memory to display
92
  lcd_puts_P("Helligkeit     %\n");      // put string from program memory to display
93
94
  for (;;)
95
  {
96
    ADCvalue1 = ADCsingleREAD(1)*100L/1023L;  // read ADC Value from Pot
97
    myItoA(ADCvalue1, buffer);
98
    lcd_gotoxy(12,0);            // move cursor to position 12 on line 1
99
    lcd_puts(buffer);            // put converted string to display
100
101
    ADCvalue2 = ADCsingleREAD(2)*100L/1023L;  // read ADC Value from Temp
102
    myItoA(ADCvalue2, buffer);
103
    lcd_gotoxy(12,1);            // move cursor to position 12 on line 2
104
    lcd_puts(buffer);            // put converted string to display
105
106
    OUT1_PORT &= ~(off);          // LED Port to LO
107
108
    if (ADCvalue2 < ADCvalue1 / 2) OUT1_PORT |= green;
109
110
    if ((ADCvalue2 >= ADCvalue1 / 2) && (ADCvalue2 < ADCvalue1)) OUT1_PORT |= yellow;
111
112
    if (ADCvalue2 >= ADCvalue1)
113
    {
114
      OUT1_PORT |= red;
115
      buzzer(1);
116
    }
117
  }
118
}

von Falk B. (falk)


Lesenswert?

@ 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.
1
void var_delay(uint16_t time_ms) {
2
  while(time_ms--) _delay_ms(1);
3
}

von Joachim B. (jar)


Lesenswert?

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

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ 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.

von Lokus P. (derschatten)


Lesenswert?

Vielleicht gibt es ja noch einen anderen Weg.

Der Sinn dahinter ist das ich damit eine PIEZO Summer ansteuern möchte.
Genaugenommen diesen hier:
http://disti-assets.s3.amazonaws.com/straightroadelectronics/files/datasheets/14737.pdf

Löst man das vielleicht anders? Bzw. ist das auch ohne _delay machbar?

von M. S. (elpaco)


Lesenswert?

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."

von Joachim B. (jar)


Lesenswert?

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.

von Lokus P. (derschatten)


Lesenswert?

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?

von Joachim B. (jar)


Lesenswert?

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

: Bearbeitet durch User
von Lokus P. (derschatten)


Lesenswert?

Und wie runden?

von Falk B. (falk)


Lesenswert?


von Lokus P. (derschatten)


Lesenswert?

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

von Joachim B. (jar)


Lesenswert?

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

: Bearbeitet durch User
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
Noch kein Account? Hier anmelden.