Hallo,
Ich habe folgende Aufgabe mit meinem ATTiny 85 (wahlweise 25 oder 45) zu
lösen: Serielle Kommunikation UART mit 57600 8N1 - vollduplex.
Da der Tiny keine Hardware UART bietet und die vorhandene Software UART
nur halbduplex kann, habe ich selber was implementiert.
- Int0 steht auf fallende Flanke und erkennt das Startbit.
- Timer 0 ist der Rx Timer und generiert alle 17µs einen Interrupt
(CTC), darin wird die Rx-Leitung gesamplet. Beim 9. mal muss ne 1
drinstehen, das Stopbit
- Timer 1 ist Tx Timer und macht vom Prinzip her das gleiche, gibt aber
Start-, Daten- und Stopbit aus.
Zum Tester der Geschichte hab ich einfach ein Echo implemetiert.
Das ganze funktioniert soweit super fuer 1 Byte mit Pause. Der Tiny ist
auch schnell genug, um die Daten in Echtzeit zubewaeltigen.
Jetzt das Problem, in der Hoffnung, das jemand hier im Forum darauf
einen guten Tip abgeben könnte.
Sobald ich 2 Byte unmittelbar nacheinnander vom PC sende, beginnt der
Tiny das 1. Byte wieder raus zu senden, während das 2. Byte gerade am
Empfangen ist. Dabei "vergisst" der Timer 0 einmal der Interrupt
auszulösen. Ich habe mir auf einem Portpin das Interruptverhalten
ausgeben lassen und es sieht so aus als ob einer der Timer-Überläufe
nicht zum Interrupt führt. Und zwar genau dann, wenn Timer 1 (Tx)
gestartet wird oder seinen 1. Interrupt hat. Dabei sollten die Timer ja
nichts miteinander zutun haben.
1 | void sio_init(void)
|
2 | {
|
3 | MCUCR |= ((1<<ISC01)|(0<<ISC00)); /*fallende flanke*/
|
4 | GIFR |= (1<<INTF0); GIMSK |= (1<<INT0); /*Int Enable*/
|
5 | /* Rx-Timer */
|
6 | TCCR0A = (0<<COM0A1)|(0<<COM0A0)|(0<<COM0B1)|(0<<COM0B0)|(1<<WGM01)|(0<<WGM00);
|
7 | OCR0A = 139; /*17,361µs = 57600 Baud >> 138,8 * 0,125µs @ 8MHZ*/
|
8 | /* Tx-Timer */
|
9 | TCCR1 = (1<<CTC1)|(1<<PWM1A)|(0<<COM1A1)|(0<<COM1A0)|(0<<CS13)|(0<<CS12)|(0<<CS11)|(0<<CS10);
|
10 | OCR1C = 139; /*17,361µs = 57600 Baud >> 138,8 * 0,125µs @ 8MHZ*/
|
11 | TIMSK |= (1<<TOIE1)|(1<<OCIE0A);
|
12 | }
|
13 |
|
14 | /* Rx - Timer */
|
15 | ISR(TIMER0_COMPA_vect)
|
16 | {
|
17 | if(sio_rx_state<8) /* datenbits 0 bis 7*/
|
18 | {
|
19 | sio_rx_state++;
|
20 | sio_rx_byte>>=1;
|
21 | if(IO_SIO_RX_GET) sio_rx_byte |= 0x80;
|
22 | }
|
23 | else
|
24 | {
|
25 | TCCR0B = 0; /*timer stoppen*/
|
26 | GIFR|=(1<<INTF0); GIMSK |= (1<<INT0); /* jetzt wieder flanke zulassen fuers naechste byte*/
|
27 | if(IO_SIO_RX_GET) /* stop-bit = 1*/
|
28 | {
|
29 | sio_in_buffer[sio_in_buffer_ptr_wr++]=sio_rx_byte;
|
30 | sio_in_buffer_ptr_wr%=SIO_IN_BUFFER_SIZE;
|
31 | }
|
32 | }
|
33 | }
|
34 |
|
35 | /* Tx Timer*/
|
36 | ISR(TIMER1_OVF_vect)
|
37 | {
|
38 | if(sio_tx_state==0) /*StartBit*/
|
39 | {
|
40 | if(sio_out_buffer_ptr_rd != sio_out_buffer_ptr_wr) /*es sind noch daten zu senden*/
|
41 | {
|
42 | IO_SIO_TX_EN; /*Startbit*/
|
43 | sio_tx_byte=sio_out_buffer[sio_out_buffer_ptr_rd++];
|
44 | sio_out_buffer_ptr_rd%=SIO_OUT_BUFFER_SIZE;
|
45 | sio_tx_state++;
|
46 | }
|
47 | else /* timer stoppen - nichts zu senden*/
|
48 | {
|
49 | TCCR1 &= ~(1<<CS10);
|
50 | }
|
51 | }
|
52 | else if(sio_tx_state<9) /* 1..8 Datenbits */
|
53 | {
|
54 | if(sio_tx_byte & 0x01) IO_SIO_TX_DIS;
|
55 | else IO_SIO_TX_EN;
|
56 | sio_tx_byte>>=1;
|
57 | sio_tx_state++;
|
58 | }
|
59 | else if(sio_tx_state<10) /*Stop-Bit*/
|
60 | {
|
61 | IO_SIO_TX_DIS;
|
62 | sio_tx_state++;
|
63 | }
|
64 | else /*Reload oder Stop Tx*/
|
65 | {
|
66 | sio_tx_state=0;
|
67 | }
|
68 | }
|
69 |
|
70 | ISR(INT0_vect) /* StartBit - Fallende Flanke*/
|
71 | {
|
72 | GIMSK &= ~(1<<INT0); /*interupt ausschalten und Timer starten nach dem 10. Bit wird der Int wieder eingeschaltet */
|
73 | TCNT0 = 0;
|
74 | sio_rx_state=0;
|
75 | TCCR0B = (0<<FOC0A)|(0<<FOC0B)|(0<<WGM02)|(0<<CS02)|(0<<CS01)|(1<<CS00); /* Timer starten - kein vorteiler */
|
76 | }
|
Schon mal vielen Danke fuer nützliche Tips