Skip to content

Commit

Permalink
rg_input: Improved GPIO and ADC1 drivers
Browse files Browse the repository at this point in the history
- Both drivers are now always available, allowing to mix gpio input with i2c input directly in `config.h`
- ADC1 inputs are now configurable in the target's `config.h`.
- GPIO pullups are now configurable in `config.h`

To sum up, there are now three macros to configure inputs:

- `RG_GAMEPAD_ADC1_MAP`: Always available
- `RG_GAMEPAD_GPIO_MAP`: Always available
- `RG_GAMEPAD_MAP`: Depends on `RG_GAMEPAD_DRIVER`
  • Loading branch information
ducalex committed Feb 2, 2024
1 parent 1a539f6 commit 01a7cd0
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 143 deletions.
4 changes: 0 additions & 4 deletions components/retro-go/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,3 @@
#ifndef RG_GPIO_LED
#define RG_GPIO_LED (-1)
#endif

#ifndef RG_GAMEPAD_MAP
#define RG_GAMEPAD_MAP {}
#endif
142 changes: 64 additions & 78 deletions components/retro-go/rg_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,41 @@

#ifdef ESP_PLATFORM
#include <driver/gpio.h>
#include <driver/adc.h>
#else
#include <SDL2/SDL.h>
#endif

#if RG_GAMEPAD_DRIVER == 1 || defined(RG_BATTERY_ADC_CHANNEL)
#if RG_BATTERY_DRIVER == 1
#include <esp_adc_cal.h>
#include <driver/adc.h>
static esp_adc_cal_characteristics_t adc_chars;
#endif

static const struct {int key, src;} keymap[] = RG_GAMEPAD_MAP;
static const size_t keymap_size = RG_COUNT(keymap);
#ifdef RG_GAMEPAD_ADC1_MAP
static rg_keymap_adc1_t keymap_adc1[] = RG_GAMEPAD_ADC1_MAP;
#endif
#ifdef RG_GAMEPAD_GPIO_MAP
static rg_keymap_gpio_t keymap_gpio[] = RG_GAMEPAD_GPIO_MAP;
#endif
#ifdef RG_GAMEPAD_MAP
static rg_keymap_t keymap[] = RG_GAMEPAD_MAP;
#endif
static bool input_task_running = false;
static uint32_t gamepad_state = -1; // _Atomic
static int battery_level = -1;
#if defined(RG_BATTERY_ADC_CHANNEL)
static esp_adc_cal_characteristics_t adc_chars;
#endif


