Skip to content

Commit

Permalink
Byteboi rev.1 support (#164)
Browse files Browse the repository at this point in the history
* Initial support for CircuitMess Byteboi Rev.1

The delay after display wakeup had to be increased, because 5ms had a very high probability of causing glitches that persisted during use (small vertical offset and incorrect pixel values). I hope this small increase does not hurt any other devices.

Audio output via the internal DAC using only one channel was completely broken. I tried to fix this in a generic way.

* add device picture

* Reworked audio code to allow for switching between internal and external DAC also in case of single channel use
  • Loading branch information
birefringence authored Nov 3, 2024
1 parent 0317235 commit 13cbae1
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 8 deletions.
2 changes: 2 additions & 0 deletions components/retro-go/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "targets/esp32s3-devkit-c/config.h"
#elif defined(RG_TARGET_FRI3D_2024)
#include "targets/fri3d-2024/config.h"
#elif defined(RG_TARGET_BYTEBOI_REV1)
#include "targets/byteboi-rev1/config.h"
#else
#warning "No target defined. Defaulting to ODROID-GO."
#include "targets/odroid-go/config.h"
Expand Down
22 changes: 18 additions & 4 deletions components/retro-go/drivers/audio/i2s.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,11 @@ static bool driver_deinit(void)
}
else if (state.device == 1)
{
#if RG_AUDIO_USE_EXT_DAC
gpio_reset_pin(RG_GPIO_SND_I2S_BCK);
gpio_reset_pin(RG_GPIO_SND_I2S_DATA);
gpio_reset_pin(RG_GPIO_SND_I2S_WS);
#endif
}
#ifdef RG_GPIO_SND_AMP_ENABLE
gpio_reset_pin(RG_GPIO_SND_AMP_ENABLE);
Expand All @@ -120,17 +122,24 @@ static bool driver_submit(const rg_audio_frame_t *frames, size_t count)
size_t written = 0;
size_t pos = 0;

// In speaker mode we use left and right as a differential mono output to increase resolution.
bool differential = state.device == 0;
bool use_internal_dac = state.device == 0;

for (size_t i = 0; i < count; ++i)
{
int left = frames[i].left * volume;
int right = frames[i].right * volume;

if (differential)
if (use_internal_dac)
{
int sample = (left + right) >> 1;
#if RG_AUDIO_USE_INT_DAC == 1
left = sample + 0x8000; // the internal DAC expects unsigned data
right = 0;
#elif RG_AUDIO_USE_INT_DAC == 2
left = 0;
right = sample + 0x8000; // the internal DAC expects unsigned data
#elif RG_AUDIO_USE_INT_DAC == 3
// In two channel mode we use left and right as a differential mono output to increase resolution.
if (sample > 0x7F00)
{
left = 0x8000 + (sample - 0x7F00);
Expand All @@ -146,6 +155,7 @@ static bool driver_submit(const rg_audio_frame_t *frames, size_t count)
left = 0x8000;
right = -0x8000 + sample;
}
#endif
}

// Clipping (not necessary, we have (int16 * vol) and volume is never more than 1.0)
Expand All @@ -171,7 +181,11 @@ static bool driver_set_mute(bool mute)
i2s_zero_dma_buffer(I2S_NUM_0);
#if defined(RG_GPIO_SND_AMP_ENABLE)
gpio_set_direction(RG_GPIO_SND_AMP_ENABLE, GPIO_MODE_OUTPUT);
gpio_set_level(RG_GPIO_SND_AMP_ENABLE, !mute);
#if defined(RG_TARGET_BYTEBOI_REV1)
gpio_set_level(RG_GPIO_SND_AMP_ENABLE, mute);
#else
gpio_set_level(RG_GPIO_SND_AMP_ENABLE, !mute);
#endif
#elif defined(RG_TARGET_QTPY_GAMER)
rg_i2c_gpio_set_direction(AW_HEADPHONE_EN, 0);
rg_i2c_gpio_set_level(AW_HEADPHONE_EN, !mute);
Expand Down
7 changes: 6 additions & 1 deletion components/retro-go/drivers/display/ili9341.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ static void lcd_set_backlight(float percent)
int error_code = 0;

#if defined(RG_GPIO_LCD_BCKL)
#if defined(RG_TARGET_BYTEBOI_REV1)
rg_i2c_gpio_set_direction(RG_GPIO_LCD_BCKL, 0);
rg_i2c_gpio_set_level(RG_GPIO_LCD_BCKL, percent > 0 ? 0:1);
#else
error_code = ledc_set_fade_time_and_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0x1FFF * level, 50, 0);
#endif
#elif defined(RG_TARGET_QTPY_GAMER)
rg_i2c_gpio_set_direction(AW_TFT_BACKLIGHT, 0);
rg_i2c_gpio_set_level(AW_TFT_BACKLIGHT, level * 255);
Expand Down Expand Up @@ -240,7 +245,7 @@ static void lcd_init(void)
#warning "LCD init sequence is not defined for this device!"
#endif
ILI9341_CMD(0x11); // Exit Sleep
rg_usleep(5 * 1000);// Wait 5ms after sleep out
rg_usleep(10 * 1000);// Wait 10ms after sleep out
ILI9341_CMD(0x29); // Display on

