Forum: Mikrocontroller und Digitale Elektronik UART 9 Bit Modus


von Frank L. (franklink)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
ich habe mal wieder zwei kleine Probleme.

Prozessor: ATMEGA328P
Eclipse + AVRGCC

Ich verwende die Routine von Peter 
Beitrag "AVR-GCC: UART mit FIFO" um eine Kommunikation 
über RS485 mit mehreren anderen Platinen aufzubauen.

Damit die Empfänger wissen, an welcher Stelle die Nachricht beginnt, 
verwende ich 9n1 als UART-Protokoll.

Die Adresse des Slave wird mit gesetztem 9Bit versendet, s.h. Beispiel.
1
ISR (USART_TX_vect)
2
{
3
  if( tx_in == tx_out )                                            // nothing to sent
4
  {
5
    UCSR0B &= ~( 1 << TXCIE0 );                                  // disable TX interrupt
6
    return;
7
  }
8
  UCSR0B &= ~( 1 << TXB80 );
9
  if ( tx_buff[tx_out] & 0x0100 )
10
  {
11
    UCSR0B |= ( 1 << TXB80 );
12
  }
13
  UDR0 = tx_buff[tx_out];
14
  ROLLOVER( tx_out, TX0_SIZE );
15
}

Aber leider kommt sie beim Slave mit nicht gesetztem 9Bit an.
1
ISR (USART_RX_vect)
2
{
3
  uint8_t i = rx_in;
4
  uint8_t data, res;
5
6
  ROLLOVER( i, RX0_SIZE );
7
  if( i == rx_out )                                                // buffer overflow
8
  {
9
    UCSR0B &= ~( 1 << RXCIE0 );                               // disable RX interrupt
10
    return;
11
  }
12
13
  /* Get 9th bit, then data from buffer */
14
  res = UCSR0B;
15
  if ( res & ( 1 << RXB80 ) )
16
  {
17
    data = UDR0;
18
    ATOMIC_BLOCK(ATOMIC_FORCEON)
19
    {
20
      rx_buff[rx_in] = ( data | 0x0100 );
21
    }
22
  }
23
  else
24
  {
25
    data = UDR0;
26
    rx_buff[rx_in] = data;
27
  }
28
  rx_in = i;
29
}

Desweiteren funktioniert das Versenden nur dann, wenn ich nachdem 
setzten des Interrupt-Flags TXCIE0 auch noch UDR0 = 0 sende.

Ich habe den gesamten Code mal soweit zusammengestrichen, dass das 
Problem nachvollzogen werden kann.

Ich hoffe es kann mir jemand den Fehler zeigen, weil ich da schon ne 
ganze Weile dran sitze.

Gruß
Frank

von Ralf G. (ralg)


Lesenswert?

für's erste:
1
  UCSR0C |= ( 1 << UCSZ02 ) | ( 1 << UCSZ01 ) | ( 1 << UCSZ00 );   // 9n1 for doing
Diese Bits sind nicht alle in einem Register.

von Frank L. (franklink)


Lesenswert?

Hallo Ralf,
danke, genau das war das Problem...
1
void uart_init( void )
2
{
3
  DDR_UART_OUT  |= ( 1 << PIN_UART_OUT );
4
  setReceiveMode();
5
6
  //USART
7
  UBRR0H = UBRR_VAL1 >> 8;                                         // store baudrate (upper byte)
8
  UBRR0L = UBRR_VAL1 & 0xFF;                                       // store baudrate (lower byte)
9
#ifdef DEBUG
10
                                                                   // 8n1 for debug
11
#else
12
  UCSR0C |= ( 1 << UCSZ01 ) | ( 1 << UCSZ00 );                     // 9n1 for doing
13
  UCSR0B |= ( 1 << UCSZ02 );
14
#endif
15
  UCSR0B |= ( 1 << RXEN0 )  | ( 1 << TXEN0 ) |
16
        ( 1 << RXCIE0 ) | ( 1 << TXCIE0 );                     // activate UART0 TX, RX, RXINT
17
18
  UDR0 = 0;
19
  rx_in = rx_out;                                               // set buffer empty
20
  tx_in = tx_out;
21
}

Jetzt gehts!

Jetzt bleibt nur noch die Frage, warum muss ich zum Aktivieren des 
TX-Interrupts immer ein UDR0 = 0 setzen?

