357 lines
8.1 KiB
C
357 lines
8.1 KiB
C
#ifndef F_CPU
|
|
#define F_CPU 1200000UL
|
|
#endif
|
|
|
|
#include "main.h"
|
|
//#include "ADC.h"
|
|
#include <avr/eeprom.h>
|
|
#include <avr/interrupt.h>
|
|
#include <util/delay.h>
|
|
|
|
// 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();
|
|
}
|