User Tools

Site Tools


projects:dmx_relay_box:home

Dmx relay box

~~META:description abstract=4 channels simple DMX relay box~~

A low cost 4 channels DMX relay box. Has a Schmitt trigger on value to avoid oscillations at the switch point. No auto mode …

Features

  • None other than switching stuff on and off on request :)

Electronics

schematic.svg

Source code

main.c
/**
 * @defgroup RGB leds DMX512 controller
 * @type firmware
 * @file main.c
 * @author Etienne Meleard <etienne@yent.eu>
 * @creation 2015-06-09
 * @tabsize 4
 * @mcu ATMEGA8L
 * @fcpu 8MHz
*/
 
 
//============================ WARNING ===============================//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
//                                                                    //
// Fuses must be like SUT1 = 1, SUT0 = 1, CKSEL3 = 1, CKSEL2 = 1,     //
// CKSEL1 = 1 and CKSEL0 = 0 to ensure using the crystal as clock     //
// (otherwise it uses the 8MHz internal clock), full swing mode is    //
// mandatory to acheive 8MHz operation.                               //
//                                                                    //
// CKDIV8 fuse must not be set to avoid clock frequency divided by 8  //
//                                                                    //
// Fuse set or = 1 means checked in ponyprog                          //
//                                                                    //
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
//====================================================================//
 
 
// ================= Fuses ================= //
 
/*FUSES = { // No success writing fuse this way, command line below ...
    .low = (FUSE_SUT1 & FUSE_SUT0 & FUSE_CKSEL3),
    .high = HFUSE_DEFAULT,
    .extended = EFUSE_DEFAULT,
};*/
 
// Run
// avrdude -c usbasp -p m8 -U lfuse:w:0xfe:m -U hfuse:w:0xd1:m -B250
// at first (-F may be needed)
 
 
// ================ Includes =============== //
 
#include <avr/io.h>
#include <sup_avr/io2.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
 
// ================ Pinouts ================ //
 
#define LED         Bit(PORTD).bit7
 
#define RX          Bit(PIND).bit0
#define RX_COMPLETE SIG_UART_RECV
#define RX_TIMER    TCNT0
#define BREAK_LEN   11
#define MAB_LEN     1
 
#define DIP_0       Bit(PINC).bit3
#define DIP_1       Bit(PINC).bit2
#define DIP_2       Bit(PINC).bit1
#define DIP_3       Bit(PINC).bit0
 
#define DIP_A       Bit(PORTB).bit3
#define DIP_B       Bit(PORTB).bit4
#define DIP_C       Bit(PORTB).bit5
 
#define OUT_1       Bit(PORTD).bit1
#define OUT_2       Bit(PORTD).bit2
#define OUT_3       Bit(PORTD).bit5
#define OUT_4       Bit(PORTD).bit6
 
 
// ================ Macros ================= //
 
#define BLINK       LED=1; _delay_ms(150); LED=0; _delay_ms(150)
#define PULSE10     LED=1; _delay_us(10); LED=0; _delay_us(10)
#define PULSE5      LED=1; _delay_us(5); LED=0; _delay_us(5)
#define PULSE3      LED=1; _delay_us(3); LED=0; _delay_us(3)
#define PULSE1      LED=1; _delay_us(1); LED=0; _delay_us(1)
 
#define RX_START    UCSRB |= 0x10; receiving = 1
#define RX_STOP     UCSRB &= 0xef; receiving = 0
 
#define DIP_SETTIME 10    // us, allows diodes to set (due to diode capacitance)
 
// Outputs change logic
#define TRIGGER_HALFWINDOW  10
#define TRIGGER_POINT       128
#define TRIGGER_HIGH        TRIGGER_POINT + TRIGGER_HALFWINDOW
#define TRIGGER_LOW         TRIGGER_POINT - TRIGGER_HALFWINDOW
 
#define RX_ERROR    ((UCSRA & 0x10) || (UCSRA & 0x08))
 
#define MAX_TIME_WITHOUT_START 10000
#define ADDRESS_REFRESH_PERIOD 5000
 
 
// ================ Globals ================ //
 
volatile uint16_t address = 0x0000;
 
volatile uint8_t receiving = 0;
volatile uint16_t received = 0;
 
 
// ================ Functions ============== //
 
