Forum: Compiler & IDEs UART UDRE Interrupt


von Mike (Gast)


Lesenswert?

Hallo

da ich mich nun doch entschieden habe RS485 zwischen Mega8 und PC zu 
verwenden, möchte ich vor jedem Senden PORTD2 auf high und nach jedem 
Senden PORTD2 auf low schalten.
1
#include <stdlib.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/signal.h>
5
#include <avr/pgmspace.h>
6
7
#include "uart.h"
8
9
 
10
#define UART_BAUD_RATE      57600
11
12
13
#ifndef F_CPU
14
#define F_CPU 16000000UL
15
#endif 
16
17
unsigned int a = 0;
18
19
20
int main(void)
21
{
22
DDRD = 0x04; 
23
24
char buffer[10];
25
unsigned int c;
26
27
28
sei();
29
30
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
31
32
PORTD |= (1 << PD2);  //umschalten auf Senden
33
uart_puts("\r\n");
34
35
PORTD |= (1 << PD2);  //umschalten auf Senden
36
uart_puts("HALLO" );
37
38
itoa( a, buffer, 10);  
39
40
uart_puts(buffer); 
41
42
43
    for(;;)
44
    {
45
        /*
46
         * Get received character from ringbuffer
47
         * uart_getc() returns in the lower byte the received character and
48
         * in the higher byte (bitmask) the last receive error
49
         * UART_NO_DATA is returned when no data is available.
50
         *
51
         */
52
       
53
    c = uart_getc();
54
55
    if ( c & UART_NO_DATA )
56
        {
57
             //no data available from UART
58
        }
59
        else
60
        {
61
        PORTD |= (1 << PD2);
62
     uart_putc( (unsigned int)c );
63
     
64
        }
65
    }
66
67
}

Das ist ein Ausschnitt aus der UART.c
1
/*************************************************************************
2
Function: UART Data Register Empty interrupt
3
Purpose:  called when the UART is ready to transmit the next byte
4
**************************************************************************/
5
{
6
    unsigned char tmptail;
7
8
    
9
    if ( UART_TxHead != UART_TxTail) {
10
        /* calculate and store new buffer index */
11
        tmptail = (UART_TxTail + 1) & UART_TX_BUFFER_MASK;
12
        UART_TxTail = tmptail;
13
        /* get one byte from buffer and write it to UART */
14
        UART0_DATA = UART_TxBuf[tmptail];  /* start transmission */
15
    }else{
16
        /* tx buffer empty, disable UDRE interrupt */
17
        UART0_CONTROL &= ~_BV(UART0_UDRIE);
18
    PORTD &= ~(1 << PD2);  //umschalten auf Empfangen
19
    }
20
}
hier schalte ich den Port wieder auf low. Der Mega springt mir aber nur 
einmal bei uart_puts("\r\n"); rein und dann nie wieder. Außerdem 
überträg er mir das "HALLO" nicht richtig.

von Jörg X. (Gast)


Lesenswert?

Dafür musst du den TXC (Transmission Complete) Interrupt nehmen, denn 
wenn der UDRE Interrupt auftritt, wird noch gesendet (der USART hat 
einen (kleinen) FIFO-Puffer).
Also, wenn dein Ringpuffer leer ist, muss sich der UDRE-Interrupt 
abschalten und der TXC schaltet dann den Pin D2 um.

Hth. Jörg

von Mike (Gast)


Lesenswert?

Das TXC Bit wird bei mir aber nie gesetzt. TXCIE habe ich auf high 
gesetzt. Liegt das an daran dass ich die Lib von Peter Fleury verwende?

von Falk B. (falk)


Lesenswert?

@ Mike (Gast)

>Das TXC Bit wird bei mir aber nie gesetzt. TXCIE habe ich auf high

Doch, das wird immer gesetzt.

>gesetzt. Liegt das an daran dass ich die Lib von Peter Fleury verwende?

Keine Ahnung. Die Frage ist schlicht, wie den Programm gestrickt ist. 
Sinnvollerweise muss das so laufen.

