Forum: Compiler & IDEs [AtMega16] UART senden


von Larry (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Leute - irgendwie wollte ich einfach nur über mein STK mit einem 
AtMega16 ein Zeichen über die serielle Schnittstelle ans Hyperterminal 
schicken da ich in weiterer Folge mehr mit der RS232 zu tun haben werde 
und irgendwie funktioniert das schon nicht - ist mein Programm einfach 
zu wenig oder ist da was Grundlegendes falsch? Header-File ist im 
Anhang.

Dies ist das Hauptprogramm.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/pgmspace.h>
4
#include "uart.h"
5
6
7
8
int main (void)
9
{
10
11
while(1)
12
{
13
uart_putc('x');
14
}
15
16
  return 0;
17
}

Bitte um ein paar Denkanstösse. Danke im Voraus.

von STK500-Besitzer (Gast)


Lesenswert?

>ist mein Programm einfach zu wenig

Ja!
Im Datenblatt findet man eine Initialisierungsroutine und Routinen zum 
Senden um Empfangen von Bytes.

von Peter D. (peda)


Lesenswert?

Larry wrote:
> zu wenig oder ist da was Grundlegendes falsch? Header-File ist im
> Anhang.

Das ist kein Header, sondern astreiner Code.

Sowas *.h zu nennen und zu includieren, ist erlaubt, aber nicht ratsam, 
wenn man auch noch in größeren Projekten die Übersicht behalten möchte.


Preisfrage:
Wozu ist wohl Init() da, wie wärs mal mit ausführen?


Peter

von Larry (Gast)


Lesenswert?

-.- Gute Idee zumindest mal die Init() zu verwenden, danke, vorlauter 
herumprobieren den ganzen Tag sowas nit mal mehr sehen - jetzt spuckt 
das Ganze zumindest irgendwelche Zeichen aus - wenn auch nicht die, die 
ich mir vorstellen würde.

von Larry (Gast)


Lesenswert?

So jetzt hab ich das ganze umgeändert - nach Anleitung vom Datenblatt 
wie vorgeschlagen und irgendwie erscheint immer noch nichts im 
HyperTerminal...sieht irgendwer das Problem.

Im Header-File prototypen.h stehen auch nur eben diese drin.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/pgmspace.h>
4
#include "prototypen.h"
5
6
#define FOSC 3686400
7
#define BAUD 9600
8
#define MYUBRR FOSC/16/BAUD-1
9
10
int main(void)
11
{
12
13
USART_Init(MYUBRR);
14
15
while(1)
16
{
17
USART_Transmit('a');
18
}
19
return 0;
20
}
21
22
void USART_Init(unsigned int ubrr)
23
{
24
25
UBRRH = (unsigned char)(ubrr>>8);  //Einstellen Baudrate
26
UBRRL = (unsigned char)ubrr;    //Einstellen Baudrate
27
28
UCSRB = (1<<RXEN)|(1<<TXEN);    //Enable Receive - Enable Transmit
29
30
UCSRC = (3<<UCSZ0);          //8bit - 1 Stopbit
31
32
}
33
34
void USART_Transmit(unsigned char data)
35
{
36
37
while ( !(UCSRA & (1<<UDRE)) );
38
UDR = data;
39
40
}
41
42
unsigned char USART_Receive(void)
43
{
44
45
while ( !(UCSRA & (1<<RXC)) );
46
return UDR;
47
48
}

Danke im Voraus

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> #define FOSC 3686400

Weiss der Atmega16, dass er mit 3686400 Hz zu laufen hat? Ab Werk macht 
er das nämlich nicht.

Die Voraussetzungen sind

1/ Das STK500 stellt eine Taktquelle von 3686400 Hz bereit

2/ Die Taktquelle kommt am Atmega16 an den richtigen Pins an

3/ Der Atmega16 ist per AVR Fuses auf die Nutzung dieser Taktquelle 
eingestellt

Keines von 1-3 macht die Definition des Taktes in dem Quellcode. Diese 
Definition ist in obigem Fall nur dazu da die Parameter für die UART 
Baudrate zu berechnen. Die Definition hilft allerdings garnichts, wenn 
die UART mit einer anderen Taktrate betrieben wird.

Wenn du jetzt Zweifel hinsichtlich der Taktrate hast, versuche ein 
Programm zu schreiben, bei dem eine LED im 10 Sekunden Takt blinkt. Eine 
Abweichung von 37 s (Atmega16 hat Werkseinstellung 1 MHz) zu 10 s 
(Wunschtakt funktioniert mit 3,686400 MHz) sollte offensichtlich sein.

Eine weitere "beliebte" Fehlerquelle ist die ZUordnung TX am AVR zum RX 
am PC. Irgendwo muss diese Verbindung (und die Verbindung der 
Masseleitung) vorhanden sein. Manchmal greift man ein vermeindlich 
passendes RS232-Kabel aus der Kiste und es ist promt das falsche - also 
1:1 Kabel (Modemkabel) statt Nullmodemkabel und umgekehrt.

Für das STK500 brauchst du ein 1:1 (Modem) RS232-Kabel zwischen dem 
STK500 RS232-Port for Communication (nicht der RS232-Port for 
Programming) und eine Verbindung zur zweiten Seriellen Schnittstelle am 
PC. Die Kreuzung TX-RX ist bereits auf dem STK500 gemacht.

Und das Terminalprogramm muss auf die zweite serielle Schnittstelle 
eingestellt sein.

Ausserdem brauchst du auf dem STK500 auch noch eine 2-Drahtverbindung 
zwischen den I/O-Pins am Atmega16 (PD0=RXD und PD1=TXD) und den 
RS232-Pins (RS232 SPARE) auf dem Board.

Zur Taktquelle würde ich sagen, dass der XTAL1 Jumper gesteckt sein 
sollte und dass OSCSEL in Stellung 1-2 geschlossen ist. In diesem Fall 
wird die software-generated clock benutzt, die per AVR Studio auf 
3,686400 MHz eingestellt werden kann. Damit diese Taktquelle 
anschliessend auch vom Atmega16 benutzt wird, muss der Atmega16 über 
seine Fuses auf externe Taktquelle eingestellt werden.

von Larry (Gast)


Lesenswert?

Also bei den Fuses hab ich nur die Möglichkeiten von 1,2,4 und 8 MHz 
jeweils + xx ms StartupTime, aber ich kann den ClockGenerator einstellen 
auf 1,843 oder 3,686 MHz - viele Zwischenmöglichkeiten gibt er mir dabei 
gar nicht.

Die 2-Drahtverbindung hab ich - das RS232-Kabel sollte auch passen.

XTAL1-Jumper ist gesteckt und OSCSEL auch auf 1-2 geschlossen.

Die Einstellung bei den Fuses hab ich jetzt auf Ext.Clock - StartupTime 
6CK+0ms.

Da ich nur ein serielles Kabel momentan zur Verfügung habe ist das 
Terminalprogramm auch auf die richtige Schnittstelle eingestellt.

Hab das ganze auch mit der Hälfte der Taktfrequenz 1,832MHz probiert und 
es geht dennoch nicht.

Was wären jetzt die richtigen Pins für den Takt?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Clockgenerator auf 3,686 MHz einstellen. XTAL1 gesteckt. OSCSEL auf 1-2 
geschlossen. Fuses sollte nach deiner Einstellung (Ext.Clock) OK sein. 
Noch die

Hat der PC eine Chance etwas zu empfangen? Modemkabel an der seriellen 
Schnittstelle des RS abziehen. Programm auf den AVR laufen lassen. Mit 
Voltmeter zwei Spannungsmessungen machen:

GND = Pin 5 am 8-pol. Stecker des Modemkabels
Messung 1: Welche Spannung ist an Pin 2?
Messung 2: Welche Spannung ist an Pin 3?

Die Leitung, auf der eine deutliche Spannung (keine im mV-Bereich bei 
einem auto range Voltmeter) zu messen ist, ist die auf RS232-Pegel 
gebrachte Sendeleitung des AVR.

Wenn der AVR nicht sendet, sollte die Spannung an einem der beiden Pins 
ca. -12V betragen. Beim Senden geht das aus dem negativen Bereich hoch 
(Betrag sinkt). Wieviel ist davon abhängig, wie gesendet wird und wie 
träge die Anzeige des Voltmeters ist. Ich kann keinen genauen Wert 
angegeben.

Wenn von der RS232-Hardware her alles OK ist, solltest du die 
Sendeleitung des AVR auf Pin 2 am Kabelende identifizieren können.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Larry wrote:

> void USART_Init(unsigned int ubrr)
> {
>
> UBRRH = (unsigned char)(ubrr>>8);  //Einstellen Baudrate
> UBRRL = (unsigned char)ubrr;    //Einstellen Baudrate
>
> UCSRB = (1<<RXEN)|(1<<TXEN);    //Enable Receive - Enable Transmit
>
> UCSRC = (3<<UCSZ0);          //8bit - 1 Stopbit
>
> }

Bitte auch kontrollieren, was hier steht:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#UART_initialisieren

Insbesondere das URSEL beachten und ggf. die Reihenfolge der 
Initialisierungsschritte (Datenblatt lesen)
1
/* USART-Init beim ATmega16 */
2
 
3
int main(void)
4
{
5
    UCSRB |= (1<<TXEN);                // UART TX einschalten
6
    UCSRC |= (1<<URSEL)|(3<<UCSZ0);    // Asynchron 8N1 
7
 
8
    UBRRH = UBRR_VAL >> 8;
9
    UBRRL = UBRR_VAL & 0xFF;
10
}

von Larry (Gast)


Lesenswert?

Also ich habe jetzt zuerst die serielle überprüft und ja auf PIN2 liegen 
während das Programm zum Senden am µC ist -2V und habe jetzt das 
URSEL-Bit auch noch gesetzt und mir scheint es funktioniert jetzt 
endlich. Endlich mal ein Erfolgserlebnis dieses verlängerte Wochenende. 
Ahja und die Reihenfolge in der Initialisierung ist jetzt zuerst 
UBRRH->UBRRL->UCSRB->UCSRC.

Vielen vielen Dank Stefan für die vielen Tipps und die guten Erklärungen 
- dann hoffe ich mal, dass ich beim Empfangen nicht wieder ähnliche 
Probleme haben werde.

Viele Grüße Larry

von Jo (Gast)


Lesenswert?

Wollte noch kurz anmerken für diejenigen die den Code hier rauskopieren 
und verwenden wollen: Bei mir hats das AVR-Studio erst richtig gemacht 
als die Zeile
UCSRC = (3<<UCSZ0);          //8bit - 1 Stopbit
in
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);          //8bit - 1 Stopbit
geändert wurde. Sonst sendet es nur 7Bit Daten!

