Forum: Mikrocontroller und Digitale Elektronik Problem mit ISR und UART


von J. T. (chaoskind)


Lesenswert?

MoinMoin
ich hab mal wieder ein kleines Problem, ich bin dabei mir C ein wenig 
nahezubringen und habe dazu eine kleine Routine geschrieben(für nen 
Mega32, in CodeBlocks, im AVR-Studio meckert er immer über Fehler?!), 
die einen Servo von einem bis zum anderen Anschlag fahren lässt. Soweit 
so gut, nach einigen kleinen Fehlschlägen hats dann auch geklappt. Der 
Servo fuhr fröhlich von der einen zur anderen Seite.
Dann fiel mir aber ein, das es auf Dauer recht langweilig ist, wenn der 
das ganze immer nur in einer Geschwindigkeit machen kann, bzw ich immer 
neu flashen muss, wenn ich ne andere Geschwindigkeit haben möchte. Also 
hab ich flugs versucht, meine UART-Routine aus Assembler nach C zu 
übersetzen. Register entsprechend gesetzt, und ich konnte schonmal 
senden. Für den Empfang habe ich dann nach Rätselraten rausgefunden, 
welcher Interruptvektor der UART-Empfangsvektor ist, da im Datenblatt 
steht es ist 14, aber in iom32.h oder wie sie heißt, ist es 13(Dem Forum 
sei dank, auch hier gefunden). Nun konnte ich endlich auch empfangen, 
als ich in der ISR folgendes stehen hatte:
1
ISR(Recieve_Int)
2
{
3
  UARTrecieve = UDR;
4
  UDR = 0;
5
}
hat es auch geklappt, wenn ich in HTERM ne Zahl (als Dezimal sowohl 
eingeben als gesendet in den Einstellungen, sprich nicht als ASCII) 
eingegeben habe, hat sich die Geschwindigkeit geändert.

Nun sieht die ISR so aus:
1
ISR(Recieve_Int)
2
{
3
    if (BefehlEmpfangen == 0)
4
    {
5
        BefehlEmpfangen = UDR;
6
    }
7
    else
8
    {
9
        if (BefehlEmpfangen == 1)
10
        {
11
            faktor = UDR;
12
            BefehlEmpfangen = 0;
13
        }
14
        else
15
        {
16
            if (BefehlEmpfangen == 2)
17
            {
18
                Verzoegern = UDR;
19
                BefehlEmpfangen = 0;
20
            }
21
            else
22
            {
23
                if (BefehlEmpfangen == 3)
24
                {
25
                    minAnschlag = UDR;
26
                    BefehlEmpfangen = 0;
27
                }
28
                else
29
                {
30
                    if (BefehlEmpfangen == 4)
31
                    {
32
                        maxAnschlag = UDR;
33
                        BefehlEmpfangen = 0;
34
                    }
35
                    else
36
                    {
37
                        BefehlEmpfangen = 0;
38
                    }
39
                }
40
            }
41
        }
42
    }
43
}
Ich wollte erst eine Zahl senden, die für einen Befehl steht, diese wird 
im ersten Durchlauf der ISR gesetzt, und beim nächsten Mal senden soll 
dann der Wert für den Befehl kommen.
Aber sobald ich das erste Zeichen gesendet habe kommt nur noch Murks an.
hier noch der ganze Code:
1
/*
2
Servoansteuerung, lässt Servo an PB0 von Links- bis Rechtsanschlag fahren
3
ATMega 32
4
*/
5
#define F_CPU 18432000UL
6
#include <avr/io.h>
7
#include <util/delay.h>
8
#include <avr/interrupt.h>
9
#include <inttypes.h>
10
#define Recieve_Int  _VECTOR(13)
11
12
    int UARTrecieve;
13
    int faktor = 1;
14
    int maxAnschlag = 1900;
15
    int minAnschlag = 100;
16
    int Verzoegern = 10;
17
    int BefehlEmpfangen = 0;
