UART-Interrupt Problem

Gast #2182597
Lesenswert?

Hallo Leute!

Mit folgendem Code möchte ich alle LEDs meines STK500s einschalten, wenn 
irgendein Zeichen vom UART empfangen wird.
Wenn nichts empfangen wird, soll die Endlosschleife ein Lauflicht 
(später Schrittmotor) leuchten lassen.

Ich habe nun versucht, mit einem Terminal ein Zeichen zum Board zu 
schicken, leider reagiert es nicht (springt nicht in die 
Interrupt-Routine), sodass ständig nur das Lauflicht leuchtet.


Wisst ihr wo mein Fehler ist?

Hier ist mein Code:

1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <inttypes.h>
4
#include <avr/interrupt.h>
5

6

7

8
#define DELAY 50
9

10
void initialisiere_UART(void)
11
{
12
  UCSRB |= (1<<RXEN);                         // Senden/empfangen aktivieren
13
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  
14

15
  UBRRH = 00;                             // Baudrate einstellen 9600 bei 8 MHz
16
  UBRRL = 51;
17
}
18

19

20
ISR (USART_RXC_vect) 
21
{
22
  
23
  PORTA =~(0xFF);
24
  _delay_ms(500);
25
  PORTA =~(0x00);  
26
  _delay_ms(500);    
27
}
28

29

30

31

32
void main (void)
33
{
34
  DDRA = 0xFF;
35

36
  PORTA = ~0x01;
37

38
  initialisiere_UART();
39
   
40

41
  sei();                  // Interrupt scharf schalten
42

43

44
  while(1)
45
  {
46
    PORTA = ~(0b00000001);
47
    _delay_ms(DELAY);
48
    PORTA = ~(0b00000011);
49
    _delay_ms(DELAY);
50
    PORTA = ~(0b00000010);
51
    _delay_ms(DELAY);
52
    PORTA = ~(0b00000110);
53
    _delay_ms(DELAY);
54
    PORTA = ~(0b00000100);
55
    _delay_ms(DELAY);
56
    PORTA = ~(0b00001100);
57
    _delay_ms(DELAY);
58
    PORTA = ~(0b00001000);
59
    _delay_ms(DELAY);
60
    PORTA = ~(0b00001001);
61
    _delay_ms(DELAY);
62
  }
63

64
}
Gast #2182620
Lesenswert?

Ok, habe es herausgefunden, das Flag "RXCIE" im UCSRA-Register war nicht 
gesetzt, welches für Interrupts über UART zuständig ist.


Nun eine weitere Frage:

Mein Controller empfängt nun das Zeichen, springt auch in die 
Interrupt-Routine, leider kommt er aber aus der Interrupt-Routine nicht 
mehr raus.
(es blinken nun alle LEDs ( ~(0xFF) und ~(0x00)   ) )...

Wisst ihr woran das liegen könnte?
Persönliche Seite #2182648
Lesenswert?

Aus dem Datenblatt (hier zu atmegaXX8):

When the Receive Complete Interrupt Enable (RXCIEn) in UCSRnB is set, 
the USART Receive Complete interrupt will be executed as long as the 
RXCn Flag is set (provided that global inter- rupts are enabled). When 
interrupt-driven data reception is used, the receive complete routine 
must read the received data from UDRn in order to clear the RXCn Flag, 
otherwise a new interrupt will occur once the interrupt routine 
terminates.

Das RXC Flag wird gelöscht, indem die Daten abgeholt werden. Wie das in 
der Hardware verdrahtet ist - irgendwas mit FlipFlop.
Gast #2182654
Lesenswert?

So...das Programm läuft jetzt soweit, allerdings habe ich noch zwei 
Problemchen:


Wenn ich die Taste "1", "2" oder "3" im Terminal drücke, dann ändert der 
Controller die Delaytime leider nicht.

Außerdem ist mein Flash jetzt schon 25 % voll! Woran liegt das?

Hier der Code:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <inttypes.h>
4
#include <avr/interrupt.h>
5

6

7