Danke und Gruß
Frank

von Karl H. (kbuchegg)


Lesenswert?

Frank Link schrieb:

> Jetzt bleibt nur noch die Frage, warum muss ich zum Aktivieren des
> TX-Interrupts immer ein UDR0 = 0 setzen?

Du meinst hier?
1
void uart_putc ( uint16_t ch )
2
{
3
  uint8_t i = tx_in;
4
5
  ROLLOVER( i, TX0_SIZE );
6
  tx_buff[tx_in] = ch;
7
  while( i == tx_out);                                         // until at least one byte free
8
                                                       // tx_out modified by interrupt !
9
  tx_in = i;
10
  UCSR0B |= ( 1 << TXCIE0 );                                       // enable TX interrupt
11
  UDR0 = 0;
12
}

Weil das C in TXCIE für 'Complete' steht.
Transmit Complete Interrupt Enable.

Wenn du nie ein Zeichen per Zuweisung an das UDR Register auf die Reise 
schickst, wird der Transmitter logischerweise auch nie damit fertig ein 
Zeichen transmitted zu haben. Kein Zeichen fertig übertragen -> kein 
Complete Interrupt.

von (prx) A. K. (prx)


Lesenswert?

Frank Link schrieb:

> Jetzt bleibt nur noch die Frage, warum muss ich zum Aktivieren des
> TX-Interrupts immer ein UDR0 = 0 setzen?

Weil du das nächste Byte mit dem UDRE Interrupt senden solltest, nicht 
mit TXC. Der UDRE Interrupt kommt solange wie das UDR leer ist. TXC 
hingegen kommt erst, wenn die Leitung frei wird, was hauptsächlich für 
die Richtungsumschaltung von RS485 nützlich ist.

von spess53 (Gast)


Lesenswert?

Hi

>Jetzt bleibt nur noch die Frage, warum muss ich zum Aktivieren des
>TX-Interrupts immer ein UDR0 = 0 setzen?

Was verstehst unter 'aktivieren'? Ein TX-Interrupt wird ausgelöst wenn 
ein Byte verschickt worden ist.

MfG Spess

von Ralf G. (ralg)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Jetzt bleibt nur noch die Frage, warum muss ich zum Aktivieren des
>>TX-Interrupts immer ein UDR0 = 0 setzen?
>
> Was verstehst unter 'aktivieren'? Ein TX-Interrupt wird ausgelöst wenn
> ein Byte verschickt worden ist.
>
> MfG Spess

A. K. schrieb:
> Weil du das nächste Byte mit dem UDRE Interrupt senden solltest, nicht mit TXC.

Hm. 'Aktiviert' wird da nichts. Ich denke, man könnte auch mit '1' 
'aktivieren'. Das Zeichen wird einfach gesendet. Dann gibt's den 
Interrupt.

... mal so als Zusammenfassung.

von (prx) A. K. (prx)


Lesenswert?

Ralf G. schrieb:

> Hm. 'Aktiviert' wird da nichts. Ich denke, man könnte auch mit '1'
> 'aktivieren'. Das Zeichen wird einfach gesendet. Dann gibt's den
> Interrupt.

Hab ich verstanden. Und da der TXC Interrupt genau dann ausgelöst wird, 
wenn der Sendepuffer leer ist und das letzte Stopbit gesendet wird, ist 
das auch das zu erwartende Verhalten. Wenn nichts gesendet wird, dann 
gibts auch kein Stopbit, also keinen Interrupt. Denn das ist ein 
Ereignis, kein Zustand.

Anders ist es beim UDRE Interrupt. Der wird immer dann ausgelöst wenn im 
Sendepuffer noch Platz ist.

Ausserdem führt dein Code zu ineffizienter Nutzung der Leitung, weil der 
Interrupt erst dann auslöst, wenn die Leitung frei ist, folglich eine 
kleine Lücke zwischen den Bytes entsteht. Während der UDRE Interrupt 
schon auf Vorrat den Sendpuffer der Hardware füllt.

von Ralf G. (ralg)


Lesenswert?

A. K. schrieb:
> Hab ich verstanden.

Glaube ich dir!
War für Frank Link (franklink) gedacht.

von Frank L. (franklink)


Lesenswert?

Super, danke an alle!

Ich hab jetzt alles auf UDRE umgestellt, jetzt läufts perfekt.

Gruß
Frank

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.