18
19
int main (void)
20
{
21
    int k = 0;
22
    int kAlt = 0;
23
24
    sei();
25
26
27
  DDRB |= (1<<PB0);
28
  UBRRH = 0x86;
29
  UBRRL = 0x1D;
30
  UCSRB |= (1<<RXCIE) | (1<<RXEN) | (1<<TXEN);
31
  UCSRC |= (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
32
33
  while( 1 )
34
  {
35
    if (k>kAlt && k<maxAnschlag)
36
    {
37
        PORTB |= (1<<PB0);
38
        _delay_us( k );
39
        PORTB &= ~(1<<PB0);
40
        _delay_ms( Verzoegern );
41
        kAlt = k;
42
        k = k+faktor;
43
        UDR = k;
44
    }
45
46
    else
47
    {
48
        if (k>kAlt && k>=maxAnschlag)
49
        {
50
            PORTB |= (1<<PB0);
51
            _delay_us( k );
52
            PORTB &= ~(1<<PB0);
53
            _delay_ms( Verzoegern );
54
            kAlt = k;
55
            k = k-faktor;
56
            UDR = k;
57
        }
58
59
        else
60
        {
61
            if (k<kAlt && k>minAnschlag)
62
            {
63
                PORTB |= (1<<PB0);
64
                _delay_us( k );
65
                PORTB &= ~(1<<PB0);
66
                _delay_ms( Verzoegern );
67
                kAlt = k;
68
                k = k-faktor;
69
                UDR = k;
70
            }
71
72
            else
73
            {
74
                if (k<kAlt && k<=minAnschlag)
75
                {
76
                    PORTB |= (1<<PB0);
77
                    _delay_us( k );
78
                    PORTB &= ~(1<<PB0);
79
                    _delay_ms( Verzoegern );
80
                    kAlt = k;
81
                    k = k+faktor;
82
                    UDR = k;
83
                }
84
85
        else
86
              {
87
                    PORTB |= (1<<PB0);
88
                    _delay_us( k );
89
                    PORTB &= ~(1<<PB0);
90
                    _delay_ms( Verzoegern );
91
                    kAlt = k;
92
                    k = k+faktor;
93
                    UDR = k;
94
              }
95
            }
96
        }
97
    }
98
  }
99
  return 0;
100
}
101
102
ISR(Recieve_Int)
103
{
104
    if (BefehlEmpfangen == 0)
105
    {
106
        BefehlEmpfangen = UDR;
107
    }
108
    else
109
    {
110
        if (BefehlEmpfangen == 1)
111
        {
112
            faktor = UDR;
113
            BefehlEmpfangen = 0;
114
        }
115
        else
116
        {
117
            if (BefehlEmpfangen == 2)
118
            {
119
                Verzoegern = UDR;
120
                BefehlEmpfangen = 0;
121
            }
122
            else
123
            {
124
                if (BefehlEmpfangen == 3)
125
                {
126
                    minAnschlag = UDR;
127
                    BefehlEmpfangen = 0;
128
                }
129
                else
130
                {
131
                    if (BefehlEmpfangen == 4)
132
                    {
133
                        maxAnschlag = UDR;
134
                        BefehlEmpfangen = 0;
135
                    }
136
                    else
137
                    {
138
                        BefehlEmpfangen = 0;
139
                    }
140
                }
141
            }
142
        }
143
    }
144
}
Vielen Dank schonmal im Vorraus, und habt Nachsicht, das sind meine 
ersten Schritte in C
MfG Chaos

von Karl H. (kbuchegg)


Lesenswert?

j. t. schrieb:

> Nun sieht die ISR so aus:
>
1
> 
2
> ISR(Recieve_Int)
3
> {
4
>     if (BefehlEmpfangen == 0)
5
>     {
6
>         BefehlEmpfangen = UDR;
7
>     }
8
>     else
9
>     {
10
>         if (BefehlEmpfangen == 1)
11
>

Du sendest wirklich Hex-bytes mit den Werten 0, 1, 2, 3, ...?

> wenn ich in HTERM ne Zahl (als Dezimal sowohl eingeben als gesendet
> in den Einstellungen, sprich nicht als ASCII)

Mit ASCII wärs einfacher. Denn dann könntest du das einfach 
zurückschicken und im HTERM sehen, was der µC empfangen hat. So ist das 
immer Rätselraten


Und schreib deine if so rum
1
ISR(Recieve_Int)
2
{
3
    if (BefehlEmpfangen == 0)
4
    {
5
        BefehlEmpfangen = UDR;
6
    }
7
8
    else if (BefehlEmpfangen == 1)
9
    {
10
      faktor = UDR;
11
      BefehlEmpfangen = 0;
12
    }
13
    
14
    else if (BefehlEmpfangen == 2)
15
    {
16
      Verzoegern = UDR;
17
      BefehlEmpfangen = 0;
18
    }
19
  
20
    else if (BefehlEmpfangen == 3)
21
    {
22
      minAnschlag = UDR;
23
      BefehlEmpfangen = 0;
24
    }
25
  
26
    else if (BefehlEmpfangen == 4)
27
    {
28
      maxAnschlag = UDR;
29
      BefehlEmpfangen = 0;
30
    }
31
32
    else
33
      BefehlEmpfangen = 0;
34
}

ist genau die gleiche Logik. Aber 8 mal so übersichtlich.

* dir ist klar, dass du dir den Inhalt von UDR abholen musst? Wenn 
nicht, dann wird die ISR wieder und wieder aufgerufen. Solange bis du 
irgendwann mal UDR ausliest.


Bei UART Problemen ist es zu Debug-Zwecken eine extrem gute Idee, wenn 
man sich eine Rückmeldung über die UART vorsieht. Denn dann sieht man 
auch, was der µC verstanden hat, bzw. wie sich die Werte verändern. und 
ob sie sich korrekt verändern.

von Falk B. (falk)


Lesenswert?

@  j. t. (chaoskind)

Nomen est omen ;-)

>senden. Für den Empfang habe ich dann nach Rätselraten rausgefunden,
>welcher Interruptvektor der UART-Empfangsvektor ist, da im Datenblatt
>steht es ist 14, aber in iom32.h oder wie sie heißt, ist es 13(Dem Forum

Hättest du einfach den passenden NAmen hingeschrieben, wäre alles OK.

>ISR(Recieve_Int)
>{
>  UARTrecieve = UDR;
>  UDR = 0;
>}

Du musst UDR nicht beschreiben, denn das sendet Daten. LEsen reicht, um 
a) an die Daten zu kommen und b) den Interrupt zu löschen.

>Nun sieht die ISR so aus:

>Aber sobald ich das erste Zeichen gesendet habe kommt nur noch Murks an.
>hier noch der ganze Code:

Zuerst sollte man statt der enlos verschachtelten if einen switch 
nutzen.
2. hast du das Problem entdeckt, dass dein Empfänger synhronisiert 
werden muss, sprich er muss wissen, wo ein Befehl anfängt und aufhört. 
Für den Anfang nutzt man deshalb eine einfaches Protokoll auf ASCII 
Ebene. Dort kann man sehr leicht das Ende eines Befehls markieren, z.B. 
mit Enter bzw. \n.
Wie man das praktisch anpackt, sieht man u.a. hier

Beitrag "Re: USART Empfangsbuffer"

http://www.mikrocontroller.net/articles/Terminal_mit_Kommandointerpreter

von Daniel (Gast)


Lesenswert?

Hallo,
zuerst würde ich diese ganzen verschachtelten IFs in der ISR als Switch 
case machen, dann wird es wesentlich übersichtlicher. Dann könntest du 
mal versuchen die variable BefehlEmfangen als volatile zu machen.

von Karl H. (kbuchegg)


Lesenswert?

>        _delay_us( k );

sowas darfst du nicht machen.
Bei _delasy_us bzw. _delay_ms kann man keine Variablen angeben. D.h. 
können tut man schon, nur die Zeiten stimmen nicht.

von Karl H. (kbuchegg)


Lesenswert?

> für nen Mega32, in CodeBlocks, im AVR-Studio meckert er immer
> über Fehler?!
Ok. Jetzt ist es schon egal. Aber in Zukunft:
Geh den Fehlern nach. Insbesondere wenn dein C schwach ist. Der Compiler 
'meckert' nicht ohne Grund und aus dem Lesen der Fehlermeldungen und dem 
Erkennen was die Ursache dafür ist, wird sich dein C zu einem nicht zu 
unterschätzenden Anteil verbessern.

von Karl H. (kbuchegg)


Lesenswert?

ho, ho, ho. Immer langsam mit den jungen Pferden
1
    if (k>kAlt && k<maxAnschlag)
2
    {
3
        PORTB |= (1<<PB0);
4
        _delay_us( k );
5
        PORTB &= ~(1<<PB0);
6
        _delay_ms( Verzoegern );
7
        kAlt = k;
8
        k = k+faktor;
9
        UDR = k;
10
    }

du kannst doch nicht so einfach mir nichts, dir nichts ans UDR was 
zuweisen! Du musst schon warten, bis dir die UART signalisiert, dass sie 
jetzt was verschicken könnte!

Und tu dir selbst einen Gefallen und verschick die Dinge so, dass du sie 
im Klartext lesen kannst. Das hat doch keinen Sinn, wenn da im hTerm ein 
Bytestrom reinkommt und du nicht weißt, welches Byte jetzt was ist.

Wenn schon Debugging, dann richtig.


Ein enormes Problem ist in deinem Code natürlich, dass deine 
Servoansteuerung im Grund, na-ja, freundlich gast, suboptimal ist 
(andere würden sagen: Müll ist). Das geht so nicht. Mit den diversen 
_delay_Funktionen wirst du da nicht glücklich werden. Wiederkehrende 
Pulse, wie eigentlich fast alles, was mit Zeitsteuerungen zu tun hat, 
läuft praktisch immer über einen Timer. Alles andere ist sinnlos.

Modellbauservo Ansteuerung

von J. T. (chaoskind)


Lesenswert?

Danke erstmal für die Antworten =)

