Guten Morgen allerseits,
ich habe bei einem AT89C4051 die Aufgabe, dass ich über die UART daten
empfangen und so schnell wie möglich wegschreiben muss.
ich habe für den Empfang eine ISR geschrieben (pollen des RI Flags in
der Main ohne eine ISR ist meines erachtens aus performance Gründen
nicht möglich)
Mein Problem ist nun, dass der Serial Interrupt so schnell nacheinander
auftritt, dass die die CPU in stolpern gerät (das 4. Byte wird als das
5. Interpretiert).
Meine überlegung war nun dass ich in der zeit in der der Serial
Interrupt extrem schnell nacheinander auftritt (16 Byte in Folge)
einfach in der ISR geblieben wird und nur noch das RI Flag in einer
while(RI == 0) gepollt wird bis ich das nächste mal den SBUF auslesen
kann (dadurch würde ich mir den aufwänden Taskswitch spaaren).
Am Anfang der ISR setze ich das RI Flag zurück auf Null, jedoch wird in
der ISR das RI Flag meiner meinung nach nicht mehr gesetzt.
Laut Datenblatt der 8051 Familie vermute ich nun, dass alle gleich und
niederprioren Interrupts maskierten werden solange ich noch in der ISR
bin und das Flag deswegen nicht mehr gesetzt wird.
Kann das sein?
von den neuen ATMega (also der neuen RISC Architektur) weiß ich dass es
dort möglich ist im Interrupt das gleiche Flag das diesen Interrupt
ausgelöst hat erneut in dessen ISR abzufragen und dass dieses flag auch
wieder gesetzt wird ...
Mein 2. Lösungsansatz war, die Fosz zu erhöhen (aktuell läuft die CPU
auf 16 MHz). Dabei taten sich aber neue Probleme auf.
Die Baudrate mit der Daten auf der UART ankommen beträgt 250kBaud. Von
anderen Projekten mit der 8051 Architektur weiß ich, dass ich 250kBaud
nur mit dem Timer2 als Baudrate-Generator erreichen kann.
Außer in dem Sonderfall man hat einen 16MHz Quarz als Fosz und verwendet
den Mode2 mit 1/64 Teiler das ergibt auch exakt 250kBaud. Genauso war es
bisher realisiert).
Da der 4051 eine Fosz von 24MHz abkann, habe ich nun also versucht bei
einer Fosz von 24MHz eine Baudrate von 250kBaud zu erreichen. Dabei habe
ich festgestellt dass laut www.atmel.com der 4051 nur 2 Timer hat und
nicht drei. Da ich es bisher nicht zum laufen gebracht habe vermute ich
nun dass nur Timer0 und Timer1 im 4051 implementiert sind. Diese beiden
Timer haben haben aber im Gegensatz zum Timer2 einen höheren
CLK-Prescaller vorgeschalten (ich glaub es ist ein 1/12 Teiler von Fosz
vor den beiden Timern im gegensatz zu Timer2 bei dem Fosz nur durch 2
geteilt wird).
Die Große Frage ist nun:
Gibt eines möglichkeit in der ISR das RI Flag abzufragen oder irgendwie
in der ISR einen neuen serial Interrupt mitzubekommen.
UND
Wie bekomme ich bei einer Fosz != 16MHz eine Baudrate von 250kBaud bei
einem AT89C4051 hin??
Viele dank schon mal für eure Mühen ...
Mfg Stefan
Stefan wrote:
> Mein Problem ist nun, dass der Serial Interrupt so schnell nacheinander> auftritt, dass die die CPU in stolpern gerät (das 4. Byte wird als das> 5. Interpretiert).
Bei 16Mhz und 250kBaud hast Du 53 Zyklen für den Interrupt, sollte
eigentlich dicke reichen.
Um mehr zu sagen, müßte man Deinen Code sehen.
> Meine überlegung war nun dass ich in der zeit in der der Serial> Interrupt extrem schnell nacheinander auftritt (16 Byte in Folge)> einfach in der ISR geblieben wird und nur noch das RI Flag in einer> while(RI == 0) gepollt wird bis ich das nächste mal den SBUF auslesen> kann (dadurch würde ich mir den aufwänden Taskswitch spaaren).
So aufwendig ist das beim 8051 nicht (6 Zyklen), ist ja schließlich kein
ARM.
> Am Anfang der ISR setze ich das RI Flag zurück auf Null, jedoch wird in> der ISR das RI Flag meiner meinung nach nicht mehr gesetzt.
Woher hast Du diese (falsche) Meinung?
Es wird natürlich immer gesetzt, sobald ein Byte empfangen wurde (Mitte
des 9. bzw. des Stopbits).
Peter
>Wie bekomme ich bei einer Fosz != 16MHz eine Baudrate von 250kBaud bei>einem AT89C4051 hin??
Baudrate = 16MHz / 32 * (256 - TH1)
Bei TH1 = 254 ergeben sich 250kBaud
> > Am Anfang der ISR setze ich das RI Flag zurück auf Null, jedoch wird in> > der ISR das RI Flag meiner meinung nach nicht mehr gesetzt.>> Woher hast Du diese (falsche) Meinung?>> Es wird natürlich immer gesetzt, sobald ein Byte empfangen wurde (Mitte> des 9. bzw. des Stopbits).
Also eigentlich bin ich davon ausgegangen, dass das RI flag immer
gesetzt werden muss auch wenn ich in der ISR bin.
Jetzt hab ich das auch ausprobiert und hab folgendes in der ISR gemacht
void Init(void)
{
SCON = 0x90; // UART Initialisierung Modus 2 & REN setzen
EA = 1; // globale Interruptfreigabe
RB8 = 1; // Framing Error rücksetzten
}
void RS_irq(void) interrupt 4 using 2
{
RI = 0;
(...)
while(RI != 1)
P1_1 = ~P1_1;
(...)
}
Leider musste ich am Osszi feststellen dass P1_1 mit vollgas pollt und
auch nicht mehr damit aufhören will ...
Die Überlegung mit dem Ausweichen auf einen anderen Prozessor hatte ich
auch schon. Da jedoch schon Platinen für viel geld gefertigt worden sind
wäre die eizigen möglichkeit einen Prozessor mit annähernd identischem
Pinout zu nehmen.
> >Wie bekomme ich bei einer Fosz != 16MHz eine Baudrate von 250kBaud bei> >einem AT89C4051 hin??>> Baudrate = 16MHz / 32 * (256 - TH1)>> Bei TH1 = 254 ergeben sich 250kBaud
Ich hatte das selbe Problem beim Sender die 250kBaud zu erziehlen. Dort
hatte ich jedoch einen AT89C51RD2 und dieser wunderschöne Typ der
8051Familie hat 3 Timer (Timer0, Timer1, Timer2). Aus meiner Erfahrung
bei der Arbeit mit diesem Controller weiß ich, dass Timer0 und Timer1
einen Festen CLK-Prescaller haben von 12, dadurch ist NUR mit Timer2 die
Baudrate von 250k zu erreichen (Timer2 hat einen CLK-Prescaller von nur
2). Deshalb geh ich mal davon aus, dass die beiden Timer des AT98C4051
auch den CLK/12 haben.
Ist das so richtig?
Und warum komme ich in der ISR nicht mehr aus der while(...) raus?
Im moment bin ich eigentlich der meinung, dass das Erhöhen des Fosz
nicht möglich ist, auf Grund des Timerproblems. Bitte korrigiert meine
Meinung wenn ich voll daneben lieg.
Eine weiter Idee die mir gekommen ist, wäre nun die ISR also
Inline-Assembler zu schreiben. Ich denke die dadurch höhere effizienz
des codes könnte schon reichen. Problem beim der sache:
1. ich kann kein Assembler
2. das Assembler was mir in den kopf gehämmert wurde hasse ich seither
:-D
Ganz davon abgesehen verstehe ich nicht warum das RI Flag scheinbar
nicht mehr gesetzt wird ...
Achja
schonmal ein ganz dickes DANKESCHÖN an euch alle für eure Mühen
Ich hab in diesem Forum schon bei vielen Problemen Lösungen gefunden
oder richtig gute Hilfe bekommen.
Bin wirklich begeister ...
Gruß Stefan
Stefan wrote:
> Leider musste ich am Osszi feststellen dass P1_1 mit vollgas pollt und> auch nicht mehr damit aufhören will ...
Kann man an diesen zusammenhanglosen Codefragmenten überhaupt nicht
erkennen.
Du mußt schon einen lauffähigen Testcode posten (als Anhang!).
Und hau mal das "using 2" weg, das bringt nichts (höchstens Probleme).
> Die Überlegung mit dem Ausweichen auf einen anderen Prozessor hatte ich> auch schon.
Solange Du nicht weißt, wo das Problem liegt, bringt es nichts. Das
Problem kann dann auf dem anderen MC genauso auftreten.
> 2). Deshalb geh ich mal davon aus, dass die beiden Timer des AT98C4051> auch den CLK/12 haben.> Ist das so richtig?
Ja.
> Und warum komme ich in der ISR nicht mehr aus der while(...) raus?
Code zeigen, ich kann nicht hellsehen.
> Im moment bin ich eigentlich der meinung, dass das Erhöhen des Fosz> nicht möglich ist, auf Grund des Timerproblems. Bitte korrigiert meine> Meinung wenn ich voll daneben lieg.
Ja, beim AT89C4051 geht nur 16Mhz für 250kBaud.
> Eine weiter Idee die mir gekommen ist, wäre nun die ISR also> Inline-Assembler zu schreiben. Ich denke die dadurch höhere effizienz> des codes könnte schon reichen.
Meistens bringt Umstellen des Programmablaufs wesentlich mehr, als
Assembler.
> Ganz davon abgesehen verstehe ich nicht warum das RI Flag scheinbar> nicht mehr gesetzt wird ...
Du sendest dem AT89C4051 aber schon mehrere Bytes?
Peter
Erst mal Guten Morgen allerseits,
Tut mir wirklich leid, dass ich nicht den vollständigen Code
veröffentlichen kann, aber da dieser im Rahmen eines Praxissemesters
entstanden ist, kann ich das leider nicht tun (der Chef hat da deutlich
was dagegen xD)
Zur Menge der Bytes...
Der datenverkehr sieht so aus, das immer als frame mehrere Hundert bytes
gesendet werden von welchen ich mir ein paar wenige rauspicken muss und
intern auf dem CPU-RAM wegschreiben muss. Nach jedem Frame gibt es eine
ausreichende Pause um die empfangenen Daten zu verarbeiten und auf den
nächsten Frame zu warten (wird durch das RB8-Bit erkannt)
Solange ich keine Daten speichern muss klappt das ganze wunderbar,
sobald ich jedoch mehr als 4 bytes speicher muss, stolpert die Cpu und
das 5. Byte wird als 4. interpretiert.
Mir ist natürlich klar dass man (speziall Peter) mir jetzt nicht mehr so
wirklich weiterhelfen kann, aber wenn ich schon mal weiß, dass auch wenn
sich die CPU in der ISR befindet das RI-Flag gesetzt werden muss, dann
ist mir schon sehr geholfe und ich hab nen Punkt wo ich ansetzen kann.
Auch danke an Peter für die klaren Worte bzgl. Timer & Fosz. Damit kann
ich diese Lösung auch schon mal ausschließen.
Ich danke euch vielmals
Stefan
Stefan wrote:
> Tut mir wirklich leid, dass ich nicht den vollständigen Code> veröffentlichen kann
Den will ja auch keiner.
Es ging erstmal nur um den vollständigen Testcode, wo Dein Problem mit
dem RI-Polling auftritt.
Für ne generelle Einschätzung, ob Du ein Problem hast, braucht man aber
den kompletten UART-Interrupthandler inclusive aller darin aufgerufenen
Unterfunktionen.
Auch muß man wissen, ob Du noch weitere Interrupts hast, die nicht eine
niedere Priorität als der UART-Interrupt haben.
Wenn Du lange Zeiten hast, wo generell Interrupts gesperrt sind oder
andere lange Interrupts gleicher Priorität den UART-Interrupt
verhindern, hat der natürlich kein Chance mehr.
> sobald ich jedoch mehr als 4 bytes speicher muss, stolpert die Cpu und> das 5. Byte wird als 4. interpretiert.
Kommt drauf an, ob die CPU jedesmal x-dimensionale Feldindexe neu
berechnen muß oder einfach linear per Pointer zugreift.
Peter