ECE3849/buttons.c
2025-03-20 11:20:21 -04:00

168 lines
6.8 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);
// 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 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
*/
static bool tic = false;
static bool running = true;
/*
if (presses & 1) { // EK-TM4C1294XL button 1 pressed
running = !running;
}
*/
if (running) {
if (tic) gTime++; // increment time every other ISR call
tic = !tic;
}
}