8
unsigned int DelayTime = 400;
9

10
void initialisiere_UART(void)
11
{
12
  UCSRB |= (1<<RXEN) | (1<<RXCIE);                         // Senden/empfangen aktivieren
13
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1) ;  
14

15
  UBRRH = 00;                             // Baudrate einstellen 9600 bei 8 MHz
16
  UBRRL = 51;
17
}
18

19

20
ISR (USART_RXC_vect) 
21
{
22
  char empfangenesZeichen = UDR;
23

24
  switch(empfangenesZeichen)
25
  {
26
    case '1':  DelayTime = 50;
27
          break;
28

29
    case '2':  DelayTime = 100;
30
          break;
31

32
    case '3':  DelayTime = 200;
33
          break;
34
  }
35
}
36

37

38
  
39

40
void main (void)
41
{
42

43
    DDRA = 0xFF;
44

45
  initialisiere_UART();
46
   
47

48
  sei();                  // Interrupt scharf schalten
49

50

51
  while(1)
52
  {
53
    PORTA = ~(0b00000001);
54
    _delay_ms(DelayTime);
55
    PORTA = ~(0b00000011);
56
    _delay_ms(DelayTime);
57
    PORTA = ~(0b00000010);
58
    _delay_ms(DelayTime);
59
    PORTA = ~(0b00000110);
60
    _delay_ms(DelayTime);
61
    PORTA = ~(0b00000100);
62
    _delay_ms(DelayTime);
63
    PORTA = ~(0b00001100);
64
    _delay_ms(DelayTime);
65
    PORTA = ~(0b00001000);
66
    _delay_ms(DelayTime);
67
    PORTA = ~(0b00001001);
68
    _delay_ms(DelayTime);
69
  }
70

71
}
#2182660
Lesenswert?

@  Ralf (Gast)

>Wenn ich die Taste "1", "2" oder "3" im Terminal drücke, dann ändert der
>Controller die Delaytime leider nicht.

Weil deine Baudrate nicht stimmt, wenn man den RC-Oszillator nutzt.

http://www.mikrocontroller.net/articles/AVR_Checkliste#UART.2FUSART

>Außerdem ist mein Flash jetzt schon 25 % voll! Woran liegt das?

Dein _delay_ms() hätte gern konstante Argumente.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29

MFG
Falk
#2183176
Lesenswert?

@  Ralf (Gast)

>Wie könnte man das mit der Delaytime machen, damit die Werte "konstant"
>werden bzw. der Flashspeicher eben nicht zu sehr belegt wird?!

Glotzn ofmachn, geeene Fedbemm fressen und orweidn!
1
void long_delay(uint16_t ms)
2
{
3
    for(; ms>0; ms--) _delay_ms(1);
4
}
Gast #2183611
Lesenswert?

Soo...Heute wollte ich dann mal wieder eine kleine Änderung am Code 
vornehmen, leider ohne Erfolg.


Da ich gerne mittels Eingabe über das Terminal von "e" und "a" (ein, 
aus) den Motor einschalten und ausschalten möchte, habe ich folgendes 
gecodet.

Leider funktioniert es aber nicht.

Anfangs soll der Motor stillstehen. Sobald ein "e" gesendet wird, soll 
der Motor eingeschaltet werden. Sobald (währenddessen) ein "a" geschickt 
wird, soll der Motor ausgeschaltet werden.
Wenn man das Board einschaltet, soll der Motor stillstehen und der 
Controller auf einen Befehl warten
.
Wo liegt mein Problem?
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <inttypes.h>
4
#include <avr/interrupt.h>
5

6

7

8
volatile int DelayTime = 20;
9

10
void initialisiere_UART(void)
11
{
12
  UCSRB |= (1<<RXEN) | (1<<RXCIE);                         // Senden/empfangen aktivieren
13
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1) ;  
14

15
  UBRRH = 00;                             // Baudrate einstellen 9600 bei 8 MHz
16
  UBRRL = 51;
17
}
18

19

