Forum: Mikrocontroller und Digitale Elektronik ADC Ausgabe zu UART viel zu schnell


von Ulli B. (Gast)


Lesenswert?

Hi,

ich habe mir aus dem Web folgendes Programm gefischt.
1
/*###########################################################################
2
#            Spannungsmessung über A/D-Wandlung          #
3
#                                      #
4
#  Hardware:  Controller ATMega16                      #
5
#        Takt 3,6864MHz                        #
6
#        Baudrate 9600                        #
7
#        UART an PortD (PIN D0 und D1)                #
8
#        AGND mit GND verbunden                    #
9
#        AVCC und AREF mit VCC verbunden                #
10
#        folgender Spannungsteiler an PC05              #
11
#      VCC                                #
12
#       |                                #
13
#      R1(Poti)            R1=10k              #
14
#       |     --> PA5                        #
15
#      R2                R2=1k              #
16
#       |                                #
17
#      GND                                #
18
#        Hyperterminal auf dem PC (Baudrate 9600)          #
19
#          (ENTER->Start der Messung|andere Taste->Messung stoppen)#
20
#        oder das beigefügte Windows-Programm            #
21
#                                      #
22
#  Funktion:  Misst die am Port anliegende Spannnung mit Hilfe des    #
23
#        eingebautem Analog-Digital-Converters.            # 
24
#        Das Messergebnis wird über das UART weitergemeldet.      #
25
#                                      #
26
#  WinAVR:    gcc version 3.3 20030421 (prerelease)            #
27
#                                      #
28
#  Autor:    Swod                            #
29
#  Kontakt:  Swod@thelastinstance.de  http://www.thelastinstance.de    #
30
#                                      #
31
###########################################################################*/
32
33
#include <avr/io.h>
34
#include <avr/interrupt.h>
35
#include <avr/signal.h>
36
#include <inttypes.h>
37
#include <stdlib.h>
38
// #include <delay.h>
39
40
#define CLK 3686400
41
#define BAUD 9600
42
#define USARTSPEED (CLK/(BAUD*16L)-1)        // Formel zur Berechnung der Werte für UBBRH/UBBRL
43
#define LOW(x)  ((x) & 0xFF)            // Makro zum Lowbyte Zugriff eines Word
44
#define HIGH(x)   (((x)  >> 8) & 0xFF)        // Makro zum Highbyte Zugriff eines Word
45
46
div_t dividend;
47
volatile uint8_t zeichen;                  //definiere zeichen als unsigned integer 8bit
48
volatile uint8_t lowByte;                  //definiere lowByte als unsigned integer 8bit
49
volatile uint8_t highByte;                  //definiere highByte als unsigned integer 8bit
50
volatile uint16_t ergebnis;                //definiere ergebnis als unsigned integer 16Bit
51
52
SIGNAL (SIG_UART_RECV)                  //Uart Receive Interrupt wurde ausgelöst
53
{
54
  zeichen = UDR;                    //den empfangenen Wert nach temp holen
55
}
56
57
void UART_SendByte(uint8_t data)            //sendet ein Byte über das Uart
58
{
59
  while(bit_is_clear(UCSRA, UDRE));          //warten bis UART bereit ist zum senden
60
  UDR = data;                      //data ausgeben
61
}
62
63
//sendet einen String über das Uart
64
void putstring(char *s)                //setze den Pointer s an den Anfang des übergebenen chararrays
65
{  
66
  while (*s != 0)                  //ist der Pointer des Zeichens=0 dann chararray zu Ende
67
  {
68
    UART_SendByte(*s);                //übergibt das Zeichen an UART_SendByte
69
    *s++;                      //zeigt auf das nächste Zeichen
70
  }
71
}
72
73
//initialisieren des UART
74
void uartinit (void)
75
{
76
  UBRRH = HIGH(USARTSPEED);              //Baudrate einstellen
77
  UBRRL = LOW(USARTSPEED);              //Baudrate einstellen
78
  UCSRB = _BV(TXEN) | _BV(RXEN) | _BV(RXCIE);    //senden, empfangen, receiveint aktivieren
79
  UCSRC = (1<<URSEL)|(3<<UCSZ0);            //Frame Format setzen:8data, 1stop bit (URSEL=1 -> UCSRC->Settings werden genutzt)
80
}
81
82
//initialisieren der I/O-schnittstellen
83
void ioinit (void)
84
{
85
  DDRD = 0xFF;                    //PortD als Ausgang konfigurieren
86
  PORTD = 0xFF;                    //Alle Ausgänge abschalten
87
}
88
89
//initialisieren des Analog-Digital-Converters
90
void adcinit (void)
91
{
92
  ADMUX = 0x05;                    //Spannungsmessung an PA5
93
  ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE)) +7;  //ADC Enable|ADC Start Conversion|ADC Free Run Select + Vorteiler 128