@Karl-Heinz

Das mit den Timern wäre dann der nächste Schritt gewesen, ich hab in 
Assembler schon ne Routine geschrieben, die den Servo per Timer steuert.

In meiner Assembler UART warte ich auch auf das Sendenbereit_Flag, aber 
hier dachte ich, das ist nicht nötig, weil ich ja mit den delays schon 
so viel Zeit verbrate, das der mit nem einzelnen Byte fertig ist, bevor 
das nächste zu senden ist.

Den Fehlern nachgehen, generell ein guter Tip *gg, dann schmeiß ich das 
Studio nochmal an, und schau nach dem Fehler, den er da ausgibt, das war 
glaub ich irgendwas von wegen der delay-Geschichten. Noch ne Frage dazu, 
wie kann ich denn ein variables delay ausgeben? Wenn _delay_us(k) nicht 
geht, kann ich doch nur noch _delay_us(200) bspw sagen?

Das Umschreiben der if else werd ich gleich mal machen, und das auslesen 
des UDR hatte ich im letzten else nicht im Kopf,*g danke dafür, werd ich 
direkt versuchen.

@Falk
den Beitrag mit dem Empfangsbuffer werd ich mir auch direkt mal ansehen!

@Daniel
Kann man in kurzen Worten erklären was n Switch-Case ist? Und was ist 
volatile? Wie gesagt, ich in C blutigster Anfänger

