Forum: Compiler & IDEs Interrupt Problem bei Atmega48


von Atmega 1. (atmega168)


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)
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include <avr/signal.h>
5
#include <avr/pgmspace.h>
6
#include <util/delay.h>
7
#include "uart.h"
8
9
10
/* define CPU frequency in Mhz here if not defined in Makefile */
11
#ifndef F_CPU
12
#define F_CPU 20000000UL
13
#endif
14
15
/* 9600 baud */
16
#define UART_BAUD_RATE      9600  
17
18
uint8_t empf;
19
volatile uint8_t var0 = 105;
20
volatile uint8_t var1 = 110;
21
22
ISR(TIMER2_COMPA_vect)
23
{
24
    //hier kommt der Code zum steuern meiner Motoren//
25
    //Das Interrupt wird alle 0,00001275 sec ausgelöst//
26
27
28
    //kommt das uart_getc() hier?//
29
    empf = uart_getc();
30
           
31
    if(empf == 'q')
32
    {
33
        var0+=2;
34
35
    }
36
}
37
void init_timer()
38
{
39
  DDRD |= (1<<PD6);
40
  DDRD |= (1<<PD5);
41
42
  TIMSK2 |= (1<<OCIE2A); //Interrupt aktivieren
43
  OCR2A = 0;
44
  TCCR2B |= (1<<CS20);   //Prescaler von 1
45
  TCNT1 = 0;
46
47
  cli();
48
  sei();
49
}
50
51
int main(void)
52
{
53
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));  
54
  init_timer();
55
        while(1)
56
        {
57
           //kommt das uart_getc() hier?//
58
           empf = uart_getc();
59
           
60
           if(empf == 'q')
61
           {
62
              var0+=2;
63
64
           }
65
        }
66
67
}

Bitte helft mir weiter

MfG Atmega168

von Oliver (Gast)


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

von Johannes M. (johnny-m)


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...

von Atmega 1. (atmega168)


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?
1
uint8_t empf;
2
3
int main()
4
{
5
     uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));  
6
     init_timer();
7
8
     while(1)
9
     {
10
         cli();
11
         empf = uart_getc();
12
         if(empf...)
13
         {
14
            ...
15
         }
16
         sei();
17
}

Hätte auch eine alternative Lösung dazu. Was wäre wenn ich folgende 
Funktion für uart_getc() verwenden würde:
1
uint8_t uart_getC()
2
{
3
     if (!(UCSR0A & (1<<RXC0)))
4
     {
5
        return 0;
6
     }
7
     else
8
     {
9
         return UDR0;
10
     }
11
}
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

von Oliver (Gast)


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

von Atmega 1. (atmega168)


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

von Atmega 1. (atmega168)


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

von Falk B. (falk)


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.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include <avr/signal.h>
5
#include <avr/pgmspace.h>
6
#include <util/delay.h>
7
#include "uart.h"
8
9
10
/* define CPU frequency in Mhz here if not defined in Makefile */
11
#ifndef F_CPU
12
#define F_CPU 20000000UL
13
#endif
14
15
/* 9600 baud */
16
#define UART_BAUD_RATE      9600  
17
18
uint8_t empf;
19
volatile uint8_t var0 = 105;
20
volatile uint8_t var1 = 110;
21
22
ISR(TIMER2_COMPA_vect)
23
{
24
    //hier kommt der Code zum steuern meiner Motoren//
25
    //Das Interrupt wird alle 12,75 us ausgelöst//
26
}
27
void init_timer()
28
{
29
  DDRD |= (1<<PD6);
30
  DDRD |= (1<<PD5);
31
32
  TIMSK2 |= (1<<OCIE2A); //Interrupt aktivieren
33
  OCR2A = 0;
34
  TCCR2B |= (1<<CS20);   //Prescaler von 1
35
  TCNT1 = 0;
36
}
37
38
int main(void)
39
{
40
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));  
41
  init_timer();
42
  sei();
43
        while(1)
44
        {
45
           empf = uart_getc();         
46
           if(empf == 'q')
47
           {
48
              var0+=2;
49
50
           }
51
        }
52
53
}

MFG
Falk

von Atmega 1. (atmega168)


Lesenswert?

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

Gibs noch eine andere Lösung???

MfG Atmega168

von Falk B. (falk)


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

von Atmega 1. (atmega168)


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:
1
int main(void)
2
{
3
  uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));  
4
  init_timer();
5
6
  while(1)
7
  {
8
        
9
     if(bit_is_set(PINC, PC2))
10
     {
11
         var0+=2;   
12
     }
13
14
     if(bit_is_set(PINC, PC1))
15
     {
16
         var0-=2;
17
     }
18
  }
19
20
}

MfG Atmega168

von Falk B. (falk)


Lesenswert?

Auch auf die Gefahr hin mich zu wiederhoen.

Poste mal VOLLSTÄNDIGEN Code als Anhang!

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.