static inline int battery_read(void)
{
#if defined(RG_BATTERY_ADC_CHANNEL)

#if RG_BATTERY_DRIVER == 1 /* ADC1 */
uint32_t adc_sample = 0;
for (int i = 0; i < 4; ++i)
adc_sample += esp_adc_cal_raw_to_voltage(adc1_get_raw(RG_BATTERY_ADC_CHANNEL), &adc_chars);
return adc_sample / 4;

#elif RG_GAMEPAD_DRIVER == 3 /* I2C */

#elif RG_BATTERY_DRIVER == 2 /* I2C */
uint8_t data[5];
if (rg_i2c_read(0x20, -1, &data, 5))
return data[4];
return -1;

#else
// No battery or unknown
return -1;
Expand All @@ -52,32 +52,25 @@ static inline uint32_t gamepad_read(void)
{
uint32_t state = 0;

#if RG_GAMEPAD_DRIVER == 1 // GPIO

int joyX = adc1_get_raw(RG_GPIO_GAMEPAD_X);
int joyY = adc1_get_raw(RG_GPIO_GAMEPAD_Y);

// Buttons
for (size_t i = 0; i < keymap_size; ++i)
#if defined(RG_GAMEPAD_ADC1_MAP)
for (size_t i = 0; i < RG_COUNT(keymap_adc1); ++i)
{
if (!gpio_get_level(keymap[i].src))
state |= keymap[i].key;
const rg_keymap_adc1_t *mapping = &keymap_adc1[i];
int value = adc1_get_raw(mapping->channel);
if (value > mapping->min && value < mapping->max)
state |= mapping->key;
}
#endif
#if defined(RG_GAMEPAD_GPIO_MAP)
for (size_t i = 0; i < RG_COUNT(keymap_gpio); ++i)
{
const rg_keymap_gpio_t *mapping = &keymap_gpio[i];
if (gpio_get_level(mapping->num) == mapping->level)
state |= mapping->key;
}
#endif

// D-PAD
#ifndef RG_TARGET_RETRO_ESP32
if (joyY > 2048 + 1024) state |= RG_KEY_UP;
else if (joyY > 1024) state |= RG_KEY_DOWN;
if (joyX > 2048 + 1024) state |= RG_KEY_LEFT;
else if (joyX > 1024) state |= RG_KEY_RIGHT;
#else
if (joyY > 2048) state |= RG_KEY_UP;
else if (joyY > 1024) state |= RG_KEY_DOWN;
if (joyX > 2048) state |= RG_KEY_LEFT;
else if (joyX > 1024) state |= RG_KEY_RIGHT;
#endif

#elif RG_GAMEPAD_DRIVER == 2 // Serial
#if RG_GAMEPAD_DRIVER == 2 // Serial

gpio_set_level(RG_GPIO_GAMEPAD_LATCH, 0);
usleep(5);
Expand All @@ -94,7 +87,7 @@ static inline uint32_t gamepad_read(void)
usleep(1);
}

for (size_t i = 0; i < keymap_size; ++i)
for (size_t i = 0; i < RG_COUNT(keymap); ++i)
{
if ((buttons & keymap[i].src) == keymap[i].src)
state |= keymap[i].key;
Expand All @@ -107,7 +100,7 @@ static inline uint32_t gamepad_read(void)
{
uint32_t buttons = ~((data[2] << 8) | data[1]);

for (size_t i = 0; i < keymap_size; ++i)
for (size_t i = 0; i < RG_COUNT(keymap); ++i)
{
if ((buttons & keymap[i].src) == keymap[i].src)
state |= keymap[i].key;
Expand All @@ -118,7 +111,7 @@ static inline uint32_t gamepad_read(void)

uint32_t buttons = ~(rg_i2c_gpio_read_port(0) | rg_i2c_gpio_read_port(1) << 8);

for (size_t i = 0; i < keymap_size; ++i)
for (size_t i = 0; i < RG_COUNT(keymap); ++i)
{
if ((buttons & keymap[i].src) == keymap[i].src)
state |= keymap[i].key;
Expand All @@ -129,7 +122,7 @@ static inline uint32_t gamepad_read(void)
int numkeys = 0;
const uint8_t *keys = SDL_GetKeyboardState(&numkeys);

for (size_t i = 0; i < keymap_size; ++i)
for (size_t i = 0; i < RG_COUNT(keymap); ++i)
{
if (keymap[i].src < 0 || keymap[i].src >= numkeys)
continue;
Expand Down Expand Up @@ -205,49 +198,42 @@ void rg_input_init(void)
if (input_task_running)
return;

#if RG_GAMEPAD_DRIVER == 1 // GPIO

const char *driver = "GPIO";

#if defined(RG_GAMEPAD_ADC1_MAP)
RG_LOGI("Initializing ADC1 gamepad driver...");
adc1_config_width(ADC_WIDTH_MAX - 1);
adc1_config_channel_atten(RG_GPIO_GAMEPAD_X, ADC_ATTEN_DB_11);
adc1_config_channel_atten(RG_GPIO_GAMEPAD_Y, ADC_ATTEN_DB_11);

gpio_set_direction(RG_GPIO_GAMEPAD_MENU, GPIO_MODE_INPUT);
gpio_set_pull_mode(RG_GPIO_GAMEPAD_MENU, GPIO_PULLUP_ONLY);
gpio_set_direction(RG_GPIO_GAMEPAD_OPTION, GPIO_MODE_INPUT);
// gpio_set_pull_mode(RG_GPIO_GAMEPAD_OPTION, GPIO_PULLUP_ONLY);
gpio_set_direction(RG_GPIO_GAMEPAD_SELECT, GPIO_MODE_INPUT);
gpio_set_pull_mode(RG_GPIO_GAMEPAD_SELECT, GPIO_PULLUP_ONLY);
gpio_set_direction(RG_GPIO_GAMEPAD_START, GPIO_MODE_INPUT);
// gpio_set_pull_mode(RG_GPIO_GAMEPAD_START, GPIO_PULLUP_ONLY);
gpio_set_direction(RG_GPIO_GAMEPAD_A, GPIO_MODE_INPUT);
gpio_set_pull_mode(RG_GPIO_GAMEPAD_A, GPIO_PULLUP_ONLY);
gpio_set_direction(RG_GPIO_GAMEPAD_B, GPIO_MODE_INPUT);
gpio_set_pull_mode(RG_GPIO_GAMEPAD_B, GPIO_PULLUP_ONLY);

#elif RG_GAMEPAD_DRIVER == 2 // Serial

const char *driver = "SERIAL";
for (size_t i = 0; i < RG_COUNT(keymap_adc1); ++i)
{
const rg_keymap_adc1_t *mapping = &keymap_adc1[i];
adc1_config_channel_atten(mapping->channel, mapping->atten);
}
#endif
#if defined(RG_GAMEPAD_GPIO_MAP)
RG_LOGI("Initializing GPIO gamepad driver...");
for (size_t i = 0; i < RG_COUNT(keymap_gpio); ++i)
{
const rg_keymap_gpio_t *mapping = &keymap_gpio[i];
gpio_set_direction(mapping->num, GPIO_MODE_INPUT);
gpio_set_pull_mode(mapping->num, mapping->pull);
}
#endif

#if RG_GAMEPAD_DRIVER == 2 // Serial

RG_LOGI("Initializing SERIAL gamepad driver...");
gpio_set_direction(RG_GPIO_GAMEPAD_CLOCK, GPIO_MODE_OUTPUT);
gpio_set_direction(RG_GPIO_GAMEPAD_LATCH, GPIO_MODE_OUTPUT);
gpio_set_direction(RG_GPIO_GAMEPAD_DATA, GPIO_MODE_INPUT);

gpio_set_level(RG_GPIO_GAMEPAD_LATCH, 0);
gpio_set_level(RG_GPIO_GAMEPAD_CLOCK, 1);

#elif RG_GAMEPAD_DRIVER == 3 // I2C

const char *driver = "I2C";

RG_LOGI("Initializing I2C gamepad driver...");
rg_i2c_init();
gamepad_read(); // First read contains garbage

#elif RG_GAMEPAD_DRIVER == 4 // I2C w/AW9523

const char *driver = "AW9523-I2C";

RG_LOGI("Initializing I2C-GPIO gamepad driver...");
rg_i2c_gpio_init();

// All that below should be moved elsewhere, and possibly specific to the qtpy...
Expand All @@ -266,15 +252,15 @@ void rg_input_init(void)

#elif RG_GAMEPAD_DRIVER == 6 // SDL2

const char *driver = "SDL2";

#else

#error "No gamepad driver selected"
RG_LOGI("Initializing SDL2 gamepad driver...");

#endif

#if defined(RG_BATTERY_ADC_CHANNEL)
// Certain drivers return garbage on first read, let's drop it
gamepad_read();

#if RG_BATTERY_DRIVER == 1 /* ADC1 */
RG_LOGI("Initializing ADC1 battery driver...");
adc1_config_width(ADC_WIDTH_MAX - 1);
adc1_config_channel_atten(RG_BATTERY_ADC_CHANNEL, ADC_ATTEN_DB_11);
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_MAX - 1, 1100, &adc_chars);
Expand All @@ -284,7 +270,7 @@ void rg_input_init(void)
rg_task_create("rg_input", &input_task, NULL, 3 * 1024, RG_TASK_PRIORITY - 1, 1);
while (gamepad_state == -1)
rg_task_delay(1);
RG_LOGI("Input ready. driver='%s', state=" PRINTF_BINARY_16 "\n", driver, PRINTF_BINVAL_16(gamepad_state));
RG_LOGI("Input ready. state=" PRINTF_BINARY_16 "\n", PRINTF_BINVAL_16(gamepad_state));
}

void rg_input_deinit(void)
Expand Down Expand Up @@ -355,4 +341,4 @@ const char *rg_input_get_key_name(rg_key_t key)
case RG_KEY_NONE: return "None";
default: return "Unknown";
}
}
}
22 changes: 22 additions & 0 deletions components/retro-go/rg_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@ typedef enum
RG_KEY_NONE = 0,
} rg_key_t;

