Forum: Mikrocontroller und Digitale Elektronik Amega8 Kommunikation mit Interrups


von Joe F. (joe1234)


Lesenswert?

Hallo liebe Forengemeinde,

also vorab: ich bin ein totaler Anfänger! Aber ich gebe mir Mühe.

Ich habe den Atmega8 und das STK500-Board. Dazu habe ich einen Code 
geschrieben, den ich dann auf den Atmega8 flashe.

CODE:

#include <avr/io.h>          // Register Definition fuer ATmega8
#include <avr/interrupt.h>    // Für Timer1 Overflow-Interrupt
#include <util/delay.h>      // Für Delay-Funktion

#define FOSC 8000000      // Clock Speed = Oszillator-Freq.
#define BAUD 9600        // Muss in Matlab übereinstimmen
#define MYUBRR FOSC/16/BAUD-1  // Siehe Datenblatt unter USART

/*------------------------------------------------------------------
globale Variablen
------------------------------------------------------------------*/
volatile uint16_t stepsTiH;
volatile uint16_t stepsTiL;
volatile uint16_t stepsTw;

/*------------------------------------------------------------------
Funktionen zum USART - RS232
------------------------------------------------------------------*/
// Initialisierung --> Einstellungen in Matlab MÜSSEN GLEICH SEIN
void USART_Init( unsigned int ubrr)
{
  /* Set baud rate */
  UBRRH = (unsigned char)(ubrr>>8);
  UBRRL = (unsigned char)ubrr;
  /* Enable receiver and transmitter */
  UCSRB = (1<<RXEN);
  /* Set frame format: 8data, 2stop bit */
  UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
}

// 8-bit Wort empfangen
uint16_t USART_Receive(void)
{
  /* Wait for data to be received */
  while(!(UCSRA & (1<<RXC)))
  {
  }
  /* Get and return received data from buffer */
  return UDR;
}

/*------------------------------------------------------------------
Funktionen zum Timer1
------------------------------------------------------------------*/
void Timer1_WriteTCNT1(void)
{
  unsigned char sreg;
  /* Save Global Interrupt Flag */
  sreg = SREG;
  /* Disable interrupts */
  cli();
  /* Set TCNT1 to i */
  TCNT1H = stepsTiH;     //Lade Timer1 mit Wert
  TCNT1L = stepsTiL;    //Anzahl Schritte bis zum Overflow
  /* Restore Global Interrupt Flag */
  SREG = sreg;
}

//Zeitpunkt des Aufrufs von Timer1_An() entspricht SOI
void Timer1_AN(void)
{
  Timer1_WriteTCNT1();
  PORTB = 0b00000000;
  TCCR1B = (1<<CS10);   //kein prescale verwenden
    TIMSK = (1<<TOIE1);   //Interrupt für Timer1 enable
  sei();          //globale Interrupts enable
  SREG |= (1 << SREG_I);
}

void Timer1_AUS(uint16_t anzSchritte)
{
  TCCR1B = (0<<CS10);    //Timer1 aus
  PORTB = 0b11111111;    //Ausgang PortB

  for(int i = 0; i < anzSchritte; i = i+1)
  {
    _delay_ms(10);    //Wartezeit
  }
}

ISR(TIMER1_OVF_vect)
{
  Timer1_AUS(stepsTw);  //Warteroutine
  Timer1_AN();
}

/*------------------------------------------------------------------
main
------------------------------------------------------------------*/
int main (void)
{
  USART_Init(MYUBRR);          //UART initialisieren

  DDRB = 0b11111111;          //PORTB als Ausgang
  PORTB = 0b11111111;

  uint16_t checkSum = 0;
  uint16_t data1 = 0;
  uint16_t data2 = 0;
  uint16_t data3 = 0;

  while(1)
  {
    data1 = USART_Receive();    //Empfangene Daten1,ti(1..8)
    data2 = USART_Receive();    //Empfangene Daten2,ti(9..16)
    data3 = USART_Receive();    //Empfangene Daten3,tw(1..8)

    checkSum = data1+data2+data3;  //Summe der 3 Daten(Urzustand)

    if(checkSum == 0)
    {
      TCCR1B = (0 << CS10);    //Timer1 aus
      PORTB = 0b11111111;      //Ausgang PortB
    }
    else
    {
      stepsTiH = data1;      //Definieren stepsTiH
      stepsTiL = data2;      //Definieren stepsTiL
      stepsTw = data3;      //Definieren stepsTw
      Timer1_AN();        //Starten von Timer1
    }
  }
}