void init(void) {
    // Led and relays as output
    DDRD = 0xe6;
 
    // USART
    UCSRA = 0x00;    // No doubling of frequency
    UCSRB = 0x80;    // Rx disabled, Rx irq enabled, 8bit mode
    UCSRC = 0x8e;    // Async, no parity, 2 stops, 8 bits
    UBRRL = 1;        // 250kbds
 
    // Detection timer (timer 0)
    TCCR0 = 0x03;    // 64 prescaler, increment every 8us
 
    // Dip inputs
    DDRC &= 0xf0;
    PORTC |= 0x0f;    // Pull-up
 
    // Dip outputs
    DDRB |= 0x38;
    DIP_A = 1;
    DIP_B = 1;
    DIP_C = 1;
}
 
void getAddress(void) { // Takes approx. 40us
    uint16_t buffer = 0x0000;
 
    DIP_A = 1;
    DIP_B = 1;
    DIP_C = 1;
 
    DIP_C = 0;
    _delay_us(DIP_SETTIME);
    if(!DIP_0) buffer += 1;
    if(!DIP_1) buffer += 2;
    if(!DIP_2) buffer += 4;
    if(!DIP_3) buffer += 8;
    DIP_C = 1;
    buffer <<= 4;
 
    DIP_B = 0;
    _delay_us(DIP_SETTIME);
    if(!DIP_0) buffer += 1;
    if(!DIP_1) buffer += 2;
    if(!DIP_2) buffer += 4;
    if(!DIP_3) buffer += 8;
    DIP_B = 1;
    buffer <<= 4;
 
    DIP_A = 0;
    _delay_us(DIP_SETTIME);
    if(!DIP_0) buffer += 1;
    if(!DIP_1) buffer += 2;
    if(!DIP_2) buffer += 4;
    if(!DIP_3) buffer += 8;
    DIP_A = 1;
 
    address = buffer;
}
 
uint8_t getFrameStart(void) {
    uint32_t t = 1500000; // 1.5s
    while(t && RX) {
        _delay_us(1);
        t--;
    }
    if(!t) return 0;
 
    RX_TIMER = 0;
    while(!RX && RX_TIMER < BREAK_LEN) _delay_us(1);
    if(RX_TIMER < BREAK_LEN) return 0; // Error, break too short
 
    while(!RX) _delay_us(1); // Wait for MAB
 
    RX_TIMER = 0;
    while(RX && RX_TIMER < MAB_LEN) _delay_us(1);
    if(RX_TIMER < MAB_LEN) return 0; // Error, break too short
 
    received = 0;
    RX_START;
 
    return 1;
}
 
 
// =============== Interrupts ============== //
 
SIGNAL(RX_COMPLETE) {
    uint8_t data, channel, state;
    data = UDR;
 
    if(!receiving) return;
 
    if(UCSRA & 0x10) return;
    if(UCSRA & 0x08)return;
 
    received++;
 
    if(received == 1) {
        //if(data != 0x00) RX_STOP; // Should be 0x00, not a setting packet otherwise
        return; // Ignore first slot
    }
 
    if(received < address + 2) {
        return; // Address not reached
    }
 
    if(received > address + 4) { // Received enough packets, stop receiving and wait for next frame
        RX_STOP;
        return;
    }
 
    channel = received - address - 2;
 
    // Got channel, get current state
    switch(channel) {
        case 0: state = OUT_1; break;
        case 1: state = OUT_2; break;
        case 2: state = OUT_3; break;
        case 3: state = OUT_4; break;
    }
 
    // Apply schmitt trigger
    if(!state && data > TRIGGER_HIGH)
        state = 1;
 
    if(state && data < TRIGGER_LOW)
        state = 0;
 
    // Set output
    switch(channel) {
        case 0: OUT_1 = state; break;
        case 1: OUT_2 = state; break;
        case 2: OUT_3 = state; break;
        case 3: OUT_4 = state; break;
    }
}
 
 
// ================== Main ================= //
 
int main(void) {
    uint16_t time_without_start = 0;
    uint16_t address_is_valid = 0;
 
    cli();        // WatchDog disabled AT ALL TIMES !
    init();
    sei();
 
    BLINK;
    BLINK;
 
    while(1) {
        if(!address_is_valid) {
            getAddress();
 
            address_is_valid = ADDRESS_REFRESH_PERIOD;
        }
 
        if(getFrameStart()) {
            LED = 0;
            time_without_start = 0;
        }
        if(time_without_start > MAX_TIME_WITHOUT_START) {
            if(receiving) RX_STOP;
            BLINK;
        }
        time_without_start++;
 
        if(address_is_valid) address_is_valid--;
        _delay_us(100);
    }
    return 0;
}

How to

Manual

dmx, microcontroller, lighting

projects/dmx_relay_box/home.txt · Last modified: 2022/06/30 21:13 by 127.0.0.1