20
ISR (USART_RXC_vect) 
21
{
22
  char empfangenesZeichen = UDR;
23

24
  switch(empfangenesZeichen)
25
  {
26
    case 'a':  motor_ausschalten();
27
          break;
28

29
    case 'e':  motor_einschalten();
30
          break;
31
    
32
    case '1':  DelayTime = 5;
33
          break;
34

35
    case '2':  DelayTime = 10;
36
          break;
37

38
    case '3':  DelayTime = 30;
39
          break;
40
  }
41
}
42

43

44

45
void motor_einschalten(void)
46
{
47
  sei();
48
  
49
  while(1)
50
  {
51
    PORTA = ~(0b00000001);
52
    _delay_ms(DelayTime);
53
    PORTA = ~(0b00000011);
54
    _delay_ms(DelayTime);
55
    PORTA = ~(0b00000010);
56
    _delay_ms(DelayTime);
57
    PORTA = ~(0b00000110);
58
    _delay_ms(DelayTime);
59
    PORTA = ~(0b00000100);
60
    _delay_ms(DelayTime);
61
    PORTA = ~(0b00001100);
62
    _delay_ms(DelayTime);
63
    PORTA = ~(0b00001000);
64
    _delay_ms(DelayTime);
65
    PORTA = ~(0b00001001);
66
    _delay_ms(DelayTime);
67
  }
68
    
69
}
70
  
71

72
void motor_ausschalten(void)
73
{
74
  
75
  sei();
76
  PORTA = ~(0x00);
77

78
}
79

80

81

82

83
void main (void)
84
{
85
    DDRA = 0xFF;
86

87
  initialisiere_UART();
88
   
89
    sei();                  // Interrupt scharf schalten
90
  
91

92
}
Gast #2183630
Lesenswert?

das mit der delay_ms und den Variablen habe ich bereits gelesen und 
werde es auch ändern.

Aber was ist das Problem, dass in der main keine Endlosschleife ist?
Ich schalte doch den Interrupt scharf (sei()), sodass ein Interrupt 
kommen kann wann und wie er will, oder sehe ich das falsch?


Was stimmt in meinem Code denn (außer der delay_ms) nicht , dass es 
nicht so funktioniert wie ich es gerne hätte? ;)


Gruß
Ralf
#2183644
Lesenswert?

Ralf schrieb:
> Aber was ist das Problem, dass in der main keine Endlosschleife ist?
> Ich schalte doch den Interrupt scharf (sei()), sodass ein Interrupt
> kommen kann wann und wie er will, oder sehe ich das falsch?

Aber ohne die Endlosschleife verlässt du main() (beendest also quasi das 
Programm), und der exit-Code der AVR-Libc beinhaltet ein cli(), schaltet 
also deine Interrupts gleich wieder ab.
#2183750
Lesenswert?

@Ralf

>> Und wie ist nun die Lösung zu meinem Problem?
>
> Ach komm, das kannst du auch alleine lösen.
> Ich wiederhole das Problem nochmal:
> "Du hast keine Endlosschleife in main()."
>
> Zusätzlicher Hinweis: eine Schleife kann auch leer sein.

Allerdings will man das eigentlich gar nicht.

> Ach komm, das kannst du auch alleine lösen.

Wenns nur das wäre. Der ganze Programmaufbau ist Mist.
Die Arbeit kommt in die Hauptschleife in main (oder später mal in eine 
Interrupt Funktion die von einem Timer angetrieben wird). Der UART 
Interrupt wertet meinetwegen das empfangene Zeichen aus und stellt ein 
paar globale Variablen (Flags) so, dass in der Hauptschleife das 
Richtige getan wird.

Delays in einer ISR sind ein absolutes NoNo. (ausser vielleicht ganz 
kurze delays im µs Bereich). Und eine Endlosschleife in einer ISR, die 
dann wieder durch einen anderen Interrupt unterbrochen wird ... dafür 
sollte man dir den µC gleich wieder wegnehmen :-)
Gast #2183762
Lesenswert?

Dass dieses Projekt wohl eines meiner Ersten mit Mikrocontrollern ist, 
solltet ihr vielleicht wissen.