Kurze Erläuterung:
Ich übertrage 3 Variablen mit Matlab. Diese werden in data1,data2,data3 
gespeichert. Die Variablen (data...) benötige ich für den Timer1_AN().
Im Grunde genommen liegt eine Spannung an, die für einen "Augenblick" 
(hier ist die Eingabe von data1 und data2 zuständig) unterbrochen wird. 
data3 ist eine Art "Wartezeit" bis zum nächsten Start.

Nun habe ich auf dieser Seite
http://www.mikrocontroller.net/articles/Interrupt
gesehen, dass man neben senden auch empfangen kann. Hier wird sogar eine 
LED zum Morsen von Daten verwendet. Leider verstehe ich den Code nicht 
wirklich. Vor allem empfängt meine Funktion "uint16_t 
USART_Receive(void)" ein 8 Bit Wort, in dem Beispielcode ist aber nur 
von einem String die Rede.

Meine Frage nun: Wie kann ich die 2 Sachen kombinieren?

Endsituation sollte sein:
Ich möchte Daten übertragen, die übertragenen Daten sollen 
zurückgesendet werden, am besten mit dieser Morseled, und dann sollten 
auch noch meine Timer1_AN() Funktion anlaufen.

Gruß Joe

von Karl H. (kbuchegg)


Lesenswert?

Johannes F. schrieb:

> Nun habe ich auf dieser Seite
> http://www.mikrocontroller.net/articles/Interrupt
> gesehen, dass man neben senden auch empfangen kann. Hier wird sogar eine
> LED zum Morsen von Daten verwendet. Leider verstehe ich den Code nicht
> wirklich. Vor allem empfängt meine Funktion "uint16_t
> USART_Receive(void)" ein 8 Bit Wort, in dem Beispielcode ist aber nur
> von einem String die Rede.
>
> Meine Frage nun: Wie kann ich die 2 Sachen kombinieren?

Hmm.
Irgendwie verstehe ich die Fragestellung nicht wirklich.
Denn auch die Sende-Funktionalität für Strings stützt sich letzten Endes 
auch auf eine Funktion die 1 Byte sendet. Du hast einzelne Bytes und 
eine Funktion die 1 Byte senden kann. Passt doch perfekt!

Am besten siehst du dir den Artikel aus dem avr-gcc-tutorial näher an:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART

Da wird dir UART senden auf die einfachere Variante (ohne Interrupt) 
näher gebracht.

> Endsituation sollte sein:
> Ich möchte Daten übertragen, die übertragenen Daten sollen
> zurückgesendet werden, am besten mit dieser Morseled,

zurückgesendet wohin?
Und wozu brauchst du dann die Morseled?
Diese LED ist doch nur 1 Beispiel, was man mit den Daten machen kann hat 
aber mit dem eigentlichen Thema UART senden/empfangen nichts zu tun.

von joe1234 (Gast)


Lesenswert?

Erstmal danke für die Antwort!

Vorab:
MorseLED sollte nur auf mein Board, wenn es möglich ist, also als ein 
kleiner Bonus.

Wie man aus meinem Programm sieht, habe ich das Senden und Empfangen 
OHNE Interrupt erfolgreich hinbekommen. Problem ist halt, dass ich in 
der while- Schleife ständig im Zustand "data1 = USART_Receive();" mich 
befinde, erst wenn was gesendet wird, dann geht es weiter. Dies wollte 
ich nun umgehen, so dass ich mich ebend NICHT in diesem Zustand befinde. 
Ich SOLL erst in diesen Zustand, wenn auch wirklich ein Information 
geschickt wird, also ein Interrupt ausgelöst wird.