rg_display_clear(C_BLACK);
Expand Down
24 changes: 23 additions & 1 deletion components/retro-go/rg_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@ bool rg_i2c_write_byte(uint8_t addr, uint8_t reg, uint8_t value)
return rg_i2c_write(addr, reg, &value, 1);
}

#if RG_I2C_DRIVER == 1

#define AW9523_REG_INPUT0 0x00 ///< Register for reading input values
#define AW9523_REG_OUTPUT0 0x02 ///< Register for writing output values
#define AW9523_REG_POLARITY0 0x04 ///< Register for polarity inversion of inputs
#define AW9523_REG_CONFIG0 0x06 ///< Register for configuring direction

#else

#define AW9523_REG_CHIPID 0x10 ///< Register for hardcode chip ID
#define AW9523_REG_SOFTRESET 0x7F ///< Register for soft resetting
#define AW9523_REG_INPUT0 0x00 ///< Register for reading input values
Expand All @@ -136,6 +145,9 @@ bool rg_i2c_write_byte(uint8_t addr, uint8_t reg, uint8_t value)
#define AW9523_REG_GCR 0x11 ///< Register for general configuration
#define AW9523_REG_LEDMODE 0x12 ///< Register for configuring const current

#endif


bool rg_i2c_gpio_init(void)
{
if (gpio_extender_initialized)
Expand All @@ -145,6 +157,16 @@ bool rg_i2c_gpio_init(void)
return false;

gpio_extender_initialized = true;
#if RG_I2C_DRIVER == 1
gpio_extender_address = 0x74;

rg_i2c_write_byte(gpio_extender_address, AW9523_REG_OUTPUT0, 0xFF);
rg_i2c_write_byte(gpio_extender_address, AW9523_REG_OUTPUT0 + 1, 0xFF);
rg_i2c_write_byte(gpio_extender_address, AW9523_REG_POLARITY0, 0x00);
rg_i2c_write_byte(gpio_extender_address, AW9523_REG_POLARITY0 + 1, 0x00);
rg_i2c_write_byte(gpio_extender_address, AW9523_REG_CONFIG0, 0xFF);
rg_i2c_write_byte(gpio_extender_address, AW9523_REG_CONFIG0 + 1, 0xFF);
#else
gpio_extender_address = 0x58;

rg_i2c_write_byte(gpio_extender_address, AW9523_REG_SOFTRESET, 0);
Expand All @@ -162,7 +184,7 @@ bool rg_i2c_gpio_init(void)
rg_i2c_write_byte(gpio_extender_address, AW9523_REG_LEDMODE, 0xFF);
rg_i2c_write_byte(gpio_extender_address, AW9523_REG_LEDMODE + 1, 0xFF);
rg_i2c_write_byte(gpio_extender_address, AW9523_REG_GCR, 1 << 4);

#endif
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions components/retro-go/rg_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ bool rg_input_read_gamepad_raw(uint32_t *out)
#if defined(RG_GAMEPAD_I2C_MAP)
uint32_t buttons = 0;
uint8_t data[5];
#if defined(RG_TARGET_QTPY_GAMER)
#if defined(RG_TARGET_QTPY_GAMER) || defined(RG_TARGET_BYTEBOI_REV1)
buttons = ~(rg_i2c_gpio_read_port(0) | rg_i2c_gpio_read_port(1) << 8);
#else
if (rg_i2c_read(0x20, -1, &data, 5))
Expand Down Expand Up @@ -281,7 +281,7 @@ void rg_input_init(void)
#if defined(RG_GAMEPAD_I2C_MAP)
RG_LOGI("Initializing I2C gamepad driver...");
rg_i2c_init();
#if defined(RG_TARGET_QTPY_GAMER)
#if defined(RG_TARGET_QTPY_GAMER) || defined(RG_TARGET_BYTEBOI_REV1)
rg_i2c_gpio_init();
#endif
UPDATE_GLOBAL_MAP(keymap_i2c);
Expand Down
102 changes: 102 additions & 0 deletions components/retro-go/targets/byteboi-rev1/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Target definition
#define RG_TARGET_NAME "BYTEBOI-REV1"

// Storage
#define RG_STORAGE_ROOT "/sd"
#define RG_STORAGE_SDSPI_HOST SPI2_HOST
#define RG_STORAGE_SDSPI_SPEED SDMMC_FREQ_DEFAULT
// #define RG_STORAGE_SDMMC_HOST SDMMC_HOST_SLOT_1
// #define RG_STORAGE_SDMMC_SPEED SDMMC_FREQ_DEFAULT
// #define RG_STORAGE_FLASH_PARTITION "vfs"

// Audio
#define RG_AUDIO_USE_INT_DAC 1 // 0 = Disable, 1 = GPIO25, 2 = GPIO26, 3 = Both
#define RG_AUDIO_USE_EXT_DAC 0 // 0 = Disable, 1 = Enable

// Video
#define RG_SCREEN_DRIVER 0 // 0 = ILI9341
#define RG_SCREEN_HOST SPI2_HOST
#define RG_SCREEN_SPEED SPI_MASTER_FREQ_40M
#define RG_SCREEN_BACKLIGHT 0
#define RG_SCREEN_WIDTH 320
#define RG_SCREEN_HEIGHT 240
#define RG_SCREEN_ROTATE 0
#define RG_SCREEN_MARGIN_TOP 0
#define RG_SCREEN_MARGIN_BOTTOM 0
#define RG_SCREEN_MARGIN_LEFT 0
#define RG_SCREEN_MARGIN_RIGHT 0
#define RG_SCREEN_INIT() \
ILI9341_CMD(0xEF, 0x03, 0x80, 0x02); \
ILI9341_CMD(0xCF, 0x00, 0xc1, 0x30); \
ILI9341_CMD(0xED, 0x64, 0x03, 0x12, 0x81); \
ILI9341_CMD(0xE8, 0x85, 0x00, 0x78); \
ILI9341_CMD(0xCB, 0x39, 0x2c, 0x00, 0x34, 0x02); \
ILI9341_CMD(0xF7, 0x20); \
ILI9341_CMD(0xEA, 0x00, 0x00); \
ILI9341_CMD(0xC0, 0x23); /* Power control //VRH[5:0] */ \
ILI9341_CMD(0xC1, 0x10); /* Power control //SAP[2:0];BT[3:0] */ \
ILI9341_CMD(0xC5, 0x3e,0x28); /* VCM control */ \
ILI9341_CMD(0xC7, 0x86); /* VCM control2 */ \
ILI9341_CMD(0x36, (0x20 | 0x80 | 0x08)); /* Memory Access Control */ \
ILI9341_CMD(0xB1, 0x00, 0x10); /* Frame Rate Control (1B=70, 1F=61, 10=119) */ \
ILI9341_CMD(0xB6, 0x08, 0xC2, 0x27); /* Display Function Control */ \
ILI9341_CMD(0xF6, 0x01, 0x30); \
ILI9341_CMD(0xF2, 0x00); /* 3Gamma Function Disable */ \
ILI9341_CMD(0x26, 0x01); /* Gamma curve selected */ \
ILI9341_CMD(0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00); \
ILI9341_CMD(0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F);



// Input
// Refer to rg_input.h to see all available RG_KEY_* and RG_GAMEPAD_*_MAP types
#define RG_GAMEPAD_I2C_MAP {\
{RG_KEY_UP, (1<<0)},\
{RG_KEY_RIGHT, (1<<2)},\
{RG_KEY_DOWN, (1<<3)},\
{RG_KEY_LEFT, (1<<1)},\
{RG_KEY_SELECT, (1<<4)},\
{RG_KEY_A, (1<<6)},\
{RG_KEY_B, (1<<5)},\
}
#define RG_GAMEPAD_VIRT_MAP {\
{RG_KEY_START, RG_KEY_A | RG_KEY_SELECT},\
{RG_KEY_MENU, RG_KEY_B | RG_KEY_SELECT},\
{RG_KEY_OPTION, RG_KEY_UP| RG_KEY_SELECT},\
}

