# HG changeset patch # User Tero Marttila # Date 1396797955 -10800 # Node ID 2d68a76322cbb3db12256674ce04940cd958ade7 # Parent a960cf5981f7d7b314fdbbd59ec8aad507d1a582 hello-dmx: working basic dmx output using DmxSimple's frame-timing inline assembler code diff -r a960cf5981f7 -r 2d68a76322cb Makefile --- a/Makefile Sat Apr 05 02:53:17 2014 +0300 +++ b/Makefile Sun Apr 06 18:25:55 2014 +0300 @@ -1,4 +1,4 @@ -PROG = hello-lkm +PROG = hello-dmx ELF = build/src/$(PROG).elf HEX = build/src/$(PROG).hex @@ -8,6 +8,8 @@ build: mkdir -p build/src +dmx: dmx.hex + ## Compiler MCU = atmega328p diff -r a960cf5981f7 -r 2d68a76322cb dmx.s --- a/dmx.s Sat Apr 05 02:53:17 2014 +0300 +++ b/dmx.s Sun Apr 06 18:25:55 2014 +0300 @@ -49,8 +49,8 @@ ; Clock select .set DMX_TIMER_CS_STOP = 0b000 -;.set DMX_TIMER_CS = 0b001 ; 1/1 -.set DMX_TIMER_CS = 0b111 ; 1/1024 +.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 @@ -59,7 +59,7 @@ .set LED_DDR = DDRB .set LED_PORT = PORTB .set LED_PIN = PINB -.set LED_BIT = PORTB5 +.set LED_BIT = PORTB0 ;; Set up DMX output DMX_Init: @@ -145,7 +145,7 @@ sbic TIFR2, OCF2A rjmp _dmx_bit_wait -sbi LED_PIN, LED_BIT +;sbi LED_PORT, LED_BIT ; Output bit ; XXX: ugly bit-testing, can't we do this using something more nifty? @@ -250,14 +250,14 @@ ; Init rcall DMX_Init -sbi LED_PORT, LED_BIT +cbi LED_PORT, LED_BIT ; Send; value _main_loop: ldi r17, 255 rcall DMX_Flood -;sbi LED_PORT, LED_BIT +cbi LED_PORT, LED_BIT ; never returns.. rjmp _main_loop diff -r a960cf5981f7 -r 2d68a76322cb docs/dmx.txt --- a/docs/dmx.txt Sat Apr 05 02:53:17 2014 +0300 +++ b/docs/dmx.txt Sun Apr 06 18:25:55 2014 +0300 @@ -1,11 +1,18 @@ + +## Specs: + * 250 kHz clock * 250000 bits / s * 4µs / bit * idle line = high (transmitter/master drives line high) - +## Pinout -Packet structure: + 1 0 GND + 2 - B + 3 + A + +## Protocol * paket format * break diff -r a960cf5981f7 -r 2d68a76322cb src/dmx_frame.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dmx_frame.c Sun Apr 06 18:25:55 2014 +0300 @@ -0,0 +1,49 @@ +/* + * Per-frame timing-critical bit-banging. + * + * Shamelessly stolen from + * http://code.google.com/p/tinkerit/source/browse/trunk/DmxSimple/DmxSimple.cpp + * + * Copyright (c) 2008-2009 Peter Knight, Tinker.it! + * + * Consumes approx. 11 * 64 ~= 705 clock cycles + * Leaves the line idle (high) on return. + */ +void dmx_frame (volatile byte value) +{ + uint8_t bitCount, delCount; + __asm__ volatile ( + "cli\n" + "ld __tmp_reg__,%a[dmxPort]\n" + "and __tmp_reg__,%[outMask]\n" + "st %a[dmxPort],__tmp_reg__\n" + "ldi %[bitCount],11\n" // 11 bit intervals per transmitted byte + "rjmp bitLoop%=\n" // Delay 2 clock cycles. + "bitLoop%=:\n"\ + "ldi %[delCount],%[delCountVal]\n" + "delLoop%=:\n" + "nop\n" + "dec %[delCount]\n" + "brne delLoop%=\n" + "ld __tmp_reg__,%a[dmxPort]\n" + "and __tmp_reg__,%[outMask]\n" + "sec\n" + "ror %[value]\n" + "brcc sendzero%=\n" + "or __tmp_reg__,%[outBit]\n" + "sendzero%=:\n" + "st %a[dmxPort],__tmp_reg__\n" + "dec %[bitCount]\n" + "brne bitLoop%=\n" + "sei\n" + : + [bitCount] "=&d" (bitCount), + [delCount] "=&d" (delCount) + : + [dmxPort] "e" (&DMX_PORT), + [outMask] "r" (~(1 << DMX_DATA)), + [outBit] "r" ((1 << DMX_DATA)), + [delCountVal] "M" (F_CPU/1000000-3), + [value] "r" (value) + ); +} diff -r a960cf5981f7 -r 2d68a76322cb src/hello-dmx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hello-dmx.c Sun Apr 06 18:25:55 2014 +0300 @@ -0,0 +1,105 @@ +/* + * DMX output. + * + * See doc/dmx.txt for the RX-485 bus protocol details... + */ + +#include +#include + +#include "stdlib.h" + +// debug +#define DEBUG_DDR DDRB +#define DEBUG_PORT PORTB +#define DEBUG_LED 0 + +void led_init () +{ + sbi(&DEBUG_DDR, DEBUG_LED); +} + +void led_on () +{ + sbi(&DEBUG_PORT, DEBUG_LED); +} + +void led_off () +{ + cbi(&DEBUG_PORT, DEBUG_LED); +} + +void led_toggle () +{ + xbi(&DEBUG_PORT, DEBUG_LED); +} + +// DMX +#define DMX_DDR DDRB +#define DMX_PORT PORTB +#define DMX_DATA 3 // SPI MOSI + +// baud rate: 250k = 4µs / bit +#define DMX_BAUD (250 * 1000) +#define DMX_US 4 + +// CPU cycles / bit: 64 @ 16Mhz +#define DMX_CYCLES (F_CPU / DMX_BAUD) + +void dmx_init () +{ + // dmx data out: idle (high) + sbi(&DMX_PORT, DMX_DATA); + sbi(&DMX_DDR, DMX_DATA); +} + +static inline void dmx_high () +{ + sbi(&DMX_PORT, DMX_DATA); +} + +static inline void dmx_low () +{ + cbi(&DMX_PORT, DMX_DATA); +} + +#define dmx_pause(bits) _delay_us((DMX_US * bits)) + +static void dmx_break () +{ + // break + dmx_low(); + dmx_pause(22); + + // mark-after-break (MAB) + dmx_high(); + dmx_pause(2); +} + +#include "dmx_frame.c" + +static void dmx_packet (byte r, byte g, byte b) +{ + dmx_break(); + dmx_frame(0); + + dmx_frame(0); // control + dmx_frame(r); + dmx_frame(g); + dmx_frame(b); + dmx_frame(0); // madness +} + +void main () +{ + led_init(); + dmx_init(); + + // dmx + while (true) { + dmx_packet(0x00, 0x00, 0xff); + dmx_pause(100); + + led_toggle(); + } +}