Forum: Mikrocontroller und Digitale Elektronik Atmega16 wie Datensatz mit TX-Interrupt senden?


von Attega (Gast)


Lesenswert?

Hallo,

Ich versuche gerade Datensätze über den Uart eines Atmega 16 zu senden.
Es soll so funktionieren, dass nach dem 1. Byte ein Interrupt ausgelöst 
wird, in dessen Interruptserviceroutine dann das nächste Byte in UDR 
geschrieben wird.

Problem ist dass mommentan scheinbar nur das 1. Byte gesendet wird, und 
in der ISR (Falls die ausfelöst wird?) jedenfalls nichts gesendet wird.

Der Programmcode sieht so aus:
1
unsigned char Test[3]={0xFE,'3','H',0x00};
2
int sendezahler=0;
3
4
int main(void)
5
{
6
7
  DDRD  = 0x02;  //Ausgabe
8
  UBRRL = 0x18;  //25 für 9600Baud bei 4000000HZ
9
  UBRRH = 0x00;
10
  sei();      //Global Interrupts Enable
11
  UCSRB |=(1 << TXCIE) | (1 << TXEN);  //Interrupt ein, Sender ein!
12
  while(1)
13
  {
14
    Senden(Test);
15
    for(int i=0;i<1000;i++)
16
    {
17
      Verlangsamen();
18
    }
19
  }  
20
}
21
22
int Senden(unsigned char z[])      //Es wird ein zeiger auf die zu sendenden Daten übergeben
23
{                  //Es wird mit 1 ein erfolg zurückgemeldet
24
  UDR = z[sendezahler];
25
  sendezahler++;
26
  return 1;
27
}
28
29
ISR(USART_TXC_vect)
30
{
31
  sendezahler++;
32
  if(Test[sendezahler]!=0x00)    //Am ende des Strings soll 0x00 stehen
33
    {UDR = Test[sendezahler];}
34
  else
35
    {sendezahler=0;}
36
}

Bitte im Tipps

von MachMal (Gast)


Lesenswert?

volatile fehlt.
> volatile < unsigned char Test[3]={0xFE,'3','H',0x00};
> volatile < int sendezahler=0;

von Karl H. (kbuchegg)


Lesenswert?

> Problem ist dass mommentan scheinbar nur das 1. Byte gesendet wird, und in der 
ISR (Falls die ausfelöst wird?)

Dannb würde ich letzteres an deiner Stelle erst mal feststellen, damit 
da das Fragezeichen wegkommt.
Zb mit einer LED, die in der ISR eingeschaltet wird.

Und PS:
Was immer auch Verlangsamen macht bzw. machen sollte.
Ich bin mir recht sicher, dass es ein einfaches
1
     _delay_ms( 5000 );
auch tut. Das hat den Vorteil, dass es dann auch wenigstens 
funktioniert, was man angesichts der 3 Millionen untauglichen 
Verzögerungsschleifen, die hier alle 2 Wochen präsentiert werden, 
wahrscheinlich von deiner nicht sagen kann.

PS2: Mit deiner Verwaltung des sendezahler ist einiges im Argen.

von Attega (Gast)


Lesenswert?

@MachMal

Druch volatile würde er die Variable inerhalb einer Funktion auch nach 
dem ende ihere ausführung behalten.
Das sollte hier nicht das problem sein, da sendezähler und die 
Zeichenkette globale variablen sind.

@Karl Heinz
Hab grade nachgemessen, die ISR wird aufgerufen.
Stimmt _delay_ms( 5000 ); ist praktischer.

Was stimmt nicht mit der Verwaltung des Sendezählers?
Er beginnt mit dem 1. Element des zu sendenden zeichen.
Die Zeichenkette hat immer 0x00 am Ende dadurch kann das senden nach dem 
letzten zeichen einfach beendet werden.
Der Zähler wird wieder auf 0 gesetzt.
In der Routine senden muss nun noch überprüft werden ob der Zähler == 0 
ist, wenn nicht soll eine 0 für senden nicht erfolgreich zurückgebeben 
werden.

von Stefan E. (sternst)


Lesenswert?

Attega schrieb:
> Was stimmt nicht mit der Verwaltung des Sendezählers?

Du überspringst das 2. Zeichen.


Und so nebenbei:
1
unsigned char Test[3]={0xFE,'3','H',0x00};
Haut dir das der Compiler nicht um die Ohren?

von Karl H. (kbuchegg)


Lesenswert?

Attega schrieb:
> @MachMal
>
> Druch volatile würde er die Variable inerhalb einer Funktion auch nach
> dem ende ihere ausführung behalten.
> Das sollte hier nicht das problem sein, da sendezähler und die
> Zeichenkette globale variablen sind.

