Beim Versuch einen Raspberry PI mit einem Atmega8 per USART zu verbinden
bin ich auf folgendes Problem gestoßen: Um die Programmierung des
Atmega8 zu testen habe ich den Atmega8 auf RXD mit seinem eigenen TXD
gefüttert. Das funktioniert auch im Prinzip, nur habe ich mit einer
seltsamen Zeichenverdopplung zu kämpfen. Dazu folgender Code (mit dem
Versuch nur das Wesentliche darzustellen):
Interruptroutine für USART RX 1 | .def data = r20
| 2 | .def flag = r21
| 3 |
| 4 | usartrx: in data, UDR ; empfangenes Byte nach data sichern
| 5 | ser flag ; flag für Hauptprogramm setzen
| 6 | reti
|
Hauptprogramm Variante 1 (mit sporadischer Zeichenverdopplung): 1 | cli ; Interrupts generell im folgenden zugelassen
| 2 | display: tst flag ; Warten bis Interruptroutine das flag setzt
| 3 | breq display
| 4 | clr flag
| 5 | mov temp, data
| 6 | ; ... Die übliche LCD Ansteuerung zur Anzeige von temp
| 7 | rjmp display
|
Hauptprogramm Variante 2 (funktioniert): 1 | loop: cli ; Auskommentieren führt auch hier zur Zeichenverdopplung
| 2 | tst flag
| 3 | brne display
| 4 | sei ; Dem Interrupt auch eine Chance geben
| 5 | rjmp loop
| 6 |
| 7 | display: clr flag
| 8 | mov temp, data
| 9 | sei
| 10 | ; ... Die übliche LCD Ansteuerung zur Anzeige von temp
| 11 | rjmp loop
|
Warum kommt es in Variante 1 zu diesen seltsamen Zeichenverdopplungen?
Klar, dass das Interrupt-Flag in Variante 2 ein Mutex für 'flag' und
'data' ist, nur kann ich mir nicht erklären, dass ich Zeichen doppelt
sehe (nein es ist nicht das Bier) sondern eher würde ich vermuten dass
welche verschluckt werden. Das ganze geschieht bei absolut moderaten
Bitraten (9600 Baud) und alle paar Sekunden ein Zeichen.
Klassischer Fehler, der Interrupt sichert das SREG nicht, ändert es
aber.
Peter Dannegger schrieb:
> Klassischer Fehler, der Interrupt sichert das SREG nicht, ändert es
> aber.
Nein, das ist es nicht. Weder 'in' noch 'ser' ändern das SREG. Die
Variante. 1 | usartrx: push temp
| 2 | in temp, SREG
| 3 | in data, UDR
| 4 | ser flag
| 5 | out SREG, temp
| 6 | pop temp
| 7 | reti
|
verhält sich ganz genauso.
Cube S. schrieb:
> eher würde ich vermuten dass
> welche verschluckt werden
Ja denn da ist eine Race Condition zwischen Abfrage und Löschen des
Flags bei Variante 1. Wenn ich mal unterstelle, dass das cli eigentlich
ein sei ist? Oder wo ist das sei?
EDIT: so wie oben dürfte es ja eher zu gar keinem Zeichen kommen...
> EDIT: so wie oben dürfte es ja eher zu gar keinem Zeichen kommen...
Kommt es aber. Die 'sei' sind durchaus vorhanden (in Variante 1 habe ich
es allerdings fälschlicherweise als 'cli' getarnt (der Kommentar
widerspricht dem). Variante 2 funktioniert ohne Probleme.
Sprich: In Variante 1 sind Interrupts generell zugelassen, in Variante 2
ist es so, dass Zugriffe auf 'data' und 'flag' quasi atomar sind.
Genau so hatte ich das ja vermutet, umso mehr stimme ich Dir zu, dass da
eigentlich höchstens was verloren gehen kann. TOCTTOU in Bezug auf das
Flag selbst als auch die Daten: 1 | tst flag
| 2 | ; ...
| 3 | clr flag
| 4 | mov temp, data
|
bei aktiviertem Interrupt sieht flasch aus.
Wie es stattdessen aber zu einer Verdopplung kommen kann, würde mich
auch interessieren. Blöde Neugier :)
Wird der ISR zu oft angesprungen oder sonstwie der Inhalt von r21
vermüllt?
Cube S. schrieb:
> Dazu folgender Code (mit dem
> Versuch nur das Wesentliche darzustellen):
Solange Du den Fehler nicht weißt, weißt Du auch nicht, was wesentlch
ist!
Zeig ein komplettes Programm (als Anhang) und nichts aus dem Gedächtnis,
sondern das exakte, getestete Programm mit stimmenden Kommentaren.
Sonst raten wir noch in 100 Jahren rum.
Gerne hier auch das komplette Programm so wie es fehlerlos funktioniert.
Allein das auskommentieren des 'cli' nach dem 'loop:'-Label zeigt den
Effekt der sporadischen Zeichenverdopplung. 1 | .include "m8def.inc"
| 2 |
| 3 |
| 4 | rjmp main ; Reset Handler
| 5 | rjmp noint ; IRQ0 Handler
| 6 | rjmp noint ; IRQ1 Handler
| 7 | rjmp noint ; Timer2 Compare Handler
| 8 | rjmp noint ; Timer2 Overflow Handler
| 9 | rjmp noint ; Timer1 Capture Handler
| 10 | rjmp noint ; Timer1 CompareA Handler
| 11 | rjmp noint ; Timer1 CompareB Handler
| 12 | rjmp timer1ov ; Timer1 Overflow Handler
| 13 | rjmp noint ; Timer0 Overflow Handler
| 14 | rjmp noint ; SPI Transfer Complete Handler
| 15 | rjmp usartrx ; USART RX Complete Handler
| 16 | rjmp noint ; UDR Empty Handler
| 17 | rjmp usarttx ; USART TX Complete Handler
| 18 | rjmp noint ; ADC Conversion Complete Handler
| 19 | rjmp noint ; EEPROM Ready Handler
| 20 | rjmp noint ; Analog Comparator Handler
| 21 | rjmp noint ; Two-wire Serial Interface Handler
| 22 | rjmp noint ; Store Program Memory Ready Handler
| 23 |
| 24 |
| 25 | .def temp = r19
| 26 | .def data = r20
| 27 | .def flag = r21
| 28 | .def count = r22
| 29 | .def ccount = r23
| 30 |
| 31 | main: ldi temp, low(ramend)
| 32 | out spl, temp
| 33 | ldi temp, high(ramend)
| 34 | out sph, temp
| 35 |
| 36 | rcall lcd_init
| 37 |
| 38 |
| 39 | ; Timer1
| 40 | ; ------
| 41 | clr temp
| 42 | out TCNT1H, temp
| 43 | out TCNT1L, temp
| 44 | ldi temp, (2<<CS10)
| 45 | out TCCR1B, temp
| 46 | ldi temp, (1<<TOIE1)
| 47 | out TIMSK, temp
| 48 | ldi temp, (1<<TOV1)
| 49 | out TIFR, temp
| 50 |
| 51 | ldi ccount, 'A'
| 52 |
| 53 | ; USART initialization
| 54 | ; --------------------
| 55 |
| 56 | clr temp
| 57 | out UBRRH, temp
| 58 | ldi temp, 12
| 59 | out UBRRL, temp
| 60 |
| 61 | ldi temp, (1<<U2X)
| 62 | out UCSRA, temp
| 63 | ldi temp, (1<<RXEN)|(1<<RXCIE)|(1<<TXEN)|(1<<TXCIE)
| 64 | out UCSRB, temp
| 65 | ldi temp, (1<<URSEL)|(3<<UCSZ0)|(0<<UPM0)
| 66 | out UCSRC, temp
| 67 | ldi temp, (1<<RXC)|(1<<TXC)
| 68 | out UCSRA, temp
| 69 |
| 70 | clr flag
| 71 | clr count
| 72 |
| 73 | loop: cli
| 74 | tst flag
| 75 | brne display
| 76 | sei
| 77 | rjmp loop
| 78 |
| 79 | display: mov lcd_temp, data
| 80 | clr flag
| 81 | sei
| 82 |
| 83 | rcall lcd_data
| 84 | inc count
| 85 | cpi count, 8
| 86 | brlt loop
| 87 | clr count
| 88 | ldi lcd_temp, $80
| 89 | rcall lcd_cmd
| 90 | ;rcall lcd_5ms
| 91 |
| 92 | rjmp loop
| 93 |
| 94 | noint: reti
| 95 |
| 96 | ; --------------------------------------
| 97 |
| 98 |
| 99 | usartrx: push temp
| 100 | in temp, SREG
| 101 | in data, UDR
| 102 | ser flag
| 103 | out SREG, temp
| 104 | pop temp
| 105 | reti
| 106 |
| 107 | usarttx:
| 108 | reti
| 109 |
| 110 | ; --------------------------------------
| 111 |
| 112 | timer1ov: out UDR, ccount
| 113 | inc ccount
| 114 | cpi ccount, 'z'+1
| 115 | brlt timer1l1
| 116 | ldi ccount, 'A'
| 117 | timer1l1: reti
| 118 |
| 119 | ; --------------------------------------
| 120 |
| 121 | lcd_hex: push lcd_temp
| 122 | push zl
| 123 | push zh
| 124 | push r0
| 125 |
| 126 | push lcd_temp
| 127 | swap lcd_temp
| 128 | andi lcd_temp,15
| 129 | rcall lcd_nibble
| 130 | pop lcd_temp
| 131 | andi lcd_temp,15
| 132 | rcall lcd_nibble
| 133 |
| 134 | pop r0
| 135 | pop zh
| 136 | pop zl
| 137 | pop lcd_temp
| 138 | ret
| 139 |
| 140 | lcd_nibble: ldi zl, low(2*hex)
| 141 | ldi zh, high(2*hex)
| 142 | add zl, lcd_temp
| 143 | clr lcd_temp
| 144 | adc zh, lcd_temp
| 145 | lpm
| 146 | mov lcd_temp, r0
| 147 | rcall lcd_data
| 148 | ret
| 149 |
| 150 | hex: .db "0123456789ABCDEF"
| 151 |
| 152 |
| 153 | ; --------------------------------------
| 154 |
| 155 | .equ lcd_port = PORTC
| 156 | .equ lcd_ddr = DDRC
| 157 | .equ lcd_en = 5
| 158 | .equ lcd_rs = 4
| 159 |
| 160 | .def lcd_temp = r16
| 161 | .def lcd_c1 = r17
| 162 | .def lcd_c2 = r18
| 163 |
| 164 | lcd_init: push lcd_temp
| 165 | ldi lcd_temp, 0x7f
| 166 | out lcd_ddr, lcd_temp
| 167 | ldi lcd_temp, 50
| 168 | lcd_init1: rcall lcd_5ms
| 169 | dec lcd_temp
| 170 | brne lcd_init1
| 171 | ldi lcd_temp, 3
| 172 | out lcd_port, lcd_temp
| 173 | rcall lcd_enable
| 174 | rcall lcd_5ms
| 175 | rcall lcd_5ms
| 176 | rcall lcd_5ms
| 177 | rcall lcd_5ms
| 178 | rcall lcd_enable
| 179 | rcall lcd_5ms
| 180 | rcall lcd_enable
| 181 | rcall lcd_5ms
| 182 | ldi lcd_temp, 2
| 183 | out lcd_port, lcd_temp
| 184 | rcall lcd_enable
| 185 | rcall lcd_5ms
| 186 |
| 187 | ldi lcd_temp, 0x28 ; Function Set
| 188 | rcall lcd_cmd
| 189 | ldi lcd_temp, 0x08 ; Display Off
| 190 | rcall lcd_cmd
| 191 | ldi lcd_temp, 0x01 ; Clear Display
| 192 | rcall lcd_cmd
| 193 | rcall lcd_5ms
| 194 | ldi lcd_temp, 0x06 ; Entry Mode Set
| 195 | rcall lcd_cmd
| 196 | rcall lcd_5ms
| 197 | ldi lcd_temp, 0x0C ; Display On
| 198 | rcall lcd_cmd
| 199 |
| 200 | pop lcd_temp
| 201 | ret
| 202 |
| 203 | lcd_cmd: push lcd_temp
| 204 | push lcd_temp
| 205 | swap lcd_temp
| 206 | andi lcd_temp, 0b1111
| 207 | out lcd_port, lcd_temp
| 208 | rcall lcd_enable
| 209 | pop lcd_temp
| 210 | andi lcd_temp, 0b1111
| 211 | out lcd_port, lcd_temp
| 212 | rcall lcd_enable
| 213 | rcall lcd_50us
| 214 | pop lcd_temp
| 215 | ret
| 216 |
| 217 | lcd_data: push lcd_temp
| 218 | push lcd_temp
| 219 | swap lcd_temp
| 220 | andi lcd_temp, 0b1111
| 221 | sbr lcd_temp, 1<<lcd_rs
| 222 | out lcd_port, lcd_temp
| 223 | rcall lcd_enable
| 224 | pop lcd_temp
| 225 | andi lcd_temp, 0b1111
| 226 | sbr lcd_temp, 1<<lcd_rs
| 227 | out lcd_port, lcd_temp
| 228 | rcall lcd_enable
| 229 | rcall lcd_50us
| 230 | pop lcd_temp
| 231 | ret
| 232 |
| 233 | lcd_enable: sbi lcd_port, lcd_en
| 234 | nop
| 235 | nop
| 236 | nop
| 237 | cbi lcd_port, lcd_en
| 238 | ret
| 239 |
| 240 | lcd_50us: push lcd_temp
| 241 | ldi lcd_temp, 66
| 242 | lcd_50us1: dec lcd_temp
| 243 | brne lcd_50us1
| 244 | pop lcd_temp
| 245 | ret
| 246 |
| 247 | lcd_5ms: push lcd_c1
| 248 | push lcd_c2
| 249 | ldi lcd_c1, $21
| 250 | lcd_5ms1: ldi lcd_c2, $C9
| 251 | lcd_5ms2: dec lcd_c2
| 252 | brne lcd_5ms2
| 253 | dec lcd_c1
| 254 | brne lcd_5ms1
| 255 | pop lcd_c2
| 256 | pop lcd_c1
| 257 | ret
|
> tst flag
> timer1ov: out UDR, ccount
> inc ccount
> cpi ccount, 'z'+1
> brlt timer1l1
> ldi ccount, 'A'
> brne display
Malte S. schrieb:
> bei aktiviertem Interrupt sieht flasch aus.
Da stimme ich absolut zu, deswegen habe ich ja auch die Variante mit den
gesperrten Interrupts gegenübergestellt. Nur bei der Baudrate und der
Häufigkeit von selbst gesendeten Zeichen hätte ich das "durchgehen
lassen". Und ich finde einfach die Erklärung nicht warum es so ist wie
es ist.
wenn's das war, wovon ich erstmal ausgehe, dann war
Peter Dannegger schrieb:
> Klassischer Fehler, der Interrupt sichert das SREG nicht, ändert es
> aber.
ins schwarze. Nur ohne den schuldigen ISR auf dem Silbertablett :)
Malte S. schrieb:
> ins schwarze
Treffer, versenkt. Das war's. Besten Dank auch. Gute Güte, das sollte ja
nur zum Testen sein. Naja gescheiter wird man durch scheitern.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|