--- a/dmx.s Wed Jul 27 01:46:27 2011 +0300
+++ b/dmx.s Tue Aug 02 09:27:19 2011 +0300
@@ -4,6 +4,7 @@
.include "m168def.inc" ; Same family as 328P
.list
+.include "macros.inc"
;; Interrupt Vector
.cseg
@@ -14,17 +15,46 @@
;; CPU cycles / second: 16 Mhz
.set CPU_CYCLES = 16 * 1000 * 1000
+;; Delays
+.include "delay.inc"
+
;; DMX baud raite: 250k
.set DMX_BAUD = 250 * 1000
;; CPU cycles / bit: 64
.set DMX_CYCLES = CPU_CYCLES / DMX_BAUD
-;; DMX output por
+;; DMX output I/O
.set DMX_DDR = DDRB
.set DMX_PORT = PORTB
.equ DMX_DATA = PORTB3
+;; DMX protocol timer
+; Registers
+.set DMX_TIMER_CRA = TCCR2A
+.set DMX_TIMER_CRB = TCCR2B
+.set DMX_TIMER_CNT = TCNT2
+.set DMX_TIMER_OCRA = OCR2A
+.set DMX_TIMER_OCRB = OCR2B
+.set DMX_TIMER_IMSK = TIMSK2
+.set DMX_TIMER_IFR = TIFR2
+
+; Compare output match isn't used
+.set DMX_TIMER_COMA = 0b00
+.set DMX_TIMER_COMB = 0b00
+
+; Control register, generation mode value
+.set DMX_TIMER_WGM_10 = 0b10 ; CTC
+.set DMX_TIMER_WGM_2 = 0b0
+
+; Clock select
+.set DMX_TIMER_CS_STOP = 0b000
+;.set DMX_TIMER_CS = 0b001 ; 1/1
+.set DMX_TIMER_CS = 0b111 ; 1/1024
+
+; Counted value
+.set DMX_TIMER_TOP = DMX_CYCLES ; number of cycles for baud
+
;; Debug LED
.set LED_DDR = DDRB
.set LED_PORT = PORTB
@@ -40,79 +70,111 @@
; drive high
sbi DMX_PORT, DMX_DATA
+ ; Setup timer
+ ; setup CTC mode with no output pins
+ poke [DMX_TIMER_CRA, r16, (DMX_TIMER_COMA << COM2A0) | (DMX_TIMER_COMB << COM2B0) | (DMX_TIMER_WGM_10 << WGM20)]
+ poke [DMX_TIMER_CRB, r16, (DMX_TIMER_WGM_2 << WGM22) | (DMX_TIMER_CS_STOP << CS20)]
+
+ ; trigger threshold for CTC
+ poke [DMX_TIMER_OCRA, r16, (DMX_TIMER_TOP)]
+
; OK
- ret
+ ret
+
+;; Start Break signal
+;;
+;; 22 bits - 1s long; then DMX_Break_Mark
+;;
+DMX_Break_Start:
+ ; Low
+ cbi DMX_PORT, DMX_DATA
+
+ ret
+
+;; Start Mark-after-break signal
+;;
+;; 2 bits - 1s long; then DMX_Break_End
+;;
+DMX_Break_Mark:
+ ; High
+ sbi DMX_PORT, DMX_DATA
+
+ ret
+
+;; End break; prepare for DMX_Frame
+DMX_Frame_Start:
+ ; Start timer
+ poke [DMX_TIMER_CRB, r20, (DMX_TIMER_WGM_2 << WGM22) | (DMX_TIMER_CS << CS20)]
+
+ ret
+
+;; Do a full DMX break, using some random length
+;;
+DMX_Break:
+ ; Break
+ ; start
+ rcall DMX_Break_Start
+
+ ; wait; about 100ms?
+ ldi r20, 82 / 10
+ rcall VarDelay
+
+ ; MAB
+ ; mark
+ rcall DMX_Break_Mark
+
+ ; short wait
+ ldi r20, 1
+ rcall VarDelay
+
+ ; Timed frames
+ ; start frame
+ rcall DMX_Frame_Start
+
+ ; ok
+ ret
;; Bitbang one DMX bit out
;; uses SREG/C to send
+;
+; Uses Timer2 as a bit sync clock, sending out the next bit once we've hit 64 cycles on the timer
DMX_Bit:
- ; Output bit: 4 cycles
+ ; Wait for bit sync clock
+_dmx_bit_wait:
+ ; test OCA hit
+ sbic TIFR2, OCF2A
+ rjmp _dmx_bit_wait
+
+sbi LED_PIN, LED_BIT
+
+ ; Output bit
+ ; XXX: ugly bit-testing, can't we do this using something more nifty?
brcs _dmx_bit_1
+ ; bit 0
cbi DMX_PORT, DMX_DATA
- rjmp _dmx_bit_wait
+ rjmp _dmx_bit_done
_dmx_bit_1:
+ ; bit 1
sbi DMX_PORT, DMX_DATA
nop
-_dmx_bit_wait:
- ; Wait: 60 cycles
- ; load: 1 cycle (offset by one less for brne)
- ldi r20, 6
-
-_dmx_bit_loop:
- ; waste 8 cycles
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
-
- ; loop: 1 + 2/1 cycles
- ; 2/1 cycles
- dec r20
- brne _dmx_bit_loop
-
- ; OK
- ret
+ ; Bit sent
+_dmx_bit_done:
+ ; reset OCA hit for next bit
+ cbi TIFR2, OCF2A
-;; Break + MAB
-DMX_Break:
- ; Break: 100 low bits
- ldi r21, 100
-
-_dmx_break_loop:
- ; bitbang zero
- clc
- rcall DMX_Bit
-
- ; count
- dec r21
- brne _dmx_break_loop
+ ; OK, bit sync clock keeps running for next bit
+ ret
- ; MAB: 4 high bits
- ldi r21, 4
-
-_dmx_break_mab:
- sec
- rcall DMX_Bit
-
- ; loop
- dec r21
- brne _dmx_break_mab
-
- ; OK
- ret
-
-;; Byte
+;; Bitbang one DMX byte out, using DMX_Bit
;; r16: byte value
+;
+; Uses Timer2 as a bit sync clock; must call DMX_Frame_Start before first DMX_Frame
DMX_Frame:
; Start bit
- sec
+ clc
rcall DMX_Bit
; Data bits: 8
@@ -128,17 +190,25 @@
brne _dmx_frame_loop
; Stop bits
- clc
+ sec
rcall DMX_Bit
rcall DMX_Bit
; OK
- ret
+ ret
-;; Send one value on all frames, continuously
+;; End of DMX frames
+DMX_Frame_End:
+ ; Keep mark from end of last frame; DMX_Break_Start starts the break
+ ; Stop the timer
+ poke [DMX_TIMER_CRB, r20, (DMX_TIMER_WGM_2 << WGM22) | (DMX_TIMER_CS_STOP << CS20)]
+
+ ; OK
+ ret
+
+;; Send one value on all frames
;; r17: byte value
DMX_Flood:
-_dmx_flood:
; Break
rcall DMX_Break
@@ -161,24 +231,21 @@
dec r22
brne _dmx_flood_channels
- ; OK; loop
-;sbi LED_PIN, LED_BIT
- rjmp _dmx_flood
+ ; End packet
+ rcall DMX_Frame_End
+ ret
+
;; Program main
Main:
; Initialization
; Debug
sbi LED_DDR, LED_BIT
-cbi LED_PORT, LED_BIT
+sbi LED_PORT, LED_BIT
; Stack
- ldi r16, high(RAMEND)
- ldi r17, low(RAMEND)
- out SPH, r16
- out SPL, r17
-
+ poke [SPL, r16:r17, RAMEND]
; Init
rcall DMX_Init
@@ -186,10 +253,12 @@
sbi LED_PORT, LED_BIT
; Send; value
+_main_loop:
ldi r17, 255
rcall DMX_Flood
-_main_loop:
+;sbi LED_PORT, LED_BIT
+
; never returns..
rjmp _main_loop