Hallo,
ich versuche eine UART ähnliche Verbindung zu implementieren. Nun habe
ich jedoch Probleme beim Empfangen von Daten.
Im Grunde ist es ganz einfach. 1 Bit hat die Länge von einer etu. Eine
etu hat die Länge von 1/4 * 372/Frequenz in Sekunden. Beim Empfangen
soll erst mittels eines Pin Change Interrupts das Startbit empfangen
werden. Dann soll bis zur Mitte des ersten Bits gewartet werden, dann
das erste Bit gelesen werden und dann bis zur Mitte des zweiten Bits
gewartet werden usw..
Folgenden Code habe ich bisher:
Funktion zum Empfangen eines Bytes:
1
uint8_treceiveByte(intetu)
2
{
3
uint8_treceivedByte=0x00;
4
volatileint8_tparityCounter=0;
5
startBitFlag=0;
6
PCICR|=(1<<PCIE1);
7
PCMSK1|=(1<<PCINT14);
8
while(startBitFlag!=1);//wait for startbit
9
startBitFlag=0;
10
PCICR=0x00;//disable Pin Change Interrupt
11
PCMSK1=0x00;
12
receiveLock=1;
13
startTimer2(x);//warte bis zur Mitte des 1. Bits (PROBLEM)
14
while(receiveLock);//Abwarten bis das erste data bit ankommt
15
receivedByte|=(receivedBit<<0);
16
startTimer2(y);//warte bis zur Mitte des 2. Bits (PROBLEM)
Das Problem ist, dass ich nicht weiß welche Werte ich jeweils für x und
y einsetzen muss. Ich habe schon versucht mit Atmel Studio die Takte zu
zählen, um eine Vorstellung davon zu bekommen, wie lange die einzelnen
Aufrufe dauern. Ich habe viel hin und her gerechnet und probiert. Leider
haben meine Versuche nicht zum Erfolg geführt. Das ganze läuft auf einem
ATmega644.
Das Ziel ist es die Parameter x und y des startTimer2 durch von etu
abhängige Parameter zu ersetzen.
Bin gespannt auf eure Antworten.
Niclas
Niclas schrieb:> receivedByte |= (receivedBit << 0);
Ähm, was tut diese Zeile?
> receivedByte |= (receivedBit << i);
Dieser Code ist auf einem AVR extrem ungünstig, weil der nämlich keinen
Barrelshifter hat. Wesentlich effizienter wäre es, eine Bitmaske zu
schieben und die dann zu verodern:
1
uint8_treceiveMask=0x01;
2
:
3
:
4
while(receiveLock);
5
if(receviedBit){
6
receivedByte|=receiveMask;
7
receiveMask<<=1;// <<<< Maske schieben
8
parityCounter++;
9
}
10
startTimer2(y);
11
receiveLock=1;
12
for(inti=1;i<8;i++)
13
{
14
while(receiveLock);
15
if(receviedBit){
16
receivedByte|=receiveMask;
17
receiveMask<<=1;// <<<< Maske schieben
18
parityCounter++;
19
}
20
receiveLock=1;
Denn das ergibt statt realen 127 Schiebevorgängen nur noch 8...
> Eine etu hat die Länge von 1/4 * 372/Frequenz in Sekunden.
Was ergibt das in Echtzeit? Eher 1us oder eher 1ms?
> Das Problem ist, dass ich nicht weiß welche Werte ich jeweils für x und> y einsetzen muss.
Ich kann dein Problem ganz einfach auf die Hälfte reduzieren: du
brauchst nur den Wert von Y. Denn es gilt ja X=Y/2.
Und wenn du weißt, wie lang ein "etu" tatsächlich ist und welche
Quarzfrequenz du hast, dann kannst du ausrechnen, wie oft der
Timerinterrupt kommen muss und die Timerregister danach berechnen. Da
muss eigentlich nichts ausprobiert werden...
BTW: angesichts der sehr kreativen englischen Schreibweisen von
"Empfang" möchte ich anmerken, dass der Compiler problemlos auch
deutsche Zeichenketten interpretieren kann... ;-)
Niclas schrieb:> ich versuche eine UART ähnliche Verbindung zu implementieren.
Wenn es keine UART ist, sondern nur "ähnlich", dann erklär erstmal das
Timing. Niemand hat Lust, das vermutliche Protokoll aus einem nicht
funktionierenden Code reverse zu engineeren.
Niclas schrieb:> Eine> etu hat die Länge von 1/4 * 372/Frequenz in Sekunden.
Ich hab keine Ahnung, was etu oder 372 bedeuten soll.
Versuchs mal in Baud, Hertz oder µs anzugeben. Dann kriegt man eine
Vorstellung, wieviel CPU-Zyklen ein Bit lang ist.
Wenn die Baudrate konstant ist, kann Dir der Compiler prima alle
weiteren Konstanten bereits zur Compilezeit ausrechen. Du mußt nur die
Formeln hinschreiben (vorzugsweise als Macro).
Hier mal ne SW-UART als Beispiel:
Beitrag "Software UART mit FIFO"
Hi,
erstmal vielen Dank für die ausführlichen Antworten! Bitte lest erst den
ganzen Text von mir. Ich habe unten einen aktualisierten Code, wodurch
vieles einfacher zu verstehen ist.
Lothar M. schrieb:>> receivedByte |= (receivedBit << 0);> Ähm, was tut diese Zeile?
Diese Zeile soll das erste aus 8 Datenbits einlesen. Danach werden dann
die restlichen 7 Datenbits gelesen. (Hier habe ich noch das Paritybit
vergessen.)
Lothar M. schrieb:> Dieser Code ist auf einem AVR extrem ungünstig, weil der nämlich keinen> Barrelshifter hat
Ok, das wusste ich nicht, bzw. ich habe mir darüber überhaupt keine
Gedanken gemacht. Ich werde mir das noch einmal genau anschauen, um es
zu verstehen. Das Einsparungspotenzial scheint ja immens zu sein.
Lothar M. schrieb:> Was ergibt das in Echtzeit? Eher 1us oder eher 1ms?
In Echtzeit ergibt das nach folgender Rechnung 24,8us:
1/4 * 372/3750000 = 2,48 * 10^-5 s = 0,0000248s = 24,8us
Kann es sein, dass mein Code zu langsam ist und ich deshalb die
Wartezeiten falsch angepasst habe?
Lothar M. schrieb:> Denn es gilt ja X=Y/2.
Muss es nicht X = Y + Y/2 sein? Ich möchte ja 1,5 Bits abwarten. Das
Startbit und bis zur Mitte des ersten Bits. (X=Y/2 gilt für den Code in
diesem Beitrag.)
Lothar M. schrieb:> Quarzfrequenz
3,75MHz
Peter D. schrieb:> Ich hab keine Ahnung, was etu oder 372 bedeuten soll.
ETU bedeutet elementary time unit und beschreibt nur die Länge eines
Bits. 372 ist einfach eine Konstante.
Nochmal zum Protokoll:
Ein Bit hat die Länge von einer etu (in diesem Fall 24,8us). Es gibt ein
Startbit, 8 Datenbits und ein Paritybit. Es soll immer zur Mitte der
Bits "gelesen" werden. Siehe Bild.
Mir sind selber noch ein paar Fehler aufgefallen, nachdem Lothar nach
der einen Zeile gefragt hat. Mein aktualisierter Code sollte viel besser
verständlich sein:
Ich denke mit der aktualisierten Fassung, habe ich auch Lothars
Anmerkungen zu dieser Zeile: "receivedByte |= (receivedBit << 0);" und
die Anmerkung "X=Y/2" verstanden und verbessert.
Gruß Niclas
Hallo Niclas,
ich habe seiner Zeit den Code "Software UART mit FIFO" von Peter
Dannegger (peda) analysiert und dann verwendet.
Beitrag "Software UART mit FIFO"
Die/ seine Ideen sind dann eingeflossen in der Realisierung von "3-fach
TX Software Uart nicht blockierend".
Uart per Software benötigt sehr viel Rechenzeit bezogen auf die Baudrate
in Relation zur CPU-Taktfrequenz.
Hier als Berechnung für eine 16MHz CPU-Taktfrequenz.
Für 9600 Bit/s ergeben sich 195/1667 Takten, dies entspricht ~11,70%
Auslastung für drei Sendetasks.
Für 57600 Bit/s sieht das noch etwas anders aus,
195/278 Takten entsprechen ~70,2% Auslastung für drei Sendetasks.
Niclas schrieb:> 3,75MHzNiclas schrieb:> Ein Bit hat die Länge von einer etu (in diesem Fall 24,8us).
Das sind ja nur 93 CPU Zyklen.
Das ist sportlich, da muß man beim Programmieren schon sehr optimieren
(kein int usw.). Jede Zeile kostet CPU-Zeit, die man berücksichtigen
muß.
Wenn Du eh nur pollst, dann kannst Du schonmal den ganzen
Interruptoverhead weglassen und die IO-Bits direkt pollen und
rücksetzen. Ein Interrupt frißt schonmal mindestens 30 Zyklen.
Der ATmega644 kann doch bis 20MHz, dann wird es deutlich leichter (496
Zyklen).
> ich versuche eine UART ähnliche Verbindung zu implementieren.
Was ist der Unterschied deines Signals zu einer UART (von der der 644A
zwei Stück hat)?
Ich würde häufiger sampeln (z.B. 3 mal pro Bit) und eine
Mehrheitsentscheidung machen.