94
}
95
96
void ausgabe (void)
97
{
98
  dividend = div(ergebnis, 20.46);          //Messergebnis durch 20,46 teilen (max.Darstellungswert 10 Bit/Anzahl der gewünschten Einheiten=> 1023/50)
99
  dividend = div(dividend.quot, 10);          //nochmalige Teilung durch 10 um im Ergebnis als Rest nur noch eine Ziffer zu haben
100
101
  switch (dividend.quot)          //Überprüfen des Divisionsergebnisses
102
  {
103
    case 0  :  putstring("0.");    //gemessene Einerstelle ist 0
104
          break;
105
    case 1  :  putstring("1.");    //gemessene Einerstelle ist 1
106
          break;
107
    case 2  :  putstring("2.");    //gemessene Einerstelle ist 2
108
          break;
109
    case 3  :  putstring("3.");    //gemessene Einerstelle ist 3
110
          break;
111
    case 4  :  putstring("4.");    //gemessene Einerstelle ist 4
112
          break;
113
    case 5  :  putstring("5.");    //gemessene Einerstelle ist 5
114
          break;
115
    default:  putstring("Error");
116
          break;
117
  }
118
    
119
  switch (dividend.rem)        //Überprüfen des Restes der Division
120
  {
121
    case 0  :  putstring("0");    //gemessene Zehntelstelle ist 0
122
          break;
123
    case 1  :  putstring("1");    //gemessene Zehntelstelle ist 1
124
          break;
125
    case 2  :  putstring("2");    //gemessene Zehntelstelle ist 2
126
          break;
127
    case 3  :  putstring("3");    //gemessene Zehntelstelle ist 3
128
          break;
129
    case 4  :  putstring("4");    //gemessene Zehntelstelle ist 4
130
          break;
131
    case 5  :  putstring("5");    //gemessene Zehntelstelle ist 5
132
          break;
133
    case 6  :  putstring("6");    //gemessene Zehntelstelle ist 6
134
          break;
135
    case 7  :  putstring("7");    //gemessene Zehntelstelle ist 7
136
          break;
137
    case 8  :  putstring("8");    //gemessene Zehntelstelle ist 8
138
          break;
139
    case 9  :  putstring("9");    //gemessene Zehntelstelle ist 9
140
          break;
141
    default:  putstring("Error");
142
          break;
143
  }
144
    
145
  putstring("V\n\r");          //Anhängen der Einheit und des LF CR an das gesendete Messergebnis
146
}
147
148
void messung (void)
149
{
150
  while(zeichen == 0x0D)
151
  {
152
    sbi (ADCSR, ADIF);                //ADIF-Bit Setzen(Messzyklus starten)
153
    loop_until_bit_is_clear(ADCSR, ADIF);      //ersten Messwert verwerfen
154
    sbi (ADCSR, ADIF);                //ADIF-Bit Setzen(Messzyklus starten)
155
    loop_until_bit_is_clear(ADCSR, ADIF);      //warten bis Messung fertig
156
    lowByte = ADCL;                  //immer zuerst das LowByte auslesen
157
    highByte = ADCH;                //dann das mittlerweile gesperrte HighByte auslesen
158
    ergebnis = highByte * 256 + lowByte;      //Zusammenführung von HighByte und LowByte zu einem Messwert
159
    
160
    ausgabe();
161
  }
162
}
163
164
int main(void)
165
{
166
  ioinit();                      //I/O-Schnittstellen initialisieren
167
  uartinit();                      //I/Os einrichten
168
  adcinit();                      //ADC initialisieren mit "Free Run" und Vorteiler 128
169
  sei ();                        //enable globale interrupts
170
  
171
  putstring("Enter druecken\n");            //zum testen Hello World ausgeben
172
173
  for(;;)                      //Loop forever
174
  {
175
    if(zeichen==0x0D)                //Solange das letzte empfangene Zeichen ein ENTER war
176
    {
177
      messung();                  //wird die Messung durchgeführt
178
    }
179
  }
180
}

