mikrocontroller.net

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


Autor: Penie Lydos (peniely)
Datum:

Bewertung
0 lesenswert
nicht 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:
#define F_CPU 4000000L
#define BAUD 9600L

#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdlib.h>
#include <avr/interrupt.h>

//überprüfen ob baudrate mit CPU Frequenz zusammenpasst
#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))          //reale Baudrate

#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille

#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10))
#error Systematischer Fehler in der Baudrate grösser 1% und damit zu hoch!
#endif

void initall();
int uart_putc(unsigned char c);

//globale Variablen definieren
int ADIteiler=0;; 
int u=0; 
int v=0; 
int unwichtig;
uint8_t result=0;char s[2];


//Initialisierung
void initall()
{
  //Initialisierung UART-Schnittstelle
  UBRRH = UBRR_VAL >> 8;
  UBRRL = UBRR_VAL & 0xFF;     
  UCSRB = (1<<RXEN)|(1<<TXEN);          //UART TX einschalten
  UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);   //Asynchron 8N1
  
//Initialisierung ADC
  
  
  TCNT0 = 5;        //interrupt            
  TIMSK |= (1<<TOIE0);  //Zählregister setzen        
//damit interrupt auch ausläst wenn überlauf erreicht wird
  TCCR0 |= (1<<CS01);
  TCCR0 |= (1<<CS00);
  TCCR0 &= ~(1<<CS01);
  TCCR0 &= ~(1<<CS02);     //CPU Takt wird verwendet --> 4MHz
  TIFR |= (1<<TOV0);    //startet interrupt bei jedem überlauf 
  
  ADCSRA = (1<<ADEN);     //AD initialisierung
// Frequenzvorteiler setzen auf 128/32 und ADC aktivieren 
  ADCSRA |= (1<<ADPS2) | (1<<ADPS0);   
  ADMUX=0;                      //(ADC0)
  ADMUX |= (1<<REFS0) | (1<<REFS1);    //int. REF Voltage Reference Selection
  ADMUX |= (0<<ADLAR);    //the result is right adjusted
 
    
  //1. Dummy-Readout
  //auf Abschluss der Konvertierung warten  
  while ( ADCSRA & (1<<ADSC) ) {;} 
  unwichtig = ADCL; 
  result = ADCH;
  result = 0;
}

//Unterprogramme UART
//Schreibt einzelne Ascii-Werte auf USART Schnitstelle
int uart_putc(unsigned char c)      
{
    while (!(UCSRA & (1<<UDRE))){}    //warten bis Senden moeglich
    UDR = c;                          //sende Zeichen 
    return 0;
}

void uart_puts (char *s)
{
    while (*s)
    {   // so lange *s != '\0' also ungleich dem "String-Endezeichen"
        uart_putc(*s);     //schreibt die einzelne Ziffer auf die UART
        s++; //geht zur (falls vorhandenen) zweiten  Ziffer des HEX-Wertes
    }
}

//Interrupt Service Routine für AD Wandlung
ISR(TIMER0_OVF_vect) 
{
  ADIteiler++;               //zählvariable um den interrupt auf jede ms zu takten
  if(ADIteiler >= 60)            //Zählschleife für alle ms 60
  {
    ADIteiler=0;      //Teiler wieder auf 0 setzen
    ADCSRA |= (1<<ADSC);              //eine Wandlung "single conversion"
    while ( ADCSRA & (1<<ADSC) ) {;} //auf Abschluss der Konvertierung  //warten
    unwichtig = ADCL; //Auslesen der 2 niederwertigsten BITs -> wird  //verworfen
    result= ADCH;//Auslesen der 8 höchstwertigsten BITS-> 8 BIT-ADC Wert
    uart_puts("\r\n");
  uart_putc( itoa( result, s, 10 ) );//HEX-Zahlen in Ascii umwandeln
    uart_putc('%');
  DDRB = 0xFF;        //define as output
  PORTB = (result / 10) + 1;
  }
}
int main(void)
{
  initall();
  sei();                //Globale Interrupts freischalten
  while (1) {;}
}

Vielen Dank im Voraus.
Gruß, peniely

Autor: Ahem (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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_fu...

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.

Autor: Penie Lydos (peniely)
Datum:

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

Autor: Penie Lydos (peniely)
Datum:

Bewertung
0 lesenswert
nicht 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

#define F_CPU 4000000L
#define BAUD 9600L

#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdlib.h>
#include <avr/interrupt.h>

//überprüfen ob baudrate mit CPU Frequenz zusammenpasst
#define UBRR_VAL ((F_CPU+BAUD * 8)/(BAUD*16)-1)      //clever runde
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))          //reale Baudrate