Dass ihr den Controller in- und auswändig kennt, das glaube ich euch 
gerne und schätze es sehr.
Doch immer vom hohen Ross auf die "Kleinen" abfällig zu kommentieren 
finde ich nicht unbedingt angebracht a la:
> Wenns nur das wäre. Der ganze Programmaufbau ist Mist.

Traurig. Wirklich traurig.
#2183769
Lesenswert?

Ralf schrieb:
> Dass dieses Projekt wohl eines meiner Ersten mit Mikrocontrollern ist,
> solltet ihr vielleicht wissen.

Ist ok. Jeder muss mal anfangen

> Dass ihr den Controller in- und auswändig kennt, das glaube ich euch
> gerne und schätze es sehr.

Das hat mit 'dem Controller' an sich nichts zu tun.

> Doch immer vom hohen Ross auf die "Kleinen" abfällig zu kommentieren
> finde ich nicht unbedingt angebracht a la:
>> Wenns nur das wäre. Der ganze Programmaufbau ist Mist.
>
> Traurig. Wirklich traurig.

Aber die Wahrheit :-)
Und ob es dir gefällt oder nicht, deinem µC ist es völlig wurscht ob es 
dein erster Kontakt mit ihm ist oder nicht. Der schlägt erbarmungslos 
zu. In deinem Fall wird nach dem (geschätzten) 300sten hintereinander 
empfangenen Zeichen 'e' Schluss mit lustig sein und dein µC wird 
erbarmungslos abstürzen oder eventuell noch schlimmere Sachen machen.

Also.
Der Grundaufbau 'jedes' µC-Programmes sieht so aus
1
int main()
2
{
3
   Hardware initialisieren und einstellen
4

5
   eventuell sei(), wenn mit Interrupts gearbeitet wird
6

7
   while( 1 ) {
8
 
9
      Hier drinn wird die eigentliche Arbeit gemacht
10

11
   }
12
}

Wenn es Interrupts gibt, dann machen deren Funktionen relativ wenig 
Arbeit selber. Wenn sie auf den Rest des Programmes einen Einfluss 
haben, dann maximal so, dass sie in globalen Variablen Kennwerte 
hinterlassen, die den Programmteil in main() der innerhalb der 
Hauptschleife steht, dazu veranlassen das Richtige zu tun.
1
volatile uint8_t Irgendwas;
2

3
ISR( ... )
4
{
5
  ...
6
  Irgendwas = ...
7
}
8

9
int main()
10
{
11
   Hardware initialisieren und einstellen
12

13
   sei();
14

15
   while( 1 ) {
16
 
17
      Hier drinn wird die eigentliche Arbeit gemacht
18

19
      if( Irgendwas == ... ) {
20
        mach die spezielle Arbeit, die von der ISR angefordert wurde
21
      }
22

23
   }
24
}

Das ist dein grundsätzlicher Programmaufbau. Es gibt auch Ausnahmen und 
es gibt auch Variationen dazu. Aber die nächsten 20 bis 30 Projekte 
wirst du praktisch immer gut fahren, wenn du dich an diesen 
Programmaufbau hältst.


AVR-GCC-Tutorial
#2183789
Lesenswert?

1
uint8_t MotorStep[] = { 0b00000001, 0b00000011, 0b00000010, 0b00000110,
2
                        0b00000100, 0b00001100, 0b00001000, 0b00001001 };
3
uint8_t curStep;
4

5
volatile uint8_t DelayTime;
6
volatile uint8_t MotorRunning;
7

8
void delay( uint8_t ms )
9
{
10
  uint8_t i;
11
  for( i = 0; i < ms; ++i )
12
    _delay_ms( 1 );
13
}
14

15
void initialisiere_UART(void)
16
{
17
  UCSRB |= (1<<RXEN);                         // Senden/empfangen aktivieren
18
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  
19

20
  UBRRH = 00;                             // Baudrate einstellen 9600 bei 8 MHz
21
  UBRRL = 51;                             // diese Konstanten besser vom Compiler
22
                                          // berechnen lassen !
23
}
24

