Forum: Mikrocontroller und Digitale Elektronik adc-usart-atmega8-problem


von Penie L. (peniely)


Lesenswert?

Hi Leute,

kurz zu meinem Problem, ich möchte mit einem ATmega8 und 4 MHz
Quarz einen Timer realisieren und jede 60 ms den ADC- Wert  durch
serielle schnittstelle rs232 auf einem PC ausgeben.
Wie gehe ich da am besten vor, weil mit dem unter geschrieben Programm
funktioniert nicht, aber ADC oder UART-programme alleine funktioniert.

Tools: AVRStudio
Device: atmega8
Alle fusse richtig angestellt.
systemtakt int. 4 mhz weil mit ext. geht nicht

Mein Programm sieht in etwa so aus:
1
#define F_CPU 4000000L
2
#define BAUD 9600L
3
4
#include <avr/io.h>
5
#include <util/delay.h>
6
#include <stdint.h>
7
#include <stdlib.h>
8
#include <avr/interrupt.h>
9
10
//überprüfen ob baudrate mit CPU Frequenz zusammenpasst
11
#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde
12
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))          //reale Baudrate
13
14
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille
15
16
#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10))
17
#error Systematischer Fehler in der Baudrate grösser 1% und damit zu hoch!
18
#endif
19
20
void initall();
21
int uart_putc(unsigned char c);
22
23
//globale Variablen definieren
24
int ADIteiler=0;; 
25
int u=0; 
26
int v=0; 
27
int unwichtig;
28
uint8_t result=0;char s[2];
29
30
31
//Initialisierung
32
void initall()
33
{
34
  //Initialisierung UART-Schnittstelle
35
  UBRRH = UBRR_VAL >> 8;
36
  UBRRL = UBRR_VAL & 0xFF;     
37
  UCSRB = (1<<RXEN)|(1<<TXEN);          //UART TX einschalten
38
  UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);   //Asynchron 8N1
39
  
40
//Initialisierung ADC
41
  
42
  
43
  TCNT0 = 5;        //interrupt            
44
  TIMSK |= (1<<TOIE0);  //Zählregister setzen        
45
//damit interrupt auch ausläst wenn überlauf erreicht wird
46
  TCCR0 |= (1<<CS01);
47
  TCCR0 |= (1<<CS00);
48
  TCCR0 &= ~(1<<CS01);
49
  TCCR0 &= ~(1<<CS02);     //CPU Takt wird verwendet --> 4MHz
50
  TIFR |= (1<<TOV0);    //startet interrupt bei jedem überlauf 
51
  
52
  ADCSRA = (1<<ADEN);     //AD initialisierung
53
// Frequenzvorteiler setzen auf 128/32 und ADC aktivieren 
54
  ADCSRA |= (1<<ADPS2) | (1<<ADPS0);   
55
  ADMUX=0;                      //(ADC0)
56
  ADMUX |= (1<<REFS0) | (1<<REFS1);    //int. REF Voltage Reference Selection
57
  ADMUX |= (0<<ADLAR);    //the result is right adjusted
58
 
59
    
60
  //1. Dummy-Readout
61
  //auf Abschluss der Konvertierung warten  
62
  while ( ADCSRA & (1<<ADSC) ) {;} 
63
  unwichtig = ADCL; 
64
  result = ADCH;
65
  result = 0;
66
}
67
68
//Unterprogramme UART
69
//Schreibt einzelne Ascii-Werte auf USART Schnitstelle
70
int uart_putc(unsigned char c)      
71
{
72
    while (!(UCSRA & (1<<UDRE))){}    //warten bis Senden moeglich
73
    UDR = c;                          //sende Zeichen 
74
    return 0;
75
}
76
77
void uart_puts (char *s)
78
{
79
    while (*s)
80
    {   // so lange *s != '\0' also ungleich dem "String-Endezeichen"
81
        uart_putc(*s);     //schreibt die einzelne Ziffer auf die UART
82
        s++; //geht zur (falls vorhandenen) zweiten  Ziffer des HEX-Wertes
83
    }
84
}
85
86
//Interrupt Service Routine für AD Wandlung
87
ISR(TIMER0_OVF_vect) 
88
{
89
  ADIteiler++;               //zählvariable um den interrupt auf jede ms zu takten
90
  if(ADIteiler >= 60)            //Zählschleife für alle ms 60
91
  {
92
    ADIteiler=0;      //Teiler wieder auf 0 setzen
93
    ADCSRA |= (1<<ADSC);              //eine Wandlung "single conversion"
94
    while ( ADCSRA & (1<<ADSC) ) {;} //auf Abschluss der Konvertierung  //warten
95
    unwichtig = ADCL; //Auslesen der 2 niederwertigsten BITs -> wird  //verworfen
96
    result= ADCH;//Auslesen der 8 höchstwertigsten BITS-> 8 BIT-ADC Wert
97
    uart_puts("\r\n");
98
  uart_putc( itoa( result, s, 10 ) );//HEX-Zahlen in Ascii umwandeln
99
    uart_putc('%');
100
  DDRB = 0xFF;        //define as output
101
  PORTB = (result / 10) + 1;
102
  }
103
}
104
int main(void)
105
{
106
  initall();
107
  sei();                //Globale Interrupts freischalten
108
  while (1) {;}
109
}

