diff options
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | lightctrl_v2.c | 526 |
2 files changed, 530 insertions, 3 deletions
@@ -1,6 +1,6 @@ .PHONY: flash love -TARGETS=door lightctrl +TARGETS=door lightctrl lightctrl_v2 CFLAGS_OPT=-Os \ -fpredictive-commoning -fmerge-all-constants -fmodulo-sched -fmodulo-sched-allow-regmoves \ @@ -11,6 +11,7 @@ CFLAGS_LD=-Wl,-T,avr4-signature.x CFLAGS_=${CFLAGS_WARN} ${CFLAGS_OPT} ${CFLAGS_LD} ${CFLAGS} MCU_door=48 MCU_lightctrl=88 +MCU_lightctrl_v2=88 AVRDUDE=avrdude love: $(foreach target,$(TARGETS),$(target).elf) @@ -18,8 +19,8 @@ love: $(foreach target,$(TARGETS),$(target).elf) ifdef TARGET flash: ${TARGET}.flash - $(AVRDUDE) -p m$(MCU_$(basename $<)) -c stk500v2 -P avrdoper -y \ - -U eeprom:r:eeprom:r +# $(AVRDUDE) -p m$(MCU_$(basename $<)) -c stk500v2 -P avrdoper -y \ +# -U eeprom:r:eeprom:r $(AVRDUDE) -p m$(MCU_$(basename $<)) -c stk500v2 -P avrdoper -y \ -U flash:w:$< \ -U eeprom:w:eeprom:r diff --git a/lightctrl_v2.c b/lightctrl_v2.c new file mode 100644 index 0000000..4e5c472 --- /dev/null +++ b/lightctrl_v2.c @@ -0,0 +1,526 @@ +#define _dbg_dump_ret() +#define dbg_dump_ret() do { \ + union { uint16_t u; void *p; } ret = { .p = __builtin_return_address(0) }; \ + _uart_putch('#'); uart_puthex16((ret.u & 0xfff) << 1); _uart_putch('\n'); } while (0) + +#define array_size(a) (sizeof(a) / sizeof(a[0])) + +#define F_CPU 8000000 +#include <stdint.h> +#include <stdbool.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/pgmspace.h> +#include <avr/eeprom.h> +#include <avr/wdt.h> +#include <util/delay.h> + +const uint8_t __signature[3] __attribute__((section (".signature"), used)) = + { SIGNATURE_2, SIGNATURE_1, SIGNATURE_0 }; + +#define B_SCK 5 +#define B_MISO 4 +#define B_MOSI 3 +#define B_SS 2 + +#define D_TXD 1 +#define D_DALII 3 +#define D_DALIO 4 +#define D_LED1 5 +#define D_LED2 6 +#define D_TAST 7 + +enum { + G_BOOTING = 0, + G_RUNNING, +} global_state = G_BOOTING; + +#define SYSTICK_USER_100HZ +static void systick_user_100hz(void); +#define SYSTICK_USER_1HZ +static void systick_user_1hz(void); +#define CAN_USER_IRQH +static void can_user_irqh(void); +#define DALI_USER_IDLE +static uint16_t dali_user_idle(void); + +#define DALI_NUMDEV 64 +#define T_EVG_COOLDOWN 5 /* seconds */ + +/* cooldown: + * set(0) => set_needed=1 + * `-> handler performs DALI "OFF", + * set_needed = 0, cooldown = T_EVG_COOLDOWN + * | no other action while cooldown <> 0 + * `-> systick_user_1hz cooldown-- + * `-> cooldown == 0 => resume normal operations + * + * ack_changes: + * set() => set_needed =1 + * `-> handler performs DALI SET + * ack_changes = 3 + * `-> + */ +struct dali_dev { + uint8_t set, actual; + + __extension__ uint8_t cooldown : 4; + __extension__ uint8_t need_set : 1; + __extension__ uint8_t need_poweron : 1; + __extension__ uint8_t _unused0 : 1; + __extension__ uint8_t present : 1; + + __extension__ uint8_t tx_read : 1; + __extension__ uint8_t tx_set : 3; + __extension__ uint8_t _unused1 : 4; +} __attribute__((packed)); + +static struct dali_dev devices[DALI_NUMDEV]; +static void target_set(uint8_t dst, uint8_t val); +#define target_get(idx) devices[idx].set +#define target_present(addr) devices[addr].present +#define target_set_present(addr) devices[addr].present = 1 + +#include "uart.c" +#include "tick.c" +#include "dali2.c" +#include "dali_ctl.c" +#include "dim.c" +#include "can.c" +#include "wdt.c" +#include "helpers.c" + +#define CANA_DALI_BASE 0x440 + +/* irq mixed */ +static void target_set(uint8_t dst, uint8_t val) +{ + uart_puts(" ["); + uart_puthex(dst); + uart_puts("="); + uart_puthex(val); + uart_puts("]"); + + devices[dst].need_set = 1; + if (!devices[dst].set && val) + devices[dst].need_poweron = 1; + devices[dst].set = val; +} + +/* irqonly */ +static void systick_user_1hz(void) +{ + for (uint8_t i = 0; i < DALI_NUMDEV; i++) { + if (!devices[i].cooldown) + continue; + devices[i].cooldown--; + if (!devices[i].cooldown) + devices[i].need_set = devices[i].need_poweron = + (devices[i].set != 0); + } +} + +/* irqonly */ +static void systick_user_100hz(void) +{ + if (global_state == G_BOOTING) + return; + + for (uint8_t i = 0; i < DALI_NUMDEV; i++) + if (devices[i].tx_set > 2) + devices[i].tx_set--; + do_tick(); +} + +/* irqonly */ +static void can_handle_light(uint16_t sublab_addr) +{ + /* - 7 allows overlapping writes to a nonaligned address. + * "base" below will start out at 0xf9~0xff in that case */ + + if (sublab_addr < CANA_DALI_BASE - 7) + return; + + uint8_t base = sublab_addr - CANA_DALI_BASE, len = can_rx_len(), pos; + for (pos = 0; pos < len; pos++) { + uint8_t dst = base + pos, val; + if (dst >= 0x40) + continue; + val = can_rx_data[pos]; + target_set(dst, val); + } +} + +/* irqonly */ +static uint16_t dali_user_idle(void) +{ + static uint8_t dali_exec_pos = 0; + uint8_t stop_at = dali_exec_pos; + uint16_t ret = DALI_INVALID; + + if (global_state == G_BOOTING) + return ret; + + do { + if (devices[dali_exec_pos].need_set + && !devices[dali_exec_pos].cooldown) { + if (devices[dali_exec_pos].need_poweron) { + devices[dali_exec_pos].need_poweron = 0; + ret = (dali_exec_pos << 9) | 0x108; + break; + } + if (devices[dali_exec_pos].set) + ret = (dali_exec_pos << 9) | + (devices[dali_exec_pos].set == 0xff + ? 0xfe : devices[dali_exec_pos].set); + else { + ret = (dali_exec_pos << 9) | 0x100; + devices[dali_exec_pos].cooldown = + T_EVG_COOLDOWN; + } + devices[dali_exec_pos].need_set = 0; + devices[dali_exec_pos].tx_set = 7; + + dali_exec_pos = (dali_exec_pos + 1) & (DALI_NUMDEV - 1); + break; + } + + dali_exec_pos = (dali_exec_pos + 1) & (DALI_NUMDEV - 1); + } while (dali_exec_pos != stop_at); + if (ret != DALI_INVALID) { + uart_puttick(); + uart_puts("dali async "); + uart_puthex16(ret); + uart_puts("\n"); + } + return ret; +} + +static struct can can_queue[4]; +static uint8_t can_queue_ptr = 0; + +/* set commands are directly applied from CAN IRQ */ +/* irqonly */ +static void can_user_irqh(void) +{ + uint16_t sublab_addr, sublab_proto; + + if (!can_rx_isext() || global_state == G_BOOTING) { + can.rx_addr.u = 0; + return; + } + + sublab_addr = can_rx_sublab_addr(); + if (sublab_addr >= CANA_DALI_BASE + DALI_NUMDEV + || sublab_addr < CANA_DALI_BASE - 7) { + can.rx_addr.u = 0; + return; + } + + sublab_proto = can_rx_sublab_proto(); + + uart_puttick(); + uart_puts("CAN "); + uart_puthex16(sublab_proto); + uart_puts(" "); + uart_puthex16(sublab_addr); + + if (sublab_proto == 0xcc08) + can_handle_light(sublab_addr); + else { + can_queue[can_queue_ptr] = can; + can_queue_ptr = (can_queue_ptr + 1) + & (array_size(can_queue) - 1); + } + can.rx_addr.u = 0; + uart_puts(" EOI\n"); +} + +static struct can perform; + +const uint8_t dalidisc_page0[6] PROGMEM = {0x01, 0x01, 0x00, 0x01, 0x00, 0x00}; +const uint8_t dalidisc_page8[4] PROGMEM = {'D', 'A', 'L', 'I'}; + +static uint8_t dali_grab_value(uint8_t busaddr, uint8_t cmd) +{ + dali_send(0x100 | (busaddr << 9) | cmd); + if (!dali_rx_avail) { + _delay_ms(2); + dali_send(0x100 | (busaddr << 9) | cmd); + } + if (!dali_rx_avail) { + _delay_ms(5); + dali_send(0x100 | (busaddr << 9) | cmd); + } + return dali_rx_avail ? dali_rx : 0xa5; +} + +static void can_handle_disco(uint16_t sublab_addr) +{ + wdt_reset(); + + if (sublab_addr < CANA_DALI_BASE) + return; + + uint8_t addr = sublab_addr - CANA_DALI_BASE; + uint8_t page = can_rx_sublab_disco_page2(perform); + uint8_t buf[8]; + + if (!can_rx_ext_rr2(perform)) { + uart_puts("nRR\n"); + switch (page) { + case 5: + if (can_rx_len2(perform) != 2) + return; + dali_send((perform.rx_data[0] << 8) | perform.rx_data[1]); + can_send(CANA_DISCOVERY_F(page, sublab_addr), !!dali_rx_avail, (uint8_t *)&dali_rx); + return; + case 6: + if (can_rx_len2(perform) == 4 + && perform.rx_data[0] == 's' + && perform.rx_data[1] == 'c' + && perform.rx_data[2] == 'a' + && perform.rx_data[3] == 'n') { + + global_state = G_BOOTING; + for (uint8_t c = 0; c < DALI_NUMDEV; c++) + devices[c].present = 0; + dali_search(); + global_state = G_RUNNING; + } else if (can_rx_len2(perform) == 3 + && perform.rx_data[0] == 'q' + && perform.rx_data[1] == 'b') { + uint8_t addr = perform.rx_data[2] & 0x3f; + uint8_t msg[3]; + + dali_send(DALI_C_QBALLAST | (addr << 9)); + if (!dali_rx_avail) + dali_send(DALI_C_QBALLAST | (addr << 9)); + devices[addr].present = dali_rx_avail; + + msg[0] = 'q'; + msg[1] = dali_rx_avail ? '+' : '-'; + msg[2] = addr; + can_send(CANA_DISCOVERY_F(page, sublab_addr), sizeof(msg), msg); + } + return; + } + return; + } + +#define loadpgm(what) for (uint8_t c = 0; c < sizeof(what); c++) buf[c] = pgm_read_byte(what + c); + switch (page) { + case 0: + loadpgm(dalidisc_page0); + can_send(CANA_DISCOVERY_F(page, sublab_addr), sizeof(dalidisc_page0), buf); + return; + case 6: + /* page 6: + * VERSION NUMBER + * DEVICE TYPE + * PHYSICAL MIN LEVEL + * RANDOM ADDRESS H + * RANDOM ADDRESS M + * RANDOM ADDRESS L + */ + buf[0] = dali_grab_value(addr, 0x97); + buf[1] = dali_grab_value(addr, 0x99); + buf[2] = dali_grab_value(addr, 0x9a); + buf[3] = dali_grab_value(addr, 0xc2); + buf[4] = dali_grab_value(addr, 0xc3); + buf[5] = dali_grab_value(addr, 0xc4); + can_send(CANA_DISCOVERY_F(page, sublab_addr), 6, buf); + return; + + case 7: + /* page 7: + * ACTUAL DIM LEVEL + * MAX LEVEL + * MIN LEVEL + * POWER ON LEVEL + * SYSTEM FAILURE LEVEL + * FADE RATE, FADE TIME (2x 4 bit) + * STATUS + * SHORT ADDRESS (or 0xff if no device) + */ + for (uint8_t offs = 0; offs < 6; offs++) + buf[offs] = dali_grab_value(addr, 0xa0 + offs); + + buf[6] = dali_grab_value(addr, 0x90); + dali_send(0x191 | (addr << 9)); + buf[7] = dali_rx_avail ? addr : 0xff; + + can_send(CANA_DISCOVERY_F(page, sublab_addr), 8, buf); + return; + case 8: + loadpgm(dalidisc_page8); + can_send(CANA_DISCOVERY_F(page, sublab_addr), sizeof(dalidisc_page8), buf); + return; + default: + can_send(CANA_DISCOVERY_F(page, sublab_addr), 0, NULL); + } +} + +static void can_delayed_exec_do(void) +{ + uint16_t sublab_addr, sublab_proto; + + sublab_addr = can_rx_sublab_addr2(perform); + sublab_proto = can_rx_sublab_proto2(perform); + + switch (sublab_proto) { + case 0x4c08: + can_handle_disco(sublab_addr); + return; + } +} + +static void can_delayed_exec(void) +{ + if (can_tx_busy()) + return; + + for (uint8_t i = 0; i < array_size(can_queue); i++) { + uint8_t idx = (can_queue_ptr + i) & (array_size(can_queue) - 1); + cli(); + if (can_queue[idx].rx_addr.u) { + perform = can_queue[idx]; + can_queue[idx].rx_addr.u = 0; + sei(); + can_delayed_exec_do(); + } else + sei(); + } +} + +static void can_send_acks(void) +{ + static uint8_t tx_ack_pos = 0; + uint8_t pkt_start_at = 0xff; + uint8_t data[8], len = 0; + + if (can_tx_busy()) + return; + + do { + if (devices[tx_ack_pos].tx_set == 1 + || devices[tx_ack_pos].tx_set == 2) { + len = 1; + break; + } + tx_ack_pos++; + } while (tx_ack_pos < DALI_NUMDEV && !len); + + if (!len) { + tx_ack_pos = 0; + return; + } + + pkt_start_at = tx_ack_pos; + data[0] = devices[tx_ack_pos].set; + devices[tx_ack_pos].tx_set--; + tx_ack_pos++; + + while (tx_ack_pos < DALI_NUMDEV && len < 8 + && devices[tx_ack_pos].tx_set) { + data[tx_ack_pos - pkt_start_at] = devices[tx_ack_pos].set; + if (devices[tx_ack_pos].tx_set > 2) + devices[tx_ack_pos].tx_set = 2; + devices[tx_ack_pos].tx_set--; + + len++; + tx_ack_pos++; + } + + can_send(CANA_LIGHT_F(0, CANA_DALI_BASE + pkt_start_at), len, data); + + if (tx_ack_pos == DALI_NUMDEV) + tx_ack_pos = 0; +} + +static void can_send_read(void) +{ + uint8_t pkt_start_at = 0, i; + uint8_t data[8], len = 0; + + if (can_tx_busy()) + return; + + for (i = 0; i < DALI_NUMDEV; i++) + if (devices[i].tx_read) + break; + if (i == DALI_NUMDEV) + return; + + pkt_start_at = i; + while (len < 8 && i < DALI_NUMDEV && devices[i].tx_read) { + devices[i].tx_read = 0; + + dali_send(0x1a0 | (i << 9)); + if (!dali_rx_avail) + dali_send(0x1a0 | (i << 9)); + if (!dali_rx_avail) { + uart_puts("!RE "); + uart_puthex(i); + uart_puts("\n"); + break; + } + data[i - pkt_start_at] = dali_rx; + len++, i++; + } + if (!len) + return; + can_send(CANA_SENSOR_F(CANA_DALI_BASE + pkt_start_at), len, data); +} + +static void can_sched(void) +{ + static uint32_t last_dump_tick = 0; + + if (systick.u32 - last_dump_tick > 15) { + last_dump_tick = systick.u32; + for (uint8_t i = 0; i < DALI_NUMDEV; i++) + if (!devices[i].tx_set) { + devices[i].tx_set = devices[i].present; + devices[i].tx_read = devices[i].present; + } + } +} + +int main(void) +{ + wdt_init(); + + DDRD |= (1 << D_LED1) | (1 << D_LED2 ) | (1 << D_TXD) | (1 << D_DALIO); + PORTD |= (1 << D_LED1) | (1 << D_TAST); + PORTD &= ~(1 << D_LED1); + + uart_init(); + tick_init(); + can_preinit(); + dali_init(); + dim_init(); + + postinit_slowboot(); + + dali_buscheck(); + + can_init(); + can_CANSTAT(); + + wdt_reset(); + + dali_search(); + + uart_puts("\ninit done\n"); + + global_state = G_RUNNING; + while (1) { + wdt_reset(); + + can_delayed_exec(); + can_sched(); + can_send_acks(); + can_send_read(); + } +} |