Das macht nichts anderes als an PA5 die Spannung zu messen und dann im 
Terminal auszugeben. Funktioniert auch soweit prima.
Das Problem welches ich jetzt habe, ich moechte das Programm als 
Datenlogger einsetzen und dazu schmeisst er mir einfach zu viele Daten 
aus. Nach etwas spielen, vielleicht 1min. hatte ich schon 23000 
Datensaetze welches Excel nicht mehr verarbeiten kann, oder mein Rechner 
zu schlapp dafuer ist.

Nun die Frage. Wie stelle ich es an, dass das Programm mir nur alle 1s 
oder 10s oder 60s einen Datensatz an das Terminal uebergibt ?

Schlau wie ich bin habe ich #include <delay.h> eingebaut und wollte ein 
_delay_ms(1000) einfuegen. Nur WO ?

Ich bin in C nicht sehr weit, fange gerade erst an, also bitte erspart 
mir Antworten wie "Lerne erst mal Grundlagen oder lies irgendwelche 
Tutorials". Das tue ich schon, aber bei fuer mich relativ komplexen 
Programmen bin ich noch gnadenlos ueberfordert.

von Marius W. (mw1987)


Lesenswert?

In der main-Schleife unter
1
messung();

MfG
Marius

von Karl H. (kbuchegg)


Lesenswert?

Marius Wensing schrieb:
> In der main-Schleife unter
1
messung();

Das wird nicht viel bringen.

@Ulli
Aber schau mal in die Funktion messung hinein

Im Prinzip sieht die so aus
1
void messung (void)
2
{
3
  solange vom UART nix neues gekommen ist
4
  {
5
    den ADC auslesen
6
7
    das Messergebnis über UART ausgeben
8
  }
9
}

und da, in die Schleife, nach dem ausgeben, hängst du deinen delay rein
1
void messung (void)
2
{
3
  solange vom UART nix neues gekommen ist
4
  {
5
    den ADC auslesen
6
7
    das Messergebnis über UART ausgeben
8
9
    Warte 1 Sekunde
10
  }
11
}


PS: Wenn du C auf dem AVR lernen willst, dann solltest du diese Programm 
schnell ad acta legen. So programmiert man das heute nicht mehr. Schau 
ins AVR-GCC-Tutorial

von Ti Chris (Gast)


Lesenswert?

Ich würde es gerade nicht in die Funktion packen. Denn wenn du dein 
Prog. erweiterst und es irgendwann sehr groß werden sollte, weist du 
nicht mehr warum du immer ein paar sek. stopst. Darum lieber in die 
main. Nur der Übersicht halber!

1
 while(1)
2
  {
3
    if(zeichen==0x0D)               
4
    {
5
      messung();                  
6
     _delay_ms(1000);
7
    }
8
  }

