Forum: Compiler & IDEs Abbruch beim Senden mit USART, Atmega16


von Witali G. (witali)


Lesenswert?

Hallo zusammen!!!

ich versuche mit dem dem unteren Code ein String bei Input Capture 
Interrupt über USART auszugeben, zunächst mal mit dem Simulator. Was mir 
völlig unverständlich ist, nach dem schreiben des zweiten Buchstaben des 
Strings (also "a") wird UDRE zurückgesetzt und das Senden geht nicht 
weiter. Was mir noch unklar ist, UDRE wird nicht zurückgesetzt wenn die 
ersten beiden Buchstaben in UDR reingeschrieben werden. Soll UDRE nicht 
nur dann auf high sein wenn UDR wirklich leer ist.

Ich wäre euch für jeden Hinweis dankbar!
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#ifndef F_CPU
5
6
#warning "F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000"
7
#define F_CPU 4000000L    // Systemtakt in Hz, das L am Ende ist wichtig, NICHT UL verwenden! 
8
#endif
9
 
10
#define BAUD 9600L          // Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!
11
 
12
// Berechnungen
13
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
14
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
15
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) // Fehler in Promille 
16
 
17
#if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))
18
  #error Systematischer Fehler der Baudrate gr?sser 1% und damit zu hoch! 
19
#endif
20
 
21
int main(void)
22
{
23
    UCSRB |= (1<<TXEN);                // UART TX einschalten
24
    UCSRC |= (1<<URSEL)|(3<<UCSZ0);    // Asynchron 8N1 
25
 
26
    UBRRH = UBRR_VAL >> 8;
27
    UBRRL = UBRR_VAL & 0xFF;
28
29
    
30
  TIMSK = 1 << TICIE1;
31
  TCCR1B = ((1 << ICNC1) | (1 << CS10));
32
  DDRB = 0x00;
33
  PORTB = 0xff;
34
  sei();
35
//  while (1)  { }
36
  while(1) {
37
    asm volatile ("nop");
38
  }
39
40
  return 0;
41
}
42
43
int uart_putc(unsigned char c)
44
{
45
    while (!(UCSRA & (1<<UDRE)))  /* warten bis Senden moeglich */
46
    {
47
    }                             
48
 
49
    UDR = c;                      /* sende Zeichen */
50
    return 0;
51
}
52
53
void uart_puts (char *s)
54
{
55
    while (*s)
56
    {   /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
57
        uart_putc(*s);
58
        s++;
59
    }
60
}
61
62
ISR(TIMER1_CAPT_vect)
63
{
64
  DDRD = 0xff;
65
  PORTD = 0x60;
66
  char a[] = "Hallo!";
67
  uart_puts(a);
68
}

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Du bist zu ungeduldig.

Du musst simulierte 1027 µs (4 MHz, 9600 Baud) warten, bis die beiden 
(Atmega8: UART puffert 2 Byte) ersten Zeichen "raus" sind und das dritte 
Zeichen per UART gesendet werden kann.

Vielleicht gibt dir das eine Idee, dass UART Senden im Timer-Interrupt 
keine gute Idee ist, wenn man einen genauen Timer braucht ;-)

von Witali G. (witali)


Lesenswert?

Vielen Dank für die Antwort!

> Du musst simulierte 1027 µs (4 MHz, 9600 Baud) warten


Aha! Und wie berechnet man diese Zeit aus dem Takt und der Baudrate?


> Vielleicht gibt dir das eine Idee, dass UART Senden im Timer-Interrupt
> keine gute Idee ist, wenn man einen genauen Timer braucht ;-)

Danke! Das war nur ein Lernprogramm. Ich habe es aus einem anderen 
zusammengebastelt, mit dem ich früher was mit dem Timer ausprobiert 
habe.

von Witali G. (witali)


Lesenswert?

Und noch etwas würde ich gerne wissen, wo erfahre ich denn solche 
Details wie, dass UART beim Atmega8  2 Byte puffert? Steht das im 
Datenblatt irgendwo?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Witali Gustschin wrote:
> Vielen Dank für die Antwort!
>
>> Du musst simulierte 1027 µs (4 MHz, 9600 Baud) warten
>
>
> Aha! Und wie berechnet man diese Zeit aus dem Takt und der Baudrate?

Mist! Erwischt ;-)

Ich habe es nicht berechnet, sondern mit der Stopwatch im AVR Studio 
beim Simulieren mitgestoppt. Breakpoint auf das return in uart_putc und 
Autostep ALT+F5 laufen lassen. Dann Input Capture auslösen auf PB0... 
Wenn es beim zweiten auszugebenden Zeichen dort stoppt, die Stoppuhr auf 
0 resetten und weiter mit Autostep oder Run laufen lassen, bis das 
dritte Zeichen ankommt. Dann sind 1027undpaarzerquetschte µs vergangen.

Berechnen... 2 Bytes sind 2x (1 Startbit, 8 Datenbits, 0 Paritybit und 1 
Stopbit) = 20 Bits. Bei 9600 Bits/s dauert die Ausgabe also 20/9600 s = 
2,08 ms.

2.08 ms bis der 2 Byte UART Puffer komplett leer ist, du brauchst aber 
nur ein Byte freien Puffer für das nächste Zeichen, also 1/2*2,08 ms = 
1,04 ms = 1042 µs. Dass der simulierte Wert etwas geringer ist, liegt 
vielleicht an der Lage des Breakpoints.

Die Taktrate spielt keine wesentliche Rolle. Ich habe sie automatisch 
mit angegeben, um meine Simulationsparameter (Atmega8, 4 MHz...) 
festzuhalten, falls wir länger an dem Fall arbeiten sollten oder wenn du 
das nachvollziehen willst, was ich gemacht habe.

>> Vielleicht gibt dir das eine Idee, dass UART Senden im Timer-Interrupt
>> keine gute Idee ist, wenn man einen genauen Timer braucht ;-)
>
> Danke! Das war nur ein Lernprogramm. Ich habe es aus einem anderen
> zusammengebastelt, mit dem ich früher was mit dem Timer ausprobiert
> habe.

Sollte auch keine Kritik in dem Sinn sein, sondern ein 
Denkanstoss/Reminder, die ISR grundsätzlich so klein wie möglich zu 
halten.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Witali Gustschin wrote:
> Und noch etwas würde ich gerne wissen, wo erfahre ich denn solche
> Details wie, dass UART beim Atmega8  2 Byte puffert? Steht das im
> Datenblatt irgendwo?

Im Datenblatt ist das Diagramm "Figure 61: USART Block Diagramm". In dem 
Transmitter-Teil ist das UDR (Transmit) Register und das Transmit Shift 
Register. Schreibst du ein Zeichen nach UDR (Transmit), wird es komplett 
nach Transmit Shift Register geschrieben und von dort aus Bit für Bit 
rausgeschickt. UDR ist dann wieder frei für das nächste Zeichen. Solange 
Transmit Shift Register noch nicht leer ist, stauen sich bei deinem 
nächsten UDR beschreiben zwei Zeichen. Also hast du einen 2 Byte 
"FIFO-Puffer". Puffer ist in Anführungszeichen zu sehen oder als 
Pseudopuffer, weil du nicht direkt auf das Transmit Shift Register 
zugreifen kannst. Die echte Pufferung beim Receive ist auf der folgenden 
Seite im Datenblatt erklärt.

von Witali G. (witali)


Lesenswert?

Besten Dank für die ausführlichen Erklärungen!!!

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.