#include <avr/io.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <math.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
//Pin layout
#define LED_PIN PINB0 //Simple digital output
#define SI_PIN PINB1 //SI Line
#define SCK_PIN PINB2
#define TAP_BUTTON PINB3 //Simple digital input -- should be sampled and average time should be calculated with this -> debounce button needed
#define _CS_PIN PINB4
#define POT_VALUE PINB5 //Reads the value from the analog pot -- RESET state might be a concern
#define DEBOUNCE_TIME_MS 30 //Debounce time in ms
#define TAP_TEMPO_BUFFER 4
#define MAX_TIME_BEFORE_TAP_RESET 2000
// TODO: copy paste the code sent to Ale, I have forgotten to update this one for the button: does it even work btw?
// the range for the pot is 3 - 5V, how do I model that?
// ADC_value = (Vin * 1024) / Vref
// ADC_value * 5 / 1024 = Vin -> Vin = 0.000488 * ADC_value
// Vref is 5V -> I can shift the curve down making an adjust adc
// Curve characteristics: when x = 3 y = 0, for the rest it's just a linear curve which is steeper than the original
// (0; 3) -> (1024; 5) use a linear equation
//// (0; 0) - (1024; Vref)
// (y-y1)/(y2 - y1) = (x-x1)/(x2-x1)
// Vin / 5 = ADC / (1024)
// ADC = VIN/5 * 1024
// ADC = VIN/VCC * 1024
// ADC_fixed = (VIN - 3) / (Vcc - 2) * 1024
// ADC_fixed =
// (ADC_fixed * VCC - 2ADC_fixed) / 1024 = VIN - 3
// (ADC_fixed * VCC - 2ADC_fixed) / 1024 + 3 = VIN
// (Vin - 3) / (Vref - 2) = ADC_fixed / 1024
// ADC_fixed = (Vin - 3) / (Vref - 2) * 1024
// (Vin - 3)/ (2) = ADC / 1024
// ADC_fixed = (Vin - 3) * 512
// ADC_fixed / ADC = ((Vin - 3) /(Vref - 2)) *Vref / ((Vin))
// ADC_fixed = ADC * (Vin - 3) * 5 / 2Vin
// ADC_fixed / ADC = ((Vin - 3) * 512) / (Vin * 1024) / Vref
// The value should be mapped to the time (31 - 600 ms)
// So each increment is 0.556ms
// t = 31ms + (pot_value)*0.556
// This code uses a pull up resistor
enum button_state{
PRESSED, //0
NOT_PRESSED //1
};
typedef struct{
volatile uint64_t tap_tempo;
volatile uint64_t debounce_time_start;
volatile enum button_state current_button_state;
volatile enum button_state actual_button_state;
volatile uint8_t debounce_time_reset;
volatile uint8_t reset_tap_buffer; // it will be initialised to 1, when tap button is pressed, it should set it to 0
// when the analog pot changes position it will set this flag to 1 instead
} footswitch;
typedef struct{
volatile uint16_t value; //this value will be updated by the ISR with every interrupt
volatile uint16_t current_cycle_value; //safe value to use during the cycle in the main while loop
} potentiometer;
volatile uint64_t elapsed_time_ms = 0;
void init(){
cli();
//CLKPR = (1 << CLKPCE); // Enable clock prescaler change
//CLKPR = 3; // Set clock division factor to 8 (1 MHz)
DDRB |= (1 << LED_PIN); //Sets LED_PIN as output
DDRB &= ~(1 << POT_VALUE); //Sets POT_value as input
DDRB &= ~(1 << TAP_BUTTON); //Sets TAP_BUTTON as input
DDRB &= ~(1 << _CS_PIN); // Debug LED --> will be changed for ~CS
PORTB |= (1 << PB3); // Activate pull-up resistor for PB3
TCCR0A |= (1 << WGM01); //Timer0 in CTC mode
TCCR0B |= 3; //We need a prescaler since OCR0A is a 8 bit register and we need it to generate an interrupt. Prescaler to 8 -> 1000 / 8 = 125
TIMSK |= (1 << OCIE0A);
TCNT0=0;
OCR0A = 124; // 125 iteration -> 125 - 1 will be the final value before the timer will reset
PORTB &= ~(1 << LED_PIN);
//AREF???
ADMUX = 0; //selects ADC0 from ADC mux
// enable ADEN in ADCSRA before aref
//ADCL + ADCH = 10 bit conversion -> ADCL are the 2 LSbits
//Prescaler for clk of ADC0 should be set to 64 as the acceptable range is 50 - 200 kHz : 8MHz / 64 --> 125kHz (8us)
// A conversion at this range takes approximately 0.1ms; first conversion takes 0.2ms
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); //110 -> prescaler tp 64
ADCSRA |= (1 << ADEN); //Enables the ADC
//ADIF flag is set when a conversion is done
// what does left and right adjusted mean??
ADCSRA |= (1 << ADSC); //Starts ADC0 conversion (this is cleared everytime the value is read so the ISR should reset to 1 whenever a conversion is performed)
ADCSRB |= (1 << ADTS0); // should automatically trigger a conversion
sei();
}
//ISR for ADC0
ISR(TIM0_COMPA_vect) {
elapsed_time_ms++; // Increment elapsed time every 1 ms
}
void ledBlink(footswitch* button){
static uint64_t previous = 0;
if(elapsed_time_ms - previous >= (uint64_t) button->tap_tempo / 2){
PORTB ^= (1 << LED_PIN); //LED TOGGLE
previous = elapsed_time_ms;
}
}
void averageTimeDiff(int64_t buffer[TAP_TEMPO_BUFFER], footswitch* button){
uint8_t i = 0;
uint64_t sum = 0;
while(buffer[i] != 0 && i != TAP_TEMPO_BUFFER -1){
sum += buffer[i];
i++;
}
if (i != 0){
button->tap_tempo = (uint64_t) sum / i;
}
PORTB ^= (1 << _CS_PIN); // LED TOGGLE DEBUG
}
void computeTapTempo(uint64_t current_time, footswitch* button){
static uint64_t last_time = 0;
static uint8_t i = 0;
static uint64_t buffer[TAP_TEMPO_BUFFER];
if (button->reset_tap_buffer || (current_time - last_time) >= MAX_TIME_BEFORE_TAP_RESET){
memset(buffer, 0, sizeof(buffer));
button->reset_tap_buffer = 0; // if the code is here it means the button has been pressed
last_time = current_time;
i = 0;
button->tap_tempo = 0; // Maybe get the analog value?
return;
}
if (button->reset_tap_buffer == 0){
buffer[i] = current_time - last_time;
i = (i + 1) % TAP_TEMPO_BUFFER;
averageTimeDiff(buffer, button);
}
last_time = current_time;
//TODO: if analog pot is touched, then the tap should be set to the tempo read by the ADC, in addition the number_of_taps should be reset
}
void debounceButton(footswitch* button) {
static uint64_t last_debounce_time = 0;
button->current_button_state = PINB & (1 << TAP_BUTTON) ? NOT_PRESSED : PRESSED;
// Check if the debounce time has passed
if ((elapsed_time_ms - last_debounce_time) > DEBOUNCE_TIME_MS) {
// If the button is pressed (LOW), and the actual state is NOT PRESSED
if (button->current_button_state == PRESSED && button->actual_button_state == NOT_PRESSED) { // Button pressed
button->actual_button_state = PRESSED; // Update to PRESSED
computeTapTempo(elapsed_time_ms, button);
}
// If the button is released (HIGH), update the actual state
else if (button->current_button_state != PRESSED && button->actual_button_state == PRESSED) { // Button released
button->actual_button_state = NOT_PRESSED; // Update to NOT PRESSED
}
}
// Update the last debounce time only if the reading has changed
if (button->current_button_state != PRESSED) {
last_debounce_time = elapsed_time_ms;
}
}
int main(){
init();
footswitch button;
potentiometer analog_pot;
footswitch* button_ptr = &button;
potentiometer* analog_pot_ptr = &analog_pot;
button.tap_tempo = 0;
button.current_button_state = NOT_PRESSED;
button.actual_button_state = NOT_PRESSED;
button.debounce_time_reset = 0;
button.debounce_time_start = 0;
button.reset_tap_buffer = 1;
//while conversion hasn't finished the program shouldn't start, then analog_pot_ptr->value = adc_Read()
while (1){
debounceButton(button_ptr);
ledBlink(button_ptr);
}
}