]>
Commit | Line | Data |
---|---|---|
a3be4966 BP |
1 | ; usbdux_firmware.asm |
2 | ; Copyright (C) 2004,2009 Bernd Porr, Bernd.Porr@f2s.com | |
3 | ; For usbdux.c | |
4 | ; | |
5 | ; This program is free software; you can redistribute it and/or modify | |
6 | ; it under the terms of the GNU General Public License as published by | |
7 | ; the Free Software Foundation; either version 2 of the License, or | |
8 | ; (at your option) any later version. | |
9 | ; | |
10 | ; This program is distributed in the hope that it will be useful, | |
11 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | ; GNU General Public License for more details. | |
14 | ; | |
15 | ; You should have received a copy of the GNU General Public License | |
16 | ; along with this program; if not, write to the Free Software | |
17 | ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | ; | |
19 | ; | |
20 | ; Firmware: usbdux_firmware.asm for usbdux.c | |
21 | ; Description: University of Stirling USB DAQ & INCITE Technology Limited | |
22 | ; Devices: [ITL] USB-DUX (usbdux.o) | |
23 | ; Author: Bernd Porr <Bernd.Porr@f2s.com> | |
24 | ; Updated: 17 Apr 2009 | |
25 | ; Status: stable | |
26 | ; | |
27 | ;;; | |
28 | ;;; | |
29 | ;;; | |
30 | ||
31 | .inc fx2-include.asm | |
32 | ||
33 | .equ CHANNELLIST,80h ; channellist in indirect memory | |
34 | ||
35 | .equ CMD_FLAG,90h ; flag if next IN transf is DIO | |
36 | .equ SGLCHANNEL,91h ; channel for INSN | |
37 | .equ PWMFLAG,92h ; PWM | |
38 | ||
39 | .equ DIOSTAT0,98h ; last status of the digital port | |
40 | .equ DIOSTAT1,99h ; same for the second counter | |
41 | ||
42 | .equ CTR0,0A0H ; counter 0 | |
43 | .equ CTR1,0A2H ; counter 1 | |
44 | ||
45 | .org 0000h ; after reset the processor starts here | |
46 | ljmp main ; jump to the main loop | |
47 | ||
48 | .org 000bh ; timer 0 irq | |
49 | ljmp timer0_isr | |
50 | ||
51 | .org 0043h ; the IRQ2-vector | |
52 | ljmp jmptbl ; irq service-routine | |
53 | ||
54 | .org 0100h ; start of the jump table | |
55 | ||
56 | jmptbl: ljmp sudav_isr | |
57 | nop | |
58 | ljmp sof_isr | |
59 | nop | |
60 | ljmp sutok_isr | |
61 | nop | |
62 | ljmp suspend_isr | |
63 | nop | |
64 | ljmp usbreset_isr | |
65 | nop | |
66 | ljmp hispeed_isr | |
67 | nop | |
68 | ljmp ep0ack_isr | |
69 | nop | |
70 | ljmp spare_isr | |
71 | nop | |
72 | ljmp ep0in_isr | |
73 | nop | |
74 | ljmp ep0out_isr | |
75 | nop | |
76 | ljmp ep1in_isr | |
77 | nop | |
78 | ljmp ep1out_isr | |
79 | nop | |
80 | ljmp ep2_isr | |
81 | nop | |
82 | ljmp ep4_isr | |
83 | nop | |
84 | ljmp ep6_isr | |
85 | nop | |
86 | ljmp ep8_isr | |
87 | nop | |
88 | ljmp ibn_isr | |
89 | nop | |
90 | ljmp spare_isr | |
91 | nop | |
92 | ljmp ep0ping_isr | |
93 | nop | |
94 | ljmp ep1ping_isr | |
95 | nop | |
96 | ljmp ep2ping_isr | |
97 | nop | |
98 | ljmp ep4ping_isr | |
99 | nop | |
100 | ljmp ep6ping_isr | |
101 | nop | |
102 | ljmp ep8ping_isr | |
103 | nop | |
104 | ljmp errlimit_isr | |
105 | nop | |
106 | ljmp spare_isr | |
107 | nop | |
108 | ljmp spare_isr | |
109 | nop | |
110 | ljmp spare_isr | |
111 | nop | |
112 | ljmp ep2isoerr_isr | |
113 | nop | |
114 | ljmp ep4isoerr_isr | |
115 | nop | |
116 | ljmp ep6isoerr_isr | |
117 | nop | |
118 | ljmp ep8isoerr_isr | |
119 | ||
120 | ||
121 | ;; dummy isr | |
122 | sudav_isr: | |
123 | sutok_isr: | |
124 | suspend_isr: | |
125 | usbreset_isr: | |
126 | hispeed_isr: | |
127 | ep0ack_isr: | |
128 | spare_isr: | |
129 | ep0in_isr: | |
130 | ep0out_isr: | |
131 | ep1in_isr: | |
132 | ibn_isr: | |
133 | ep0ping_isr: | |
134 | ep1ping_isr: | |
135 | ep2ping_isr: | |
136 | ep4ping_isr: | |
137 | ep6ping_isr: | |
138 | ep8ping_isr: | |
139 | errlimit_isr: | |
140 | ep2isoerr_isr: | |
141 | ep4isoerr_isr: | |
142 | ep6isoerr_isr: | |
143 | ep8isoerr_isr: | |
144 | ep6_isr: | |
145 | ep2_isr: | |
146 | ep4_isr: | |
147 | ||
148 | push dps | |
149 | push dpl | |
150 | push dph | |
151 | push dpl1 | |
152 | push dph1 | |
153 | push acc | |
154 | push psw | |
155 | ||
156 | ;; clear the USB2 irq bit and return | |
157 | mov a,EXIF | |
158 | clr acc.4 | |
159 | mov EXIF,a | |
160 | ||
161 | pop psw | |
162 | pop acc | |
163 | pop dph1 | |
164 | pop dpl1 | |
165 | pop dph | |
166 | pop dpl | |
167 | pop dps | |
168 | ||
169 | reti | |
170 | ||
171 | ||
172 | ;;; main program | |
173 | ;;; basically only initialises the processor and | |
174 | ;;; then engages in an endless loop | |
175 | main: | |
176 | mov DPTR,#CPUCS ; CPU control register | |
177 | mov a,#00010000b ; 48Mhz | |
178 | lcall syncdelaywr | |
179 | ||
180 | mov dptr,#REVCTL | |
181 | mov a,#00000011b ; allows skip | |
182 | lcall syncdelaywr | |
183 | ||
184 | mov IP,#0 ; all std 8051 int have low priority | |
185 | mov EIP,#0FFH ; all FX2 interrupts have high priority | |
186 | ||
187 | mov dptr,#INTSETUP ; IRQ setup register | |
188 | mov a,#08h ; enable autovector | |
189 | lcall syncdelaywr | |
190 | ||
191 | lcall initAD ; init the ports to the converters | |
192 | ||
193 | lcall initeps ; init the isochronous data-transfer | |
194 | ||
195 | lcall init_timer | |
196 | ||
197 | mloop2: nop | |
198 | ||
199 | ;;; pwm | |
200 | mov r0,#PWMFLAG ; pwm on? | |
201 | mov a,@r0 ; get info | |
202 | jz mloop2 ; it's off | |
203 | ||
204 | mov a,GPIFTRIG ; GPIF status | |
205 | anl a,#80h ; done bit | |
206 | jz mloop2 ; GPIF still busy | |
207 | ||
208 | mov a,#01h ; WR,EP4, 01 = EP4 | |
209 | mov GPIFTRIG,a ; restart it | |
210 | ||
211 | sjmp mloop2 ; loop for ever | |
212 | ||
213 | ||
214 | ;;; GPIF waveform for PWM | |
215 | waveform: | |
216 | ;; 0 1 2 3 4 5 6 7(not used) | |
217 | ;; len (gives 50.007Hz) | |
218 | .db 195, 195, 195, 195, 195, 195, 1, 1 | |
219 | ||
220 | ;; opcode | |
221 | .db 002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H | |
222 | ||
223 | ;; out | |
224 | .db 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH | |
225 | ||
226 | ;; log | |
227 | .db 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H | |
228 | ||
229 | ||
230 | stopPWM: | |
231 | mov r0,#PWMFLAG ; flag for PWM | |
232 | mov a,#0 ; PWM (for the main loop) | |
233 | mov @r0,a ; set it | |
234 | ||
235 | mov dptr,#IFCONFIG ; switch off GPIF | |
236 | mov a,#10000000b ; gpif, 30MHz, internal IFCLK | |
237 | lcall syncdelaywr | |
238 | ret | |
239 | ||
240 | ||
241 | ;;; init PWM | |
242 | startPWM: | |
243 | mov dptr,#IFCONFIG ; switch on IFCLK signal | |
244 | mov a,#10000010b ; gpif, 30MHz, internal IFCLK | |
245 | lcall syncdelaywr | |
246 | ||
247 | mov OEB,0FFH ; output to port B | |
248 | ||
249 | mov DPTR,#EP4CFG | |
250 | mov a,#10100000b ; valid, out, bulk | |
251 | movx @DPTR,a | |
252 | ||
253 | ;; reset the endpoint | |
254 | mov dptr,#FIFORESET | |
255 | mov a,#80h ; NAK | |
256 | lcall syncdelaywr | |
257 | mov a,#84h ; reset EP4 + NAK | |
258 | lcall syncdelaywr | |
259 | mov a,#0 ; normal op | |
260 | lcall syncdelaywr | |
261 | ||
262 | mov dptr,#EP4BCL | |
263 | mov a,#0H ; discard packets | |
264 | lcall syncdelaywr ; empty FIFO buffer | |
265 | lcall syncdelaywr ; empty FIFO buffer | |
266 | ||
267 | ;; aborts all transfers by the GPIF | |
268 | mov dptr,#GPIFABORT | |
269 | mov a,#0ffh ; abort all transfers | |
270 | lcall syncdelaywr | |
271 | ||
272 | ;; wait for GPIF to finish | |
273 | wait_f_abort: | |
274 | mov a,GPIFTRIG ; GPIF status | |
275 | anl a,#80h ; done bit | |
276 | jz wait_f_abort ; GPIF busy | |
277 | ||
278 | mov dptr,#GPIFCTLCFG | |
279 | mov a,#10000000b ; tri state for CTRL | |
280 | lcall syncdelaywr | |
281 | ||
282 | mov dptr,#GPIFIDLECTL | |
283 | mov a,#11110000b ; all CTL outputs low | |
284 | lcall syncdelaywr | |
285 | ||
286 | ;; abort if FIFO is empty | |
287 | mov a,#00000001b ; abort if empty | |
288 | mov dptr,#EP4GPIFFLGSEL | |
289 | lcall syncdelaywr | |
290 | ||
291 | ;; | |
292 | mov a,#00000001b ; stop if GPIF flg | |
293 | mov dptr,#EP4GPIFPFSTOP | |
294 | lcall syncdelaywr | |
295 | ||
296 | ;; transaction counter | |
297 | mov a,#0ffH | |
298 | mov dptr,#GPIFTCB3 | |
299 | lcall syncdelaywr | |
300 | ||
301 | ;; transaction counter | |
302 | mov a,#0ffH | |
303 | mov dptr,#GPIFTCB2 | |
304 | lcall syncdelaywr | |
305 | ||
306 | ;; transaction counter | |
307 | mov a,#0ffH ; 512 bytes | |
308 | mov dptr,#GPIFTCB1 | |
309 | lcall syncdelaywr | |
310 | ||
311 | ;; transaction counter | |
312 | mov a,#0ffH | |
313 | mov dptr,#GPIFTCB0 | |
314 | lcall syncdelaywr | |
315 | ||
316 | ;; RDY pins. Not used here. | |
317 | mov a,#0 | |
318 | mov dptr,#GPIFREADYCFG | |
319 | lcall syncdelaywr | |
320 | ||
321 | ;; drives the output in the IDLE state | |
322 | mov a,#1 | |
323 | mov dptr,#GPIFIDLECS | |
324 | lcall syncdelaywr | |
325 | ||
326 | ;; direct data transfer from the EP to the GPIF | |
327 | mov dptr,#EP4FIFOCFG | |
328 | mov a,#00010000b ; autoout=1, byte-wide | |
329 | lcall syncdelaywr | |
330 | ||
331 | ;; waveform 0 is used for FIFO out | |
332 | mov dptr,#GPIFWFSELECT | |
333 | mov a,#00000000b | |
334 | movx @dptr,a | |
335 | lcall syncdelay | |
336 | ||
337 | ;; transfer the delay byte from the EP to the waveform | |
338 | mov dptr,#0e781h ; EP1 buffer | |
339 | movx a,@dptr ; get the delay | |
340 | mov dptr,#waveform ; points to the waveform | |
341 | mov r2,#6 ; fill 6 bytes | |
342 | timloop: | |
343 | movx @dptr,a ; save timing in a xxx | |
344 | inc dptr | |
345 | djnz r2,timloop ; fill the 6 delay bytes | |
346 | ||
347 | ;; load waveform | |
348 | mov AUTOPTRH2,#0E4H ; XDATA0H | |
349 | lcall syncdelay | |
350 | mov AUTOPTRL2,#00H ; XDATA0L | |
351 | lcall syncdelay | |
352 | ||
353 | mov dptr,#waveform ; points to the waveform | |
354 | ||
355 | mov AUTOPTRSETUP,#7 ; autoinc and enable | |
356 | lcall syncdelay | |
357 | ||
358 | mov r2,#20H ; 32 bytes to transfer | |
359 | ||
360 | wavetr: | |
361 | movx a,@dptr | |
362 | inc dptr | |
363 | push dpl | |
364 | push dph | |
365 | push dpl1 | |
366 | push dph1 | |
367 | mov dptr,#XAUTODAT2 | |
368 | movx @dptr,a | |
369 | lcall syncdelay | |
370 | pop dph1 | |
371 | pop dpl1 | |
372 | pop dph | |
373 | pop dpl | |
374 | djnz r2,wavetr | |
375 | ||
376 | mov dptr,#OUTPKTEND | |
377 | mov a,#084H | |
378 | lcall syncdelaywr | |
379 | lcall syncdelaywr | |
380 | ||
381 | mov r0,#PWMFLAG ; flag for PWM | |
382 | mov a,#1 ; PWM (for the main loop) | |
383 | mov @r0,a ; set it | |
384 | ||
385 | ret | |
386 | ||
387 | ||
388 | ||
389 | ;;; initialise the ports for the AD-converter | |
390 | initAD: | |
391 | mov OEA,#27H ;PortA0,A1,A2,A5 Outputs | |
392 | mov IOA,#22H ;/CS = 1, disable transfers to the converters | |
393 | ret | |
394 | ||
395 | ||
396 | ;;; init the timer for the soft counters | |
397 | init_timer: | |
398 | ;; init the timer for 2ms sampling rate | |
399 | mov CKCON,#00000001b; CLKOUT/12 for timer | |
400 | mov TL0,#010H ; 16 | |
401 | mov TH0,#0H ; 256 | |
402 | mov IE,#82H ; switch on timer interrupt (80H for all IRQs) | |
403 | mov TMOD,#00000000b ; 13 bit counters | |
404 | setb TCON.4 ; enable timer 0 | |
405 | ret | |
406 | ||
407 | ||
408 | ;;; from here it's only IRQ handling... | |
409 | ||
410 | ;;; A/D-conversion: | |
411 | ;;; control-byte in a, | |
412 | ;;; result in r3(low) and r4(high) | |
413 | ;;; this routine is optimised for speed | |
414 | readAD: ; mask the control byte | |
415 | anl a,#01111100b ; only the channel, gain+pol are left | |
416 | orl a,#10000001b ; start bit, external clock | |
417 | ;; set CS to low | |
418 | clr IOA.1 ; set /CS to zero | |
419 | ;; send the control byte to the AD-converter | |
420 | mov R2,#8 ; bit-counter | |
421 | bitlp: jnb ACC.7,bitzero ; jump if Bit7 = 0? | |
422 | setb IOA.2 ; set the DIN bit | |
423 | sjmp clock ; continue with the clock | |
424 | bitzero:clr IOA.2 ; clear the DIN bit | |
425 | clock: setb IOA.0 ; SCLK = 1 | |
426 | clr IOA.0 ; SCLK = 0 | |
427 | rl a ; next Bit | |
428 | djnz R2,bitlp | |
429 | ||
430 | ;; continue the aquisition (already started) | |
431 | clr IOA.2 ; clear the DIN bit | |
432 | mov R2,#5 ; five steps for the aquision | |
433 | clockaq:setb IOA.0 ; SCLK = 1 | |
434 | clr IOA.0 ; SCLK = 0 | |
435 | djnz R2,clockaq ; loop | |
436 | ||
437 | ;; read highbyte from the A/D-converter | |
438 | ;; and do the conversion | |
439 | mov r4,#0 ; Highbyte goes into R4 | |
440 | mov R2,#4 ; COUNTER 4 data bits in the MSB | |
441 | mov r5,#08h ; create bit-mask | |
442 | gethi: ; loop get the 8 highest bits from MSB downw | |
443 | setb IOA.0 ; SCLK = 1 | |
444 | clr IOA.0 ; SCLK = 0 | |
445 | mov a,IOA ; from port A | |
446 | jnb ACC.4,zerob ; the in-bit is zero | |
447 | mov a,r4 ; get the byte | |
448 | orl a,r5 ; or the bit to the result | |
449 | mov r4,a ; save it again in r4 | |
450 | zerob: mov a,r5 ; get r5 in order to shift the mask | |
451 | rr a ; rotate right | |
452 | mov r5,a ; back to r5 | |
453 | djnz R2,gethi | |
454 | ;; read the lowbyte from the A/D-converter | |
455 | mov r3,#0 ; Lowbyte goes into R3 | |
456 | mov r2,#8 ; COUNTER 8 data-bits in the LSB | |
457 | mov r5,#80h ; create bit-mask | |
458 | getlo: ; loop get the 8 highest bits from MSB downw | |
459 | setb IOA.0 ; SCLK = 1 | |
460 | clr IOA.0 ; SCLK = 0 | |
461 | mov a,IOA ; from port A | |
462 | jnb ACC.4,zerob2 ; the in-bit is zero | |
463 | mov a,r3 ; get the result-byte | |
464 | orl a,r5 ; or the bit to the result | |
465 | mov r3,a ; save it again in r4 | |
466 | zerob2: mov a,r5 ; get r5 in order to shift the mask | |
467 | rr a ; rotate right | |
468 | mov r5,a ; back to r5 | |
469 | djnz R2,getlo | |
470 | setb IOA.1 ; set /CS to one | |
471 | ;; | |
472 | ret | |
473 | ||
474 | ||
475 | ||
476 | ;;; aquires data from A/D channels and stores them in the EP6 buffer | |
477 | conv_ad: | |
478 | mov AUTOPTRH1,#0F8H ; auto pointer on EP6 | |
479 | mov AUTOPTRL1,#00H | |
480 | mov AUTOPTRSETUP,#7 | |
481 | mov r0,#CHANNELLIST ; points to the channellist | |
482 | ||
483 | mov a,@r0 ; number of channels | |
484 | mov r1,a ; counter | |
485 | ||
486 | mov DPTR,#XAUTODAT1 ; auto pointer | |
487 | convloop: | |
488 | inc r0 | |
489 | mov a,@r0 ; Channel | |
490 | lcall readAD | |
491 | mov a,R3 ; | |
492 | movx @DPTR,A | |
493 | mov a,R4 ; | |
494 | movx @DPTR,A | |
495 | djnz r1,convloop | |
496 | ||
497 | ret | |
498 | ||
499 | ||
500 | ||
501 | ||
502 | ;;; initilise the transfer | |
503 | ;;; It is assumed that the USB interface is in alternate setting 3 | |
504 | initeps: | |
505 | mov dptr,#FIFORESET | |
506 | mov a,#80H | |
507 | movx @dptr,a ; reset all fifos | |
508 | mov a,#2 | |
509 | movx @dptr,a ; | |
510 | mov a,#4 | |
511 | movx @dptr,a ; | |
512 | mov a,#6 | |
513 | movx @dptr,a ; | |
514 | mov a,#8 | |
515 | movx @dptr,a ; | |
516 | mov a,#0 | |
517 | movx @dptr,a ; normal operat | |
518 | ||
519 | mov DPTR,#EP2CFG | |
520 | mov a,#10010010b ; valid, out, double buff, iso | |
521 | movx @DPTR,a | |
522 | ||
523 | mov dptr,#EP2FIFOCFG | |
524 | mov a,#00000000b ; manual | |
525 | movx @dptr,a | |
526 | ||
527 | mov dptr,#EP2BCL ; "arm" it | |
528 | mov a,#00h | |
529 | movx @DPTR,a ; can receive data | |
530 | lcall syncdelay ; wait to sync | |
531 | movx @DPTR,a ; can receive data | |
532 | lcall syncdelay ; wait to sync | |
533 | movx @DPTR,a ; can receive data | |
534 | lcall syncdelay ; wait to sync | |
535 | ||
536 | mov DPTR,#EP1OUTCFG | |
537 | mov a,#10100000b ; valid | |
538 | movx @dptr,a | |
539 | ||
540 | mov dptr,#EP1OUTBC ; "arm" it | |
541 | mov a,#00h | |
542 | movx @DPTR,a ; can receive data | |
543 | lcall syncdelay ; wait until we can write again | |
544 | movx @dptr,a ; make shure its really empty | |
545 | lcall syncdelay ; wait | |
546 | ||
547 | mov DPTR,#EP6CFG ; ISO data from here to the host | |
548 | mov a,#11010010b ; Valid | |
549 | movx @DPTR,a ; ISO transfer, double buffering | |
550 | ||
551 | mov DPTR,#EP8CFG ; EP8 | |
552 | mov a,#11100000b ; BULK data from here to the host | |
553 | movx @DPTR,a ; | |
554 | ||
555 | mov dptr,#EPIE ; interrupt enable | |
556 | mov a,#10001000b ; enable irq for ep1out,8 | |
557 | movx @dptr,a ; do it | |
558 | ||
559 | mov dptr,#EPIRQ ; clear IRQs | |
560 | mov a,#10100000b | |
561 | movx @dptr,a | |
562 | ||
563 | ;; enable interrups | |
564 | mov DPTR,#USBIE ; USB int enables register | |
565 | mov a,#2 ; enables SOF (1ms/125us interrupt) | |
566 | movx @DPTR,a ; | |
567 | ||
568 | mov EIE,#00000001b ; enable INT2 in the 8051's SFR | |
569 | mov IE,#80h ; IE, enable all interrupts | |
570 | ||
571 | ret | |
572 | ||
573 | ||
574 | ;;; counter | |
575 | ;;; r0: DIOSTAT | |
576 | ;;; r1: counter address | |
577 | ;;; r2: up/down-mask | |
578 | ;;; r3: reset-mask | |
579 | ;;; r4: clock-mask | |
580 | counter: | |
581 | mov a,IOB ; actual IOB input state | |
582 | mov r5,a ; save in r5 | |
583 | anl a,r3 ; bit mask for reset | |
584 | jz no_reset ; reset if one | |
585 | clr a ; set counter to zero | |
586 | mov @r1,a | |
587 | inc r4 | |
588 | mov @r1,a | |
589 | sjmp ctr_end | |
590 | no_reset: | |
591 | mov a,@r0 ; get last state | |
592 | xrl a,r5 ; has it changed? | |
593 | anl a,r5 ; is it now on? | |
594 | anl a,r4 ; mask out the port | |
595 | jz ctr_end ; no rising edge | |
596 | mov a,r5 ; get port B again | |
597 | anl a,r2 ; test if up or down | |
598 | jnz ctr_up ; count up | |
599 | mov a,@r1 | |
600 | dec a | |
601 | mov @r1,a | |
602 | cjne a,#0ffh,ctr_end ; underflow? | |
603 | inc r1 ; high byte | |
604 | mov a,@r1 | |
605 | dec a | |
606 | mov @r1,a | |
607 | sjmp ctr_end | |
608 | ctr_up: ; count up | |
609 | mov a,@r1 | |
610 | inc a | |
611 | mov @r1,a | |
612 | jnz ctr_end | |
613 | inc r1 ; high byte | |
614 | mov a,@r1 | |
615 | inc a | |
616 | mov @r1,a | |
617 | ctr_end: | |
618 | mov a,r5 | |
619 | mov @r0,a | |
620 | ret | |
621 | ||
622 | ;;; implements two soft counters with up/down and reset | |
623 | timer0_isr: | |
624 | push dps | |
625 | push acc | |
626 | push psw | |
627 | push 00h ; R0 | |
628 | push 01h ; R1 | |
629 | push 02h ; R2 | |
630 | push 03h ; R3 | |
631 | push 04h ; R4 | |
632 | push 05h ; R5 | |
633 | ||
634 | mov r0,#DIOSTAT0 ; status of port | |
635 | mov r1,#CTR0 ; address of counter0 | |
636 | mov a,#00000001b ; bit 0 | |
637 | mov r4,a ; clock | |
638 | rl a ; bit 1 | |
639 | mov r2,a ; up/down | |
640 | rl a ; bit 2 | |
641 | mov r3,a ; reset mask | |
642 | lcall counter | |
643 | inc r0 ; to DISTAT1 | |
644 | inc r1 ; to CTR1 | |
645 | inc r1 | |
646 | mov a,r3 | |
647 | rl a ; bit 3 | |
648 | rl a ; bit 4 | |
649 | mov r4,a ; clock | |
650 | rl a ; bit 5 | |
651 | mov r2,a ; up/down | |
652 | rl a ; bit 6 | |
653 | mov r3,a ; reset | |
654 | lcall counter | |
655 | ||
656 | pop 05h ; R5 | |
657 | pop 04h ; R4 | |
658 | pop 03h ; R3 | |
659 | pop 02h ; R2 | |
660 | pop 01h ; R1 | |
661 | pop 00h ; R0 | |
662 | pop psw | |
663 | pop acc | |
664 | pop dps | |
665 | ||
666 | reti | |
667 | ||
668 | ;;; interrupt-routine for SOF | |
669 | ;;; is for full speed | |
670 | sof_isr: | |
671 | push dps | |
672 | push dpl | |
673 | push dph | |
674 | push dpl1 | |
675 | push dph1 | |
676 | push acc | |
677 | push psw | |
678 | push 00h ; R0 | |
679 | push 01h ; R1 | |
680 | push 02h ; R2 | |
681 | push 03h ; R3 | |
682 | push 04h ; R4 | |
683 | push 05h ; R5 | |
684 | push 06h ; R6 | |
685 | push 07h ; R7 | |
686 | ||
687 | mov a,EP2468STAT | |
688 | anl a,#20H ; full? | |
689 | jnz epfull ; EP6-buffer is full | |
690 | ||
691 | lcall conv_ad ; conversion | |
692 | ||
693 | mov DPTR,#EP6BCH ; byte count H | |
694 | mov a,#0 ; is zero | |
695 | lcall syncdelaywr ; wait until we can write again | |
696 | ||
697 | mov DPTR,#EP6BCL ; byte count L | |
698 | mov a,#10H ; is 8x word = 16 bytes | |
699 | lcall syncdelaywr ; wait until we can write again | |
700 | ||
701 | epfull: | |
702 | ;; do the D/A conversion | |
703 | mov a,EP2468STAT | |
704 | anl a,#01H ; empty | |
705 | jnz epempty ; nothing to get | |
706 | ||
707 | mov dptr,#0F000H ; EP2 fifo buffer | |
708 | lcall dalo ; conversion | |
709 | ||
710 | mov dptr,#EP2BCL ; "arm" it | |
711 | mov a,#00h | |
712 | lcall syncdelaywr ; wait for the rec to sync | |
713 | lcall syncdelaywr ; wait for the rec to sync | |
714 | ||
715 | epempty: | |
716 | ;; clear INT2 | |
717 | mov a,EXIF ; FIRST clear the USB (INT2) interrupt request | |
718 | clr acc.4 | |
719 | mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable | |
720 | ||
721 | mov DPTR,#USBIRQ ; points to the SOF | |
722 | mov a,#2 ; clear the SOF | |
723 | movx @DPTR,a | |
724 | ||
725 | nosof: | |
726 | pop 07h | |
727 | pop 06h | |
728 | pop 05h | |
729 | pop 04h ; R4 | |
730 | pop 03h ; R3 | |
731 | pop 02h ; R2 | |
732 | pop 01h ; R1 | |
733 | pop 00h ; R0 | |
734 | pop psw | |
735 | pop acc | |
736 | pop dph1 | |
737 | pop dpl1 | |
738 | pop dph | |
739 | pop dpl | |
740 | pop dps | |
741 | reti | |
742 | ||
743 | ||
744 | reset_ep8: | |
745 | ;; erase all data in ep8 | |
746 | mov dptr,#FIFORESET | |
747 | mov a,#80H ; NAK | |
748 | lcall syncdelaywr | |
749 | mov dptr,#FIFORESET | |
750 | mov a,#8 ; reset EP8 | |
751 | lcall syncdelaywr | |
752 | mov dptr,#FIFORESET | |
753 | mov a,#0 ; normal operation | |
754 | lcall syncdelaywr | |
755 | ret | |
756 | ||
757 | ||
758 | reset_ep6: | |
759 | ;; throw out old data | |
760 | mov dptr,#FIFORESET | |
761 | mov a,#80H ; NAK | |
762 | lcall syncdelaywr | |
763 | mov dptr,#FIFORESET | |
764 | mov a,#6 ; reset EP6 | |
765 | lcall syncdelaywr | |
766 | mov dptr,#FIFORESET | |
767 | mov a,#0 ; normal operation | |
768 | lcall syncdelaywr | |
769 | ret | |
770 | ||
771 | ;;; interrupt-routine for ep1out | |
772 | ;;; receives the channel list and other commands | |
773 | ep1out_isr: | |
774 | push dps | |
775 | push dpl | |
776 | push dph | |
777 | push dpl1 | |
778 | push dph1 | |
779 | push acc | |
780 | push psw | |
781 | push 00h ; R0 | |
782 | push 01h ; R1 | |
783 | push 02h ; R2 | |
784 | push 03h ; R3 | |
785 | push 04h ; R4 | |
786 | push 05h ; R5 | |
787 | push 06h ; R6 | |
788 | push 07h ; R7 | |
789 | ||
790 | mov dptr,#0E780h ; FIFO buffer of EP1OUT | |
791 | movx a,@dptr ; get the first byte | |
792 | mov r0,#CMD_FLAG ; pointer to the command byte | |
793 | mov @r0,a ; store the command byte for ep8 | |
794 | ||
795 | mov dptr,#ep1out_jmp; jump table for the different functions | |
796 | rl a ; multiply by 2: sizeof sjmp | |
797 | jmp @a+dptr ; jump to the jump table | |
798 | ;; jump table, corresponds to the command bytes defined | |
799 | ;; in usbdux.c | |
800 | ep1out_jmp: | |
801 | sjmp storechannellist; a=0 | |
802 | sjmp single_da ; a=1 | |
803 | sjmp config_digital_b; a=2 | |
804 | sjmp write_digital_b ; a=3 | |
805 | sjmp storesglchannel ; a=4 | |
806 | sjmp readcounter ; a=5 | |
807 | sjmp writecounter ; a=6 | |
808 | sjmp pwm_on ; a=7 | |
809 | sjmp pwm_off ; a=8 | |
810 | ||
811 | pwm_on: | |
812 | lcall startPWM | |
813 | sjmp over_da | |
814 | ||
815 | pwm_off: | |
816 | lcall stopPWM | |
817 | sjmp over_da | |
818 | ||
819 | ;; read the counter | |
820 | readcounter: | |
821 | lcall reset_ep8 ; reset ep8 | |
822 | lcall ep8_ops ; fill the counter data in there | |
823 | sjmp over_da ; jump to the end | |
824 | ||
825 | ;; write zeroes to the counters | |
826 | writecounter: | |
827 | mov dptr,#0e781h ; buffer | |
828 | mov r0,#CTR0 ; r0 points to counter 0 | |
829 | movx a,@dptr ; channel number | |
830 | jz wrctr0 ; first channel | |
831 | mov r1,a ; counter | |
832 | wrctrl: | |
833 | inc r0 ; next counter | |
834 | inc r0 ; next counter | |
835 | djnz r1,wrctrl ; advance to the right counter | |
836 | wrctr0: | |
837 | inc dptr ; get to the value | |
838 | movx a,@dptr ; get value | |
839 | mov @r0,a ; save in ctr | |
840 | inc r0 ; next byte | |
841 | inc dptr | |
842 | movx a,@dptr ; get value | |
843 | mov @r0,a ; save in ctr | |
844 | sjmp over_da ; jump to the end | |
845 | ||
846 | storesglchannel: | |
847 | mov r0,#SGLCHANNEL ; the conversion bytes are now stored in 80h | |
848 | mov dptr,#0e781h ; FIFO buffer of EP1OUT | |
849 | movx a,@dptr ; | |
850 | mov @r0,a | |
851 | ||
852 | lcall reset_ep8 ; reset FIFO | |
853 | ;; Save new A/D data in EP8. This is the first byte | |
854 | ;; the host will read during an INSN. If there are | |
855 | ;; more to come they will be handled by the ISR of | |
856 | ;; ep8. | |
857 | lcall ep8_ops ; get A/D data | |
858 | ||
859 | sjmp over_da | |
860 | ||
861 | ||
862 | ;;; Channellist: | |
863 | ;;; the first byte is zero: | |
864 | ;;; we've just received the channel list | |
865 | ;;; the channel list is stored in the addresses from CHANNELLIST which | |
866 | ;;; are _only_ reachable by indirect addressing | |
867 | storechannellist: | |
868 | mov r0,#CHANNELLIST ; the conversion bytes are now stored in 80h | |
869 | mov r2,#9 ; counter | |
870 | mov dptr,#0e781h ; FIFO buffer of EP1OUT | |
871 | chanlloop: | |
872 | movx a,@dptr ; | |
873 | mov @r0,a | |
874 | inc dptr | |
875 | inc r0 | |
876 | djnz r2,chanlloop | |
877 | ||
878 | lcall reset_ep6 ; reset FIFO | |
879 | ||
880 | ;; load new A/D data into EP6 | |
881 | ;; This must be done. Otherwise the ISR is never called. | |
882 | ;; The ISR is only called when data has _left_ the | |
883 | ;; ep buffer here it has to be refilled. | |
884 | lcall ep6_arm ; fill with the first data byte | |
885 | ||
886 | sjmp over_da | |
887 | ||
888 | ;;; Single DA conversion. The 2 bytes are in the FIFO buffer | |
889 | single_da: | |
890 | mov dptr,#0e781h ; FIFO buffer of EP1OUT | |
891 | lcall dalo ; conversion | |
892 | sjmp over_da | |
893 | ||
894 | ;;; configure the port B as input or output (bitwise) | |
895 | config_digital_b: | |
896 | mov dptr,#0e781h ; FIFO buffer of EP1OUT | |
897 | movx a,@dptr ; get the second byte | |
898 | mov OEB,a ; set the output enable bits | |
899 | sjmp over_da | |
900 | ||
901 | ;;; Write one byte to the external digital port B | |
902 | ;;; and prepare for digital read | |
903 | write_digital_b: | |
904 | mov dptr,#0e781h ; FIFO buffer of EP1OUT | |
905 | movx a,@dptr ; get the second byte | |
906 | mov OEB,a ; output enable | |
907 | inc dptr ; next byte | |
908 | movx a,@dptr ; bits | |
909 | mov IOB,a ; send the byte to the I/O port | |
910 | ||
911 | lcall reset_ep8 ; reset FIFO of ep 8 | |
912 | ||
913 | ;; fill ep8 with new data from port B | |
914 | ;; When the host requests the data it's already there. | |
915 | ;; This must be so. Otherwise the ISR is not called. | |
916 | ;; The ISR is only called when a packet has been delivered | |
917 | ;; to the host. Thus, we need a packet here in the | |
918 | ;; first instance. | |
919 | lcall ep8_ops ; get digital data | |
920 | ||
921 | ;; | |
922 | ;; for all commands the same | |
923 | over_da: | |
924 | mov dptr,#EP1OUTBC | |
925 | mov a,#00h | |
926 | lcall syncdelaywr ; arm | |
927 | lcall syncdelaywr ; arm | |
928 | lcall syncdelaywr ; arm | |
929 | ||
930 | ;; clear INT2 | |
931 | mov a,EXIF ; FIRST clear the USB (INT2) interrupt request | |
932 | clr acc.4 | |
933 | mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable | |
934 | ||
935 | mov DPTR,#EPIRQ ; | |
936 | mov a,#00001000b ; clear the ep1outirq | |
937 | movx @DPTR,a | |
938 | ||
939 | pop 07h | |
940 | pop 06h | |
941 | pop 05h | |
942 | pop 04h ; R4 | |
943 | pop 03h ; R3 | |
944 | pop 02h ; R2 | |
945 | pop 01h ; R1 | |
946 | pop 00h ; R0 | |
947 | pop psw | |
948 | pop acc | |
949 | pop dph1 | |
950 | pop dpl1 | |
951 | pop dph | |
952 | pop dpl | |
953 | pop dps | |
954 | reti | |
955 | ||
956 | ||
957 | ||
958 | ;;; all channels | |
959 | dalo: | |
960 | movx a,@dptr ; number of channels | |
961 | inc dptr ; pointer to the first channel | |
962 | mov r0,a ; 4 channels | |
963 | nextDA: | |
964 | movx a,@dptr ; get the first low byte | |
965 | mov r3,a ; store in r3 (see below) | |
966 | inc dptr ; point to the high byte | |
967 | movx a,@dptr ; get the high byte | |
968 | mov r4,a ; store in r4 (for writeDA) | |
969 | inc dptr ; point to the channel number | |
970 | movx a,@dptr ; get the channel number | |
971 | inc dptr ; get ready for the next channel | |
972 | lcall writeDA ; write value to the DAC | |
973 | djnz r0,nextDA ; next channel | |
974 | ret | |
975 | ||
976 | ||
977 | ||
978 | ;;; D/A-conversion: | |
979 | ;;; control-byte in a, | |
980 | ;;; value in r3(low) and r4(high) | |
981 | writeDA: ; mask the control byte | |
982 | anl a,#11000000b ; only the channel is left | |
983 | orl a,#00110000b ; internal clock, bipolar mode, +/-5V | |
984 | orl a,r4 ; or the value of R4 to it | |
985 | ;; set CS to low | |
986 | clr IOA.5 ; set /CS to zero | |
987 | ;; send the first byte to the DA-converter | |
988 | mov R2,#8 ; bit-counter | |
989 | DA1: jnb ACC.7,zeroda ; jump if Bit7 = 0? | |
990 | setb IOA.2 ; set the DIN bit | |
991 | sjmp clkda ; continue with the clock | |
992 | zeroda: clr IOA.2 ; clear the DIN bit | |
993 | clkda: setb IOA.0 ; SCLK = 1 | |
994 | clr IOA.0 ; SCLK = 0 | |
995 | rl a ; next Bit | |
996 | djnz R2,DA1 | |
997 | ||
998 | ||
999 | ;; send the second byte to the DA-converter | |
1000 | mov a,r3 ; low byte | |
1001 | mov R2,#8 ; bit-counter | |
1002 | DA2: jnb ACC.7,zeroda2 ; jump if Bit7 = 0? | |
1003 | setb IOA.2 ; set the DIN bit | |
1004 | sjmp clkda2 ; continue with the clock | |
1005 | zeroda2:clr IOA.2 ; clear the DIN bit | |
1006 | clkda2: setb IOA.0 ; SCLK = 1 | |
1007 | clr IOA.0 ; SCLK = 0 | |
1008 | rl a ; next Bit | |
1009 | djnz R2,DA2 | |
1010 | ;; | |
1011 | setb IOA.5 ; set /CS to one | |
1012 | ;; | |
1013 | noDA: ret | |
1014 | ||
1015 | ||
1016 | ||
1017 | ;;; arm ep6 | |
1018 | ep6_arm: | |
1019 | lcall conv_ad | |
1020 | ||
1021 | mov DPTR,#EP6BCH ; byte count H | |
1022 | mov a,#0 ; is zero | |
1023 | lcall syncdelaywr ; wait until the length has arrived | |
1024 | ||
1025 | mov DPTR,#EP6BCL ; byte count L | |
1026 | mov a,#10H ; is one | |
1027 | lcall syncdelaywr ; wait until the length has been proc | |
1028 | ret | |
1029 | ||
1030 | ||
1031 | ||
1032 | ;;; converts one analog/digital channel and stores it in EP8 | |
1033 | ;;; also gets the content of the digital ports B and D depending on | |
1034 | ;;; the COMMAND flag | |
1035 | ep8_ops: | |
1036 | mov dptr,#0fc01h ; ep8 fifo buffer | |
1037 | clr a ; high byte | |
1038 | movx @dptr,a ; set H=0 | |
1039 | mov dptr,#0fc00h ; low byte | |
1040 | mov r0,#CMD_FLAG | |
1041 | mov a,@r0 | |
1042 | movx @dptr,a ; save command byte | |
1043 | ||
1044 | mov dptr,#ep8_jmp ; jump table for the different functions | |
1045 | rl a ; multiply by 2: sizeof sjmp | |
1046 | jmp @a+dptr ; jump to the jump table | |
1047 | ;; jump table, corresponds to the command bytes defined | |
1048 | ;; in usbdux.c | |
1049 | ep8_jmp: | |
1050 | sjmp ep8_err ; a=0, err | |
1051 | sjmp ep8_err ; a=1, err | |
1052 | sjmp ep8_err ; a=2, err | |
1053 | sjmp ep8_dio ; a=3, digital read | |
1054 | sjmp ep8_sglchannel ; a=4, analog A/D | |
1055 | sjmp ep8_readctr ; a=5, read counter | |
1056 | sjmp ep8_err ; a=6, write counter | |
1057 | ||
1058 | ;; reads all counters | |
1059 | ep8_readctr: | |
1060 | mov r0,#CTR0 ; points to counter0 | |
1061 | mov dptr,#0fc02h ; ep8 fifo buffer | |
1062 | mov r1,#8 ; transfer 4 16bit counters | |
1063 | ep8_ctrlp: | |
1064 | mov a,@r0 ; get the counter | |
1065 | movx @dptr,a ; save in the fifo buffer | |
1066 | inc r0 ; inc pointer to the counters | |
1067 | inc dptr ; inc pointer to the fifo buffer | |
1068 | djnz r1,ep8_ctrlp ; loop until ready | |
1069 | ||
1070 | sjmp ep8_send ; send the data | |
1071 | ||
1072 | ;; read one A/D channel | |
1073 | ep8_sglchannel: | |
1074 | mov r0,#SGLCHANNEL ; points to the channel | |
1075 | mov a,@r0 ; Ch0 | |
1076 | ||
1077 | lcall readAD ; start the conversion | |
1078 | ||
1079 | mov DPTR,#0fc02h ; EP8 FIFO | |
1080 | mov a,R3 ; get low byte | |
1081 | movx @DPTR,A ; store in FIFO | |
1082 | inc dptr ; next fifo entry | |
1083 | mov a,R4 ; get high byte | |
1084 | movx @DPTR,A ; store in FIFO | |
1085 | ||
1086 | sjmp ep8_send ; send the data | |
1087 | ||
1088 | ;; read the digital lines | |
1089 | ep8_dio: | |
1090 | mov DPTR,#0fc02h ; store the contents of port B | |
1091 | mov a,IOB ; in the next | |
1092 | movx @dptr,a ; entry of the buffer | |
1093 | ||
1094 | inc dptr | |
1095 | clr a ; high byte is zero | |
1096 | movx @dptr,a ; next byte of the EP | |
1097 | ||
1098 | ep8_send: | |
1099 | mov DPTR,#EP8BCH ; byte count H | |
1100 | mov a,#0 ; is zero | |
1101 | lcall syncdelaywr | |
1102 | ||
1103 | mov DPTR,#EP8BCL ; byte count L | |
1104 | mov a,#10H ; 16 bytes | |
1105 | lcall syncdelaywr ; send the data over to the host | |
1106 | ||
1107 | ep8_err: | |
1108 | ret | |
1109 | ||
1110 | ||
1111 | ||
1112 | ;;; EP8 interrupt: gets one measurement from the AD converter and | |
1113 | ;;; sends it via EP8. The channel # is stored in address 80H. | |
1114 | ;;; It also gets the state of the digital registers B and D. | |
1115 | ep8_isr: | |
1116 | push dps | |
1117 | push dpl | |
1118 | push dph | |
1119 | push dpl1 | |
1120 | push dph1 | |
1121 | push acc | |
1122 | push psw | |
1123 | push 00h ; R0 | |
1124 | push 01h ; R1 | |
1125 | push 02h ; R2 | |
1126 | push 03h ; R3 | |
1127 | push 04h ; R4 | |
1128 | push 05h ; R5 | |
1129 | push 06h ; R6 | |
1130 | push 07h ; R7 | |
1131 | ||
1132 | lcall ep8_ops | |
1133 | ||
1134 | ;; clear INT2 | |
1135 | mov a,EXIF ; FIRST clear the USB (INT2) interrupt request | |
1136 | clr acc.4 | |
1137 | mov EXIF,a ; Note: EXIF reg is not 8051 bit-addressable | |
1138 | ||
1139 | mov DPTR,#EPIRQ ; | |
1140 | mov a,#10000000b ; clear the ep8irq | |
1141 | movx @DPTR,a | |
1142 | ||
1143 | pop 07h | |
1144 | pop 06h | |
1145 | pop 05h | |
1146 | pop 04h ; R4 | |
1147 | pop 03h ; R3 | |
1148 | pop 02h ; R2 | |
1149 | pop 01h ; R1 | |
1150 | pop 00h ; R0 | |
1151 | pop psw | |
1152 | pop acc | |
1153 | pop dph1 | |
1154 | pop dpl1 | |
1155 | pop dph | |
1156 | pop dpl | |
1157 | pop dps | |
1158 | reti | |
1159 | ||
1160 | ||
1161 | ;; need to delay every time the byte counters | |
1162 | ;; for the EPs have been changed. | |
1163 | ||
1164 | syncdelay: | |
1165 | nop | |
1166 | nop | |
1167 | nop | |
1168 | nop | |
1169 | nop | |
1170 | nop | |
1171 | nop | |
1172 | nop | |
1173 | nop | |
1174 | ret | |
1175 | ||
1176 | syncdelaywr: | |
1177 | movx @dptr,a | |
1178 | lcall syncdelay | |
1179 | ret | |
1180 | ||
1181 | ||
1182 | .End | |
1183 | ||
1184 |