von Johannes M. (johnny-m)


Lesenswert?

Jo wrote:
> Wollte noch kurz anmerken für diejenigen die den Code hier rauskopieren
> und verwenden wollen: Bei mir hats das AVR-Studio erst richtig gemacht
> als die Zeile
> UCSRC = (3<<UCSZ0);          //8bit - 1 Stopbit
> in
> UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);          //8bit - 1 Stopbit
> geändert wurde. Sonst sendet es nur 7Bit Daten!
Der ATMega16 (um den es hier bisher ging) hat aber erstens kein UCSR0C 
sondern nur ein UCSRC und zweitens hat das nichts mit AVRStudio zu 
tun, sondern wenn überhaupt, dann mit dem Compiler und drittens lässt 
sich die zweite Version auch wie oben schreiben:
1
UCSR0C = 3 << UCSZ00;
...

von Mike (Gast)


Lesenswert?

Hallo.

Ich habe folgendes Problem:

Ich habe die UART-Initialisierung wie im Datenblatt des ATmega16 
beschrieben programmiert (STK500,AVR Studio,JTAGICE mkII zum 
debuggen).Möchte einen Wert über den UART an HyperTerminal schicken.

void init_uart(uint16_t baudrate)
{
 DDRD  |= (1 << RXD) | (1 << TXD);
 PORTD |= (1 << RXD) | (1 << TXD);// receive and transmit pin as output

 UCSRC |= (1 << UCSZ1) | (1 << UCSZ0) | (1 << USBS);// 8 databits,1 
stopbit(USBS=0)
 UCSRB |= (1 << TXEN) | (1 << RXEN);// enable transmitter and receiver

 UBRRH = baudrate >> 8;          // extract high-byte from baudrate 
value
 UBRRL = baudrate & 0xFF;  // extract low-byte from baudrate value
}