Im UDRE Interrupt werden jeweils die Daten aus einem Puffer ins UDR 
geschrieben. Wenn das letzte Byte geschrieben ist, wird der UDRE 
Interrupt abgeschaltet! Und der TXC Interrupt angeschaltet.

Im TXC Interrupt wird dann der Tranceiver (RS485?) von Senden auf 
Empfangen umgestellt und der TXC Interrupt wieder abgeschaltet.

Eh Voila.

MfG
Falk

von Mike (Gast)


Lesenswert?

Das ist die Senderoutine.
1
/*************************************************************************
2
Function: uart_putc()
3
Purpose:  write byte to ringbuffer for transmitting via UART
4
Input:    byte to be transmitted
5
Returns:  none          
6
**************************************************************************/
7
void uart_putc(unsigned char data)
8
{
9
    unsigned char tmphead;
10
11
    
12
    tmphead  = (UART_TxHead + 1) & UART_TX_BUFFER_MASK;
13
    
14
    while ( tmphead == UART_TxTail ){
15
        ;/* wait for free space in buffer */
16
    }
17
    
18
    UART_TxBuf[tmphead] = data;
19
    UART_TxHead = tmphead;
20
21
    /* enable UDRE interrupt */
22
    UART0_CONTROL    |= _BV(UART0_UDRIE);
23
24
}/* uart_putc */
ich habe das Programm in der Simulation Schritt für Schritt durchlaufen 
lassen aber er springt mir nie in die ISR(USART_TXC_vect). Wenn ich das 
TXC Bit manuell auf high setze dann springt er mir sofort in die 
Interruptschleife rein.

von Falk B. (falk)


Lesenswert?

@ Mike (Gast)

>Das ist die Senderoutine.

Nöö, die Funktion fügt nur Zeichen in einen Softwarepuffer ein. Die 
richtige Senderoutine liegt im UDRE Interrupt.

>ich habe das Programm in der Simulation Schritt für Schritt durchlaufen
>lassen aber er springt mir nie in die ISR(USART_TXC_vect). Wenn ich das
>TXC Bit manuell auf high setze dann springt er mir sofort in die
>Interruptschleife rein.

Du musst den TXC Interrupt auch freischalten (Maskenbit auf 1).

MfG
Falk

von Mike (Gast)


Lesenswert?

freigeschalten habe ich ihn. UCSRB |= (1<<TXCIE);
Hier nochmal der ganze Code.
1
#include <stdlib.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/signal.h>
5
#include <avr/pgmspace.h>
6
7
#include "uart.h"
8
9
#define UART_BAUD_RATE      57600
10
11
#ifndef F_CPU
12
#define F_CPU 16000000UL
13
#endif 
14
15
unsigned int a = 0;
16
17
ISR(USART_TXC_vect)
18
{
19
a++;
20
}
21
22
23
int main(void)
24
{
25
DDRD = 0x04; 
26
27
char buffer[10];
28
unsigned int c;
29
30
31
sei();
32
33
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
34
UCSRB |= (1<<TXCIE);
35
36
PORTD |= (1 << PD2);  //umschalten auf Senden
37
uart_puts("\r\n");
38
39
PORTD |= (1 << PD2);  //umschalten auf Senden
40
uart_puts("HALLO" );
41
42
itoa( a, buffer, 10);  
43
44
uart_puts(buffer); 
45
46
47
    for(;;)
48
    {
49
        /*
50
         * Get received character from ringbuffer
51
         * uart_getc() returns in the lower byte the received character and
52
         * in the higher byte (bitmask) the last receive error
53
         * UART_NO_DATA is returned when no data is available.
54
         *
55
         */
56
       
57
    c = uart_getc();
58
59
    if ( c & UART_NO_DATA )
60
        {
61
             //no data available from UART
62
        }
63
        else
64
        {
65
        PORTD |= (1 << PD2);
66
     uart_putc( (unsigned int)c );
67
     
68
        }
69
    }
70
}

von Mike (Gast)


Lesenswert?

nun funktionierts doch. der Interrupt kommt nur nicht wenn man im AVR 
Studio Simuliert.
Danke für die Hilfe

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.