25
ISR (USART_RXC_vect) 
26
{
27
  char empfangenesZeichen = UDR;
28

29
  switch(empfangenesZeichen)
30
  {
31
    case 'a':  MotorRunning = 0;
32
          break;
33

34
    case 'e':  MotorRunning = 1;
35
          break;
36
    
37
    case '1':  DelayTime = 5;
38
          break;
39

40
    case '2':  DelayTime = 10;
41
          break;
42

43
    case '3':  DelayTime = 30;
44
          break;
45
  }
46
}
47

48
void main (void)
49
{
50
  DDRA = 0xFF;
51

52
  MotorRunning = 0;
53
  DelayTime = 30;
54
  curStep = 0;
55

56
  initialisiere_UART();
57
   
58
  sei();                  // Interrupt scharf schalten
59

60
  while( 1 ) {
61
    if( MotorRunning ) {
62
      PORTA = ~( MotorStep[curStep] );
63
      curStep++;
64
      if( curStep == 8 )
65
        curStep = 0;
66
      delay( DelayTime );
67
    }
68
  } 
69
}
#2183815
Lesenswert?

Steffen H. schrieb:
> Ach ja, wie würde es denn aussehen wenn "MotorRunning" nicht gleich ein
> ganzes Byte einnehmen würde sondern wirklich nur ein Flag (Bit) in einem
> Byte?

Grundsätzlich ist die kleinste Einheit, die du in C ansprechen kannst 
immer ein komplettes Byte. Willst du auf einzelne Bits runter, dann 
musst du mittels | bzw & die entsprechende Bitposition ausmaskieren.

>  Mal angenommen das Byte sollte "Control" heißen und das Flag
> "MotorRunning" ?

OK. Für die Bitposition benutze ich ein #define, damit ich mir keine 
Zahlen merken muss :-) Bin ja auch schon älter und ausserdem wird so der 
Code schon mal einen ersten Schritt in Richtung selbstdokumentierend 
nehmen.
1
#define MOTOR_RUNNING 0   // Bit 0 in Control
2
volatile uint8_t  Control;
3

4
ISR( ... )
5
{
6

7
   ....
8

9
   switch 'a':
10
     Control &= ~( 1 << MOTOR_RUNNING );    // Bit löschen
11
     break;
12

13
   switch 'b'
14
     Control |= ( 1 << MOTOR_RUNNING );     // Bit setzen
15
     break;
16

17
   ....
18
}
19

20

21
....
22

23
int main()
24
{
25
  ....
26
  while( 1 ) {
27

28
    if( Control & ( 1 << MOTOR_RUNNING ) ) {
29
      ...
30
    }
31
  }
32
}

Ganz normale Bit-Setz, Bit-Lösch und Bit-Abfrage Operationen, wie sie in 
jedem Programm zu Hauf vorkommen. Nur war bei dir wahrscheinlich Bit 
setzen bzw. Bit löschen meistens auf einen Port bezogen um damit einen 
Portpin zu schalten. Aber die Syntax ist ja nicht auf Ports beschränkt. 
Die Operationen setzen bzw. löschen bzw. fragen ein Bit ab. Wo das 
zugehörige Byte herkommt interessiert ja die Operationen nicht.
#2183825
Lesenswert?

Danke Karl Heinz.
Mir ist nämlich aufgefallen das in C schnell mal ein ganzes Byte für nur 
ein Bit herhalten muss. Ich finde die Lösung über nur einem Bit besser, 
da man nicht so viel Resourcen verschwendet, natürlich nur wenn noch ein 
paar Flags mehr in das Byte "Control" kommen. Also geht es in C genauso 
einfach wie in asm.

Steffen
#2183828
Lesenswert?

Das ist die Version, die du als C-Neuling machen wirst.

Wenn deine C-Kentnisse umfangreicher sind (werden) (Lehrbücher lesen und 
studieren!), dann wirst du das anders schreiben und dem Compiler mehr 
Arbeit aufhalsen :-)
1
struct control_ {
2
  uint8_t motorRunning : 1;
3
  uint8_t motorDirection : 1;
4
}
5

