255 lines
9.6 KiB
C
255 lines
9.6 KiB
C
/*
|
|
* buttons.c
|
|
*
|
|
* Created on: Aug 12, 2012, modified 9/8/2017
|
|
* Author: Gene Bogdanov
|
|
*
|
|
* ECE 3849 Lab button handling
|
|
*/
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <math.h>
|
|
#include "inc/hw_memmap.h"
|
|
#include "inc/hw_ints.h"
|
|
#include "driverlib/sysctl.h"
|
|
#include "driverlib/gpio.h"
|
|
#include "driverlib/timer.h"
|
|
#include "driverlib/interrupt.h"
|
|
#include "driverlib/adc.h"
|
|
#include "sysctl_pll.h"
|
|
#include "buttons.h"
|
|
|
|
// public globals
|
|
volatile uint32_t gButtons = 0; // debounced button state, one per bit in the lowest bits
|
|
// button is pressed if its bit is 1, not pressed if 0
|
|
uint32_t gJoystick[2] = {0}; // joystick coordinates
|
|
uint32_t gADCSamplingRate; // [Hz] actual ADC sampling rate
|
|
|
|
// imported globals
|
|
extern uint32_t gSystemClock; // [Hz] system clock frequency
|
|
extern volatile uint32_t gTime; // time in hundredths of a second
|
|
|
|
// initialize all button and joystick handling hardware
|
|
void ButtonInit(void)
|
|
{
|
|
// initialize a general purpose timer for periodic interrupts
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
|
|
TimerDisable(TIMER0_BASE, TIMER_BOTH);
|
|
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
|
|
TimerLoadSet(TIMER0_BASE, TIMER_A, roundf((float)gSystemClock / BUTTON_SCAN_RATE) - 1);
|
|
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
|
|
TimerEnable(TIMER0_BASE, TIMER_BOTH);
|
|
|
|
// initialize interrupt controller to respond to timer interrupts
|
|
IntPrioritySet(INT_TIMER0A, BUTTON_INT_PRIORITY);
|
|
IntEnable(INT_TIMER0A);
|
|
// GPIO PJ0 and PJ1 = EK-TM4C1294XL buttons 1 and 2
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
|
|
GPIOPinTypeGPIOInput(GPIO_PORTJ_BASE, GPIO_PIN_0 | GPIO_PIN_1);
|
|
GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_0 | GPIO_PIN_1, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
|
|
|
|
// Button S1
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
|
|
GPIOPinTypeGPIOInput(GPIO_PORTH_BASE, GPIO_PIN_1);
|
|
GPIOPadConfigSet(GPIO_PORTH_BASE, GPIO_PIN_1, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
|
|
|
|
// Button S2
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
|
|
GPIOPinTypeGPIOInput(GPIO_PORTK_BASE, GPIO_PIN_6);
|
|
GPIOPadConfigSet(GPIO_PORTK_BASE, GPIO_PIN_6, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
|
|
|
|
// Select Button
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
|
|
GPIOPinTypeGPIOInput(GPIO_PORTD_BASE, GPIO_PIN_4);
|
|
GPIOPadConfigSet(GPIO_PORTD_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
|
|
|
|
// analog input AIN13, at GPIO PD2 = BoosterPack Joystick HOR(X)
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
|
|
GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_2);
|
|
// analog input AIN17, at GPIO PK1 = BoosterPack Joystick VER(Y)
|
|
//SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
|
|
GPIOPinTypeADC(GPIO_PORTK_BASE, GPIO_PIN_1);
|
|
|
|
// initialize ADC0 peripheral
|
|
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
|
|
uint32_t pll_frequency = SysCtlFrequencyGet(CRYSTAL_FREQUENCY);
|
|
uint32_t pll_divisor = (pll_frequency - 1) / (16 * ADC_SAMPLING_RATE) + 1; // round divisor up
|
|
gADCSamplingRate = pll_frequency / (16 * pll_divisor); // actual sampling rate may differ from ADC_SAMPLING_RATE
|
|
ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, pll_divisor); // only ADC0 has PLL clock divisor control
|
|
|
|
// initialize ADC sampling sequence
|
|
ADCSequenceDisable(ADC0_BASE, 0);
|
|
ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
|
|
ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH13); // Joystick HOR(X)
|
|
ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH17 | ADC_CTL_IE | ADC_CTL_END); // Joystick VER(Y)
|
|
ADCSequenceEnable(ADC0_BASE, 0);
|
|
}
|
|
|
|
// update the debounced button state gButtons
|
|
void ButtonDebounce(uint32_t buttons)
|
|
{
|
|
int32_t i, mask;
|
|
static int32_t state[BUTTON_COUNT]; // button state: 0 = released
|
|
// BUTTON_PRESSED_STATE = pressed
|
|
// in between = previous state
|
|
for (i = 0; i < BUTTON_COUNT; i++) {
|
|
mask = 1 << i;
|
|
if (buttons & mask) {
|
|
state[i] += BUTTON_STATE_INCREMENT;
|
|
if (state[i] >= BUTTON_PRESSED_STATE) {
|
|
state[i] = BUTTON_PRESSED_STATE;
|
|
gButtons |= mask; // update debounced button state
|
|
}
|
|
}
|
|
else {
|
|
state[i] -= BUTTON_STATE_DECREMENT;
|
|
if (state[i] <= 0) {
|
|
state[i] = 0;
|
|
gButtons &= ~mask;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// sample joystick and convert to button presses
|
|
void ButtonReadJoystick(void)
|
|
{
|
|
ADCProcessorTrigger(ADC0_BASE, 0); // trigger the ADC sample sequence for Joystick X and Y
|
|
while(!ADCIntStatus(ADC0_BASE, 0, false)); // wait until the sample sequence has completed
|
|
ADCSequenceDataGet(ADC0_BASE, 0, gJoystick);// retrieve joystick data
|
|
ADCIntClear(ADC0_BASE, 0); // clear ADC sequence interrupt flag
|
|
|
|
// process joystick movements as button presses using hysteresis
|
|
if (gJoystick[0] > JOYSTICK_UPPER_PRESS_THRESHOLD) gButtons |= 1 << 5; // joystick right in position 5
|
|
if (gJoystick[0] < JOYSTICK_UPPER_RELEASE_THRESHOLD) gButtons &= ~(1 << 5);
|
|
|
|
if (gJoystick[0] < JOYSTICK_LOWER_PRESS_THRESHOLD) gButtons |= 1 << 6; // joystick left in position 6
|
|
if (gJoystick[0] > JOYSTICK_LOWER_RELEASE_THRESHOLD) gButtons &= ~(1 << 6);
|
|
|
|
if (gJoystick[1] > JOYSTICK_UPPER_PRESS_THRESHOLD) gButtons |= 1 << 7; // joystick up in position 7
|
|
if (gJoystick[1] < JOYSTICK_UPPER_RELEASE_THRESHOLD) gButtons &= ~(1 << 7);
|
|
|
|
if (gJoystick[1] < JOYSTICK_LOWER_PRESS_THRESHOLD) gButtons |= 1 << 8; // joystick down in position 8
|
|
if (gJoystick[1] > JOYSTICK_LOWER_RELEASE_THRESHOLD) gButtons &= ~(1 << 8);
|
|
}
|
|
|
|
// autorepeat button presses if a button is held long enough
|
|
uint32_t ButtonAutoRepeat(void)
|
|
{
|
|
static int count[BUTTON_AND_JOYSTICK_COUNT] = {0}; // autorepeat counts
|
|
int i;
|
|
uint32_t mask;
|
|
uint32_t presses = 0;
|
|
for (i = 0; i < BUTTON_AND_JOYSTICK_COUNT; i++) {
|
|
mask = 1 << i;
|
|
if (gButtons & mask)
|
|
count[i]++; // increment count if button is held
|
|
else
|
|
count[i] = 0; // reset count if button is let go
|
|
if (count[i] >= BUTTON_AUTOREPEAT_INITIAL &&
|
|
(count[i] - BUTTON_AUTOREPEAT_INITIAL) % BUTTON_AUTOREPEAT_NEXT == 0)
|
|
presses |= mask; // register a button press due to auto-repeat
|
|
}
|
|
return presses;
|
|
}
|
|
|
|
// ISR for scanning and debouncing buttons
|
|
void ButtonISR(void) {
|
|
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT); // clear interrupt flag
|
|
// read hardware button state
|
|
uint32_t gpio_buttons =
|
|
~GPIOPinRead(GPIO_PORTJ_BASE, 0xff) & (GPIO_PIN_1 | GPIO_PIN_0); // EK-TM4C1294XL buttons in positions 0 and 1
|
|
|
|
uint32_t gpio_s1 =
|
|
GPIOPinRead(GPIO_PORTH_BASE, GPIO_PIN_1) >> 1; // S1
|
|
gpio_s1 ^= 1;
|
|
gpio_buttons |= gpio_s1 << 2;
|
|
|
|
|
|
uint32_t gpio_s2 =
|
|
GPIOPinRead(GPIO_PORTK_BASE, GPIO_PIN_6) >> 6; // S2
|
|
gpio_s2 ^= 1;
|
|
gpio_buttons |= gpio_s2 << 3;
|
|
|
|
uint32_t gpio_select =
|
|
GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_4) >> 4; // select
|
|
gpio_select ^= 1;
|
|
gpio_buttons |= gpio_select << 4;
|
|
|
|
uint32_t old_buttons = gButtons; // save previous button state
|
|
ButtonDebounce(gpio_buttons); // Run the button debouncer. The result is in gButtons.
|
|
ButtonReadJoystick(); // Convert joystick state to button presses. The result is in gButtons.
|
|
uint32_t presses = ~old_buttons & gButtons; // detect button presses (transitions from not pressed to pressed)
|
|
presses |= ButtonAutoRepeat(); // autorepeat presses if a button is held long enough
|
|
#ifdef DISPLAY_TIME
|
|
static bool tic = false;
|
|
static bool running = true;
|
|
if (presses & 1) { // EK-TM4C1294XL button 1 pressed
|
|
running = !running;
|
|
}
|
|
if (presses & 2) { // EK-TM4C1294XL button 2 pressed
|
|
gTime = 0;
|
|
}
|
|
if (running) {
|
|
if (tic) gTime++; // increment time every other ISR call
|
|
tic = !tic;
|
|
}
|
|
#endif // DISPLAY_TIME
|
|
Button buttons[9];
|
|
uint8_t length, i;
|
|
length = buttons_from_mask(buttons, presses);
|
|
for (i=0; i<length; i++) {
|
|
fifo_put(buttons[i]);
|
|
}
|
|
}
|
|
|
|
uint8_t buttons_from_mask(Button *array, uint32_t buttons) {
|
|
uint8_t i, len;
|
|
len = 0;
|
|
|
|
for(i=0; i<9; i++) {
|
|
if (buttons & 1) {
|
|
array[len++] = (Button) i;
|
|
}
|
|
|
|
buttons >>= 1;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
volatile DataType button_fifo[FIFO_SIZE]; // FIFO storage array
|
|
volatile int fifo_head = 0; // index of the first item in the FIFO
|
|
volatile int fifo_tail = 0; // index one step past the last item
|
|
|
|
// put data into the FIFO, skip if full
|
|
// returns 1 on success, 0 if FIFO was full
|
|
int fifo_put(DataType data)
|
|
{
|
|
int new_tail = fifo_tail + 1;
|
|
if (new_tail >= FIFO_SIZE) new_tail = 0; // wrap around
|
|
if (fifo_head != new_tail) { // if the FIFO is not full
|
|
button_fifo[fifo_tail] = data; // store data into the FIFO
|
|
fifo_tail = new_tail; // advance FIFO tail index
|
|
return 1; // success
|
|
}
|
|
return 0; // full
|
|
}
|
|
|
|
// get data from the FIFO
|
|
// returns 1 on success, 0 if FIFO was empty
|
|
int fifo_get(DataType *data)
|
|
{
|
|
if (fifo_head != fifo_tail) { // if the FIFO is not empty
|
|
*data = button_fifo[fifo_head]; // read data from the FIFO
|
|
|
|
if (fifo_head >= FIFO_SIZE-1) {
|
|
fifo_head = 0; // reset if past end
|
|
} else {
|
|
fifo_head++; // increment if within buffer
|
|
}
|
|
|
|
return 1; // success
|
|
}
|
|
return 0; // empty
|
|
}
|