Vielen Dank im Voraus.
Gruß, peniely

von Ahem (Gast)


Lesenswert?

Schon fast ganz gut Dein Code. Aber ein wenig mehr Analyse wäre schon 
wichtig, damit DU erkennst, was das Problem ist.

Aber ein kleiner Tip:
Schau Dir mal den Timer-Interrupt an. Man wartet grundsätzlich nicht 
in Interrupts! . Nimm das starten der Wandlung, die Ausgabe und das 
Port-Setzen aus dem Interrupt raus. Raus. Raus....
In Interrupts wird nur erledigt, was unmittelbar mit dem Ereignis 
zusammenhängt. Daten wegspeichern, Timer-Register neu laden uswusf. Aber 
man fängt nicht noch an Kaffee zu kochen und die Steuererklärung zu 
machen. Das macht man in main.

Hier wartest DU indirekt in dem Timer Interrupt bis jeweils das letzte 
Zeichen gesendet wurde. Das ist nicht gut. Nein, nein. Nimm für die UART 
einen extra Buffer (Ringpuffer).

von Karl H. (kbuchegg)


Lesenswert?

Alles das was Ahem schon gesagt hat.

Dein Programm ist noch ungeschliffen, aber auch nicht so schlecht.
Beim schnellen Durchlesen ist mir das aufgefallen

  uart_putc( itoa( result, s, 10 ) );//HEX-Zahlen in Ascii umwandeln

Das kann nicht gehen. (Hat dein Compiler denn dazu keine Meinung?)

uart_putc gibt ein einzelnes Zeichen aus. Du hast einen String!
(Solange du noch unerfahren bist, schachtle nicht zuviele 
Funktionsaufrufe ineinander. Das ist am Anfang verwirrend)


  itoa( result, s, 10 );
  uart_puts( s );

(uart_puts und nicht uart_putc)

Auch noch:

char s[2];

Das wird zuwenig sein um ein maximal 3 stellige Zahl in ihrer Textform 
aufzunehmen. Dafür muss s mindestens die Länge 4 haben.

http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F

Bei solchen Hilfsstrings, die man immer wieder zur Textwandlung braucht, 
lohnt es nicht zu kleckern. Klotzen ist hier angesagt

char s[20];

NOch was.
result ist ein uint8_t. Also unsigned. Die Funktion die für sowas die 
Textrepräsentierung erzeugt heißt utoa. itoa macht das ganze für einen 
int, also etwas mit Vorzeichen.

von Penie L. (peniely)


Lesenswert?

vielen Dank für ihre Antworten.
jetzt werde ich die Tipp einsetzen, und ich melde mich dann wider
gruss
peniely

von Penie L. (peniely)


Lesenswert?

Hallo,
jetzt ist mein geänderter Code:
ich kann jetzt die ADC werte am PortB sehen, leider auf dem Bildschirm 
meines Terminal, immer nur die Wiederholung von dem Zeichen "m$"
vielen Dank im voraus für die Hilfe

1
#define F_CPU 4000000L
2
#define BAUD 9600L
3
4
#include <avr/io.h>
5
#include <util/delay.h>
6
#include <stdint.h>
7
#include <stdlib.h>
8
#include <avr/interrupt.h>
9
10
//überprüfen ob baudrate mit CPU Frequenz zusammenpasst
11
#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde
12
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))          //reale Baudrate
13
14
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille
15
16
#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10))
17
#error Systematischer Fehler in der Baudrate grösser 1% und damit zu hoch!
18
#endif
19
20
void initall();
21
int uart_putc(unsigned char c);
22
void uart_puts (char *s);
23
uint16_t ADC_Reading(uint8_t Mux_channel);
24
25
//globale Variablen definieren
26
int ADIteiler=0;; int u=0; int v=0; int unwichtig;
27
uint8_t result=0;char s[20];
28
29
30
//Initialisierung
31
void initall()
32
{
33
  //Initialisierung UART-Schnittstelle
34
  UBRRH = UBRR_VAL >> 8;
35
  UBRRL = UBRR_VAL & 0xFF;     
36
  UCSRB = (1<<RXEN)|(1<<TXEN);          //UART TX einschalten
37
  UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);   //Asynchron 8N1
