#ifndef F_CPU #define F_CPU 1200000UL #endif #include "main.h" //#include "ADC.h" #include #include #include // These are only included during development for the LSP server. // #include "iotn13.h" // #include "iotn13a.h" // ############################# // Globals // ############################# /* This line isn't really quite right.*/ // uint8_t saved_position EEMEM; // EEPROM storage for fader position btn_state btn1; /*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; // ############################# // Prototypes // ############################# static inline void InitTimer0(void); static void InitBtn(btn_state *b, uint8_t input_pin); static void ClearButtonTimer(btn_state *b); static void StartButtonTimer(btn_state *b); static void CheckButtonLongpress(btn_state *b); static void UpdateButtonInput(btn_state *b); static void UpdateButtonOutput(btn_state *b); // ############################# // MAIN // ############################# int main() { InitProg(); while (1) { UpdateButtonOutput(&btn1); UpdateButtonInput(&btn1); } return 0; } // ############################# // FUNCTIONS // ############################# void InitProg(void) { /*Set the debounced state to all high*/ debounced_state = 0xFF; InitBtn(&btn1, BUTTON_PIN); /*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) { MotorMoveTo(eeprom_read_byte((uint8_t *)POSITION1_ADR)); } else { // otherwise we write the init values for the start pattern and states. eeprom_write_byte((uint8_t *)ROM_SP_ADR, START_PATTERN); eeprom_write_byte((uint8_t *)POSITION1_ADR, 0x7F); } InitTimer0(); } uint8_t ReadSpeed(void) { // Initialize ADC ADMUX = (0 << MUX1) | (1 << MUX0); // Select ADC1 (PB2) ADCSRA = (1 << ADEN) | (1 << ADPS1) | (1 << ADPS0); // Enable ADC, prescaler 8 ADCSRA |= (1 << ADSC); // Start conversion while (ADCSRA & (1 << ADSC)) { } // Wait for conversion to finish //Normally a bitshift of 2 is done for 8bit ADC values, //however we want to divide by two after this as well so we //do a third shift followed by an addition of 127 to yield a mapping of //approximatly 50%-96.6% duty cycle range. uint8_t val = (uint8_t)(ADC >> 3) + 127; return val; } // change to ReadFader(void) uint8_t ReadFader(void) { // Initialize ADC ADMUX = (1 << MUX1) | (1 << MUX0); // Select ADC3 (PB3) ADCSRA = (1 << ADEN) | (1 << ADPS1) | (1 << ADPS0); // Enable ADC, prescaler 8 ADCSRA |= (1 << ADSC); // Start conversion while (ADCSRA & (1 << ADSC)) { } // Wait for conversion to finish return (uint8_t)(ADC >> 2); } /* * ############################ * Motor methods * ############################ */ static inline uint8_t diff(uint8_t a, uint8_t b) { if (a > b) { return a - b; } return b - a; } void MotorSetSavePos(uint8_t *ADR) { uint8_t pos = ReadFader(); eeprom_write_byte((uint8_t *)ADR, pos); return; } uint8_t MotorGetSavedPos(uint8_t *ADR) { return (uint8_t)eeprom_read_byte((uint8_t *)ADR); } void MotorMoveTo(uint8_t target) { uint8_t on_delay = ReadSpeed(); uint8_t pos = ReadFader(); uint8_t idx = 0; while (diff(target, pos) > 4) { on_delay = ReadSpeed(); pos = ReadFader(); if (target > pos) { MotorMove(1); } else { MotorMove(0); } // The delay ratio controlls the PWM waveforms. for (idx = 0; idx < on_delay; idx++) { _delay_us(MOTOR_PULSE); } MotorCoast(); for (; idx < 255; idx++) { _delay_us(MOTOR_PULSE); } } return; } // Using the compatable bool type. // The motor being used seems to stop working below a 50% duty cycle. void MotorMove(uint8_t fwd) { if (fwd) { PORTB |= (1 << PWM_PIN1); PORTB &= ~(1 << PWM_PIN2); } else { PORTB |= (1 << PWM_PIN2); PORTB &= ~(1 << PWM_PIN1); } return; } void MotorCoast(void) { PORTB &= ~((1 << PWM_PIN1) | (1 << PWM_PIN2)); // Disable motor drive } /* * ############################ * BUTTON methods * ############################ */ /*This is kinda like our button constructor*/ static void InitBtn(btn_state *b, uint8_t input_pin) { b->is_long_pressed = 0; b->is_pressed = 0; b->is_active = 0; b->pressed_ticks = 0; b->timer_enabled = 0; b->input_pin = input_pin; /*Configure the buttons inputs and outputs*/ DDRB &= ~(1 << b->input_pin); PORTB |= (1 << b->input_pin); } static void ClearButtonTimer(btn_state *b) { b->timer_enabled = 0; b->is_long_pressed = 0; b->pressed_ticks = 0; } static void StartButtonTimer(btn_state *b) { ClearButtonTimer(b); b->timer_enabled = 1; } static void CheckButtonLongpress(btn_state *b) { if (b->pressed_ticks >= LONG_PRESS_TICKS) { b->is_long_pressed = 1; b->timer_enabled = 0; } } static void UpdateButtonInput(btn_state *b) { /*Check from the global debounced port input*/ /*check for pin HIGH*/ if (debounced_state & (1 << b->input_pin)) { b->is_pressed = 0; } /*otherwise assume pin LOW*/ else { b->is_pressed = 1; } } static void UpdateButtonOutput(btn_state *b) { /*If the button is actually pressed.*/ if (b->is_pressed) { b->is_active = 1; /*If this is a new event.*/ if (!b->is_long_pressed && !b->timer_enabled) { /*Then start the timer and update the output*/ // ToggleOutput(b); StartButtonTimer(b); return; } /*If the timer is already going.*/ else if (b->timer_enabled) { /*Then just check if it's hit the threshold.*/ CheckButtonLongpress(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) { MotorSetSavePos((uint8_t *)POSITION1_ADR); } /*If the button pres was a short one.*/ else if (!b->is_long_pressed) { if (b->is_active) { /*If already in saved position then go back to original pos.*/ if(diff(MotorGetSavedPos((uint8_t *)POSITION1_ADR), ReadFader()) < 4){ MotorMoveTo(MotorGetSavedPos((uint8_t *)POSITION2_ADR)); } else{ /*Save the current position into position 2 EEPROM*/ MotorSetSavePos((uint8_t *)POSITION2_ADR); MotorMoveTo(MotorGetSavedPos((uint8_t *)POSITION1_ADR)); } } else { MotorCoast(); } } b->is_active = 0; ClearButtonTimer(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 DebounceSwitch() { 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 InitTimer0(void) { /*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 << CS01) | (1 << CS00); /*Enable global interrupts*/ sei(); /*Enabling timer0 interrupt*/ TCNT0 = 0; TIMSK0 |= (1 << TOIE0); } // ISR(TIMER0_OVF_vect) /*The interrupt service routine for the timer0*/ ISR(TIM0_OVF_vect) { /*Disable global interrupts*/ cli(); /*Check the state of the switches*/ DebounceSwitch(); /*Update the tick_count*/ if (btn1.timer_enabled && btn1.pressed_ticks <= UINT8_MAX) { btn1.pressed_ticks += 1; } /*Re-Enable global interrupts*/ sei(); }