summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--lightctrl_v2.c526
2 files changed, 530 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 1f3dcc7..bf5d355 100644
--- a/Makefile
+++ b/Makefile
@@ -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();
+ }
+}