hello-dmx: working basic dmx output using DmxSimple's frame-timing inline assembler code
--- 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
--- 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
--- 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
--- /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)
+ );
+}
--- /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 <avr/io.h>
+#include <util/delay.h>
+
+#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();
+ }
+}