ECE3849/buttons.c
Andy Killorin e8946f2a03 handle button input
config read/writes should be protected with a semaphore?

changing adc speed works somehow, might need to change to avoid unseen TI-RTOS clobbers
2025-04-12 17:57:48 -04:00

216 lines
7.9 KiB
C

/*
* buttons.c
*
* Created on: Aug 12, 2012, modified 9/8/2017
* Author: Gene Bogdanov
*
* ECE 3849 Lab button handling
*/
/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/cfg/global.h>
/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Semaphore.h>
#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)
{
// 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;
}
void clock_buttons(void) {
Semaphore_post(button_clock_sem);
}
void button_task(void) {
while(1) {
Semaphore_pend(button_clock_sem, BIOS_WAIT_FOREVER);
handle_buttons();
}
}
// Task for scanning and debouncing buttons
void handle_buttons(void) {
// 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
Button buttons[9];
uint8_t length, i;
length = buttons_from_mask(buttons, presses);
for (i=0; i<length; i++) {
Mailbox_post(button_mailbox,buttons+i, BIOS_WAIT_FOREVER);
}
}
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;
}