typedef struct
{
rg_key_t key;
int channel;
int atten;
int min, max;
} rg_keymap_adc1_t;

typedef struct
{
rg_key_t key;
int num;
int pull;
int level;
} rg_keymap_gpio_t;

typedef struct
{
rg_key_t key;
int src;
} rg_keymap_t;

void rg_input_init(void);
void rg_input_deinit(void);
bool rg_input_key_is_pressed(rg_key_t key);
Expand Down
12 changes: 1 addition & 11 deletions components/retro-go/targets/esplay-micro/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,8 @@
{RG_KEY_B, (1<<6)},\
}

// Experimental. Caused "Menu" to be mapped to a D-pad direction.
//#define RG_GPIO_GAMEPAD_X GPIO_NUM_NC
//#define RG_GPIO_GAMEPAD_Y GPIO_NUM_NC
//#define RG_GPIO_GAMEPAD_SELECT GPIO_NUM_0
//#define RG_GPIO_GAMEPAD_START GPIO_NUM_36
//#define RG_GPIO_GAMEPAD_A GPIO_NUM_32
//#define RG_GPIO_GAMEPAD_B GPIO_NUM_33
//#define RG_GPIO_GAMEPAD_MENU GPIO_NUM_35
//#define RG_GPIO_GAMEPAD_OPTION GPIO_NUM_NC