38
  
39
  //interrupt
40
  TCNT0 = 5;         //Zählregister setzen
41
  TIMSK |= (1<<TOIE0);  //damit interrupt auch ausläst wenn überlauf //erreicht wird
42
  TCCR0 |= (1<<CS01);
43
  TCCR0 |= (1<<CS00);
44
  TCCR0 &= ~(1<<CS01);
45
  TCCR0 &= ~(1<<CS02);       //CPU Takt wird verwendet --> 12MHz
46
  TIFR |= (1<<TOV0);    //startet interrupt bei jedem überlauf   
47
}
48
49
uint16_t ADC_Reading(uint8_t Mux_channel) 
50
{
51
  uint8_t i;
52
  uint16_t result = 0;
53
54
  //Initialisierung ADC  
55
  ADCSRA = (1<<ADEN);       //Enables the ADC
56
  ADCSRA |= (1<<ADPS2) | (1<<ADPS0);   //Determine the division factor 
57
  ADMUX = Mux_channel;      //Select pin ADC0 using MUX
58
  _delay_us(40);
59
  ADMUX |= (1<<REFS0) | (1<<REFS1);//Int. REF Voltage Reference Selection
60
  ADMUX |= (0<<ADLAR);    //The result is right adjusted
61
  ADCSRA |= (1<<ADSC);      //Start conversion
62
  while(ADCSRA & (1<<ADSC));  //wait until converstion completed
63
  result = ADCW;    //ADCW must be read once
64
  result = 0;  //Otherwise the result of the next transformation is not //taken over
65
                          
66
//Now reads 4 times the similar tension and selected channel channel 
67
//And then calculate the average value divided by 4. Because the ADC //measeres 0..1023, And the Port however can only 0..255 show
68
  for(i=0; i<4; i++) 
69
  {    
70
    ADCSRA |= (1<<ADSC);  //A single conversion    
71
    while(ADCSRA & (1<<ADSC));  //Waiting on conversion closing    
72
    result += ADCW;
73
  }
74
  ADCSRA &= (0<<ADEN);      // ADC reanables  
75
  result /= 4;
76
  return result;
77
}//End int ADC_Read() 
78
79
//Unterprogramme UART
80
int uart_putc(unsigned char c)
81
{
82
    while (!(UCSRA & (1<<UDRE))){}    //warten bis Senden moeglich
83
    UDR = c;                          //sende Zeichen 
84
    return 0;
85
}
86
87
void uart_puts (char *s)
88
{
89
    while (*s)
90
    {   // so lange *s != '\0' also ungleich dem "String-Endezeichen"
91
        uart_putc(*s);   //schreibt die einzelne Ziffer auf die UART
92
        s++;//geht zur (falls vorhandenen) zweiten Ziffer des HEX-Wertes
93
    }
94
}
95
96
//Interrupt Service Routine für AD Wandlung
97
ISR(TIMER0_OVF_vect) 
98
{
99
  ADIteiler++;   //zählvariable um den interrupt auf jede ms zu takten
100
  if(ADIteiler >= 60)    //Zählschleife für alle ms 60
101
  {
102
    ADIteiler=0;    //Teiler wieder auf 0 setzen
103
    uint16_t result = ADC_Reading(0);//Reading the Analog voltage PinC 0
104
    uart_puts("\r\n");
105
    utoa( result, s, 2 ); //HEX-Zahlen in Ascii umwandeln
106
    uart_puts( s );  
107
    uart_putc('$');
108
  
109
    DDRB = 0xFF;    //define as output
110
    PORTB = (result / 10) + 1;
111
  }
112
}
113
int main(void)
114
{
115
  initall();
116
  
117
  sei();                //Globale Interrupts freischalten
118
  while (1) {;}
119
}

von Penie L. (peniely)


Lesenswert?

danke an alle es funktioniert,
war einen fehler bei mir
gruss

von Lutz (Gast)


Lesenswert?

> ich möchte mit einem ATmega8 und 4 MHz Quarz einen Timer realisieren ...
> systemtakt int. 4 mhz weil mit ext. geht nicht

Wie jetzt ???

Wenn Du wirklich ohne Quarz arbeitest wird es bei Sonnenschein wieder 
nicht mehr funktionieren ...

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.