From e96562e36df96ca755d527e479e597ae4e4e09e1 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 18 Aug 2017 18:12:17 -0600 Subject: Some early ideas for Round Table. Nothing close to compile-able yet. --- RoundTable/momentary.c | 68 +++++++++ RoundTable/round-table.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++ RoundTable/rt-ramping.h | 26 ++++ tk-attiny.h | 82 ++++++++++- 4 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 RoundTable/momentary.c create mode 100644 RoundTable/round-table.c create mode 100644 RoundTable/rt-ramping.h diff --git a/RoundTable/momentary.c b/RoundTable/momentary.c new file mode 100644 index 0000000..120406e --- /dev/null +++ b/RoundTable/momentary.c @@ -0,0 +1,68 @@ +/* + * Momentary: Very simple example UI for RoundTable. + * Is intended to be the simplest possible RT e-switch UI. + * The light is in while the button is held; off otherwise. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define RT_EMISAR_D4_LAYOUT +#include "round-table.c" + +volatile uint8_t brightness; + +void light_on() { + PWM1_LVL = brightness; + PWM2_LVL = 0; +} + +void light_off() { + PWM1_LVL = 0; + PWM2_LVL = 0; +} + +uint8_t momentary_state(EventPtr event, uint16_t arg) { + switch(event) { + + case EV_press: + brightness = 255; + light_on(); + // reset current event queue + empty_event_sequence(); + return 0; + + case EV_release: + light_off(); + // reset current event queue + empty_event_sequence(); + return 0; + + // LVP / low-voltage protection + case EV_voltage_low: + case EV_voltage_critical: + if (brightness > 0) brightness >>= 1; + else { + light_off(); + standby_mode(); + } + return 0; + } + return 1; // event not handled +} + +void setup() { + set_state(momentary_state); +} diff --git a/RoundTable/round-table.c b/RoundTable/round-table.c new file mode 100644 index 0000000..6cd1b10 --- /dev/null +++ b/RoundTable/round-table.c @@ -0,0 +1,367 @@ +#include "tk-attiny.h" + +// +typedef uint8_t (*EventCallbackPtr)(EventPtr event, uint16_t arg); +typedef uint8_t EventCallback(EventPtr event, uint16_t arg); +// FIXME: Why does a state need a number? Why not just a function pointer? +// (I don't think it actually needs a number...) +typedef struct State { + uint8_t num; + EventCallback event_callback; +} State; +typedef struct State* StatePtr; + +volatile StatePtr current_state; +#define EV_MAX_LEN 16 +volatile uint8_t current_event[EV_MAX_LEN]; + +volatile int16_t voltage; +#ifdef USE_THERMAL_REGULATION +volatile int16_t temperature; +#endif + +#define A_ENTER_STATE 1 +#define A_LEAVE_STATE 2 +#define A_TICK 3 +#define A_PRESS 4 +#define A_HOLD_START 5 +#define A_HOLD_TICK 6 +#define A_RELEASE 7 +#define A_RELEASE_TIMEOUT 8 +// TODO: add events for over/under-heat conditions (with parameter for severity) +#define A_OVERHEATING 9 +#define A_UNDERHEATING 10 +// TODO: add events for low voltage conditions +#define A_VOLTAGE_LOW 11 +#define A_VOLTAGE_CRITICAL 12 + +// TODO: maybe compare events by number instead of pointer? +// (number = index in event types array) +// (comparison would use full event content, but send off index to callbacks) +// (saves space by using uint8_t instead of a pointer) +// (also eliminates the need to duplicate single-entry events like for voltage or timer tick) + +// Event types +typedef PROGMEM const uint8_t Event; +typedef Event* EventPtr; +Event EV_enter_state[] = { + A_ENTER_STATE, + 0 } ; +Event EV_leave_state[] = { + A_LEAVE_STATE, + 0 } ; +Event EV_tick[] = { + A_TICK, + 0 } ; +Event EV_press[] = { + A_PRESS, + 0 }; +// shouldn't normally happen, but UI might reset event while button is down +// so a release with no recorded prior hold could be possible +Event EV_release[] = { + A_RELEASE, + 0 }; +Event EV_press_release[] = { + A_PRESS, + A_RELEASE, + 0 }; +#define EV_1click EV_press_release_timeout +Event EV_press_release_timeout[] = { + A_PRESS, + A_RELEASE, + A_RELEASE_TIMEOUT, + 0 }; +#define EV_hold EV_press_hold +// FIXME: Should holds use "start+tick" or just "tick" with a tick number? +// Or "start+tick" with a tick number? +Event EV_press_hold[] = { + A_PRESS, + A_HOLD_TIMEOUT, + 0 }; +Event EV_press_hold_release[] = { + A_PRESS, + A_HOLD_TIMEOUT, + A_RELEASE, + 0 }; +Event EV_press_release_press[] = { + A_PRESS, + A_RELEASE, + A_PRESS, + 0 }; +Event EV_press_release_press_release[] = { + A_PRESS, + A_RELEASE, + A_PRESS, + A_RELEASE, + 0 }; +#define EV_2clicks EV_press_release_press_release_timeout +Event EV_press_release_press_release_timeout[] = { + A_PRESS, + A_RELEASE, + A_PRESS, + A_RELEASE, + A_RELEASE_TIMEOUT, + 0 }; +// ... and so on + +// A list of event types for easy iteration +EventPtr event_sequences[] = { + EV_press, + EV_press_release, + EV_press_release_timeout, + EV_press_release_press, + EV_press_release_press_release, + EV_press_release_press_release_timeout, + // ... +}; + +// TODO: move this to a separate UI-specific file +/* +State states[] = { + +}; +*/ + +// TODO? add events to a queue when inside an interrupt +// instead of calling the event functions directly? +// (then empty the queue in main loop?) + +// TODO? new delay() functions which handle queue consumption? +// TODO? new interruptible delay() functions? + +//static uint8_t ticks_since_last_event = 0; // TODO: 16 bits? + +void WDT_tick() { + timer ++; + + //static uint8_t hold_ticks = 0; // TODO: 16 bits? + + // callback on each timer tick + emit(EV_tick, timer); + + // if time since last event exceeds timeout, + // append timeout to current event sequence, then + // send event to current state callback + // //hold_event(ticks) + // //emit(EV_press_hold, hold_ticks); + // emit_current(hold_ticks); + // or + // //release_timeout() + // //emit(EV_press_release_timeout, 0); + // emit_current(0); + + // add 4-step voltage / temperature thing? + // with averaged values, + // once every N ticks? +} + +void button_change_interrupt() { + // TODO: debounce a little + + //ticks_since_last_event = 0; // something happened + + // TODO: add event to current sequence + + // check if sequence matches any defined sequences + // if so, send event to current state callback + emit_current(0); +} + +uint8_t emit_current(uint16_t arg) { + uint8_t err = 1; + for (uint8_t i=0; i. + */ + +// TODO: ramp tables +// TODO: RAMP_SIZE / MAX_LVL +// TODO: actual_lvl +// TODO: target_lvl +// TODO: set_lvl +// TODO: set_lvl_smooth diff --git a/tk-attiny.h b/tk-attiny.h index 72ed2f0..ebd997f 100644 --- a/tk-attiny.h +++ b/tk-attiny.h @@ -4,7 +4,7 @@ * Attiny portability header. * This helps abstract away the differences between various attiny MCUs. * - * Copyright (C) 2015 Selene ToyKeeper + * Copyright (C) 2017 Selene ToyKeeper * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,12 +31,25 @@ #define EEPSIZE 64 #define V_REF REFS0 #define BOGOMIPS 950 + #define ADMUX_VCC 0b00001100 #elif (ATTINY == 25) // TODO: Use 6.4 MHz instead of 8 MHz? #define F_CPU 8000000UL #define EEPSIZE 128 #define V_REF REFS1 #define BOGOMIPS (F_CPU/4000) + #define ADMUX_VCC 0b00001100 + #define ADMUX_THERM 0b10001111 +#elif (ATTINY == 85) + // TODO: Use 6.4 MHz instead of 8 MHz? + #define F_CPU 8000000UL + #define EEPSIZE 512 + #define V_REF REFS1 + #define BOGOMIPS (F_CPU/4000) + // (1 << V_REF) | (0 << ADLAR) | (VCC_CHANNEL) + #define ADMUX_VCC 0b00001100 + // (1 << V_REF) | (0 << ADLAR) | (THERM_CHANNEL) + #define ADMUX_THERM 0b10001111 #else Hey, you need to define ATTINY. #endif @@ -145,6 +158,73 @@ #endif // NANJG_LAYOUT + +#ifdef RT_EMISAR_D4_LAYOUT +/* + * ---- + * Reset -|1 8|- VCC + * eswitch -|2 7|- + * AUX LED -|3 6|- PWM (FET) + * GND -|4 5|- PWM (1x7135) + * ---- + */ + +#define AUXLED_PIN PB4 // pin 3 + +#define SWITCH_PIN PB3 // pin 2, OTC +#define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4) +#define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3 + +#define PWM2_PIN PB1 // pin 6, FET PWM +#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1 +#define PWM1_PIN PB0 // pin 5, 1x7135 PWM +#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0 + +#define VOLTAGE_PIN PB2 // pin 7, voltage ADC +#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2 +#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 +#define ADC_PRSCL 0x06 // clk/64 + +//#define TEMP_DIDR ADC4D +#define TEMP_CHANNEL 0b00001111 + +#define FAST 0xA3 // fast PWM both channels +#define PHASE 0xA1 // phase-correct PWM both channels + +#endif + + +#ifdef RT_TKSABER_LAYOUT +/* + * ---- + * Reset -|1 8|- VCC + * PWM 4 (A) -|2 7|- e-switch + * PWM 3 (B) -|3 6|- PWM 2 (G) + * GND -|4 5|- PWM 1 (R) + * ---- + */ + +#define PWM1_PIN PB0 // pin 5 +#define PWM1_LVL OCR0A +#define PWM2_PIN PB1 // pin 6 +#define PWM2_LVL OCR0B +#define PWM3_PIN PB4 // pin 3 +#define PWM3_LVL OCR1B +#define PWM4_PIN PB3 // pin 2 +#define PWM4_LVL OCR1A // FIXME: does this work? + +#define SWITCH_PIN PB2 // pin 7 + +#define ADC_PRSCL 0x07 // clk/128 (no need to be super fast) +// FIXME: What is the DIDR for pin 8? +//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 + +#define FAST 0xA3 // fast PWM both channels +#define PHASE 0xA1 // phase-correct PWM both channels + +#endif // TKSABER_LAYOUT + + #ifndef PWM_LVL Hey, you need to define an I/O pin layout. #endif -- cgit v1.2.3