www.mikrocontroller.net

Forum: Compiler & IDEs Interrupt Problem bei Atmega48


Autor: Atmega 168 (atmega168)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute, ich bins mal wieder. Hab folgendes Problem. Möchte gerne 
über einen Mikrokontroller meine Servomotoren ansteuern. Hab mir nun 
einen C-Code geschrieben mit dem ich mehrere Sevo`s ansteuern kann. 
Dieser funktioniert einwandfrei. Habe dafür nur einen Timer des Atmega 
48 verwendet, musst allerdings auch ein Interrupt verwenden. Nun möchte 
ich zusätzlich aber die einzelnen Motorenstellungen über meinen Computer 
steuern und dies geht nun mal nur über Uart. Ich möchte beispielsweise 
Motor1 2 Schritte nach rechts bewegen. Ich schicke nun über mein 
Terminalprogramm (Terminal_G-A-System) einen Char an den µC. Und hier 
sind wir dann auch schon bei meinem Problem:

Mein C-Code auf meinem µC löst alle 0,00001275 sec einen Interrupt aus. 
Wo muss ich nun den Char den ich vom Pc bekomme auswerten? Direkt im 
Interrupt? Soll ich mit cli(); und sei() arbeiten? Schickt mir doch 
bitte ein paar vorschläge. Ich poste euch hier mal meinen C-Code:(var0 
und var1 sind die Vergleichswerte die ich brauche um PD5 oder PD6 auf 
high zu setzen)
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "uart.h"


/* define CPU frequency in Mhz here if not defined in Makefile */
#ifndef F_CPU
#define F_CPU 20000000UL
#endif

/* 9600 baud */
#define UART_BAUD_RATE      9600  

uint8_t empf;
volatile uint8_t var0 = 105;
volatile uint8_t var1 = 110;

ISR(TIMER2_COMPA_vect)
{
    //hier kommt der Code zum steuern meiner Motoren//
    //Das Interrupt wird alle 0,00001275 sec ausgelöst//


    //kommt das uart_getc() hier?//
    empf = uart_getc();
           
    if(empf == 'q')
    {
        var0+=2;

    }
}
void init_timer()
{
  DDRD |= (1<<PD6);
  DDRD |= (1<<PD5);

  TIMSK2 |= (1<<OCIE2A); //Interrupt aktivieren
  OCR2A = 0;
  TCCR2B |= (1<<CS20);   //Prescaler von 1
  TCNT1 = 0;

  cli();
  sei();
}

int main(void)
{
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));  
  init_timer();
        while(1)
        {
           //kommt das uart_getc() hier?//
           empf = uart_getc();
           
           if(empf == 'q')
           {
              var0+=2;

           }
        }

}

Bitte helft mir weiter

MfG Atmega168

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wo muss ich nun den Char den ich vom Pc bekomme auswerten?

Müssen gar nicht. Empfehlen würde ich in main(). Bei 9600 baud kommen 
die Zeichen um eine Größenordnung langsamer, als der Timerinterrupt 
läuft. Sollte deine uart_gtc gar auf das eintreffen eines Zeichen 
warten, hat die in der ISR gar nichts zu suchen.

>Soll ich mit cli(); und sei() arbeiten?

Das sollte man immer. Aber hier ist das dann nicht erforderlich. var0 
ist eine 8-bi-Variable, Zugriffe darauf sind damit nicht unterbrechbar.

Oliver

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du löst alle 12,75 µs einen Timer-Interrupt aus, in dessen Handler ein 
Zeichen von einer seriellen Schnittstelle mit 9600 Baud eingelesen 
wird? Und da wunderst Du Dich, dass nicht wirklich das rauskommt, was Du 
erwartest? Ich verstehe auch absolut nicht, wozu Du den Timer-Interrupt 
brauchst. Wenn Du irgendwas empfangen willst, musst Du sowieso warten, 
bis der Sender was gesendet hat.

Dinge, die mit serieller Eingabe/Ausgabe zu tun haben, gehören sowieso 
nicht in Interrupt-Handler. Aber als erstes solltest Du Dich hinsetzen 
und nachrechnen, wie lange es bei 9600 Bd mindestens dauert, bis ein 
Zeichen übertragen ist und Dir die Beschreibung der Funktion getc mal 
näher ansehen...

Autor: Atmega 168 (atmega168)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johannes M. wrote:
>Ich verstehe auch absolut nicht, wozu Du den Timer-Interrupt
> brauchst. Wenn Du irgendwas empfangen willst, musst Du sowieso warten,
> bis der Sender was gesendet hat.
>


Ich brauche das Timer-Interrupt zum ansteuern meiner Motoren.

Sollte ich meine uart_getc()in die main so einbauen?

uint8_t empf;

int main()
{
     uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));  
     init_timer();

     while(1)
     {
         cli();
         empf = uart_getc();
         if(empf...)
         {
            ...
         }
         sei();
}

Hätte auch eine alternative Lösung dazu. Was wäre wenn ich folgende 
Funktion für uart_getc() verwenden würde:

uint8_t uart_getC()
{
     if (!(UCSR0A & (1<<RXC0)))
     {
        return 0;
     }
     else
     {
         return UDR0;
     }
}

Ich würde mit dieser Funktion also nicht warten bis etwas im Ringbuffer 
ist sondern einfach nachschauen ob etwas drin ist. Wenn was drin ist gib 
ich das Zeichen zurück ansonsten nur 0.
Dies könnte ich dann auch prima als Alternative für uart_getc() 
verwenden. Oder???

MfG Atmega168

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lass das cli und sei weg. Dein Timer läuft mit fast 80kHz, das dürfte 
auch für einen mit 20MHz getakteten AVR eine ziemliche Last darstellen. 
Anscheinend brauchts du aber solch eine hohe Frequenz, also stör die 
nicht ohne wichtigen Grund.

Ob der Code des Hauptprogramms während des Empfangens unterbrochen wird, 
oder nicht, ist völlig egal. Wichtig ist nur, daß der Schreibzugriff auf 
die Variable var0 nicht so unterbrochen werden kann, daß fehlerhafte 
Daten enstehen. Das kann aber nur bei Datentypen passieren, die 2 oder 
mehr Byte breit sind. Und selbst da würde man(n) dann die ISR'S nur für 
den eigentlichen Zugriff auf die Variable sperrren, nicht für die ganze 
UART-Leseprozedur.

Deine neue Variante von uart_getC() ist prima, hat aber trotzdem in der 
Timer-ISR nichts zu suchen. Brauchen wirst du die, wenn in der 
Hauptschleife mal noch andere Dinge passieren sollen.

Oliver

Autor: Atmega 168 (atmega168)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oliver wrote:
> Lass das cli und sei weg. Dein Timer läuft mit fast 80kHz, das dürfte
> auch für einen mit 20MHz getakteten AVR eine ziemliche Last darstellen.
> Anscheinend brauchts du aber solch eine hohe Frequenz, also stör die
> nicht ohne wichtigen Grund.
>
> Ob der Code des Hauptprogramms während des Empfangens unterbrochen wird,
> oder nicht, ist völlig egal. Wichtig ist nur, daß der Schreibzugriff auf
> die Variable var0 nicht so unterbrochen werden kann, daß fehlerhafte
> Daten enstehen. Das kann aber nur bei Datentypen passieren, die 2 oder
> mehr Byte breit sind. Und selbst da würde man(n) dann die ISR'S nur für
> den eigentlichen Zugriff auf die Variable sperrren, nicht für die ganze
> UART-Leseprozedur.
>
> Deine neue Variante von uart_getC() ist prima, hat aber trotzdem in der
> Timer-ISR nichts zu suchen. Brauchen wirst du die, wenn in der
> Hauptschleife mal noch andere Dinge passieren sollen.
>
> Oliver

Vielen dank Oliver

Wie würdest du dann die main schreiben um ein char vom Pc auszuwerten? 
Könntest du mir bitte einen C-Code posten?

MfG Atmega168

Autor: Atmega 168 (atmega168)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab jetzt mal probiert das uart_getc() in die main zu tun, allerdings 
ruckelt mein servo da nur rum und macht nicht das was er soll.

Kann mir bitte einer behilflich sein und sagen wie ich mein Problem am 
besten lösen kann?

MfG Atmega168

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Atmega 168 (atmega168)

>Hab jetzt mal probiert das uart_getc() in die main zu tun, allerdings
>ruckelt mein servo da nur rum und macht nicht das was er soll.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "uart.h"


/* define CPU frequency in Mhz here if not defined in Makefile */
#ifndef F_CPU
#define F_CPU 20000000UL
#endif

/* 9600 baud */
#define UART_BAUD_RATE      9600  

uint8_t empf;
volatile uint8_t var0 = 105;
volatile uint8_t var1 = 110;

ISR(TIMER2_COMPA_vect)
{
    //hier kommt der Code zum steuern meiner Motoren//
    //Das Interrupt wird alle 12,75 us ausgelöst//
}
void init_timer()
{
  DDRD |= (1<<PD6);
  DDRD |= (1<<PD5);

  TIMSK2 |= (1<<OCIE2A); //Interrupt aktivieren
  OCR2A = 0;
  TCCR2B |= (1<<CS20);   //Prescaler von 1
  TCNT1 = 0;
}

int main(void)
{
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));  
  init_timer();
  sei();
        while(1)
        {
           empf = uart_getc();         
           if(empf == 'q')
           {
              var0+=2;

           }
        }

}

MFG
Falk

Autor: Atmega 168 (atmega168)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Falk, hab das aber schon probiert, da ruckelt mein Motor nur rum.

Gibs noch eine andere Lösung???

MfG Atmega168

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Atmega 168 (atmega168)

>Danke Falk, hab das aber schon probiert, da ruckelt mein Motor nur rum.

Dann hast du noch woanders einen Fehler drin. Poste mal VOLLSTÄNDIGEN 
Code als Anhang.

MFG
Falk

Autor: Atmega 168 (atmega168)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falk Brunner wrote:
> Dann hast du noch woanders einen Fehler drin. Poste mal VOLLSTÄNDIGEN
> Code als Anhang.
>
> MFG
> Falk

Das Interrupt ist ok schreib ich diesen Code in die main dann geht der 
Servo nach rechts wenn ich PINC2 auf high setze und nach links wenn ich 
PINC1 auf high setzte: Hier ist der Code:
int main(void)
{
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));  
  init_timer();

  while(1)
  {
        
     if(bit_is_set(PINC, PC2))
     {
         var0+=2;   
     }

     if(bit_is_set(PINC, PC1))
     {
         var0-=2;
     }
  }

}


MfG Atmega168

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auch auf die Gefahr hin mich zu wiederhoen.

Poste mal VOLLSTÄNDIGEN Code als Anhang!

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.