Aalso ich hab mir die AppNote AVR304 etwas angepasst und mir ein
Testprogramm geschrieben, welches Daten vom PC empfängt und an diesen
zurück gibt.
1
...
2
for(;;)
3
{
4
// Wenn Daten am SW-UART angekommen sind
5
if(swuart_getstate()==DATA_PENDING){
6
// hole diese ab
7
data=swuart_getchar();
8
// und sende sie zurück
9
swuart_putchar(data);
10
}
11
12
}
13
...
Zurück bekomme ich aber nur Datenmüll..
Interessant ist allerdings, dass dieser Datenmüll Struktur hat.
Sende ich beispielsweise die Zahl 85 (praktisch, weil binär 01010101)
bekomme ich 234 (11101010) zurück. Sende ich 175 (binär 10101010)
bekomme ich 96 (binär 01100000).
Jetzt hab ich mir den Spaß gemacht, das Empfangene in den EEPROM zu
schreiben und siehe da, genau das, was ich zurück bekomme, ist auch
gespeichert- der Sender funktioniert also problemfrei- nur am Empfang
haperts.
Die AppNote beschreibt eine einfache State machine, die durch beim
Empfang int0 getriggert wird. Da der Port bei dem von mir genutzten
Tiny25 mit am I²C hängt, den ich anderweitig brauche, hab ich einfach
mal die Dreistigkeit besessen und auf nen PCINT umgestellt- wodurch mir
zwar die Flankenmessung verloren geht, aber das Problem hab ich
hoffentlich umgangen, indem ich vor dem Aktivieren des Interrupts auf
ein High am Eingang warte..
Der PCINT aktiviert nun den Timer-Interrupt mit CTC. Zuerst wird 1.5 * T
(T=1/Baudrate) gewartet, um in der Mitte des ersten Bits zu liegen. Das
erste Bit wird empfangen. Anschließend wird der OCR auf 1T umgestellt.
Somit können sehr große Instabilitäten in der Taktung abgefangen werden.
Da der Sender ja einwandfrei funktioniert, frage ich mich, ob die
Umstellung des OCR nicht funktioniert..
Hier mal der Code der ISR()s
1
/*
2
* Timer-Interrupt Routine
3
* State-Machine über den AsynchronousStates_t
4
*/
5
ISR(TIMER0_COMPA_vect){
6
switch(state){
7
// Transmit the start bit
8
caseTRANSMIT_START_BIT:
9
...
10
break;
11
12
// Go to idle after stop bit was sent.
13
caseTRANSMIT_STOP_BIT1:
14
...
15
break;
16
17
// Go to idle after stop bit was sent.
18
caseTRANSMIT_STOP_BIT2:
19
...
20
break;
21
22
caseTRANSMIT_END:
23
...
24
break;
25
26
//Receive Byte.
27
caseRECEIVE:
28
OCR=TICKS2WAITONE;// Count one period after the falling edge is trigged.
29
//Receiving, LSB first.
30
if(SwUartRXBitCount<8){
31
SwUartRXBitCount++;
32
SwUartRXData=(SwUartRXData>>1);// Shift due to receiving LSB first.
33
if(GET_RX_PIN()!=0){
34
SwUartRXData|=0x80;// If a logical 1 is read, let the data mirror this.
35
}
36
}
37
38
//Done receiving
39
else{
40
state=DATA_PENDING;// Enter DATA_PENDING when one byte is received.
41
DISABLE_TIMER_INTERRUPT();// Disable this interrupt.
42
EXT_IFR|=(1<<INTF0);// Reset flag not to enter the ISR one extra time.
43
ENABLE_EXTERNAL0_INTERRUPT();// Enable interrupt to receive more bytes.
44
}
45
break;
46
47
// Unknown state.
48
default:
49
state=IDLE;// Error, should not occur. Going to a safe state.
50
}
51
}
1
/*
2
* Externer Interrupt- Es wird etwas empfangen..
3
*/
4
ISR(PCINT0_vect){
5
//if ((TRXPIN & (1 << RX_PIN)) == 0 ) { // beim Flankenwechsel nach low
6
state=RECEIVE;// Change state
7
DISABLE_EXTERNAL0_INTERRUPT();// Disable interrupt during the data bits.
8
9
DISABLE_TIMER_INTERRUPT();// Disable timer to change its registers.
10
TCCR_P&=~(1<<CS01);// Reset prescaler counter.
11
12
TCNT0=INTERRUPT_EXEC_CYCL;// Clear counter register. Include time to run interrupt rutine.
13
14
TCCR_P|=(1<<CS01);// Start prescaler clock.
15
16
OCR=TICKS2WAITONE_HALF;// Count one and a half period into the future.
17
18
SwUartRXBitCount=0;// Clear received bit counter.
19
CLEAR_TIMER_INTERRUPT();// Clear interrupt bits
20
ENABLE_TIMER_INTERRUPT();// Enable timer0 interrupt on again
21
//}
22
}
Die definierten Makros sind diese:
1
#define TICKS2COUNT 103 //!< Ticks between two bits.
2
#define TICKS2WAITONE 103 //!< Wait one bit period.
3
#define TICKS2WAITONE_HALF 155 //!< Wait one and a half bit period.
4
5
#define INTERRUPT_EXEC_CYCL 9 //!< Cycles to execute interrupt rutine from interrupt.
1)
In der PCINT-ISR liegt einiger Code vor dem Starten des Timers und in
der COMPA-ISR wiederum einiger Code vor dem Einlesen des Pins. Dein
Abtastpunkt ist also nicht in der Mitte, sondern reichlich nach hinten
verschoben. Der Ausgleich per INTERRUPT_EXEC_CYCL ist da ja nur ein
Tropfen auf den heißen Stein.
2)
1
EXT_IFR|=(1<<INTF0);
Wieso INTF0? Du hast doch auf PCINT umgestellt. Außerdem löscht man
gezielt ein einzelnes Bit mit einem "=", nicht mit "|=".
Das ist aber nicht der Auslöser deines aktuellen Problems, denn die
beiden Fehler "ergänzen" sich quasi. Das "|=" sorgt dafür, dass das
richtige Bit "versehentlich" mit gelöscht wird.
Aaalso ich versteh nicht wirklich, warum ich ein Bit mittels = löschen
sollte. Zumal das | ein Bit setzt ;) Löschen passiert bei der
Und-Verknüpfung mit dem Reziproken Wert
das = würde das ganze Byte verändern, ich will aber nur mit einem Bit
maskieren..
Trotzdem stimmt es, dass das Int0 ein Relikt ist.. Der Kommentar hinter
dem ist auch etwas uneindeutig für mich..
Werd mir die Interrupt-Maskierung nochmal ansehen..
TecDroiD wrote:
> Aaalso ich versteh nicht wirklich, warum ich ein Bit mittels = löschen> sollte. Zumal das | ein Bit setzt ;) Löschen passiert bei der> Und-Verknüpfung mit dem Reziproken Wert> das = würde das ganze Byte verändern, ich will aber nur mit einem Bit> maskieren.
Nein! Interruptflags werden durch Schreiben einer Eins in das
entsprechende Bit gelöscht. Mit einem "|=" löscht du daher gleich alle
Flags in dem jeweiligen Register.
Aber wie ich bereits sagte, ist das nicht der Auslöser deines Problems.
Der ist eher im Abtastzeitpunkt zu suchen.
hmmm...
1 << 4 = 0b00010000
0b10001001 |= 0b00010000 entspricht
0b10001001 | 0b00010000 = 0b10011001
die oder-verknüpfung setzt eine 1, wenn auf einem der verknüpften bits
eine 1 steht..
gut, wenn anschließend eine zuweisung passiert hab ich da gelöscht, wo
vorher eine 1 war..
TecDroiD wrote:
> gut, wenn anschließend eine zuweisung passiert hab ich da gelöscht, wo> vorher eine 1 war..
Wieso "wenn"? Die Zuweisung ist doch im "|=" enthalten, die passiert auf
jeden Fall.
Du könntest z.B. damit anfangen, das Einlesen des Pins ganz an den
Anfang der COMPA-ISR zu verlagern (noch vor das switch).
Und dann kannst du einen Blick in den Asm-Code werfen, und abzählen, wie
viele Clocks Versatz du in den beiden ISRs zusammen etwa hast. Um diesen
Wert musst du den ersten Timer-Durchlauf verkürzen.
Mit der Definition für
#define TICKS2WAITONE 103 //!< Wait one bit period.
sieht es nach gewünschter Baudrate 9600 aus.
1/9600s => ca. 0,104 ms/Bit => ca. 104 µs/Bit. Der Timer könnte dann mit
1us Ticks arbeiten, d.h. z.B. kein Prescaler und F_CPU 1 MHz. Du kannst
im Prinzip in die gleichen Probleme mit Baudratenerror kommen wie bei
einer Hardware-UART, d.h. z.B. ungenauer, temperaturabhängiger interner
RC-Oszillator...
Da ich die Initialisierung des Timers und die Taktrate des µC nicht
sehe, kann ich das aber nur raten.
Zum Ausrechnen sehe ich da kaum eine Chance. Man könnte die Bitbreite am
Oszi ausmessen und dann korrigieren. Oder empirisch vorgehen und mit
einem +/i Taster im Betrieb die OCR manipulieren. Oder den PC einen
bekannten Datenstrom senden lassen und auf dem AVR auf fehlerfreie
Erkennung synchronisieren lassen. D.h. du machst eine Kalibrierung. Btw.
da sollte es aber auch eine Appnote geben, wie man den internen
Taktgeber kalibriert.
Stefan Ernst wrote:
> ... Außerdem löscht man> gezielt ein einzelnes Bit mit einem "=", nicht mit "|=".
Einzelne Bits kann man auch zuverlässig so löschen:
1
PORTA&=~(1<<PA1)
für Pin1
und bei mehreren Bits einfach verodert anhängen:
Viel gewinnst du bei UART nicht, du brauchst weiterhin die Erkennung der
fallenden Flanke des Startbits. Und du musst weiterhin einen Timer
mitlaufen lassen, um die Abtastzeitpunkte zu erzeugen bei denen du die
Bits eines Bytes am Portpin erkennen willst. Mit einer exakteren
Taktquelle funktioniert das letztere exakter.
Pi*Daumen gerechnet wieviel Genauigkeit du brauchst:
Die benötigte Genauigkeit ist ja um die +- 1/2 Bitdauer auf den typ.
folgenden 8 Bits (8N1), wenn der erste Abtastzeitpunkt gut in der Mitte
des 2. Bits liegt. Also +- 1/16 Bitdauer worst case bei den folgenden 8
einzelnen Bits. Bei 9600 Baud dauert 1 Bit ca. 104 µs, d.h. du kannst
einen Fehler von +- 6,5 µs verschmerzen, selbst wenn er sich über die
10 Bits aufsummiert. Das sind wuchtige +- 6,25 % Toleranz bzw. +- 4,8%,
wenn man auf alle 10 Bits rechnet! Bei einer Hardware-UART sagt man,
dass 2% die Obergrenze sind. So gesehen ist der Software-UART Code sogar
robuster, oder?
Bist du jetzt eigentlich weiter gekommen, läuft die Übertragung
fehlerfrei, oder soll man noch nach deinem Code schauen?
Da ich ein wenig Familie zuhause habe, kann ich leider nicht immer so,
wie ich will.. werd aber heut abend mal nen Experimentierbrett
verdrahten und dann schauen, was es bringt, nen Quartz dran zu hängen.
Nichts desto trotz darf sich gern jemand meinen Code anschauen. Ich hab
die state machine aus der AppNote ein wenig verändert. So hat sie z.b.
zwei Stopbits.
Allerdings ist sie auch ein wenig umständlicher geworden.
also ich hab jetzt mal den prescaler von 8 auf 64 gesetzt. das ergibt
eine baudrate von 1200. trotzdem empfange ich nur müll. das realterm
zeigt mir übrigens immer einen framing error an..