terom@62: /* terom@62: * DMX output. terom@62: * terom@62: * See doc/dmx.txt for the RX-485 bus protocol details... terom@62: */ terom@62: terom@62: #include terom@62: terom@62: #include "stdlib.h" terom@65: #include "timer.c" terom@65: #include "serial.c" terom@65: #include "dmx.c" terom@62: terom@62: // debug terom@62: #define DEBUG_DDR DDRB terom@62: #define DEBUG_PORT PORTB terom@62: #define DEBUG_LED 0 terom@62: terom@62: void led_init () terom@62: { terom@62: sbi(&DEBUG_DDR, DEBUG_LED); terom@62: } terom@62: terom@62: void led_on () terom@62: { terom@62: sbi(&DEBUG_PORT, DEBUG_LED); terom@62: } terom@62: terom@62: void led_off () terom@62: { terom@62: cbi(&DEBUG_PORT, DEBUG_LED); terom@62: } terom@62: terom@62: void led_toggle () terom@62: { terom@62: xbi(&DEBUG_PORT, DEBUG_LED); terom@62: } terom@62: terom@65: // dmx terom@66: #define DMX_COUNT 255 terom@66: terom@65: /* terom@65: * DMX state terom@65: */ terom@65: static struct dmx_state { terom@66: byte out[DMX_COUNT]; terom@65: byte count; terom@65: } dmx; terom@62: terom@65: enum state { terom@65: START = '\n', terom@65: CMD = ';', terom@65: ARG = ',', terom@65: ERROR = '!', terom@65: }; terom@62: terom@65: #define CONSOLE_ARGS 8 terom@65: terom@65: /* terom@65: * Console input state. terom@65: */ terom@65: static struct console { terom@66: byte state; terom@66: byte cmd; terom@66: byte argc; terom@66: byte argv[CONSOLE_ARGS]; terom@66: } console; terom@65: terom@66: /* terom@66: * Clear output (no state). terom@66: */ terom@66: int cmd_clear () terom@66: { terom@66: dmx.count = 0; terom@66: terom@66: return 0; terom@66: } terom@66: terom@66: /* terom@66: * Set output to given sequence. terom@66: */ terom@66: int cmd_out () terom@66: { terom@66: byte i; terom@66: terom@67: for (i = 0; i < console.argc && i < DMX_COUNT; i++) { terom@66: dmx.out[i] = console.argv[i]; terom@66: } terom@66: terom@66: dmx.count = i; terom@66: terom@66: return 0; terom@66: } terom@66: terom@66: /* terom@66: * Set output at offset. terom@66: */ terom@66: int cmd_set () terom@66: { terom@66: if (console.argc < 1) { terom@66: return '!'; terom@66: } terom@66: terom@67: if (console.argv[0] == 0) { terom@67: return '!'; terom@67: } terom@66: terom@67: byte i = console.argv[0] - 1; terom@67: terom@67: for (byte a = 1; a < console.argc && i < DMX_COUNT; a++) { terom@66: dmx.out[i++] = console.argv[a]; terom@66: } terom@66: terom@67: if (i > dmx.count) { terom@66: dmx.count = i; terom@67: } terom@66: terom@66: return 0; terom@66: } terom@66: terom@66: /* terom@70: * Fill output range with values. terom@70: * terom@70: * r ... terom@68: */ terom@68: int cmd_fill () terom@68: { terom@70: if (console.argc < 3) { terom@70: return '!'; terom@70: } terom@70: terom@70: byte start = console.argv[0]; terom@70: byte end = console.argv[1]; terom@70: terom@70: if (!start) { terom@70: return '!'; terom@70: } terom@70: terom@70: if (end < start) { terom@70: return '!'; terom@70: } terom@70: terom@72: if (end >= DMX_COUNT) { terom@72: return '!'; terom@72: } terom@72: terom@70: // apply terom@70: // arg is 1..256 terom@70: byte c = start - 1; terom@70: terom@72: // c < end actually means inclusive to end of range, as the index is -1 terom@72: for ( ; c < end; ) { terom@72: for (byte a = 2; a < console.argc && c < end; a++) { terom@70: dmx.out[c++] = console.argv[a]; terom@70: } terom@70: } terom@70: terom@70: if (c > dmx.count) { terom@70: dmx.count = c; terom@70: } terom@70: terom@70: return 0; terom@70: } terom@70: terom@70: /* terom@70: * Set output start-stop/step range to value. terom@70: * terom@70: * r terom@70: */ terom@70: int cmd_range () terom@70: { terom@68: if (console.argc != 4) { terom@68: return '!'; terom@68: } terom@68: terom@68: byte start = console.argv[0]; terom@68: byte end = console.argv[1]; terom@68: byte skip = console.argv[2]; terom@68: byte value = console.argv[3]; terom@68: byte c; terom@68: terom@68: if (!start) { terom@68: return '!'; terom@68: } terom@68: terom@68: if (end < start) { terom@68: return '!'; terom@68: } terom@68: terom@68: if (!skip) { terom@68: return '!'; terom@68: } terom@68: terom@68: for (c = start; c <= end && c < DMX_COUNT; c += skip) { terom@68: dmx.out[c - 1] = value; terom@68: } terom@68: terom@68: // XXX: -1 terom@68: if (c > dmx.count) { terom@68: dmx.count = c; terom@68: } terom@68: terom@68: return 0; terom@68: } terom@68: /* terom@66: * Set output to max. zeroes terom@66: */ terom@66: int cmd_zero () terom@66: { terom@66: byte count; terom@66: terom@66: if (console.argc == 0) { terom@66: count = DMX_COUNT; terom@66: } else if (console.argc == 1) { terom@66: count = console.argv[0]; terom@66: } else { terom@66: return '!'; terom@66: } terom@66: terom@66: // set terom@66: byte i; terom@66: for (i = 0; i < count; i++) { terom@66: dmx.out[i] = 0; terom@66: } terom@66: dmx.count = i; terom@66: terom@66: return 0; terom@66: } terom@65: terom@65: /* terom@65: * Process console command. terom@65: */ terom@65: int command () terom@62: { terom@65: switch (console.cmd) { terom@66: case 'c': return cmd_clear(); terom@66: case 'o': return cmd_out(); terom@68: case 'f': return cmd_fill(); terom@70: case 'r': return cmd_range(); terom@66: case 's': return cmd_set(); terom@66: case 'z': return cmd_zero(); terom@66: terom@66: default: return '?'; terom@65: } terom@62: } terom@62: terom@65: /* terom@65: * Process console input. terom@65: */ terom@65: char input (char c) terom@62: { terom@65: // control terom@65: if (c == '\r') { terom@65: char ret = '?'; terom@62: terom@65: if (console.state == CMD) { terom@65: console.argc = 0; terom@65: } else if (console.state == ARG) { terom@65: console.argc++; terom@65: } else { terom@65: console.state = START; terom@65: return '\n'; terom@65: } terom@65: terom@65: // command terom@65: if ((ret = command(console.cmd))) { terom@65: terom@65: } else { terom@65: ret = '\n'; terom@65: } terom@77: terom@65: // return to START with response terom@65: console.state = START; terom@66: return '\n'; // XXX: required to delimit command reponses.. terom@77: terom@66: } else if (c == ' ' || c == '\t' || c == ',') { terom@65: // argument terom@65: if (console.state == CMD) { terom@65: console.state = ARG; terom@65: console.argc = 0; terom@67: console.argv[0] = 0; terom@65: terom@66: return ','; terom@65: terom@65: } else if (console.state == ARG) { terom@65: if (console.argc++ < CONSOLE_ARGS) { terom@65: console.argv[console.argc] = 0; terom@65: terom@66: return ','; terom@65: } terom@65: } terom@65: terom@77: // printable terom@65: } else if (32 < c && c < 128) { terom@65: // process input char terom@65: if (console.state == START) { terom@65: console.cmd = c; terom@65: console.state = CMD; terom@65: terom@65: return c; terom@65: terom@65: } else if (console.state == ARG) { terom@65: if (c >= '0' && c <= '9') { terom@65: console.argv[console.argc] *= 10; terom@65: console.argv[console.argc] += (c - '0'); terom@65: terom@65: return c; terom@65: } terom@65: } terom@65: } else { terom@65: // ignore terom@65: return ' '; terom@65: } terom@77: terom@65: // reject terom@65: console.state = ERROR; terom@65: return ERROR; terom@62: } terom@62: terom@65: /* terom@65: * Tick output state terom@65: */ terom@65: void update () terom@62: { terom@66: dmx_packet(dmx.count, dmx.out); terom@62: } terom@62: terom@62: void main () terom@62: { terom@62: led_init(); terom@65: timer_init(); terom@65: serial_init(); terom@66: dmx_init(); terom@62: terom@65: // mainloop terom@65: char c = '>'; terom@65: unsigned timeout = 8000; // 2Hz terom@77: terom@66: // start terom@65: sei(); terom@66: serial_write(c); terom@65: terom@62: while (true) { terom@65: // sleep terom@65: //led_on(); terom@65: if (timer_sleep(timeout)) { terom@66: led_toggle(); terom@69: } terom@65: terom@69: // input terom@69: while ((c = serial_read())) { terom@69: serial_write(input(c)); terom@65: } terom@69: terom@65: //led_off(); terom@77: terom@65: // output terom@65: update(); terom@62: } terom@62: }