dmx.s
author Tero Marttila <terom@paivola.fi>
Mon, 21 Apr 2014 00:20:27 +0300
changeset 82 b5878197d017
parent 62 2d68a76322cb
permissions -rw-r--r--
dmx-web: change dmx layout for more light types
;;; vim: set ft=avr:

.nolist
.include "m168def.inc"      ; Same family as 328P
.list

.include "macros.inc"

;; Interrupt Vector
.cseg
.org 0x0000
        rjmp        Main


;; 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 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
.set LED_PIN = PINB
.set LED_BIT = PORTB0

;; Set up DMX output
DMX_Init:
    ; Setup output port
        ; out
        sbi     DMX_DDR, DMX_DATA

        ; 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

;; 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:
    ; Wait for bit sync clock
_dmx_bit_wait:
        ; test OCA hit
        sbic    TIFR2, OCF2A     
        rjmp    _dmx_bit_wait   

;sbi         LED_PORT, 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_done

_dmx_bit_1:
        ; bit 1
        sbi     DMX_PORT, DMX_DATA
        nop

    ; Bit sent
_dmx_bit_done:
        ; reset OCA hit for next bit
        cbi     TIFR2, OCF2A

    ; OK, bit sync clock keeps running for next bit
    ret

;; 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
        clc
        rcall       DMX_Bit

    ; Data bits: 8
        ldi         r21, 8
        
_dmx_frame_loop:
        ; shift + send bit
        lsl         r16
        rcall       DMX_Bit

        ; loop
        dec         r21
        brne        _dmx_frame_loop

    ; Stop bits
        sec
        rcall       DMX_Bit
        rcall       DMX_Bit
    
    ; OK
    ret

;; 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:
    ; Break
        rcall       DMX_Break

    ; Start code
        ldi         r16, 0
        rcall       DMX_Frame

    ; Channels
        ; number of channels to send
        ldi         r22, 100

_dmx_flood_channels:
        ; restore channel value
        mov         r16, r17

        ; send channel value
        rcall       DMX_Frame

        ; loop
        dec         r22
        brne        _dmx_flood_channels

    ; End packet
        rcall       DMX_Frame_End


    ret

;; Program main
Main:
; Initialization
    ; Debug
        sbi         LED_DDR, LED_BIT
sbi         LED_PORT, LED_BIT

    ; Stack
		poke		[SPL, r16:r17, RAMEND]

    ; Init
        rcall       DMX_Init

cbi         LED_PORT, LED_BIT

    ; Send; value
_main_loop:
        ldi         r17, 255
        rcall       DMX_Flood

cbi         LED_PORT, LED_BIT

        ; never returns..
        rjmp        _main_loop