Timerproblem 89C5131A-UM

OP #1918587
Lesenswert?

Hallo,
ich hab versucht mich mit dem Datenblatt ins Thema Timer einzulesen, 
aber komme nicht so recht weiter.

Also ich habe einen Controller: AT89C5131A-UM.
Ich will eine Baudrate generieren für die serielle Schnittstelle.
Das Quartz ist ein 12 Mhz, da ich dieses für die integrierte 
USB-Schnittstelle zum flashen brauche.

Was ich jetzt nicht verstehe, ist ob man weiterhin Timer0 und Timer1 
benutzen kann, wie beim 89C51CC03, den ich von der Schule bekommen habe, 
oder ob man das jetzt alles mit dem Timer2 realisieren muss, weil im 
Datenblatt nichts mehr von Timer0/1 beschrieben ist.
Zudem sind für Timer2 mehrere Formeln angebenen, einmal in der Sektion 
Serial I/O Port, und in Timer2. Da steig ich momentan nicht richtig 
durch.

Wie muss ich da jetzt allgemein vorgehen wenn ich eine Baudrate von 9600 
haben möchte?
Meine momentane Initialisierung ist folgende:
1
SCON=0x50;
2
BDRCON &= 0xEC //BRR=0, SRC=0
3
BDRCON |= 0x0E //TBCK=1, RBCK=1, SPD=1
4
BRL= 0xF9 //Wert vom Keil-Rechner, Teiler fragwürdig
5
ES=1
6
BDRCON |= 0x10 //starten

Gruß derdiek
Gast #1918624
Lesenswert?

sieht eigentlich ok aus
ich würde klare Verhältnisse schaffen mit
BDRCON = 0x0E

also
BDRCON = 0x0E ; stop baudrateclock
SCON = 0x50
BRL = 217 ; 256-39 = 9600 baud @ 12 MHz, SMOD1 = 0
ES = 1
BDRCON |= 0x10


für polling statt interrupt setze ich
SCON = 0x52
Gast #1918636
Lesenswert?

Timer1 und 0 kann man weiterhin benutzen.
Deren Dokumentation hat Atmel in ein allgemeines
8051-PDF verschoben.
Auch Timer 2 kann man unabhängig von dem Baudraten-
Generator benutzen, wenn Du den gesonderten Baudraten-
takt (BRR) benutzt, wie Du es oben codiert hast.
Es stehen also alle drei Timer 0, 1 und 2 frei zur
Verfügung.
Gast #1918647
Lesenswert?

Die Beschreibung zu Timer 0 und 1 und weitere 8051-
Architektur-Details hat Atmel in folgendes Dokument
verschoben:
doc4316.pdf

Eine Beschreibung des Befehlssatzes zu 8051 findet
man in:
doc0509.pdf
Für Dich als C-Programmierer weniger interessant,
aber es lesen hier vielleicht noch Andere mit.
Gast #1918681
Lesenswert?

Ein wenig mehr source-code wäre hilfreich...
Insbesondere, was die Interrupt-Behandlung
der TX-Routine betrifft.
Das TI-Flag ist eine beliebte Falle.

Vielleicht eine wiederkehrende Sequenz schicken,
z.B. 0x55. Dann mit hterm das empfangene Bit-
Muster analysieren.
Gast #1918683
Lesenswert?

Ja, SMOD1 ist default 0, aber ich sehe ja nicht den
ganzen source-code. Wer weiß, ob da nicht irgendwo
SMOD1 = 1 steht? ;-)
Ich setze z.B. SMOD1 immer auf 1, aber das braucht
es in diesem Falle nicht.
OP #1918712
Lesenswert?

Im restlichen Code wird nichts mehr mit den Registern gemacht, nur noch 
SBUF. Das was ich gepostet hab steht in der Serial_Init und die wird nur 
einmal aufgerufen.

Hab das Scope mal angeschlossen, wenn ich 0x55 in einer Schleife 
schicke, sind die Flanken 190uS auseinander, 1/0,00019 = 5263,16
Also fallende Flanke 1 -> steigende Flanke 1. |_|

1/9600 = 104,2 uS..
Dann müsste ich also den Reloadwert größer machen damit der Timer nicht 
so lange läuft oder?
OP #1918749
Lesenswert?

Komisch ist eigentlich das selbst wenn ich per Scope die Baudrate auf 
ca. 100uS einstelle nur Quatsch ankommt..

Du meinst also SMOD = 1, und dann

BDRCON = 0x0E ; stop baudrateclock
SCON = 0x50
BRL = 217 ; 256-39 = 9600 baud @ 12 MHz, SMOD1 = 0
ES = 1
BDRCON |= 0x10

