diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..f9696af --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(main + main.c +) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..4c7be5f --- /dev/null +++ b/src/main.c @@ -0,0 +1,421 @@ +/* + * FileName: *.c + * Date: 2023 + * 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 4800000UL +#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 10 + +// 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 + + + +/*The timing of "ticks" is dependent on the AVR timer's counter register + * so for an 8bit register the maximum value is 256. Given we stick with + * a 1Mhz cpu frequency can use this formula to calculate the number of + * interrupts(timer overflows) per second: + * + * OVF_S = (F_CPU / DIV)/ (2^8) + * 61 = (1000000 / 64) / 256 + * + * 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. + * */ +//original sent had 60 +#define LONG_PRESS_TICKS 66 + + +/*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; + +volatile uint16_t tick_count; + +/* + * ############################### + * 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() { + /*Zero out the tick_count var*/ + tick_count = 0; + + /*config to normal mode.*/ + TCCR0A = 0x00; //stop timer + TCCR0B = 0x00; //zero timer + + /*set prescaler*/ + //Set to div64 + TCCR0B |= (1<