P.S.
Ja ich sende die Befehle als direkt als Dezimal, was von HTERM aber 
binär übertragen wird. Sprich ich sag ihm sende 3 dann sendet der 
0b00000011

von Karl H. (kbuchegg)


Lesenswert?

j. t. schrieb:

> Noch ne Frage dazu,
> wie kann ich denn ein variables delay ausgeben? Wenn _delay_us(k) nicht
> geht, kann ich doch nur noch _delay_us(200) bspw sagen?

Vergiss die ganze delay Sache. Machs gleich mit Timern.
Zumindest die Erzeugung der Servopulse.

_delay_xx  ist selten die Lösung, aber oft das Problem.
Die Legitimation der _delay_xx Funktionen leitet sich daher, dass man 
als Neueinsteiger mit irgendwas anfangen muss. Man kann nicht gleich 
alles auf einmal machen und irgendwie will man ja seine LED auch zum 
Blinken bzw. das Lauflicht zum Weiterschalten bringen. Aber in dem 
Moment, in dem das Programm (scheinbar) mehrere Dinge gleichzeitig 
machen soll (bei dir Servopulse erzeugen, die zeitlich zu variieren und 
dann auch noch die UART überwachen), ist _delay_xx ein Klotz am Bein, 
der dich auf die falsche Fährte führt.