?
Dann müsst ich ja bei über 10k Baud liegen oder nicht?
Gast #1918777
Lesenswert?

ok, also das mit SMOD1 in der Formel passt schon. ;-)

Ich hatte übersehen, dass ich meine 5131-CPU mit X2 = 1
betreibe, also im 6-clock-Modus.
Damit verdoppelt sich die Taktfrequenz für den Bauraten-
Generator.
Für die Einstellung der richtigen Baudrate sind also
nicht nur BRL, SPD und SMOD1 zu beachten, sondern auch
X2 (in CKCON0).

Mit
X2 = 0 (default)
SMOD1 = 0 (default)
SPD = 0 (default)
BRL = 253 // 256-3
für 9600 Baud zu setzen

Ich würde bevorzugen
X2 = 1
SMOD1 = 1
SPD = 1
BRL = 178 // 256-78
ebenfalls für 9600 Baud
Gast #1918798
Lesenswert?

Also um das abzuschließen - es geht ja jetzt -
in meinem ersten Beispiel hatte ich meine X2 = 1
unterschlagen, womit bei Dir mit X2 = 0 dann
natürlich nicht 9600 Baud, sondern 4800 Baud
erzeugt wurde. Das passt zu Deiner Messung von
ca. 190 us pro Bit - genau wären es 208 us für
4800 Baud gewesen.

Die beste Näherung an die 9600 Baud bekommst Du
mit den Einstellungen
X2 = 1
SMOD1 = 1
SPD = 1 // steckt in dem BDRCON = 0x0E drin
BRL = 178 // 256-78

die Variante
X2 = 0
SMOD = 0
SPD = 0
BRL = 253 // 256-3
liefert 10416 Baud als Näherung an 9600 Baud
und verbietet sich eigentlich wegen der über
8% Abweichung.

Womit auch klar wird, warum ich gerne SMOD = 1
setze.
Und X2 = 1 verdoppelt die Leistungsfähigkeit
Deiner CPU.

Viel Spaß mit dem AT89C5131!
Gast #1918847
Lesenswert?

für die 256-78:
12 MHz  16  9600 = 78,125 Hz, näherungsweise 78 Hz
Also ist der Teiler 78, um aus 12 MHz die 9600 Baud
zu gewinnen.

Der Baudratenteiler ist aber ein Aufwärtszähler, d.h.
er wird immer beim Überlauf 255->256 neu gesetzt.
Um einen Teiler durch 78 zu erhalten, muss der Teiler
mit 256 minus 78 vorbelegt werden, damit er nach 78 Takten
"überläuft" und BRL als Startwert neu nach BRH geladen
wird.
256-78 = 178 als Startwert.
178..179..180.. usw. 255..256(=0) peng! neu laden mit
178..179.. usw.

mit 256-3 das Gleiche, der Zähler wird aber nicht mit
12 MHz gefüttert, sondern mit 12 MHz /12 /6 und danach
noch einmal /2 (SMOD1).
12 MHz /2 /6 2  16 / 9600 = 3,255 Hz, näherungsweise 3 Hz
aber viel weiter weg als 78,125 zu 78...
Der Teiler wäre daher 3, bzw. da ein Aufwärtszähler
253..254..255..256(=0), durch 253 ersetzt

Die Gurus und Egonamen in disem Forum mögen meine
unprofessionelle Zusammenfassung verzeihen...
Andere können das natürlich viel besser erklären. ;-)
#1958067
Lesenswert?

Noch ein Tip zur Nutzung der seriellen Schnittstelle in C.
Wenn man sich von der Assembler-SBUF-Fummelei löst und auf Standard-C 
Funktionen aus der stdio.h zurückgreift wird ein Programm leichter 
portierbar. Mit printf("Hallo"); wird z.B. direkt auf die serielle 
Schnittstelle gesendet. Zeichen=getch(); oder putch(Zeichen); 
funktionieren auch ganz nett. getch() macht dabei automatisch ein Echo 
(mit putch).

Nun aber zur Falle dabei: printf() und putch() prüfen das TI-Falg VOR 
dem Versenden eines Zeichens und sparen damit evtl. die Wartezeit, wenn 
der UART eh frei war. Dabei muss man aber das TI-Flag bei der 
Initialisierung einmal setzen, sonst hängt das Programm nach printf.
Also SCON = 0x52; schreiben.
(zumindest gilt das für uVision und RIDE. Beim SDCC ist putch() leer und 
muss selber programmiert werden, da hat man dann das TI-Flag selber wie 
gewünscht zu behandeln.)

tschuessle
B.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren