#define DALI_INVALID 0xa0cc enum dali_state { DALI_IDLE = 0, DALI_SPACE, // 1 DALI_DEAD_SPACE, // 2 DALI_RX_SBIT, // 3 DALI_RX_DATA, // 4 DALI_TX_SBIT0, // 5 DALI_TX_SBIT1, // 6 DALI_TX_0, // 7 DALI_TX_1, // 8 DALI_TX_STOP, // 9 }; static volatile uint8_t dali_state; static volatile uint8_t dali_rx; static volatile bool dali_rx_avail; static volatile uint16_t dali_tx; static volatile bool dali_tx_rq; struct dalistat { uint16_t rxok, falsestart, noise, manchester; } dalistat; #undef DEBUG #ifdef DEBUG #define XBUF 36 static uint8_t dbuf[XBUF], sbuf[XBUF], fbuf[XBUF]; static int8_t dbufpos; #define F_MAJ 0x01 #define F_MCH 0x10 #endif #define dali_s_listening() (dali_state <= DALI_SPACE) #define dali_s_cdr() (dali_state == DALI_RX_DATA) ISR(TIMER0_COMPA_vect) { static uint8_t history = 0; #define hist_size 5 #define hist_mask ((1 << hist_size) - 1) #define hist_majority_1() (((history | (history + 1)) & hist_mask) == hist_mask) #define hist_majority_0() (((history & (history - 1)) & hist_mask) == 0) static uint8_t bitpos = 0; static uint8_t rx_prevstate_errs, rx_data; static uint16_t tx_data; uint8_t bit = 0; static uint8_t subsamp = 0; uint8_t flags = 0; subsamp++; subsamp &= 7; while (TCNT0 < 52) asm volatile ("nop\n" ::: "memory"); /* CTC mode didn't work, CBA to debug */ TCNT0 = 0; history <<= 1; history |= (PIND >> D_DALII) & 1; /* * subsamp 1 2 3 4 5 6 7 0 || * ^ state trigger * /edge/ <----------- stable / sample ----------> */ if (dali_s_cdr()) { if (subsamp >= 2 && subsamp <= 6 && ((history & 0x0f) == 0x03 || (history & 0x0f) == 0x0c)) subsamp = 4; } if (dali_s_listening() && hist_majority_1()) { rx_prevstate_errs = dali_state; dali_state = DALI_RX_SBIT; subsamp = 0; return; } if (subsamp) return; #ifdef DEBUG if (dali_state != DALI_IDLE && dbufpos < XBUF) { if (dbufpos >= 0) { sbuf[dbufpos] = dali_state; dbuf[dbufpos] = history; } dbufpos++; } #endif switch (dali_state) { case DALI_SPACE: case DALI_DEAD_SPACE: if (bitpos > 0) { bitpos--; return; } dali_state = DALI_IDLE; case DALI_IDLE: break; case DALI_RX_SBIT: if (!hist_majority_0()) { if (history == 0xf8) { subsamp = 7; return; } dalistat.falsestart++; dali_state = rx_prevstate_errs; return; } dali_state = DALI_RX_DATA; rx_prevstate_errs = 0; rx_data = 0; bitpos = 0; break; case DALI_RX_DATA: if (!(hist_majority_1() || hist_majority_0())) { rx_prevstate_errs++; dalistat.noise++; bit = 0; } else bit = hist_majority_1(); if (bitpos & 1) { if (bit == (rx_data & 1)) { rx_prevstate_errs++; dalistat.manchester++; } } else { rx_data <<= 1; rx_data |= bit; } bitpos++; if (bitpos == 16) { bitpos = 20; dali_state = DALI_DEAD_SPACE; if (!rx_prevstate_errs) { dali_rx = rx_data; dali_rx_avail = 1; dalistat.rxok++; } } break; case DALI_TX_SBIT0: bit = 1; dali_state = DALI_TX_SBIT1; break; case DALI_TX_SBIT1: bit = 0; dali_state = DALI_TX_0; break; case DALI_TX_0: if (bitpos == 16) { bitpos = 8; dali_state = DALI_TX_STOP; PORTD |= (1 << D_DALIO); } else { bit = (tx_data & (1 << (15 - bitpos))) ? 1 : 0; dali_state = DALI_TX_1; } break; case DALI_TX_1: bit = (tx_data & (1 << (15 - bitpos))) ? 0 : 1; bitpos++; dali_state = DALI_TX_0; break; case DALI_TX_STOP: if (bitpos == 0) { dali_tx_rq = 0; bitpos = 25; dali_state = DALI_SPACE; } else bitpos--; break; } if ((dali_state == DALI_TX_SBIT1) || (dali_state == DALI_TX_0) || (dali_state == DALI_TX_1)) { if (bit) { PORTD &= ~(1 << D_DALIO); } else { PORTD |= (1 << D_DALIO); } } #ifdef DALI_USER_IDLE if (dali_state == DALI_IDLE && !dali_tx_rq) { uint16_t next = dali_user_idle(); if (next != DALI_INVALID) { tx_data = next; bitpos = 0; dali_state = DALI_TX_SBIT0; subsamp = 7; } } #endif if (dali_state == DALI_IDLE && dali_tx_rq) { tx_data = dali_tx; bitpos = 0; dali_tx = DALI_INVALID; dali_tx_rq = 2; dali_state = DALI_TX_SBIT0; subsamp = 7; } } static void dali_send(uint16_t word) { #if 0 uart_wait(); uart_puts("\n\n\ndali_send "); uart_puthex(word >> 8); uart_puthex(word & 0xff); uart_puts("\n"); uart_wait(); #endif dali_rx_avail = 0; dali_tx = word; do asm volatile ("" ::: "memory"); while (dali_tx_rq); dali_tx_rq = 1; while (dali_tx_rq || dali_state != DALI_IDLE) { asm volatile ("" ::: "memory"); } #if 0 uart_wait(); uart_puts("dali_sent "); uart_puthex(word >> 8); uart_puthex(word & 0xff); uart_puts(" "); if (dali_rx_avail) uart_puthex(dali_rx); else uart_puts("norx"); uart_puts("\n"); uart_wait(); for (uint8_t x = 0; x < dbufpos; x++) uart_puthex(sbuf[x]); uart_puts("\n"); uart_wait(); for (uint8_t x = 0; x < dbufpos; x++) uart_puthex(dbuf[x]); uart_puts("\n"); uart_wait(); for (uint8_t x = 0; x < dbufpos; x++) uart_puthex(fbuf[x]); uart_puts("\n"); uart_wait(); #endif } static void dali_twice(uint16_t word) { dali_send(word); _delay_ms(25); dali_send(word); } static void dali_init(void) { PORTD |= (1 << D_DALII) | (1 << D_DALIO); dali_state = DALI_IDLE; dali_tx_rq = 0; dali_rx_avail = 0; dalistat.falsestart = dalistat.noise = dalistat.manchester = dalistat.rxok = 0; TIFR0 = (1 << OCF0A); TCNT0 = 0; OCR0A = 49; /*52*/ // 8 MHz / 8 / 52 = 19230. asm volatile ("" ::: "memory"); TIMSK0 = (1 << OCIE0A); TCCR0A = 0; asm volatile ("" ::: "memory"); TCCR0B = (0 << CS02) | (1 << CS01) | (0 << CS00); // 8 MHz / 8 = 1MHz } static void dali_buscheck(void) { while ((PIND >> D_DALII) & 1) { uart_puttick(); uart_puts("DALI bus stuck/not powered/active\n"); _delay_ms(20); } }