]>
Commit | Line | Data |
---|---|---|
1 | ; Serial interface using the ATmega8/88 USART. | |
2 | ; This is part of the Z80-CP/M emulator written by Sprite_tm. | |
3 | ; | |
4 | ; Copyright (C) 2010 Leo C. | |
5 | ; | |
6 | ; This file is part of avrcpm. | |
7 | ; | |
8 | ; avrcpm is free software: you can redistribute it and/or modify it | |
9 | ; under the terms of the GNU General Public License as published by | |
10 | ; the Free Software Foundation, either version 3 of the License, or | |
11 | ; (at your option) any later version. | |
12 | ; | |
13 | ; avrcpm is distributed in the hope that it will be useful, | |
14 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | ; GNU General Public License for more details. | |
17 | ; | |
18 | ; You should have received a copy of the GNU General Public License | |
19 | ; along with avrcpm. If not, see <http://www.gnu.org/licenses/>. | |
20 | ; | |
21 | ; $Id$ | |
22 | ; | |
23 | ||
24 | #ifdef __ATmega8__ | |
25 | #error "ATmega8 is not supported (yet)! Please update this driver, or buy an ATmega88." | |
26 | #endif | |
27 | ||
28 | #define SSER_BIT_TC (F_CPU+BAUD/2) / BAUD | |
29 | #define SSER_CHAR_TC (10 * 1000 / BAUD) + 2 | |
30 | ||
31 | #define RXBUFMASK RXBUFSIZE-1 | |
32 | #define TXBUFMASK TXBUFSIZE-1 | |
33 | ||
34 | .dseg | |
35 | ||
36 | srx_state: | |
37 | .byte 1 | |
38 | srx_char_to: | |
39 | .byte 1 | |
40 | srx_char_time: | |
41 | .byte 1 | |
42 | srx_dr: | |
43 | .byte 1 | |
44 | srx_lastedge: | |
45 | .byte 2 | |
46 | stx_bitcount: | |
47 | .byte 1 | |
48 | stx_dr: | |
49 | .byte 1 | |
50 | rxcount: | |
51 | .byte 1 | |
52 | rxidx_w: | |
53 | .byte 1 | |
54 | rxidx_r: | |
55 | .byte 1 | |
56 | txcount: | |
57 | .byte 1 | |
58 | txidx_w: | |
59 | .byte 1 | |
60 | txidx_r: | |
61 | .byte 1 | |
62 | rxfifo: | |
63 | .byte RXBUFSIZE | |
64 | txfifo: | |
65 | .byte TXBUFSIZE | |
66 | ||
67 | ||
68 | .cseg | |
69 | ||
70 | ; Init | |
71 | uart_init: | |
72 | ||
73 | ; - Init clock/timer system and serial port | |
74 | ||
75 | ; Init timer 1 as | |
76 | ; - Soft UART TX (OC1A/OCR1A). | |
77 | ; - Soft UART RX (ICP1/ICR1). | |
78 | ; - 1ms System timer is already configured at this point. | |
79 | ||
80 | ||
81 | cbi P_TXD-1,TXD ;TXD pin as input | |
82 | ldi temp,(1<<COM1A1)|(1<<COM1A0) ;OC1A high on compare match (UART TX) | |
83 | ldi temp2,(1<<FOC1A) ;force compare match | |
84 | outm8 TCCR1A,temp | |
85 | outm8 TCCR1C,temp2 | |
86 | sbi P_TXD-1,TXD ;TXD pin now output (OC1A) | |
87 | ||
88 | ldi temp,(1<<ICF1) ;clear pending input capture int | |
89 | out TIFR1,temp ; | |
90 | inm8 temp,TIMSK1 ; | |
91 | ori temp,(1<<ICIE1) ;Enable input capture int. (UART RX) | |
92 | outm8 TIMSK1,temp ; | |
93 | ||
94 | ldi temp,SSER_CHAR_TC ;Character TO | |
95 | sts srx_char_time,temp | |
96 | ||
97 | ret | |
98 | ||
99 | ;------------------------------------------------------------------ | |
100 | ||
101 | .cseg | |
102 | ||
103 | ; Timer/Counter1 Input Capture interrupt | |
104 | ||
105 | INTERRUPT ICP1addr | |
106 | ||
107 | push temp | |
108 | in temp,sreg | |
109 | push temp | |
110 | push zh | |
111 | push zl | |
112 | inm8 zl,ICR1L | |
113 | inm8 zh,ICR1H | |
114 | push temp2 | |
115 | ldi temp2,(1<<ICES1) | |
116 | inm8 temp,TCCR1B | |
117 | eor temp,temp2 ;toggle edge | |
118 | outm8 TCCR1B,temp | |
119 | ldi temp,(1<<ICF1) ;clear pending int | |
120 | out TIFR1,temp | |
121 | ||
122 | #if 0 | |
123 | lds temp,srx_state | |
124 | subi temp,-'0' | |
125 | rcall uartputc | |
126 | lds temp,srx_dr | |
127 | rcall printhex | |
128 | #endif | |
129 | lds temp,srx_state | |
130 | cpi temp,0 | |
131 | brne srxi_S1 | |
132 | ||
133 | ; State 0: Wait for start bit | |
134 | ||
135 | sts srx_lastedge,zl ;save beginning of start bit | |
136 | sts srx_lastedge+1,zh | |
137 | ; movw srx_lastedgel,zl | |
138 | sts srx_dr,_0 | |
139 | ldi temp,1 | |
140 | sts srx_state,temp | |
141 | lds temp,srx_char_time | |
142 | sts srx_char_to,temp | |
143 | sbis P_RXD-2,RXD ;RXD still low? | |
144 | rjmp srxi_end | |
145 | ldi zl,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge | |
146 | outm8 TCCR1B,zl | |
147 | ldi zh,(1<<ICF1) ;clear pending int | |
148 | out TIFR1,zh | |
149 | sts srx_state,_0 | |
150 | sts srx_char_to,_0 | |
151 | rjmp srxi_end | |
152 | ||
153 | srxi_S1: | |
154 | cpi temp,1 | |
155 | brne srxi_S2 | |
156 | ||
157 | ; State 1: Check start bit (and collect 0-bits) | |
158 | ||
159 | lds temp,srx_lastedge | |
160 | lds temp2,srx_lastedge+1 | |
161 | sts srx_lastedge,zl | |
162 | sts srx_lastedge+1,zh | |
163 | ||
164 | ; movw temp,srx_lastedgel | |
165 | ; movw srx_lastedgel,zl | |
166 | ||
167 | sub zl,temp | |
168 | sbc zh,temp2 | |
169 | subi zl,low ((SSER_BIT_TC+1)/2) | |
170 | sbci zh,high((SSER_BIT_TC+1)/2) | |
171 | brcs srxi_sberr | |
172 | ||
173 | ; mov temp,zh | |
174 | ; rcall printhex | |
175 | ; mov temp,zl | |
176 | ; rcall printhex | |
177 | ||
178 | ldi temp,0x80 | |
179 | srxi_1l: | |
180 | subi zl,low(SSER_BIT_TC) | |
181 | sbci zh,high(SSER_BIT_TC) | |
182 | brcs srxi_1be | |
183 | lsr temp | |
184 | brcc srxi_1l | |
185 | ||
186 | subi zl,low(SSER_BIT_TC) ; stop bit? | |
187 | sbci zh,high(SSER_BIT_TC) | |
188 | brcc srxi_1fe | |
189 | rjmp srxi_complete0 ; ok, x00 (^@) received | |
190 | srxi_1fe: | |
191 | sts srx_char_to,_0 ; no stop bit --> framing error --> break | |
192 | sts srx_state,_0 | |
193 | sbr intstat,(1<<i_break) ; | |
194 | sts rxcount,_0 ;clear rx buffer | |
195 | sts rxidx_w,_0 | |
196 | sts rxidx_r,_0 | |
197 | ||
198 | rjmp srxi_end | |
199 | ||
200 | srxi_1be: | |
201 | sts srx_dr,temp | |
202 | ldi temp,2 | |
203 | sts srx_state,temp | |
204 | rjmp srxi_end | |
205 | ||
206 | srxi_sberr: | |
207 | ldi temp,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge | |
208 | outm8 TCCR1B,temp | |
209 | ldi temp,(1<<ICF1) ;clear pending int | |
210 | out TIFR1,temp | |
211 | sts srx_state,_0 ;next state | |
212 | #if 1 | |
213 | ldi temp,'?' | |
214 | rcall uartputc | |
215 | subi zl,low (-(SSER_BIT_TC+1)/2) | |
216 | sbci zh,high(-(SSER_BIT_TC+1)/2) | |
217 | mov temp,zh | |
218 | rcall printhex | |
219 | mov temp,zl | |
220 | rcall printhex | |
221 | #endif | |
222 | rjmp srxi_end | |
223 | ||
224 | srxi_S2: | |
225 | cpi temp,2 | |
226 | brne srxi_S3 | |
227 | ||
228 | ; State 2: collect 1-bits | |
229 | ||
230 | lds temp,srx_lastedge | |
231 | lds temp2,srx_lastedge+1 | |
232 | sts srx_lastedge,zl | |
233 | sts srx_lastedge+1,zh | |
234 | ||
235 | ; movw temp,srx_lastedgel | |
236 | ; movw srx_lastedgel,zl | |
237 | ||
238 | sub zl,temp | |
239 | sbc zh,temp2 | |
240 | subi zl,low ((SSER_BIT_TC+1)/2) | |
241 | sbci zh,high((SSER_BIT_TC+1)/2) | |
242 | ||
243 | lds temp,srx_dr | |
244 | srxi_2l: | |
245 | sec ;one more 1 bit | |
246 | ror temp | |
247 | brcs srxi_complete1 ;8 bits recieved | |
248 | subi zl,low(SSER_BIT_TC) | |
249 | sbci zh,high(SSER_BIT_TC) | |
250 | brcc srxi_2l | |
251 | ||
252 | sts srx_dr,temp | |
253 | ldi temp,3 | |
254 | sts srx_state,temp | |
255 | rjmp srxi_end | |
256 | ||
257 | srxi_complete1: | |
258 | ldi temp2,1 ;We are in start bit now. | |
259 | sts srx_state,temp2 | |
260 | lds temp2,srx_char_time | |
261 | sts srx_char_to,temp2 | |
262 | rjmp srxi_complete | |
263 | ||
264 | srxi_S3: | |
265 | cpi temp,3 | |
266 | brne srxi_S4 | |
267 | ||
268 | ; State 3: collect 0-bits | |
269 | ||
270 | lds temp,srx_lastedge | |
271 | lds temp2,srx_lastedge+1 | |
272 | sts srx_lastedge,zl | |
273 | sts srx_lastedge+1,zh | |
274 | ||
275 | ; movw temp,srx_lastedgel | |
276 | ; movw srx_lastedgel,zl | |
277 | ||
278 | sub zl,temp | |
279 | sbc zh,temp2 | |
280 | subi zl,low ((SSER_BIT_TC+1)/2) | |
281 | sbci zh,high((SSER_BIT_TC+1)/2) | |
282 | ||
283 | lds temp,srx_dr | |
284 | srxi_3l: | |
285 | ;one more 0 bit | |
286 | lsr temp | |
287 | brcs srxi_complete0 ;8 bits recieved | |
288 | subi zl,low(SSER_BIT_TC) | |
289 | sbci zh,high(SSER_BIT_TC) | |
290 | brcc srxi_3l | |
291 | ||
292 | sts srx_dr,temp | |
293 | ldi temp,2 | |
294 | sts srx_state,temp | |
295 | rjmp srxi_end | |
296 | ||
297 | srxi_S4: | |
298 | ldi zl,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge | |
299 | outm8 TCCR1B,zl | |
300 | ldi zl,(1<<ICF1) ;clear pending int | |
301 | sts srx_state,_0 ;next state | |
302 | rjmp srxi_end | |
303 | ||
304 | srxi_complete0: | |
305 | sts srx_char_to,_0 ;clear timeout | |
306 | sts srx_state,_0 ;next state | |
307 | srxi_complete: | |
308 | #if 0 | |
309 | ldi zl,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge | |
310 | outm8 TCCR1B,zl | |
311 | ldi zl,(1<<ICF1) ;clear pending int | |
312 | out TIFR1,zl | |
313 | #endif | |
314 | ||
315 | ; Save received character in a circular buffer. Do nothing if buffer overflows. | |
316 | ||
317 | lds zh,rxcount ;2 if rxcount < RXBUFSIZE | |
318 | cpi zh,RXBUFSIZE ;1 (room for at least 1 char?) | |
319 | brsh srxi_ov ;1 | |
320 | inc zh ;1 | |
321 | sts rxcount,zh ;2 rxcount++ | |
322 | ||
323 | ldi zl,low(rxfifo) ;1 | |
324 | lds zh,rxidx_w ;2 | |
325 | add zl,zh ;1 | |
326 | inc zh ;1 | |
327 | andi zh,RXBUFMASK ;1 | |
328 | sts rxidx_w,zh ;2 rxidx_w = ++rxidx_w % RXBUFSIZE | |
329 | ldi zh,high(rxfifo) ;1 | |
330 | adc zh,_0 ;1 | |
331 | st z,temp ;2 rxfifo[rxidx_w] = char | |
332 | srxi_ov: ;=19 endif | |
333 | ||
334 | srxi_end: | |
335 | pop temp2 | |
336 | pop zl | |
337 | pop zh | |
338 | pop temp | |
339 | out sreg,temp | |
340 | pop temp | |
341 | reti | |
342 | ||
343 | ||
344 | ;---------------------------------------------------------------------- | |
345 | ||
346 | .cseg | |
347 | ||
348 | ; Timer/Counter1 Compare Match A interrupt | |
349 | ||
350 | INTERRUPT OC1Aaddr | |
351 | ||
352 | push zl | |
353 | in zl,sreg | |
354 | push zl | |
355 | push zh | |
356 | ||
357 | inm8 zl,OCR1AL | |
358 | inm8 zh,OCR1AH | |
359 | subi zl,low(-SSER_BIT_TC) | |
360 | sbci zh,high(-SSER_BIT_TC) | |
361 | outm8 OCR1AH,zh | |
362 | outm8 OCR1AL,zl | |
363 | ||
364 | lds zl,stx_bitcount | |
365 | dec zl | |
366 | brpl stxi_nextbit | |
367 | ||
368 | ; bit counter was 0, more characters? | |
369 | ||
370 | stxi_nxtchar: | |
371 | lds zl,txcount ;if txcount != 0 | |
372 | dec zl | |
373 | brmi stxi_dis | |
374 | ||
375 | ; get next char | |
376 | sts txcount,zl ; --txcount | |
377 | push temp ; | |
378 | ldi zl,low(txfifo) ; | |
379 | ldi zh,high(txfifo) ; | |
380 | lds temp,txidx_r ; | |
381 | add zl,temp ; | |
382 | adc zh,_0 | |
383 | inc temp ; | |
384 | andi temp,TXBUFMASK ; | |
385 | sts txidx_r,temp ; | |
386 | ld temp,z | |
387 | com temp | |
388 | sts stx_dr,temp | |
389 | ldi temp,9 | |
390 | sts stx_bitcount,temp | |
391 | pop temp | |
392 | ||
393 | ldi zh,(1<<COM1A1) | |
394 | rjmp stxi_ex | |
395 | ||
396 | ; disable transmitter | |
397 | stxi_dis: | |
398 | inm8 zl,TIMSK1 | |
399 | andi zl,~(1<<OCIE1A) | |
400 | outm8 TIMSK1,zl | |
401 | ||
402 | ldi zh,(1<<COM1A1)|(1<<COM1A0) | |
403 | rjmp stxi_ex | |
404 | ||
405 | stxi_nextbit: | |
406 | sts stx_bitcount,zl | |
407 | ||
408 | ldi zh,(1<<COM1A1) | |
409 | lds zl,stx_dr | |
410 | sbrs zl,0 | |
411 | ldi zh,(1<<COM1A1)|(1<<COM1A0) | |
412 | lsr zl | |
413 | sts stx_dr,zl | |
414 | stxi_ex: | |
415 | outm8 TCCR1A,zh | |
416 | pop zh | |
417 | pop zl | |
418 | out sreg,zl | |
419 | pop zl | |
420 | reti | |
421 | ||
422 | ;------------------------------------------------------------------ | |
423 | ||
424 | srx_to: | |
425 | #if 0 | |
426 | ldi zl,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge | |
427 | outm8 TCCR1B,zl | |
428 | ldi zl,(1<<ICF1) ;clear pending int | |
429 | out TIFR1,zl | |
430 | #endif | |
431 | sts srx_state,_0 ;next state | |
432 | push temp | |
433 | ||
434 | #if 0 | |
435 | ldi temp,'|' | |
436 | rcall uartputc | |
437 | #endif | |
438 | lds temp,srx_dr ;only 0 if timeout after leading edge of start bit. | |
439 | tst temp ; --> break | |
440 | brne srxto_store | |
441 | sbr intstat,(1<<i_break) | |
442 | sts rxcount,_0 ;clear rx buffer | |
443 | sts rxidx_w,_0 | |
444 | sts rxidx_r,_0 | |
445 | rjmp srxto_ov | |
446 | ||
447 | srxto_store: | |
448 | mov zl,temp | |
449 | com zl | |
450 | andi zl,0x80 | |
451 | srxto_l: | |
452 | lsr temp | |
453 | or temp,zl | |
454 | brcc srxto_l | |
455 | ||
456 | ; Save received character in a circular buffer. Do nothing if buffer overflows. | |
457 | ||
458 | lds zh,rxcount ;if rxcount < RXBUFSIZE | |
459 | cpi zh,RXBUFSIZE ; (room for at least 1 char?) | |
460 | brsh srxto_ov ; | |
461 | inc zh ; | |
462 | sts rxcount,zh ; rxcount++ | |
463 | ||
464 | ldi zl,low(rxfifo) ; | |
465 | lds zh,rxidx_w ; | |
466 | add zl,zh ; | |
467 | inc zh ; | |
468 | andi zh,RXBUFMASK ; | |
469 | sts rxidx_w,zh ; rxidx_w = ++rxidx_w % RXBUFSIZE | |
470 | ldi zh,high(rxfifo) ; | |
471 | brcc PC+2 ; | |
472 | inc zh ; | |
473 | st z,temp ; rxfifo[rxidx_w] = char | |
474 | srxto_ov: ;endif | |
475 | ||
476 | pop temp | |
477 | ret | |
478 | ||
479 | ||
480 | ;Fetches a char from the buffer to temp. If none available, waits till one is. | |
481 | ||
482 | uartgetc: | |
483 | push zh | |
484 | push zl | |
485 | push temp2 | |
486 | ugetc_w: | |
487 | lds temp,rxcount ;Number of characters in buffer | |
488 | tst temp | |
489 | breq ugetc_w ;Wait for char | |
490 | ||
491 | ldi zl,low(rxfifo) | |
492 | ldi zh,high(rxfifo) | |
493 | lds temp2,rxidx_r | |
494 | add zl,temp2 | |
495 | adc zh,_0 | |
496 | inc temp2 | |
497 | andi temp2,RXBUFMASK | |
498 | cli | |
499 | lds temp,rxcount | |
500 | subi temp,1 | |
501 | brcc ugetc_fin | |
502 | sei | |
503 | rjmp ugetc_w | |
504 | ||
505 | ugetc_fin: | |
506 | sts rxcount,temp | |
507 | sts rxidx_r,temp2 | |
508 | sei | |
509 | ld temp,z ;don't forget to get the char | |
510 | pop temp2 | |
511 | pop zl | |
512 | pop zh | |
513 | ret | |
514 | ||
515 | ;Sends a char from temp to the soft uart. | |
516 | ||
517 | uartputc: | |
518 | push zh | |
519 | push zl | |
520 | in zl,sreg | |
521 | push zl | |
522 | push temp | |
523 | sputc_l: | |
524 | lds temp,txcount ;do { | |
525 | cpi temp,TXBUFSIZE ; | |
526 | brsh sputc_l ;} while (txcount >= TXBUFSIZE) | |
527 | ||
528 | ldi zl,low(txfifo) ; | |
529 | ldi zh,high(txfifo) ; | |
530 | lds temp,txidx_w ; | |
531 | add zl,temp ; | |
532 | adc zh,_0 ; | |
533 | inc temp ; | |
534 | andi temp,TXBUFMASK ; | |
535 | sts txidx_w,temp ; txidx_w = ++txidx_w % TXBUFSIZE | |
536 | pop temp ; | |
537 | st z,temp ; txfifo[txidx_w] = char | |
538 | cli | |
539 | lds zh,txcount | |
540 | inc zh | |
541 | sts txcount,zh | |
542 | cpi zh,1 | |
543 | brne sputc_e | |
544 | ; Enable transmitter | |
545 | inm8 zh,TIMSK1 | |
546 | sbrc zh,OCIE1A | |
547 | rjmp sputc_e | |
548 | ori zh,(1<<OCIE1A) | |
549 | outm8 TIMSK1,zh | |
550 | ||
551 | inm8 zl,TCNT1L ; | |
552 | inm8 zh,TCNT1H ; | |
553 | adiw zl,30 ; | |
554 | outm8 OCR1AH,zh ; | |
555 | outm8 OCR1AL,zl ; | |
556 | ldi zl,(1<<OCF1A) | |
557 | outm8 TIFR1,zl | |
558 | ||
559 | sputc_e: | |
560 | pop zl | |
561 | out sreg,zl | |
562 | pop zl | |
563 | pop zh | |
564 | ret | |
565 | ||
566 | ||
567 | ; Wait, till tx buffer is empty. | |
568 | ||
569 | uart_wait_empty: | |
570 | push temp | |
571 | uwe_loop: | |
572 | lds temp,txcount | |
573 | tst temp | |
574 | brne uwe_loop | |
575 | pop temp | |
576 | ret | |
577 | ||
578 | ||
579 | ; vim:set ts=8 noet nowrap | |
580 |