Wenn ich nun debugge,dann beschreibt es bei mir nur das Register UCSRB.
Im Register UCSRA steht wie im Datenblatt beschrieben das Bit UDRE auf 
1.Ansonsten kann das Register ebenso wie UCRSC nicht beschrieben werden.

Hat jemand eine Idee woran das liegen kann?

von Justus S. (jussa)


Lesenswert?

Warum gräbst du dafür einen eineinhalb Jahre alten Thread aus?

Mike schrieb:

>  DDRD  |= (1 << RXD) | (1 << TXD);
>  PORTD |= (1 << RXD) | (1 << TXD);// receive and transmit pin as output

warum einen  Eingang als Ausgang definieren?

>  UCSRC |= (1 << UCSZ1) | (1 << UCSZ0) | (1 << USBS);// 8 databits,1
> stopbit(USBS=0)

Was nun USBS = 1 oder 0?

Und vielleicht solltest du auch mal ins Datenblatt schauen, wie man auf 
UCSRC zugreift...

von Mike (Gast)


Lesenswert?

> Warum gräbst du dafür einen eineinhalb Jahre alten Thread aus?

Warum Unmengen von Threads aufmachen,wenn dieser genau mein Anliegen 
betrifft?

>  DDRD  |= (1 << TXD);
>  PORTD |= (1 << TXD);