// Battery
// #define RG_BATTERY_ADC_CHANNEL ADC1_CHANNEL_3
#define RG_BATTERY_DRIVER 2
#define RG_BATTERY_CALC_PERCENT(raw) (((raw) - 170) / 30.f * 100.f)
#define RG_BATTERY_CALC_VOLTAGE(raw) ((raw) * 2.f * 0.001f)

Expand Down
15 changes: 7 additions & 8 deletions components/retro-go/targets/esplay-s3/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@
#define RG_GAMEPAD_DRIVER 3 // 1 = ODROID-GO, 2 = Serial, 3 = I2C
#define RG_GAMEPAD_HAS_MENU_BTN 1
#define RG_GAMEPAD_HAS_OPTION_BTN 1
// Note: Depending on the driver, the button map can be a bitmask, an index, or a GPIO.
// Refer to rg_input.h to see all available RG_KEY_*
#define RG_GAMEPAD_MAP {\
{RG_KEY_UP, (1<<2)},\
{RG_KEY_RIGHT, (1<<5)},\
Expand All @@ -55,8 +53,15 @@
{RG_KEY_A, (1<<6)},\
{RG_KEY_B, (1<<7)},\
}
#define RG_GAMEPAD_GPIO_MAP {\
{RG_KEY_L, GPIO_NUM_40, GPIO_PULLUP_ONLY, 0},\
{RG_KEY_R, GPIO_NUM_41, GPIO_PULLUP_ONLY, 0},\
{RG_KEY_MENU, GPIO_NUM_42, GPIO_PULLUP_ONLY, 0},\
{RG_KEY_OPTION, GPIO_NUM_41, GPIO_PULLUP_ONLY, 0},\
}

// Battery
#define RG_BATTERY_DRIVER 1
#define RG_BATTERY_ADC_CHANNEL ADC1_CHANNEL_3
#define RG_BATTERY_CALC_PERCENT(raw) (((raw) * 2.f - 3500.f) / (4200.f - 3500.f) * 100.f)
#define RG_BATTERY_CALC_VOLTAGE(raw) ((raw) * 2.f * 0.001f)
Expand All @@ -68,12 +73,6 @@
#define RG_GPIO_I2C_SDA GPIO_NUM_10
#define RG_GPIO_I2C_SCL GPIO_NUM_11

// Built-in gamepad
#define RG_GPIO_GAMEPAD_L GPIO_NUM_40
#define RG_GPIO_GAMEPAD_R GPIO_NUM_41
#define RG_GPIO_GAMEPAD_MENU GPIO_NUM_42
#define RG_GPIO_GAMEPAD_OPTION GPIO_NUM_41

// SPI Display
#define RG_GPIO_LCD_MISO GPIO_NUM_NC
#define RG_GPIO_LCD_MOSI GPIO_NUM_12
Expand Down
2 changes: 1 addition & 1 deletion components/retro-go/targets/mrgc-g32/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
}

// Battery
// #define RG_BATTERY_ADC_CHANNEL ADC1_CHANNEL_0
#define RG_BATTERY_DRIVER 2
#define RG_BATTERY_CALC_PERCENT(raw) (((raw) - 170) / 30.f * 100.f)
#define RG_BATTERY_CALC_VOLTAGE(raw) (0)

Expand Down
2 changes: 1 addition & 1 deletion components/retro-go/targets/mrgc-gbm/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
}

// Battery
// #define RG_BATTERY_ADC_CHANNEL ADC1_CHANNEL_0 // Default 0, commented out.
#define RG_BATTERY_DRIVER 2
#define RG_BATTERY_CALC_PERCENT(raw) (((raw) - 170) / 30.f * 100.f)
#define RG_BATTERY_CALC_VOLTAGE(raw) (0)

Expand Down
Loading

0 comments on commit 01a7cd0

Please sign in to comment.