6
volatile struct control_ Control;
7

8
ISR( ... )
9
{
10

11
   ....
12

13
   switch 'a':
14
     Control.motorRunning = 1;
15
     break;
16

17
   switch 'e'_
18
     Control.motorRunning = 0;
19
     break;
20

21
   switch '+':
22
     Control.motorDirection = 0;
23
     break;
24

25
   switch '-':
26
     Control.motorDirection = 1;
27

28
   ....
29
}
30

31
....
32

33
// zur Vereinfachung der Schreibweise wieder ein paar Makros
34
#define MOTOR_RUNNING    Control.motorRunnnig
35
#define MOTOR_DIRECTION  Control.motorDirection
36

37
int main()
38
{
39
  ....
40
  while( 1 ) {
41

42
    if( MOTOR_RUNNING ) {
43
      if( MOTOR_DIRECTION ) {
44
        ...
45
      }
46
      else {
47
        ...
48
      }
49
    }
50
  }
51
}
#2183833
Lesenswert?

Steffen H. schrieb:
> Danke Karl Heinz.
> Mir ist nämlich aufgefallen das in C schnell mal ein ganzes Byte für nur
> ein Bit herhalten muss. Ich finde die Lösung über nur einem Bit besser,
> da man nicht so viel Resourcen verschwendet, natürlich nur wenn noch ein
> paar Flags mehr in das Byte "Control" kommen. Also geht es in C genauso
> einfach wie in asm.

Kommt drauf an.
Wenn du die Bytes im SRAM hast, dann ist es kein Problem dafür jeweils 
ein komplettes Byte zu benutzen. Wenn du den Platz nicht hast, dann muss 
man wohl oder übel zusammenlegen.

die Überlegung
* du bekommst für nicht benutztes SRAM kein Geld zurück
* Bits auszumaskieren bringt zwar ein paar Bytes SRAM, kostet
  aber immer etwas Programmcode. Umsonst ist nun mal nur der Tod
#2183875
Lesenswert?

Karl Heinz Buchegger schrieb:
>* du bekommst für nicht benutztes SRAM kein Geld zurück
Da hast du Recht (-:

Aber diese Sache mit einem Struct anzugehen finde ich ja toll:
1
struct control_ {
2
  uint8_t motorRunning : 1;
3
  uint8_t motorDirection : 1;
4
}
Das sieht dann auch sehr übersichtlich aus.
1
   switch 'a':
2
     Control.motorRunning = 1;
3
     break;
4

5
   switch 'e'_
6
     Control.motorRunning = 0;
7
     break;
8

9
   switch '+':
10
     Control.motorDirection = 0;
11
     break;
12

13
   switch '-':
14
     Control.motorDirection = 1;
 -> Und wieder was dazu gelernt..

Dank an dieses tolle Forum! Hier lernt mann mehr als in manch einem 
Studium!

Weiter so..
Gast #2183912
Lesenswert?

1
switch 'a':
2
     Control.motorRunning = 1;
3
     break;
4

5
   switch 'e'_
6
     Control.motorRunning = 0;
7
     break;
8

9
   switch '+':
10
     Control.motorDirection = 0;
11
     break;
12

13
   switch '-':
14
     Control.motorDirection = 1;

wie hast Du das mit den switch anweisungen hinbekommen? :-)
Gast #2184478
Lesenswert?

Hallo Karl-Heinz!


Vielen Dank für deinen umfangreichen Lösungsansatz, finde ich wiederum 
super, dass du mir so geholfen hast.

Ich werde den Programmablauf mal so in etwas übernehmen und testen.

Ob es für mein Referat allerdings nicht etwas zu umfangreich ist (Leute, 
die noch NIE etwas von einem Mikrocontroller gehört haben), wage ich zu 
bezweifeln :-/
Denn der Code selbst würde wohl schon die Zeit des Referats (10-12 
Minuten) sprengen.



Gruß
Ralf

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren