Mal wieder UART @AtMega168

Hemi

Aktives Mitglied
Premium Benutzer
30. Nov. 2008
1.103
19
38
Korntal-Münchingen, Germany
Sprachen
  1. ANSI C
  2. C++
  3. PHP
  4. Java
Servus miteinander,

ich habe ein Problem mit meinem UART beim AtMega168.

Das hier ist meine Init-Routine:
Code:
void uart_init (unsigned int BaudRate) {
    int ubrr = (F_CPU/16/BaudRate - 1);
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)ubrr;

    UCSR0B =( 1 << RXCIE0 )|( 1 << RXEN0 )|( 1 << TXEN0 );
    UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}
Die MCU läuft mit einem externen Crystal mit 18,xxxx MHz. Der ist auch im Betrieb über die Fuse-Bits.

Das hier ist meine Senderoutine:
Code:
void sendUart(char* cmd)
{
  do
  {
    while (!(UCSR0A & (1<<UDRE0)))  {}              // Warten bis mansenden kann
    UDR0 = *cmd++;                                  // senden und zumnächsten zeichen wechseln
  }
  while(*cmd);                                      // so lange bis dasende erreicht ist
}
Und das hier ist die ISR:
Code:
ISR(USART_RX_vect) {
    PORTC |=(1 << PC1); // Ausgang 1 aktivieren
}
Main-Rotine:

Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include "uart.h"
#include "mcu_init.h"

int main (void) {

    uart_init(9600);
    set_ports();
    sei();

    sendUart("Programm gestartet\n");

    while (1) {

    }


    return 0;
}
Das Problem:

Ich kann nichts senden, bzw. ich sehe nichts im HyperTerminal.

Wenn ich aber irgendwas an die MCU sende, löst die ISR sauber aus und die LED, die angeschlossen ist, geht auch an.

Ideen woran es liegen könnte?

Danke & Grüsse
Heinrich
 
Hallo Heinrich,

du hast die Datenübertragung des USART auf 8N2 eingestellt, ist dein Terminal-Programm ebenso eingestellt?

Dann könnte die Fehlerursache noch am Fusebit CKDIV8 liegen, dieses ist von Werk aus immer programmiert (CKDIV8=0), der Systemtakt ist dann 18MHz/8, die Baudrate stimmt somit nicht.

Vielleicht liegt die Ursache des Problems ja ein einen der beiden Sachen.

Grüße,
Dirk
:ciao:
 
Hallo Dirk,

danke für die schnelle Antwort.

Wegen 8-N-2: Ja, mein Programm habe ich auf 8-N-2 eingestellt, das hat nichts gebracht.

Das mit dem Fusebit schaue ich nochmal nach, aber was mir aufgefallen ist: Ich habe dieses Beispiel:
Code:
#include <avr/io.h>
#include <inttypes.h>

// Sollte schon im Makefile definiert sein.
// In dem Fall hier einfach löschen.
#define F_CPU       7372800UL

#define BAUD        19200UL
#define UBRR_BAUD   ((F_CPU/(16UL*BAUD))-1)

// USART initialisieren
void uart_init(void)
{
    // Baudrate einstellen (Normaler Modus)
    UBRRH = (uint8_t) (UBRR_BAUD>>8);
    UBRRL = (uint8_t) (UBRR_BAUD & 0x0ff);

    // Aktivieren von receiver und transmitter
    UCSRB = (1<<RXEN)|(1<<TXEN);

    // Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit
    UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}

int main(void)
{
    uint8_t buffer;

    // USART initialisieren
    uart_init();

    while (1)
    {
        // Warten bis Daten empfangen wurden
        while ( !(UCSRA & (1<<RXC)) )
            ;

        // Empfangsregister auslesen
        buffer = UDR;

        // Warten bis der Sendepuffer frei ist
        while ( !( UCSRA & (1<<UDRE)) )
            ;

        // Daten in den Puffer schreiben und damit senden
        UDR = buffer;
    }
}
von www.kreatves-chaos.com ausprobiert und es funktioniert. Also ich bekomme das, was ich gesendet habe zurück, aber die Zahlen gehen nicht, nur die Buchstaben.

Was bedeutet das "UL" am bei der Baudrate? Und warum ist die Formel für UBRR (F_CPU/16UL*Baud)-1 und nicht F_CPU/16/Baud-1?

Danke & Grüsse
Heinrich
 
Hallo Heinrich,
Was bedeutet das "UL" am bei der Baudrate? Und warum ist die Formel für UBRR (F_CPU/16UL*Baud)-1 und nicht F_CPU/16/Baud-1

UL steht für unsigned long (uint32_t). Bei der zweiten Gleichung wird zuerst F_CPU/16 berechnet, das Ergebnis durch BAUD geteilt, dann 1 subtrahiert, dies entspricht der ersten Gleichung. Es sollte normalerweise mit beiden Gleichungen funktionieren. Achtung, bei der ersten Gleichung oben fehlen zwei Klammern (F_CPU/(16UL*Baud))-1, im Sourcecode stimmt es aber.

Du hast nun einige Sachen in deinem Source geändert:

Code:
#define F_CPU       [COLOR=RoyalBlue][B]7372800UL[/B][/COLOR]

// ...

// USART initialisieren
void uart_init(void)
{
    // Baudrate einstellen (Normaler Modus)
    UBRRH = (uint8_t) (UBRR_BAUD>>8);
    UBRRL = (uint8_t) (UBRR_BAUD & 0x0ff);

    // Aktivieren von receiver und transmitter
    UCSRB = (1<<RXEN)|(1<<TXEN);

    // Einstellen des Datenformats: 8 Datenbits, 1 Stoppbit
    UCSRC = (1<<[B][COLOR=RoyalBlue]URSEL[/COLOR][/B])|(1<<UCSZ1)|(1<<UCSZ0);
}
Du verwendest nun nicht mehr 18MHz sondern 7,3728MHz Systemclock.
Du hast 8N1 eingestellt, das musst du auch auf der PC-Seite einstellen, hast du aber wahrscheinlich gemacht.

Achtung: Das Bit URSEL gibt es bei dem ATmega168 nicht mehr. URSEL steht für UsartRegisterSelect. Ältere Mikrocontroller haben für UBRRH und UCSRC die selbe IO-Adresse. Um auf UCSRC zugreifen zu können, muss das Bit URSEL gesetzt sein. Wenn du das Bit hier verwendest, setzt du irgendein höhrewertiges Bit in UCSRC und verstellst die Funktion des USART.

Grüße,
Dirk
 
Hallo Dirk,

ich habe das Problem gefunden.

Also, ich verwende Eclipse mit CDT und AVR-Plugin. als IDE. Da gibt es die Möglichkeit die Hardware und die Frequenz auszuwählen, das mache ich auch, über die Einstellungen vom Projekt.

Das Beispiel, welches funktioniert, rechnet mit UL, also unsigned long. Mein Beispiel rechnet aber mit unsigned int. Ich habe gerade ein komplett neues Progrämmchen geschrieben, in dem ich unsigned int verwendet habe, compiliert, gebrannt -> nix geht. Dann das gleiche Progrämmchen überall auf unsigned long umgestellt und siehe da, es tut.

Daraus schliesse ich, dass Eclipse die Frequenz als 1843200UL ablegt.

Dann habe ich mit CKDIV8 gespielt. Im AVR Studio (verwende ich um die Fusebits zu brennen) war das angehackt. So, habe das Häckchen rausgenommen und geht wieder nichts, bzw. er sendet nur Müll, unabhängig was ich drücke, empfange ich "w".

Habe es wieder angehackt und siehe da, es tut. Datenformat war immer 19200 8N1 und hier ist die Init-Routine:

Code:
void uart_init (unsigned long BaudRate) {
	unsigned long ubrr = ((F_CPU / (16*BaudRate))-1);

	UBRR0H = (unsigned long)(ubrr >> 8);
	UBRR0L = (unsigned long)ubrr;

	UCSR0B = (1<<RXEN0)|(1<<TXEN0);
	UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}

Das hier funktioniert (wird im while(1) im Main aufgerufen):

Code:
void uart_response (void) {

	unsigned int buffer;

	while ( !(UCSR0A & (1<<RXC0)) );
	buffer = UDR0;

	while ( !( UCSR0A & (1<<UDRE0)) );
	UDR0 = buffer;
}

Aber das hier nicht:

Code:
void uart_response (void) {

	unsigned int buffer;

	/*
	while ( !(UCSR0A & (1<<RXC0)) );
	buffer = UDR0;
	 */

	_delay_ms(40);
	while ( !( UCSR0A & (1<<UDRE0)) );
	UDR0 = 'A';
}

bzw. kommt nur Schrott an :( Es kommt HEX C1 an.

Irgendwie stehe ich voll auf dem Schlauch, das dürfte doch nicht so schwer sein. :banghead: :banghead: :banghead:

Grüssle
Heinrich
 
Hallo Heinrich,

wenn du nicht die Baudrate während der Laufzeit ändern möchtest, würde ich BaudRate mit #define festlegen und nicht als Argument der Routine uart_init übergeben. Wichtig ist die richtige Zuweisung der Inhalte von Variablen unterschiedlicher Wertebereiche 8, 16, 32bit.

Warum es bei dir allerdings mit CKDIV8 funktioniert und ohne nicht, da bin ich jetzt echt überfragt.

Probiere es einmal mit folgender Initialisierung:



CodeBox C

#define F_CPU 18432000UL // oder per Projekteinstellung definieren.
#define BaudRate 19400UL

void uart_init(void)
{
// ubrr ist 2 Byte groß, das Berechnungsergebnis 4Byte
uint16_t ubrr = (uint16_t) ((uint32_t)F_CPU / (16*BaudRate)-1);

UBRRH = (uint8_t) (ubrr>>8); // high byte von ubrr
UBRRL = (uint8_t) (ubrr); // low byte von ubrr

// Receiver und Transmitter aktivieren
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
// asynchron, 8N1 einstellen
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}

Gruß,
Dirk
 
Hallo Dirk,

ich habe es gerade ausprobiert. Hier ist das Ganze main.c wo ich habe:

Code:
#define BaudRate 19200UL
#include <avr/io.h>

void uart_init (void);

void uart_init (void){
	uint16_t ubrr = (uint16_t) ((uint32_t)F_CPU/(16*BaudRate)-1);
	UBRR0H = (uint8_t)(ubrr >> 8);
	UBRR0L = (uint8_t)(ubrr);

	UCSR0B = (1<<RXEN0)|(TXEN0);
	UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}


int main (void) {
	uart_init();
	unsigned char buffer;

	while (1) {
		while ( !(UCSR0A & (1<<RXC0)) );
		buffer = UDR0;

		while ( !( UCSR0A & (1<<UDRE0)) );
		UDR0 = buffer;
	}

	return 0;
}

Also absolut minimalistisch. Ergebnis: nichts. Es kommt nichts zurück, ich kann senden was ich will, nichts. :stupido3:

Grüsse
Heinrich
 
Hallo Heinrich,

in der Zeile 6 hast du "1<<" vergessen. Füge das mal ein und probiere es nochmal.

Grüße,
Dirk



CodeBox C

void uart_init (void)
{
uint16_t ubrr = (uint16_t ((uint32_t)F_CPU/(16*BaudRate)-1);
UBRR0H = (uint8_t)(ubrr >> 8);
UBRR0L = (uint8_t)(ubrr);
UCSR0B = (1<<RXEN0)|(TXEN0);
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}
 
Hallo Dirk,

ähm, ja. Habe es korrigiert, hier ist der Code:

Code:
#define BaudRate 19200UL
#include <avr/io.h>

void uart_init (void);

void uart_init (void){
	uint16_t ubrr = (uint16_t) ((uint32_t)F_CPU/(16*BaudRate)-1);
	UBRR0H = (uint8_t)(ubrr >> 8);
	UBRR0L = (uint8_t)(ubrr);

	UCSR0B = (1<<RXEN0)|(1<<TXEN0);
	UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}


int main (void) {
	uart_init();
	char buffer;

	while (1) {
		while ( !(UCSR0A & (1<<RXC0)) );
		buffer = UDR0;

		while ( !( UCSR0A & (1<<UDRE0)) );
		UDR0 = buffer;
	}

	return 0;
}

Prinzipiell funktioniert es, jedoch liefert er HEX 80 zurück, egal, was ich sende.

Dann habe ich die BaudRate im Terminalprogramm auf 1200 gesetzt und siehe da, es kommt was brauchbareres zurück. Zum Beispiel: sende ich "a", kommt "q" zurück, "s" wird zum "s", "d" zum "t", "f" zum "v".

Habe jetzt den CKDIV8 ausgehackt und nochmal geflashed, BaudRate im Terminalprogramm auf 19200 hoch und siehe da, alles was ich sende, empfange ich auch. Das Senden eines bestimmten Zeichens klappt auch einwandfrei.

Herzlichen Dank für Hilfe Dirk, top Support :flowers:

Grüsse
Heinrich
 
Hallo Heinrich,

freut mich, dass es nun funktioniert. :cheers:

Noch mal einen Tipp:

Nach der Initialisierung des Usart könnte sich ggf. ein Datenbyte im ReceiveBuffer befinden. Den Buffer würde ich direkt nach der Initialisierung erst einmal leeren, das geht zum Beispiel so:



CodeBox C

void usart_flushbuffer(void)
{
unsigned char c;

while (UCSR0A & (1 << RXC0)) c = UDR0;
}


Grüße,
Dirk
 
Danke Dirk, schon eingebaut.

Aber mal was anderes, brauche ich innerhalb vom ISR ein
Code:
while ( !(UCSR0A & (1<<RXC0)) );

Oder kann ich einfach direkt mit
Code:
variable = UDR0;

drauf zugreifen?

Grüssle
Heinrich
 
Hallo Heinrich,

du kannst in der Interrupt-Routine (Rx Complete Interrupt) direkt das Datenregister des Usart lesen. Für das Interrupt-Event gibt es nämlich folgende Bedingungen:
  • Interrupt ist freigegeben, Globale Interrupts freigegeben
  • Bit RXC0 in UCSR0A ist gesetzt
Du brauchst also nicht mehr selber RXC0 auswerten.

Grüße,
Dirk
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)