#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000)      //Fehler in Promille

#if ((BAUD_ERROR>10)||(BAUD_ERROR<-10))
#error Systematischer Fehler in der Baudrate grösser 1% und damit zu hoch!
#endif

void initall();
int uart_putc(unsigned char c);
void uart_puts (char *s);
uint16_t ADC_Reading(uint8_t Mux_channel);

//globale Variablen definieren
int ADIteiler=0;; int u=0; int v=0; int unwichtig;
uint8_t result=0;char s[20];


//Initialisierung
void initall()
{
  //Initialisierung UART-Schnittstelle
  UBRRH = UBRR_VAL >> 8;
  UBRRL = UBRR_VAL & 0xFF;     
  UCSRB = (1<<RXEN)|(1<<TXEN);          //UART TX einschalten
  UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);   //Asynchron 8N1
  
  //interrupt
  TCNT0 = 5;         //Zählregister setzen
  TIMSK |= (1<<TOIE0);  //damit interrupt auch ausläst wenn überlauf //erreicht wird
  TCCR0 |= (1<<CS01);
  TCCR0 |= (1<<CS00);
  TCCR0 &= ~(1<<CS01);
  TCCR0 &= ~(1<<CS02);       //CPU Takt wird verwendet --> 12MHz
  TIFR |= (1<<TOV0);    //startet interrupt bei jedem überlauf   
}

uint16_t ADC_Reading(uint8_t Mux_channel) 
{
  uint8_t i;
  uint16_t result = 0;

  //Initialisierung ADC  
  ADCSRA = (1<<ADEN);       //Enables the ADC
  ADCSRA |= (1<<ADPS2) | (1<<ADPS0);   //Determine the division factor 
  ADMUX = Mux_channel;      //Select pin ADC0 using MUX
  _delay_us(40);
  ADMUX |= (1<<REFS0) | (1<<REFS1);//Int. REF Voltage Reference Selection
  ADMUX |= (0<<ADLAR);    //The result is right adjusted
  ADCSRA |= (1<<ADSC);      //Start conversion
  while(ADCSRA & (1<<ADSC));  //wait until converstion completed
  result = ADCW;    //ADCW must be read once
  result = 0;  //Otherwise the result of the next transformation is not //taken over
                          
//Now reads 4 times the similar tension and selected channel channel 
//And then calculate the average value divided by 4. Because the ADC //measeres 0..1023, And the Port however can only 0..255 show
  for(i=0; i<4; i++) 
  {    
    ADCSRA |= (1<<ADSC);  //A single conversion    
    while(ADCSRA & (1<<ADSC));  //Waiting on conversion closing    
    result += ADCW;
  }
  ADCSRA &= (0<<ADEN);      // ADC reanables  
  result /= 4;
  return result;
}//End int ADC_Read() 

//Unterprogramme UART
int uart_putc(unsigned char c)
{
    while (!(UCSRA & (1<<UDRE))){}    //warten bis Senden moeglich
    UDR = c;                          //sende Zeichen 
    return 0;
}

void uart_puts (char *s)
{
    while (*s)
    {   // so lange *s != '\0' also ungleich dem "String-Endezeichen"
        uart_putc(*s);   //schreibt die einzelne Ziffer auf die UART
        s++;//geht zur (falls vorhandenen) zweiten Ziffer des HEX-Wertes
    }
}

//Interrupt Service Routine für AD Wandlung
ISR(TIMER0_OVF_vect) 
{
  ADIteiler++;   //zählvariable um den interrupt auf jede ms zu takten
  if(ADIteiler >= 60)    //Zählschleife für alle ms 60
  {
    ADIteiler=0;    //Teiler wieder auf 0 setzen
    uint16_t result = ADC_Reading(0);//Reading the Analog voltage PinC 0
    uart_puts("\r\n");
    utoa( result, s, 2 ); //HEX-Zahlen in Ascii umwandeln
    uart_puts( s );  
    uart_putc('$');
  
    DDRB = 0xFF;    //define as output
    PORTB = (result / 10) + 1;
  }
}
int main(void)
{
  initall();
  
  sei();                //Globale Interrupts freischalten
  while (1) {;}
}

Autor: Penie Lydos (peniely)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
danke an alle es funktioniert,
war einen fehler bei mir
gruss

Autor: Lutz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.