Gut,hab ich vergessen rauszunehmen.Will sowieso nur senden. :)

>Was nun USBS = 1 oder 0?

O.k.!Ein stoppbit,also USBS = 0

>Und vielleicht solltest du auch mal ins Datenblatt schauen, wie man auf
>UCSRC zugreift...

hatte ich vergessen: UCSRC |= (1 << URSEL); muss natürlich noch da 
stehen.

von Mike (Gast)


Lesenswert?

Gut,die Hyperterminal-Kommunikation geht nun schon mal.Lag daran,dass 
die Baudrate auf 115200 eingestellt werden muss.

Aber trotzdem kann ich die Register UCSRC und UCSRA immer noch nicht 
beschreiben.
Im Terminalprogramm wird entweder ein Leerzeichen oder irgendein 
ASCii-Zeichen ausgegeben?!?

Initialsierungscode sieht so aus:

void init_uart(uint16_t baudrate)
{
 DDRD  |= (1 << TXD);
 PORTD |= (1 << TXD);  // receive and transmit pin as output

 UCSRC |= (1 << URSEL);
 UCSRC |= (1 << UCSZ1) | (1 << UCSZ0);// 8 databits,1 stopbit(USBS=0)
 UCSRB |= (1 << TXEN);              // enable transmitter and receiver

 UCSRC &= (1 << URSEL);         // for write operation of UBRRH
 UBRRH = baudrate >> 8;         // extract high-byte from baudrate value
 UBRRL = baudrate & 0xFF;       // extract low-byte from baudrate value
}

von Stefan E. (sternst)


Lesenswert?

1
 UCSRC |= (1 << URSEL);
2
 UCSRC |= (1 << UCSZ1) | (1 << UCSZ0);// 8 databits,1 stopbit(USBS=0)
3
...
4
 UCSRC &= (1 << URSEL);         // for write operation of UBRRH
Sorry, aber das ist Unsinn. URSEL muss gleichzeitig mit den anderen Bits 
geschrieben werden. Und es muss auch nicht wieder gelöscht werden.
1
 UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0);
Mal ganz davon abgesehen, dass dieses Schreiben nach UCSRC eh 
überflüssig ist. Was du da reinschreiben willst ist eh schon der 
Default-Inhalt.

PS: Und das gewöhn dir bitte ab:
> Warum Unmengen von Threads aufmachen,wenn dieser genau mein Anliegen
> betrifft?
Neue Frage -> neuer Thread (auch wenn die Frage ähnlich ist)

von Mike (Gast)


Lesenswert?

Ja du hast recht.Das ist der default-Inhalt.Aber trotzdem sehe ich 
diesen eben nicht in meinem view I/O-Fenster.
Dort setzt es nur das Bit meinen UCSRB-Registers,sonst nichts.Ist doch 
schon komisch oder?

von Stefan E. (sternst)


Lesenswert?

Mike schrieb:
> Ja du hast recht.Das ist der default-Inhalt.Aber trotzdem sehe ich
> diesen eben nicht in meinem view I/O-Fenster.
> Dort setzt es nur das Bit meinen UCSRB-Registers,sonst nichts.Ist doch
> schon komisch oder?

Du kannst den Inhalt nicht direkt im Debugger sehen. Wenn der UCSRC 
ausliest, liest er ja in Wirklichkeit UBRRH.

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.