// Battery
#define RG_BATTERY_DRIVER 1
#define RG_BATTERY_ADC_UNIT ADC_UNIT_1
#define RG_BATTERY_ADC_CHANNEL ADC_CHANNEL_0
#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)

// Status LED
//#define RG_GPIO_LED GPIO_NUM_14

// I2C BUS
#define RG_I2C_DRIVER 1 // 0 = AW9523, 1 = PCF9539
#define RG_GPIO_I2C_SDA GPIO_NUM_23
#define RG_GPIO_I2C_SCL GPIO_NUM_22

// SPI Display
#define RG_GPIO_LCD_MISO GPIO_NUM_5
#define RG_GPIO_LCD_MOSI GPIO_NUM_32
#define RG_GPIO_LCD_CLK GPIO_NUM_26
#define RG_GPIO_LCD_CS GPIO_NUM_33
#define RG_GPIO_LCD_DC GPIO_NUM_21
#define RG_GPIO_LCD_BCKL GPIO_NUM_12
#define RG_GPIO_LCD_RST GPIO_NUM_27

// SPI SD Card
#define RG_GPIO_SDSPI_MISO GPIO_NUM_5
#define RG_GPIO_SDSPI_MOSI GPIO_NUM_32
#define RG_GPIO_SDSPI_CLK GPIO_NUM_26
#define RG_GPIO_SDSPI_CS GPIO_NUM_2