Frage:
Welche Funktion im Beispielcode entspricht meiner "USART_Receive()" 
Funktion?

Gruß Joe

von Karl H. (kbuchegg)


Lesenswert?

joe1234 schrieb:

> Wie man aus meinem Programm sieht, habe ich das Senden und Empfangen
> OHNE Interrupt erfolgreich hinbekommen. Problem ist halt, dass ich in
> der while- Schleife ständig im Zustand "data1 = USART_Receive();" mich
> befinde, erst wenn was gesendet wird, dann geht es weiter.

Dann ändere das doch :-)
Deine USART_Receive muss ja nicht in der while Schleife hängen, wenn 
nichts da ist. Du bist der Programmierer. Du bestimmst, was passieren 
soll. Die Funktion kann genau so gut auch zum Aufrufer zurückkehren und 
mitteilen: "War nichts da!"
Und der Aufruf sagt sich dann: Na gut, wenn nichts da ist, dann mach ich 
auch nichts oder ich mach in der Zwischenzeit was anderes und schau in 
einer Hunderstelsekunde oder wann ich das nächste mal wieder Zeit habe, 
erneut nach. Das nennt sich dann Polling: in regelmässigen Zeitabständen 
nachsehen, ob es was zu tun gibt.

> Welche Funktion im Beispielcode entspricht meiner "USART_Receive()"
> Funktion?

Das kann man so nicht vergleichen
IM Beispielcode wird von der UART ein Interrupt ausgelöst, wenn ein 
Zeichen vollständig reinkommt und die ISR(USART_RXC_vect) aufgerufen. 
Die behandelt dann das eine Zeichen.

von joe1234 (Gast)


Lesenswert?