> Kann man in kurzen Worten erklären was n Switch-Case ist? Und was ist
> volatile? Wie gesagt, ich in C blutigster Anfänger

Du #brauchst# ein C-Buch!
Das (ok, volatile nicht) ist noch nicht mal die Spitze des Eisbergs, der 
noch auf dich wartet.

> Ja ich sende die Befehle als direkt als Dezimal, was von HTERM aber
> binär übertragen wird. Sprich ich sag ihm sende 3 dann sendet der
> 0b00000011

Sowas geht super, wenn man nur 1 Byte immer und immer wieder zu 
übertragen hat. Sobald da aber sowas wie ein 'Command-Interface' ins 
Spiel kommt, wirds genauso aufwändig, wie eine ASCII Lösung (nur das bei 
ASCII die Prioritäten ganz anders liegen und das ganze leichter zu 
bedienen, zu lesen und zu debuggen ist)

von J. T. (chaoskind)


Lesenswert?

Gibt es da auch gute Literatur als PDF zu finden? Ich hab da bisher 
nicht viel gefunden. Das meiste was ich an Tutorials gefunden hab, ist 
entweder nach dem Motto "Es gibt verschiedene Variablen, int zb hat 
keine Nachkommastellen" und dann wird stundenlang drauf rumgeritten, das 
ne ganze Zahl ist, statt darauf einzugehen, wozu das ganze gut ist.

von Karl H. (kbuchegg)


Lesenswert?

j. t. schrieb:
> Gibt es da auch gute Literatur als PDF zu finden?

Ein wirklich gut gemeinter Rat.
Kauf dir ein C-Buch als Papierform. Du wirst es laaange benutzen und du 
wirst es immer wieder brauchen. Die 30 Euro sind gut angelegtes Geld.

von Karl H. (kbuchegg)


Lesenswert?

> ist entweder nach dem Motto "Es gibt verschiedene Variablen, int
> zb hat keine Nachkommastellen" und dann wird stundenlang drauf
> rumgeritten, das ne ganze Zahl ist

ich weiß nicht worauf du dich hier beziehst, aber ... ja, da gibt es so 
manche Falle und so manches was man dazu wissen muss.

>, statt darauf einzugehen, wozu das ganze gut ist.

na, wofür wirds gut sein. Um darin Werte abzulegen, die sich mit ganzen 
Zahlen repräsentieren lassen. Die Anzahl der Kinder eines Ehepaares ist 
nun mal eine ganze Zahl. 2,5 Kinder gibt es nicht. Aber im 
bundesdeutschen Durchschnitt gibt es sie. Und

   int SummeAllerKinder = ...;
   int AnzahlPaare = ....;

   double Durchschnitt = SummeAllerKinder / AnzahlPaare;

wäre nun mal grundfalsch um diesen Durchschnitt auszurechnen. Und nach 
der Lektüre bzw. dem Studium bzw. dem Durcharbeiten der Übungen deines 
C-Buchs zum Thema "Datentypen und wie sie in Ausdrücken verwendet 
werden" wäre dir sogar klar, warum das falsch ist und die Ergebnisse 
nicht stimmen.

von J. T. (chaoskind)


Lesenswert?

Schönes Beispiel,
genau sowas mein ich, was fehlt. Ich mein ich weiß noch aus der Schule 
was ein int ist, aber genau so eine prägnante Erklärung fehlt dann, da 
wird nur "Seitenweise" darüber rumlamentiert, "es ist ne ganze Zahl, es 
ist ne Zahl ohne Nachkommastellen, es ist ne ganze Zahl".

Ein konkretes Beispiel hab ich grad leider nicht zur Hand...

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.