diff options
| -rw-r--r-- | RoundTable/momentary.c | 72 | ||||
| -rw-r--r-- | RoundTable/round-table.c | 489 | ||||
| -rw-r--r-- | tk-attiny.h | 17 | ||||
| -rw-r--r-- | tk-delay.h | 4 |
4 files changed, 423 insertions, 159 deletions
diff --git a/RoundTable/momentary.c b/RoundTable/momentary.c index 120406e..4394f46 100644 --- a/RoundTable/momentary.c +++ b/RoundTable/momentary.c @@ -20,7 +20,12 @@ */ #define RT_EMISAR_D4_LAYOUT +#define USE_LVP +#define USE_DEBUG_BLINK +#define OWN_DELAY +#define USE_DELAY_MS #include "round-table.c" +#include "tk-delay.h" volatile uint8_t brightness; @@ -34,35 +39,58 @@ void light_off() { PWM2_LVL = 0; } +//State momentary_state { 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; + if (event == EV_click1_press) { + brightness = 255; + light_on(); + // reset current event queue + empty_event_sequence(); + return 0; + } + + else if (event == EV_release) { + light_off(); + // reset current event queue + empty_event_sequence(); + return 0; + } - case EV_release: + /* + // LVP / low-voltage protection + //else if ((event == EV_voltage_low) || (event == EV_voltage_critical)) { + else if (event == EV_voltage_low) { + if (brightness > 0) brightness >>= 1; + else { light_off(); - // reset current event queue - empty_event_sequence(); - return 0; + standby_mode(); + } + 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; + // event not handled + return 1; +} + +// LVP / low-voltage protection +void low_voltage() { + if (brightness > 0) brightness >>= 1; + else { + light_off(); + standby_mode(); } - return 1; // event not handled } void setup() { - set_state(momentary_state); + //debug_blink(1); + /* + brightness = 255; + light_on(); + delay_ms(10); + light_off(); + */ + + push_state(momentary_state); } diff --git a/RoundTable/round-table.c b/RoundTable/round-table.c index 6cd1b10..c6ffc08 100644 --- a/RoundTable/round-table.c +++ b/RoundTable/round-table.c @@ -1,23 +1,51 @@ #include "tk-attiny.h" -// +#include <avr/pgmspace.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/wdt.h> +#include <avr/eeprom.h> +#include <avr/sleep.h> +#include <util/delay_basic.h> + +// typedefs +typedef PROGMEM const uint8_t Event; +typedef Event * EventPtr; 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; +typedef uint8_t State(EventPtr event, uint16_t arg); +typedef State * StatePtr; volatile StatePtr current_state; #define EV_MAX_LEN 16 -volatile uint8_t current_event[EV_MAX_LEN]; +uint8_t current_event[EV_MAX_LEN]; +// at 0.016 ms per tick, 255 ticks = 4.08 s +// TODO: 16 bits? +static volatile uint8_t ticks_since_last_event = 0; +#ifdef USE_LVP volatile int16_t voltage; +void low_voltage(); +#endif #ifdef USE_THERMAL_REGULATION volatile int16_t temperature; +void low_temperature(); +void high_temperature(); +#endif + +#ifdef USE_DEBUG_BLINK +#define OWN_DELAY +#define USE_DELAY_4MS +#include "tk-delay.h" +#define DEBUG_FLASH PWM1_LVL = 64; delay_4ms(2); PWM1_LVL = 0; +void debug_blink(uint8_t num) { + for(; num>0; num--) { + PWM1_LVL = 32; + delay_4ms(100/4); + PWM1_LVL = 0; + delay_4ms(100/4); + } +} #endif #define A_ENTER_STATE 1 @@ -33,7 +61,7 @@ volatile int16_t temperature; #define A_UNDERHEATING 10 // TODO: add events for low voltage conditions #define A_VOLTAGE_LOW 11 -#define A_VOLTAGE_CRITICAL 12 +//#define A_VOLTAGE_CRITICAL 12 // TODO: maybe compare events by number instead of pointer? // (number = index in event types array) @@ -42,8 +70,6 @@ volatile int16_t temperature; // (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 } ; @@ -53,7 +79,20 @@ Event EV_leave_state[] = { Event EV_tick[] = { A_TICK, 0 } ; -Event EV_press[] = { +#ifdef USE_LVP +Event EV_voltage_low[] = { + A_VOLTAGE_LOW, + 0 } ; +#endif +#ifdef USE_THERMAL_REGULATION +Event EV_temperature_high[] = { + A_OVERHEATING, + 0 } ; +Event EV_temperature_low[] = { + A_UNDERHEATING, + 0 } ; +#endif +Event EV_click1_press[] = { A_PRESS, 0 }; // shouldn't normally happen, but UI might reset event while button is down @@ -61,41 +100,66 @@ Event EV_press[] = { Event EV_release[] = { A_RELEASE, 0 }; -Event EV_press_release[] = { +Event EV_click1_release[] = { A_PRESS, A_RELEASE, 0 }; -#define EV_1click EV_press_release_timeout -Event EV_press_release_timeout[] = { +#define EV_1click EV_click1_complete +Event EV_click1_complete[] = { A_PRESS, A_RELEASE, A_RELEASE_TIMEOUT, 0 }; -#define EV_hold EV_press_hold +#define EV_hold EV_click1_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[] = { +Event EV_click1_hold[] = { A_PRESS, - A_HOLD_TIMEOUT, + A_HOLD_START, 0 }; -Event EV_press_hold_release[] = { +Event EV_click1_hold_release[] = { A_PRESS, - A_HOLD_TIMEOUT, + A_HOLD_START, A_RELEASE, 0 }; -Event EV_press_release_press[] = { +Event EV_click2_press[] = { A_PRESS, A_RELEASE, A_PRESS, 0 }; -Event EV_press_release_press_release[] = { +Event EV_click2_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[] = { +#define EV_2clicks EV_click2_complete +Event EV_click2_complete[] = { + A_PRESS, + A_RELEASE, + A_PRESS, + A_RELEASE, + A_RELEASE_TIMEOUT, + 0 }; +Event EV_click3_press[] = { + A_PRESS, + A_RELEASE, + A_PRESS, + A_RELEASE, + A_PRESS, + 0 }; +Event EV_click3_release[] = { + A_PRESS, + A_RELEASE, + A_PRESS, + A_RELEASE, + A_PRESS, + A_RELEASE, + 0 }; +#define EV_3clicks EV_click3_complete +Event EV_click3_complete[] = { + A_PRESS, + A_RELEASE, A_PRESS, A_RELEASE, A_PRESS, @@ -106,90 +170,41 @@ Event EV_press_release_press_release_timeout[] = { // 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, + EV_click1_press, + EV_release, + EV_click1_release, + EV_click1_complete, + EV_click1_hold, + EV_click1_hold_release, + EV_click2_press, + EV_click2_release, + EV_click2_complete, + EV_click3_press, + EV_click3_release, + EV_click3_complete, // ... }; -// 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<sizeof(event_sequences); i++) { - if (events_match(event_sequences[i], current_event)) { - err = emit(event_sequences[i], arg); - break; - } - } - return err; -} - #define events_match(a,b) compare_event_sequences(a,b) -uint8_t compare_event_sequences(uint8_t *a, uint8_t *b) { +// return 1 if (a == b), 0 otherwise +uint8_t compare_event_sequences(uint8_t *a, const uint8_t *b) { + for(uint8_t i=0; (i<EV_MAX_LEN) && (a[i] == pgm_read_byte(b+i)); i++) { + // end of zero-terminated sequence + if (a[i] == 0) return 1; + } + // if we ever fall out, that means something was different + // (or the sequence is too long) + return 0; } void empty_event_sequence() { for(uint8_t i=0; i<EV_MAX_LEN; i++) current_event[i] = 0; } -void append_event(uint8_t ev_type) { +void push_event(uint8_t ev_type) { ticks_since_last_event = 0; // something happened uint8_t i; - for(i=0; current_event[i], i<EV_MAX_LEN; i++); + for(i=0; current_event[i] && (i<EV_MAX_LEN); i++); if (i < EV_MAX_LEN) { current_event[i] = ev_type; } else { @@ -199,6 +214,9 @@ void append_event(uint8_t ev_type) { // TODO: stack for states, to allow shared utility states like "input a number" // and such, which return to the previous state after finishing +#define STATE_STACK_SIZE 8 +StatePtr state_stack[STATE_STACK_SIZE]; +uint8_t state_stack_len = 0; // TODO: if callback doesn't handle current event, // pass event to next state on stack? // Callback return values: @@ -206,46 +224,157 @@ void append_event(uint8_t ev_type) { // 1: event not handled // 255: error (not sure what this would even mean though, or what difference it would make) // TODO: function to call stacked callbacks until one returns "handled" +// Call stacked callbacks for the given event until one handles it. uint8_t emit(EventPtr event, uint16_t arg) { // TODO: implement - for(;;) { - uint8_t err = foo.callback(event, arg); + for(int8_t i=state_stack_len-1; i>=0; i--) { + uint8_t err = state_stack[i](event, arg); if (! err) return 0; } return 1; // event not handled } -uint8_t push_state(State *new_state) { - // TODO: implement +// Search the pre-defined event list for one matching what the user just did, +// and emit it if one was found. +uint8_t emit_current_event(uint16_t arg) { + uint8_t err = 1; + for (uint8_t i=0; i<(sizeof(event_sequences)/sizeof(EventPtr)); i++) { + if (events_match(current_event, event_sequences[i])) { + //DEBUG_FLASH; + err = emit(event_sequences[i], arg); + return err; + } + } + return err; } -State * pop_state() { - // TODO: implement +void _set_state(StatePtr new_state) { + // call old state-exit hook (don't use stack) + if (current_state != NULL) current_state(EV_leave_state, 0); + // set new state + current_state = new_state; + // call new state-enter hook (don't use stack) + if (new_state != NULL) current_state(EV_enter_state, 0); +} + +int8_t push_state(StatePtr new_state) { + if (state_stack_len < STATE_STACK_SIZE) { + // TODO: call old state's exit hook? + // new hook for non-exit recursion into child? + state_stack[state_stack_len] = new_state; + state_stack_len ++; + _set_state(new_state); + return state_stack_len; + } else { + // TODO: um... how is a flashlight supposed to handle a recursion depth error? + return -1; + } } -uint8_t set_state(State *new_state) { +StatePtr pop_state() { + // TODO: how to handle pop from empty stack? + StatePtr old_state = NULL; + StatePtr new_state = NULL; + if (state_stack_len > 0) { + state_stack_len --; + old_state = state_stack[state_stack_len]; + } + if (state_stack_len > 0) { + new_state = state_stack[state_stack_len-1]; + } + _set_state(new_state); + return old_state; +} + +uint8_t set_state(StatePtr new_state) { + // FIXME: this calls exit/enter hooks it shouldn't pop_state(); return push_state(new_state); } -uint8_t _set_state(State *new_state) { - // call old state-exit hook (don't use stack) - current_state.callback(EV_leave_state, 0); - // set new state - current_state = new_state; - // call new state-enter hook (don't use stack) - current_state.callback(EV_enter_state, 0); +// 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? + + +uint8_t button_is_pressed() { + // debounce a little + uint8_t measurement = 0; + // measure for 8/64ths of a ms + for(uint8_t i=0; i<8; i++) { + // check current value + measurement = (measurement << 1) | ((PINB & (1<<SWITCH_PIN)) == 0); + // wait a moment + _delay_loop_2(BOGOMIPS/64); + } + // 0 bits mean "pressed", 1 bits mean "not pressed" + // only return "pressed" if all bits match + return (measurement != 0); +} + +//void button_change_interrupt() { +ISR(PCINT0_vect) { + + //DEBUG_FLASH; + + // something happened + //ticks_since_last_event = 0; + + // add event to current sequence + if (button_is_pressed()) { + push_event(A_PRESS); + } else { + push_event(A_RELEASE); + } + + // check if sequence matches any defined sequences + // if so, send event to current state callback + emit_current_event(0); } // TODO: implement ISR(WDT_vect) { + //timer ++; // Is this needed at all? + + /* + if (ticks_since_last_event & 0b00000111 ) { + DEBUG_FLASH; + } + */ + + if (ticks_since_last_event < 0xff) ticks_since_last_event ++; + + //static uint8_t hold_ticks = 0; // TODO: 16 bits? + + // callback on each timer tick + emit(EV_tick, ticks_since_last_event); + + // 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_event(hold_ticks); + // or + // //release_timeout() + // //emit(EV_press_release_timeout, 0); + // emit_current_event(0); + + // add 4-step voltage / temperature thing? + // with averaged values, + // once every N ticks? } // TODO: implement? (or is it better done in main()?) ISR(ADC_vect) { static uint8_t adc_step = 0; + #ifdef USE_LVP static uint8_t lvp_timer = 0; #define LVP_TIMER_START 255 // TODO: calibrate this value + #endif #ifdef USE_THERMAL_REGULATION #define NUM_THERMAL_VALUES 4 @@ -259,6 +388,7 @@ ISR(ADC_vect) { adc_step = (adc_step + 1) & (ADC_STEPS-1); + #ifdef USE_LVP // TODO: voltage // TODO: if low, callback EV_voltage_low / EV_voltage_critical // (but only if it has been more than N ticks since last call) @@ -270,15 +400,22 @@ ISR(ADC_vect) { // if (!err) lvp_timer = LVP_TIMER_START; // } // } + #endif // TODO: temperature // start another measurement for next time #ifdef USE_THERMAL_REGULATION - if (adc_step < 2) ADMUX = ADMUX_VCC; - else ADMUX = ADMUX_THERM; + #ifdef USE_LVP + if (adc_step < 2) ADMUX = ADMUX_VCC; + else ADMUX = ADMUX_THERM; + #else + ADMUX = ADMUX_THERM; + #endif #else - ADMUX = ADMUX_VCC; + #ifdef USE_LVP + ADMUX = ADMUX_VCC; + #endif #endif } @@ -293,6 +430,44 @@ inline void ADC_on() ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; } +inline void ADC_off() { + ADCSRA &= ~(1<<ADEN); //ADC off +} + +inline void PCINT_on() { + // enable pin change interrupt for pin N + GIMSK |= (1 << PCIE); + // only pay attention to the e-switch pin + //PCMSK = (1 << SWITCH_PCINT); + // set bits 1:0 to 0b01 (interrupt on rising *and* falling edge) (default) + // MCUCR &= 0b11111101; MCUCR |= 0b00000001; +} + +inline void PCINT_off() { + // disable all pin-change interrupts + GIMSK &= ~(1 << PCIE); +} + +void WDT_on() +{ + // interrupt every 16ms + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence + WDTCR = (1<<WDIE); // Enable interrupt every 16ms + //sei(); // Enable interrupts +} + +inline void WDT_off() +{ + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag + WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence + WDTCR = 0x00; // Disable WDT + //sei(); // Enable interrupts +} + // low-power standby mode used while off but power still connected #define standby_mode sleep_until_eswitch_pressed void sleep_until_eswitch_pressed() @@ -301,7 +476,7 @@ void sleep_until_eswitch_pressed() ADC_off(); // TODO: make sure switch isn't currently pressed - while (switch_pressed()) {} + while (button_is_pressed()) {} PCINT_on(); // wake on e-switch event @@ -311,35 +486,71 @@ void sleep_until_eswitch_pressed() // something happened; wake up sleep_disable(); - PCINT_off(); + //PCINT_off(); // we use this for more than just waking up ADC_on(); WDT_on(); } -// boot-time tasks -/* Define this in your RoundTable recipe -void setup() { +// last-called state on stack +// handles default actions for LVP, thermal regulation, etc +uint8_t default_state(EventPtr event, uint16_t arg) { + if (0) {} + + #ifdef USE_LVP + else if (event == EV_voltage_low) { + low_voltage(); + return 0; + } + #endif + + #ifdef USE_THERMAL_REGULATION + else if (event == EV_temperature_high) { + high_temperature(); + return 0; + } + + else if (event == EV_temperature_low) { + low_temperature(); + return 0; + } + #endif + + // event not handled + return 1; } -*/ -void main() { +// boot-time tasks +// Define this in your RoundTable recipe +void setup(); + +int main() { // Don't allow interrupts while booting cli(); - WDT_off(); - PCINT_off(); - - // TODO: configure PWM channels - #if PWM_CHANNELS == 1 - #elif PWM_CHANNELS == 2 - #elif PWM_CHANNELS == 3 + //WDT_off(); + //PCINT_off(); + + // configure PWM channels + #if PWM_CHANNELS >= 1 + DDRB |= (1 << PWM1_PIN); + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + #elif PWM_CHANNELS >= 2 + DDRB |= (1 << PWM2_PIN); + #elif PWM_CHANNELS >= 3 + DDRB |= (1 << PWM3_PIN); + // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum #elif PWM_CHANNELS == 4 // FIXME: How exactly do we do PWM on channel 4? + DDRB |= (1 << PWM4_PIN); #endif // TODO: turn on ADC? - // TODO: configure e-switch - //PORTB = (1 << SWITCH_PIN); // e-switch is the only input - //PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin // TODO: configure sleep mode set_sleep_mode(SLEEP_MODE_PWR_DOWN); @@ -349,18 +560,26 @@ void main() { // TODO: handle long press vs short press (or even medium press)? - // TODO: call recipe's setup - setup(); + #ifdef USE_DEBUG_BLINK + //debug_blink(1); + #endif // all booted -- turn interrupts back on PCINT_on(); WDT_on(); + ADC_on(); sei(); + // fallback for handling a few things + push_state(default_state); + + // call recipe's setup + setup(); + // main loop while (1) { - // TODO: update e-switch press state - // TODO: if wait queue not empty, process and pop first item in queue + // TODO: update e-switch press state? + // TODO: if wait queue not empty, process and pop first item in queue? // TODO: check voltage? // TODO: check temperature? } diff --git a/tk-attiny.h b/tk-attiny.h index ebd997f..22b73f9 100644 --- a/tk-attiny.h +++ b/tk-attiny.h @@ -55,8 +55,11 @@ #endif +#include <avr/interrupt.h> + /******************** I/O pin and register layout ************************/ #ifdef FET_7135_LAYOUT +#define LAYOUT_DEFINED /* * ---- * Reset -|1 8|- VCC @@ -92,6 +95,7 @@ #endif // FET_7135_LAYOUT #ifdef TRIPLEDOWN_LAYOUT +#define LAYOUT_DEFINED /* * ---- * Reset -|1 8|- VCC @@ -128,6 +132,7 @@ #endif // TRIPLEDOWN_LAYOUT #ifdef FERRERO_ROCHER_LAYOUT +#define LAYOUT_DEFINED /* * ---- * Reset -|1 8|- VCC @@ -142,6 +147,7 @@ #endif // FERRERO_ROCHER_LAYOUT #ifdef NANJG_LAYOUT +#define LAYOUT_DEFINED #define STAR2_PIN PB0 #define STAR3_PIN PB4 #define STAR4_PIN PB3 @@ -160,6 +166,7 @@ #ifdef RT_EMISAR_D4_LAYOUT +#define LAYOUT_DEFINED /* * ---- * Reset -|1 8|- VCC @@ -169,9 +176,12 @@ * ---- */ +#define PWM_CHANNELS 2 + #define AUXLED_PIN PB4 // pin 3 -#define SWITCH_PIN PB3 // pin 2, OTC +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt #define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4) #define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3 @@ -195,6 +205,7 @@ #ifdef RT_TKSABER_LAYOUT +#define LAYOUT_DEFINED /* * ---- * Reset -|1 8|- VCC @@ -204,6 +215,7 @@ * ---- */ +#define PWM_CHANNELS 4 #define PWM1_PIN PB0 // pin 5 #define PWM1_LVL OCR0A #define PWM2_PIN PB1 // pin 6 @@ -214,6 +226,7 @@ #define PWM4_LVL OCR1A // FIXME: does this work? #define SWITCH_PIN PB2 // pin 7 +#define SWITCH_PCINT PCINT2 // pin 7 pin change interrupt #define ADC_PRSCL 0x07 // clk/128 (no need to be super fast) // FIXME: What is the DIDR for pin 8? @@ -225,7 +238,7 @@ #endif // TKSABER_LAYOUT -#ifndef PWM_LVL +#ifndef LAYOUT_DEFINED Hey, you need to define an I/O pin layout. #endif @@ -25,6 +25,7 @@ #include <util/delay_basic.h> #ifdef USE_DELAY_MS // Having own _delay_ms() saves some bytes AND adds possibility to use variables as input +#define delay_ms _delay_ms void _delay_ms(uint16_t n) { // TODO: make this take tenths of a ms instead of ms, @@ -40,17 +41,20 @@ void _delay_ms(uint16_t n) } #endif #ifdef USE_FINE_DELAY +#define delay_zero _delay_zero void _delay_zero() { _delay_loop_2(BOGOMIPS/3); } #endif #ifdef USE_DELAY_4MS +#define delay_4ms _delay_4ms void _delay_4ms(uint8_t n) // because it saves a bit of ROM space to do it this way { while(n-- > 0) _delay_loop_2(BOGOMIPS*4); } #endif #ifdef USE_DELAY_S +#define delay_s _delay_s void _delay_s() // because it saves a bit of ROM space to do it this way { #ifdef USE_DELAY_4MS |