Aber Karl-Heinz hat schon recht. Das ist ein völlig veralteter Code 
(Interrupthandler).

von Karl H. (kbuchegg)


Lesenswert?

Ti Chris schrieb:
> Ich würde es gerade nicht in die Funktion packen. Denn wenn du dein
> Prog. erweiterst und es irgendwann sehr groß werden sollte, weist du
> nicht mehr warum du immer ein paar sek. stopst.

Das Problem ist die Funktion messung() !
Die loopt solange, bis sie vom Benutzer per UART gestoppt wird!

> Darum lieber in die
> main. Nur der Übersicht halber!

Hast du recht. Dann sollte aber die while Schleife in der Funktion 
ersatzlos gestrichen weren. Die ist sowieso schlecht, weil die 
Hauptschleife diese Funktion sowieso schon erfüllt.

von spess53 (Gast)


Lesenswert?

Hi

Der ADC kann im Free Running Mode auch durch Timer getriggert werden. 
Mit dem Timer1 lassen sich die Abstände zwischen 2 Messungen bis auf 
über 18s ausdehnen.

MfG Spess

von Ulli B. (Gast)


Lesenswert?

Halt, Stop, Aus :)

Karl Heinz Buchegger schrieb:
> PS: Wenn du C auf dem AVR lernen willst, dann solltest du diese Programm
> schnell ad acta legen. So programmiert man das heute nicht mehr. Schau
> ins AVR-GCC-Tutorial

Ich befolge den Rat und loesche das Programm gleich aus meiner Sammlung. 
Um ein _delay_ms(); einzufuegen musste ich den compiler aktualisieren. 
Dann hat er bei dem delay zwar nicht mehr gemeckert, allerdings kennt er 
jetzt 3 andere Funktionen nicht mehr. sbi z.B.

Das AVR-GCC-Tutorial und auch das Andere habe ich schon mehrmals 
angefangen zu lesen, aber das ist fuer einen voelligen Neuling, der die 
Schule schon 20 Jahre hinter sich hat, nicht mehr ganz so einfach. Ich 
habe dazu aber auch noch ein paar Buecher besorgt und bei dem Wetter hat 
man doch sowieso mehr Lust zu hause zu bleiben.

Jedenfalls habe ich eingesehen, dass es keinen Sinn und auch keinen 
Spass macht, sich ohne Ahnung an solche Geschichten zu wagen. Denn 
weiter wie eine LED blinken zu lassen oder ein "Ich brauche Kaffee" auf 
der shell laufen zu lassen bin ich noch nicht. Ist also noch ein weiter 
Weg.

Ich danke Euch vielmals.

von Ti Chris (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das Problem ist die Funktion messung() !
> Die loopt solange, bis sie vom Benutzer per UART gestoppt wird!

Sry. Die while habe ich garnicht gesehen. Die ist ja mal sowas von über 
:D.

von Karl H. (kbuchegg)


Lesenswert?

Ti Chris schrieb:
> Karl Heinz Buchegger schrieb:
>> Das Problem ist die Funktion messung() !
>> Die loopt solange, bis sie vom Benutzer per UART gestoppt wird!
>
> Sry. Die while habe ich garnicht gesehen. Die ist ja mal sowas von über
> :D.

Du sagst es.
Da sind auch noch andere Sachen drinn, die man so heute einfach nicht 
mehr macht. zb die Funktion ausgabe()


Wenn ich nicht auf Flash schauen muss
1
void ausgabe( void )
2
{
3
  char buffer[10];
4
  uint16_t volt = ergebnis * 50 / 1024;
5
6
  sprintf( buffer, "%d.%0dV\n\r", volt / 10, volt % 10 );
7
  putstring( buffer );
8
}

Fertig. Ganze Bildschirmseite Code eingespart.

"Ja, aber das ist langsamer!"
Schon, aber er legt danach ja sowieso 1 Sekunde Däumchendrehen ein.

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.