// External I2S DAC
// #define RG_GPIO_SND_I2S_BCK GPIO_NUM_NC
// #define RG_GPIO_SND_I2S_WS GPIO_NUM_NC
// #define RG_GPIO_SND_I2S_DATA GPIO_NUM_NC
#define RG_GPIO_SND_AMP_ENABLE GPIO_NUM_13
28 changes: 28 additions & 0 deletions components/retro-go/targets/byteboi-rev1/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# CircuitMess Byteboi Rev. 1
- Status: completed
- Ref: https://circuitmess.com/products/byteboi-8-bit-game-console
- Ref: https://circuitmess.com/blogs/resources/byteboi-anatomy-guide-ch-1-pg-2

Button mapping:
- Start: Select + A
- Menu: Select + B
- Option: Select + Up

# Hardware info
- ESP-32-WROOM-32
- 4MB Flash
- 4MB LY68L6400 PSRAM
- RGB LEDs
- TC8002D amplifier
- PCA9539APW GPIO extender which connects the buttons and display backlight

# Known issues:
- Battery meter needs to be calibrated.
- No disk LED (yet). Theoretically, it should be possible to use the RGB-LED connected to the GPIO extender in the future.
- Only 4MB flash, so the full image does not fit and individual parts need to be selected. No OTA, no flashing of .fw file from SD-card.
- Really only supports the first hardware revision. The second revision has a different display and the buttons are connected differently, so it will definitely not work at all with this configuration!
- No display brightness support (the backlight is connected to the GPIO extender with no PWM support).
- Low sound quality due to use of internal 8-bit DAC (especially at low volumes).

# Images
![device.jpg](device.jpg)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions components/retro-go/targets/byteboi-rev1/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import os

os.environ["IDF_TARGET"] = "esp32"
#os.environ["FW_FORMAT"] = "odroid"
Loading

0 comments on commit 13cbae1

Please sign in to comment.