/* * FileName: *.c * Date: 2024 * Descripton: Program for Atiny13A controllers, controls DPDT relays and * has options to save settings in EEPROM. */ #ifndef MCU #define MCU atiny13a #endif #ifndef F_CPU #define F_CPU 9600000UL #endif #include #include #include #include //These are just defined in case for compatability with Arduino #ifndef HIGH #define HIGH 1 #endif #ifndef LOW #define LOW 0 #endif #ifndef UINT8_MAX #define UINT8_MAX 256 #endif //This is the bitpattern that should be at address 0x00 #define START_PATTERN 0xAA #define END_PATTERN 0x55 //Addresses in the eeprom for the two bypasses #define ROM_SP_ADR 0x0 #define ROM_BP1_ADR 0x1 #define ROM_BP2_ADR 0x2 #define ROM_EP_ADR 0x3 //Debounce check number. #define MAX_CHECKS 5 // Define DISABLE_TEMPORARY_SWITCH to turn off the ability to hold // the switch to temporarily engage/bypass. //#define DISABLE_TEMPORARY_SWITCH #define PIN_SW2 PINB3 #define PIN_BYPASS2 PB4 #define PIN_SW1 PINB2 #define PIN_BYPASS1 PB1 #define PIN_MUTE PB0 //The BLINK_QTY can be edited from 0-255 blinks #define BLINK_QTY 2 #define BLINK_CNT 2 * BLINK_QTY #define BLINK_DELAY 200 /*Assuming default fuses are used then it's approximatly 33.4375ms per tick. * * This is important because the ATiny13/A only have a single timer built into * them. * * Ticks are used as our way of keep track of long button presses. * */ #define LONG_PRESS_TICKS 32 /*Approximatly 510ms*/ /*A structure to hold the button info*/ typedef struct { uint8_t is_pressed: 1; uint8_t is_bypassed: 1; uint8_t is_long_pressed: 1; uint8_t timer_enabled: 1; uint8_t pressed_ticks; uint8_t input_pin; uint8_t output_pin; }btn_state; /* * ############################### * Global Variables * ############################### */ /*Create two diffent instances of the button state structure*/ btn_state btn1; btn_state btn2; /*Some variables for managing the debouncing*/ volatile uint8_t debounced_state; volatile uint8_t db_state[MAX_CHECKS]; volatile uint8_t idx; /* * ############################### * FUNCTION PROTOTYPES * ############################### */ static void toggle_output(btn_state *b); static inline void init_btn(btn_state *b, uint8_t input_pin, uint8_t output_pin); static void clear_button_timer(btn_state *b); static inline void start_button_timer(btn_state *b); static inline void check_button_longpress(btn_state *b); static void update_button_output(btn_state *b); static void update_button_input(btn_state *b); static inline void blink_bypass1(void); static inline void blink_bypass2(void); static inline void init_timer0(); static inline void debounce_switch(); /* * ############################### * SETUP AND LOOP * ############################### */ void init_prog() { /*Set the debounced state to all high*/ debounced_state = 0xFF; /*Configures the PINS for the input and output.*/ init_btn(&btn1, PIN_SW1, PIN_BYPASS1); init_btn(&btn2, PIN_SW2, PIN_BYPASS2); /*Wait 5ms for pull-up resistors voltage to become stable.*/ //_delay_ms(5); /*check if the eeprom has info. */ while(! eeprom_is_ready()) {} //Blocks until eeprom is ready. //Checks against a bit pattern we defined to represent the start of data. if(eeprom_read_byte((uint8_t *)ROM_SP_ADR) == START_PATTERN) { //Reads the two bytes representing the two states. btn1.is_bypassed = eeprom_read_byte((uint8_t *)ROM_BP1_ADR); btn2.is_bypassed = eeprom_read_byte((uint8_t *)ROM_BP2_ADR); } else { //otherwise we write the init values for the start pattern and bypass states. eeprom_write_byte((uint8_t *)ROM_SP_ADR, START_PATTERN); eeprom_write_byte((uint8_t *)ROM_BP1_ADR, 0x0); eeprom_write_byte((uint8_t *)ROM_BP2_ADR, 0x0); } btn1.is_pressed = ((PINB & (1<is_long_pressed = 0; b->is_pressed = 0; b->is_bypassed = 0; b->pressed_ticks = 0; b->timer_enabled = 0; b->input_pin = input_pin; b->output_pin = output_pin; /*Configure the buttons inputs and outputs*/ DDRB &= ~(1<input_pin); PORTB |= (1<input_pin); DDRB |= (1<output_pin); PORTB &= ~(1<output_pin); } /*This is the fancy function to toggle output pins*/ static void toggle_output(btn_state *b) { if(!b->is_bypassed){ b->is_bypassed = 1; PORTB |= (1<output_pin); } else{ b->is_bypassed = 0; PORTB &= ~(1<output_pin); } } static void clear_button_timer(btn_state *b) { b->timer_enabled = 0; b->is_long_pressed = 0; b->pressed_ticks = 0; } static void start_button_timer(btn_state *b) { clear_button_timer(b); b->timer_enabled = 1; } static void check_button_longpress(btn_state *b) { if(b->pressed_ticks >= LONG_PRESS_TICKS) { b->is_long_pressed = 1; b->timer_enabled = 0; } } static void update_button_input(btn_state *b) { /*Check from the global debounced port input*/ /*check for pin HIGH*/ if(debounced_state & (1<input_pin)) { b->is_pressed = 0; } /*otherwise assume pin LOW*/ else{ b->is_pressed = 1; } } static void update_button_output(btn_state *b) { /*If the button is actually pressed.*/ if(b->is_pressed){ /*If this is a new event.*/ if(!b->is_long_pressed && !b->timer_enabled){ /*Then start the timer and update the output*/ toggle_output(b); start_button_timer(b); return; } /*If the timer is already going.*/ else if(b->timer_enabled){ /*Then just check if it's hit the threshold.*/ check_button_longpress(b); return; } else{ return; } } /*Else the button was realeased*/ else if(!b->is_pressed){ /*If the button was released on a long press.*/ if(b->is_long_pressed){ toggle_output(b); } clear_button_timer(b); } } /* * ############################ * DEBOUNCING CODE * ############################ */ /* * INPUT: The port to check. * OUTPUT: None * DESCRIPTION: Updates the global debounced state. This function * should be called in a ISR a set rate using the hardware timer. */ static inline void debounce_switch() { uint8_t i, j; db_state[idx] = PINB & 0xFF; idx+=1; j = 0xff; /*Loop through a number of checks*/ for(i = 0; i < MAX_CHECKS; i++) { j = j & db_state[i]; } debounced_state = j; if(idx >= MAX_CHECKS) { idx = 0; } } /*Setup the timer0 on the AVR*/ static inline void init_timer0() { /*config to normal mode.*/ TCCR0A = 0x00; //stop timer TCCR0B = 0x00; //zero timer /*set prescaler*/ //Set to div64 TCCR0B |= (1<