Hier habe ich nun einen Code geschrieben, leider OHNE Interrups. :-(
Problem bei meinem Code ist, dass wenn ich z.B. ti und tw auf 10 setze, 
bruacht der Atmega8 fast 1:40 min um die Operation auszuführen. Danach 
geht die LED erst wieder an. Mache ich da was flasch???

Code:
#include <avr/io.h>          // Register Definition fuer ATmega8
#include <avr/interrupt.h>    // Für Timer1 Overflow-Interrupt
#include <util/delay.h>      // Für Delay-Funktion

#define FOSC 8000000      // Clock Speed = Oszillator-Freq.
#define BAUD 9600        // Muss in Matlab übereinstimmen
#define MYUBRR FOSC/16/BAUD-1  // Siehe Datenblatt unter USART

/*------------------------------------------------------------------
globale Variablen
------------------------------------------------------------------*/
volatile uint16_t stepsTiH;
volatile uint16_t stepsTiL;
volatile uint16_t stepsTw;

/*------------------------------------------------------------------
Funktionen zum USART - RS232
------------------------------------------------------------------*/
// Initialisierung --> Einstellungen in Matlab MÜSSEN GLEICH SEIN
void USART_Init(unsigned int ubrr)
{
  /* Set baud rate */
  UBRRH = (unsigned char)(ubrr>>8);
  UBRRL = (unsigned char)ubrr;
  /* Enable receiver and transmitter */
  UCSRB = (1<<RXEN)|(1<<TXEN);
  /* Set frame format: 8data, 2stop bit */
  UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
}

// 8-bit Wort empfangen
uint16_t USART_Receive(void)
{
  /* Wait for data to be received */
  while(!(UCSRA & (1<<RXC)))
  {
  }
  /* Get and return received data from buffer */
  return UDR;
}

// 8-bit Wort senden
void USART_Transmit(unsigned char data)
{
  /* Wait for empty transmit buffer */
  while ( !( UCSRA & (1<<UDRE)) )
  {
  }
  /* Put data into buffer, sends the data */
  UDR = data;
}

/*------------------------------------------------------------------
Funktionen zum Timer1
------------------------------------------------------------------*/
void Timer1_WriteTCNT1(void)
{
  unsigned char sreg;
  /* Save Global Interrupt Flag */
  sreg = SREG;
  /* Disable interrupts */
  cli();
  /* Set TCNT1 to i */
  TCNT1H = stepsTiH;     //Lade Timer1 mit Wert
  TCNT1L = stepsTiL;    //Anzahl Schritte bis zum Overflow
  /* Restore Global Interrupt Flag */
  SREG = sreg;
}

//Zeitpunkt des Aufrufs von Timer1_An() entspricht SOI
void Timer1_AN(void)
{
  Timer1_WriteTCNT1();
  //PORTC = 0b00000000;
  PORTC = (0 << PC0);
  TCCR1B = (1<<CS10);   //kein prescale verwenden
    TIMSK = (1<<TOIE1);   //Interrupt für Timer1 enable
  sei();          //globale Interrupts enable
  SREG |= (1 << SREG_I);
}

void Timer1_AUS(uint16_t anzSchritte)
{
  TCCR1B = (0<<CS10);    //Timer1 aus
  //PORTC = 0b11111111;  //Ausgang PortC
  PORTC = (1 << PC0);

  for(int i = 0; i < anzSchritte; i++)
  {
    _delay_ms(10);    //Wartezeit midestens 10 ms
  }
}

ISR(TIMER1_OVF_vect)
{
  Timer1_AUS(stepsTw);  //Warteroutine
  Timer1_AN();
}

/*------------------------------------------------------------------
main
------------------------------------------------------------------*/
int main (void)
{
  USART_Init(MYUBRR);        //UART initialisieren

  DDRC = (1 << PC0);         // PC0 als Ausgang festlegen für Impuls
  PORTC = (1 << PC0);        // PC0 aktivieren
  DDRB = (1 << PB1);         // PB1 als Ausgang festlegen für Diode


  uint16_t checkSum = 0;
  uint16_t data1 = 0;
  uint16_t data2 = 0;
  uint16_t data3 = 0;


  while(1)
  {
    PORTB = (0 << PB1);       //PB1 aktivieren, LED leuchtet

    data1 = USART_Receive();    //Empfangene Daten1,ti(1..8)
    data2 = USART_Receive();    //Empfangene Daten2,ti(9..16)
    data3 = USART_Receive();    //Empfangene Daten3,tw(1..8)

    checkSum = data1+data2+data3;  //Summe der 3 Daten(Urzustand)


    if(checkSum == 0)
    {
      USART_Transmit(73);      //I
      USART_Transmit(78);      //N
      USART_Transmit(74);      //J
      USART_Transmit(69);      //E
      USART_Transmit(67);      //C
      USART_Transmit(84);      //T
      USART_Transmit(79);      //O
      USART_Transmit(82);      //R
      USART_Transmit(data1);    //Übertragen data1
      USART_Transmit(data2);    //Übertragen data2
      USART_Transmit(data3);    //Übertragen data3
      PORTB = (1 << PB1);     //PB1 deaktivieren, LED geht aus
      TCCR1B = (0 << CS10);    //Timer1 aus
      PORTC = (1<<PC0);      //PC0 auf 1 legen, Ausgangszustand
    }
    else
    {
      stepsTiH = data1;      //Definieren stepsTiH
      stepsTiL = data2;      //Definieren stepsTiL
      stepsTw = data3;      //Definieren stepsTw
      USART_Transmit(data1);    //Übertragen data1
      USART_Transmit(data2);    //Übertragen data2
      USART_Transmit(data3);    //Übertragen data3
      PORTB = (1 << PB1);     //PB1 deaktivieren, LED geht aus
      Timer1_AN();        //Starten von Timer1
    }
  }
}

von joe1234 (Gast)


Lesenswert?

Ich bin schier am verzweifeln...
Wenn ich die 2 Befehle mit LED aus der while- Schleife entferne, dann 
läuft alles rund. Sobald ich sie wieder benutze, zickt der Atmega8!!!

Gruß Joe

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.