/* invalid command, used as default/do-nothing value * 101CCCCX == special commands, X must be 1. * X is 0 below => invalid */ #define DALI_INVALID 0xa000 static uint16_t dali_word, dali_sendq; static uint8_t dali_subctr, dali_state, dali_bit; static uint8_t dali_prevpin; enum dali_state { DALI_IDLE = 0, DALI_TX_SPACE, DALI_TX_STARTBIT1, DALI_TX_STARTBIT2, DALI_TX_LEFT = 4, DALI_TX_RIGHT, DALI_TX_STOP, DALI_RX_LEFT = 8, DALI_RX_RIGHT, }; ISR(TIMER0_COMPA_vect) { uint8_t inv = 1, data; TCNT0 = 0; /* CTC mode didn't work, CBA to debug */ data = (PIND >> D_DALII) & 1; if (dali_state <= DALI_TX_SPACE && data) { dali_state = DALI_RX_LEFT; dali_subctr = 4; dali_word = 1; } /* really simple CDR - resync clock on edge. * this is ABSOLUTELY neccessary. */ if (dali_state >= DALI_RX_LEFT && (dali_prevpin ^ data)) { if (dali_subctr >= 2 && dali_subctr <= 5) dali_subctr = 4; } dali_prevpin = data; dali_subctr++; dali_subctr &= 7; if (dali_subctr) return; /* this code runs @ 2400 Hz */ /********** actual state processing *********/ switch (dali_state) { /* idle */ case DALI_IDLE: if (dali_sendq != DALI_INVALID) { dali_word = dali_sendq; dali_sendq = DALI_INVALID; dali_state = DALI_TX_STARTBIT1; } return; case DALI_TX_SPACE: /* 12 ms inter-command spacing */ if (dali_bit > 28) dali_state = DALI_IDLE; dali_bit++; return; /* write */ /* 1 + 2: start bit */ case DALI_TX_STARTBIT1: uart_puts("dali["); uart_puthex(dali_word >> 8); uart_puthex(dali_word & 0xff); uart_puts("]\n"); PORTD |= (1 << D_DALIO); dali_state = DALI_TX_STARTBIT2; return; case DALI_TX_STARTBIT2: PORTD &= ~(1 << D_DALIO); dali_state = DALI_TX_LEFT; dali_bit = 0; return; case DALI_TX_LEFT: inv = 0; case DALI_TX_RIGHT: dali_state ^= 1; data = (dali_word >> (15 - dali_bit)) & 1; data ^= inv; if (data) PORTD |= (1 << D_DALIO); else PORTD &= ~(1 << D_DALIO); if (inv) dali_bit++; if (dali_bit == 16) dali_state = DALI_TX_STOP; break; case DALI_TX_STOP: PORTD &= ~(1 << D_DALIO); dali_state = DALI_IDLE; uart_puts("dali sent\n"); return; /* read */ case DALI_RX_RIGHT: dali_word <<= 1; dali_word |= 1 - data; dali_state = DALI_RX_LEFT; if (dali_word & 0x1000) { dali_state = DALI_IDLE; dali_word >>= 3; uart_puts("dali_rx "); uart_puthex(dali_word & 0xff); uart_puts("\n"); } break; case DALI_RX_LEFT: dali_state = DALI_RX_RIGHT; break; } } static void dali_send(uint16_t word) { cli(); dali_sendq = word; sei(); } static bool dali_queue_busy(void) { asm volatile ("" : : : "memory"); return dali_sendq != DALI_INVALID; } static void dali_init(void) { PORTD |= (1 << D_DALII); PORTD &= ~(1 << D_DALIO); dali_subctr = 0; dali_state = DALI_IDLE; dali_prevpin = 0; TCNT0 = 0; OCR0A = 52; // 8 MHz / 8 / 52 = 19230. TIMSK0 = (1 << OCIE0A); TIFR0 = (1 << OCF0A); TCCR0A = 0; TCCR0B = (0 << CS02) | (1 << CS01) | (0 << CS00); // 8 MHz / 8 = 1MHz }