Es sollte bei dieser Funktion
1
int Senden(unsigned char z[])      //Es wird ein zeiger auf die zu sendenden Daten übergeben
2
{                  //Es wird mit 1 ein erfolg zurückgemeldet
3
  UDR = z[sendezahler];
4
  sendezahler++;
5
  return 1;
tatsächlich kein Problem sein, da dieser Code sicherlich schneller 
ausgeführt wird, als das Zeichen versandt wird und damit ist die 
Funktion längst fertig, ehe dann der Transmit Complete Interrupt 
anspricht. Bei einem UDRE Interrupt würde das anders aussehen, aber den 
Fall hast du nicht.

Die variable volatile zu machen ist nichts desto trotz eine gute Idee. 
Denn die Dinge ändern sich auch schon mal.

> @Karl Heinz
> Hab grade nachgemessen, die ISR wird aufgerufen.
> Stimmt _delay_ms( 5000 ); ist praktischer.
>
> Was stimmt nicht mit der Verwaltung des Sendezählers?
> Er beginnt mit dem 1. Element des zu sendenden zeichen.

Spielen wirs durch.
die Sache beginnt mit
1
int Senden(unsigned char z[])      //Es wird ein zeiger auf die zu sendenden Daten übergeben
2
{                  //Es wird mit 1 ein erfolg zurückgemeldet
3
  UDR = z[sendezahler];
soweit so gut. Also, fast so gut. denn senezähler hat hier nur deshalb 
den Wert 0, weil er als globale Variable mit 0 initialisiert wurde.
Aber ok.

Wie gehts weiter.
In diesem Moment beginnt die UART zu arbeiten. Die Funktion läuft weiter
1
  sendezahler++;

sendezahler wird um 1 erhöht und hat jetzt den Wert 1.
Die UART ist immer noch dabei das Zeichen zu versenden.

Die Funktion kehrt zu main zurück und geht dort in die Verzögerung rein. 
Das ist jetzt erst mal wichtig, dass diese Verzögerung auch tatsächlich 
greift. Dann wenn da nicht verzögert wird und die Funktion Senden in 
paar µs später erneut aufgerufen wird, dann wirds schwierig 
vorherzusehen, was da alles passiert. Auf jeden Fall hätte sendezahler 
dann schon nicht mehr den Wert von 0 (sondern 1), was schon mal ein 
Hinweis darauf ist, dass es eine verdammt gute Idee wäre, diese 0 in 
sendezahler in der Funktion Senden() sicherzustellen.

Aber seis drum. Nehmen wir an die Verzögerung wirkt.
Die UART arbeitet.

Irgendwann ist das Zeichen draussen, ein TRansmit Complete Interrupt 
wird ausgelöst. Wie gehts weiter?
1
ISR(USART_TXC_vect)
2
{
3
  sendezahler++;

sendezahler war 1, und wird jetzt auf 2 erhöht.
1
  if(Test[sendezahler]!=0x00)

Nun, Test[sendezahler], sendezahler hat den Wert 2, das ist also Test[2] 
und das ist
1
unsigned char Test[3]={0xFE,'3','H',0x00};
das 'H'.
Ich halte also fest. Bei der ersten Übertragung wurde das 0xFE auf den 
Weg gebracht, bei der nächsten das 'H'. Was ist mit dem '3'?
Die sind unter den Tisch gefallen.

Egal.
'H' ist nicht gleich 0. Daher
1
    {UDR = Test[sendezahler];}
ok, also das 'H' wird in die UART gegeben, welches das Zeichen auf den 
Weg bringt.
In der ISR passiert nichts weiter mehr, die Kontrolle geht zurück an 
main(), welches (hoffentlich) immer noch in der Verzögerung hängt.

Irgendwann ist das 'H' übertragen, ein erneuter Transmit Complete 
Interrupt wird ausgelöst.
1
ISR(USART_TXC_vect)
2
{
3
  sendezahler++;
sendezahler war 2, wird jetzzt zu 3.
1
  if(Test[sendezahler]!=0x00)
2
3
... unsigned char Test[3]={0xFE,'3','H',0x00};

Test[3] hat den Wert. Ja welchen eigentlich?
Dein Array ist mit einer Länge von 3 definiert. Du versuchst aber mit 4 
Zeichen zu initialisieren. Was passiert in diesem Fall? Welche Angabe 
gilt?
Es gilt die 3 in der Arrayangabe. D.h. es gibt ein Test[0], Test[1] und 
ein Test[2]. Zähl nach, sind genau 3 Stück. Ein Test[3] existiert aber 
gar nicht. D.h. man kann an dieser Stelle überhaupt nicht sagen, welchen 
Wert Test[3] eigentlich ergibt, denn der kann alles mögliche sein, je 
nachdem was da gerade zufällig im Speicher steht.

Und damit sind alle Wetten zur Makulatur geworden, wie es weiter geht.

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.