Prototype 4

This commit is contained in:
Renze Nicolai 2022-04-23 04:00:55 +02:00
parent fd5d4da353
commit e42aefed7f
17 changed files with 463 additions and 284 deletions

View file

@ -5,7 +5,6 @@ idf_component_register(
"appfs"
"bus-i2c"
"i2c-bno055"
"i2c-pca9555"
"pax-graphics"
"renze-graphics"
"sdcard"

View file

@ -8,41 +8,44 @@
static const char *TAG = "hardware";
static PCA9555 dev_pca9555 = {0};
static BNO055 dev_bno055 = {0};
static ILI9341 dev_ili9341 = {0};
static ICE40 dev_ice40 = {0};
static RP2040 dev_rp2040 = {0};
esp_err_t ice40_get_done_wrapper(bool* done) {
return pca9555_get_gpio_value(&dev_pca9555, PCA9555_PIN_FPGA_CDONE, done);
uint8_t buttons[2];
esp_err_t res = rp2040_read_buttons(&dev_rp2040, buttons);
if (res != ESP_OK) return res;
*done = !((buttons[0] >> 5) & 0x01);
printf("FPGA done is %u\n", *done);
return ESP_OK;
}
esp_err_t ice40_set_reset_wrapper(bool reset) {
return pca9555_set_gpio_value(&dev_pca9555, PCA9555_PIN_FPGA_RESET, reset);
printf("FPGA reset set to %u\n", reset);
esp_err_t res = rp2040_set_fpga(&dev_rp2040, reset);
vTaskDelay(100 / portTICK_PERIOD_MS);
return res;
}
void ili9341_set_lcd_mode(bool mode) {
ESP_LOGI(TAG, "LCD mode switch to %s", mode ? "FPGA" : "ESP32");
rp2040_set_lcd_mode(&dev_rp2040, (lcd_mode_t) mode);
esp_err_t res = gpio_set_level(GPIO_LCD_MODE, mode);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Setting LCD mode failed");
}
}
static esp_err_t _bus_init() {
esp_err_t res;
// System I2C bus
// I2C bus
res = i2c_init(I2C_BUS_SYS, GPIO_I2C_SYS_SDA, GPIO_I2C_SYS_SCL, I2C_SPEED_SYS, false, false);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Initializing system I2C bus failed");
return res;
}
// User I2C bus
res = i2c_init(I2C_BUS_EXT, GPIO_I2C_EXT_SDA, GPIO_I2C_EXT_SCL, I2C_SPEED_EXT, false, false);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Initializing user I2C bus failed");
return res;
}
// SPI bus
spi_bus_config_t busConfiguration = {0};
@ -63,7 +66,8 @@ static esp_err_t _bus_init() {
// Board init
esp_err_t board_init() {
esp_err_t board_init(bool* aLcdReady) {
if (aLcdReady != NULL) *aLcdReady = false;
esp_err_t res;
// Interrupts
@ -77,10 +81,36 @@ esp_err_t board_init() {
res = _bus_init();
if (res != ESP_OK) return res;
// LCD display
dev_ili9341.spi_bus = SPI_BUS;
dev_ili9341.pin_cs = GPIO_SPI_CS_LCD;
dev_ili9341.pin_dcx = GPIO_SPI_DC_LCD;
dev_ili9341.pin_reset = GPIO_LCD_RESET;
dev_ili9341.rotation = 1;
dev_ili9341.color_mode = true; // Blue and red channels are swapped
dev_ili9341.spi_speed = 60000000; // 60MHz
dev_ili9341.spi_max_transfer_size = SPI_MAX_TRANSFER_SIZE;
dev_ili9341.callback = ili9341_set_lcd_mode; // Callback for changing LCD mode between ESP32 and FPGA
res = gpio_set_direction(GPIO_LCD_MODE, GPIO_MODE_OUTPUT);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Initializing LCD mode GPIO failed");
return res;
}
res = ili9341_init(&dev_ili9341);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Initializing LCD failed");
return res;
}
if (aLcdReady != NULL) *aLcdReady = true;
// RP2040 co-processor
dev_rp2040.i2c_bus = I2C_BUS_SYS;
dev_rp2040.i2c_address = RP2040_ADDR;
dev_rp2040.pin_interrupt = GPIO_INT_RP2040;
dev_rp2040.queue = xQueueCreate(8, sizeof(rp2040_input_message_t));
res = rp2040_init(&dev_rp2040);
if (res != ESP_OK) {
@ -88,45 +118,13 @@ esp_err_t board_init() {
return res;
}
// PCA9555 IO expander on system I2C bus
res = pca9555_init(&dev_pca9555, I2C_BUS_SYS, PCA9555_ADDR, GPIO_INT_PCA9555);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Initializing PCA9555 failed");
return res;
}
res = pca9555_set_gpio_direction(&dev_pca9555, PCA9555_PIN_FPGA_RESET, true);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Setting the FPGA reset pin on the PCA9555 to output failed");
return res;
}
res = pca9555_set_gpio_value(&dev_pca9555, PCA9555_PIN_FPGA_RESET, false);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Setting the FPGA reset pin on the PCA9555 to low failed");
return res;
}
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_START, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_SELECT, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_MENU, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_HOME, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_JOY_LEFT, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_JOY_PRESS, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_JOY_DOWN, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_JOY_UP, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_JOY_RIGHT, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_BACK, true);
pca9555_set_gpio_polarity(&dev_pca9555, PCA9555_PIN_BTN_ACCEPT, true);
dev_pca9555.pin_state = 0;
// FPGA
dev_ice40.spi_bus = SPI_BUS;
dev_ice40.pin_cs = GPIO_SPI_CS_FPGA;
dev_ice40.pin_done = -1;
dev_ice40.pin_reset = -1;
dev_ice40.pin_int = GPIO_INT_FPGA;
dev_ice40.spi_speed = 23000000; // 23MHz
dev_ice40.spi_speed = 10000000; // 10 MHz //23000000; // 23MHz
dev_ice40.spi_max_transfer_size = SPI_MAX_TRANSFER_SIZE;
dev_ice40.get_done = ice40_get_done_wrapper;
dev_ice40.set_reset = ice40_set_reset_wrapper;
@ -137,23 +135,6 @@ esp_err_t board_init() {
return res;
}
// LCD display
dev_ili9341.spi_bus = SPI_BUS;
dev_ili9341.pin_cs = GPIO_SPI_CS_LCD;
dev_ili9341.pin_dcx = GPIO_SPI_DC_LCD;
dev_ili9341.pin_reset = -1;
dev_ili9341.rotation = 1;
dev_ili9341.color_mode = true; // Blue and red channels are swapped
dev_ili9341.spi_speed = 60000000; // 60MHz
dev_ili9341.spi_max_transfer_size = SPI_MAX_TRANSFER_SIZE;
dev_ili9341.callback = ili9341_set_lcd_mode; // Callback for changing LCD mode between ESP32 and FPGA
res = ili9341_init(&dev_ili9341);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Initializing LCD failed");
return res;
}
// BNO055 sensor on system I2C bus
res = bno055_init(&dev_bno055, I2C_BUS_SYS, BNO055_ADDR, GPIO_INT_BNO055, true);
@ -164,10 +145,6 @@ esp_err_t board_init() {
return res;
}
PCA9555* get_pca9555() {
return &dev_pca9555;
}
BNO055* get_bno055() {
return &dev_bno055;
}

View file

@ -3,52 +3,35 @@
#include <sdkconfig.h>
#include <esp_err.h>
#include <driver/spi_master.h>
#include "pca9555.h"
#include "bno055.h"
#include "ili9341.h"
#include "ice40.h"
#include "rp2040.h"
// Interrupts
#define GPIO_INT_RP2040 0
#define GPIO_INT_PCA9555 34
#define GPIO_INT_RP2040 34
#define GPIO_INT_BNO055 36
#define GPIO_INT_FPGA 39
// SD card
#define SD_PWR 5
#define SD_PWR 19 // Also LED power
#define SD_D0 2
#define SD_CLK 14
#define SD_CMD 15
// LEDs
#define GPIO_LED_DATA 5
// I2S audio
#define GPIO_I2S_CLK 14
#define GPIO_I2S_DATA 13
#define GPIO_I2S_LR 4
// System I2C bus
// I2C bus
#define GPIO_I2C_SYS_SCL 21
#define GPIO_I2C_SYS_SDA 22
#define I2C_BUS_SYS 0
#define I2C_SPEED_SYS 20000 // 20 kHz
// PCA9555 IO expander
#define PCA9555_ADDR 0x26
#define PCA9555_PIN_RP2040_RESET 0
#define PCA9555_PIN_RP2040_BOOTSEL 1
#define PCA9555_PIN_FPGA_RESET 2
#define PCA9555_PIN_FPGA_CDONE 3
#define PCA9555_PIN_BTN_START 5
#define PCA9555_PIN_BTN_SELECT 6
#define PCA9555_PIN_BTN_MENU 7
#define PCA9555_PIN_BTN_HOME 8
#define PCA9555_PIN_BTN_JOY_LEFT 9
#define PCA9555_PIN_BTN_JOY_PRESS 10
#define PCA9555_PIN_BTN_JOY_DOWN 11
#define PCA9555_PIN_BTN_JOY_UP 12
#define PCA9555_PIN_BTN_JOY_RIGHT 13
#define PCA9555_PIN_BTN_BACK 14
#define PCA9555_PIN_BTN_ACCEPT 15
#define I2C_SPEED_SYS 8000 // 8 kHz //20000 // 20 kHz
// RP2040 co-processor
#define RP2040_ADDR 0x17
@ -56,27 +39,24 @@
// BNO055 sensor
#define BNO055_ADDR 0x28
// User I2C bus
#define GPIO_I2C_EXT_SCL 25
#define GPIO_I2C_EXT_SDA 26
#define I2C_BUS_EXT 1
#define I2C_SPEED_EXT 100000 // 100 kHz
// SPI bus
#define GPIO_SPI_CLK 18
#define GPIO_SPI_MOSI 23
#define GPIO_SPI_MISO 35
#define GPIO_SPI_CS_RP2040 19
#define GPIO_SPI_CS_FPGA 27
#define GPIO_SPI_CS_LCD 32
#define GPIO_SPI_DC_LCD 33
#define SPI_BUS VSPI_HOST
#define SPI_MAX_TRANSFER_SIZE 4094
#define SPI_DMA_CHANNEL 2
esp_err_t board_init();
// LCD display
#define GPIO_LCD_RESET 25
#define GPIO_LCD_MODE 26
#define GPIO_SPI_CS_LCD 32
#define GPIO_SPI_DC_LCD 33
esp_err_t board_init(bool* aLcdReady);
PCA9555* get_pca9555();
BNO055* get_bno055();
ILI9341* get_ili9341();
ICE40* get_ice40();

View file

@ -11,12 +11,52 @@
static const char *TAG = "RP2040";
inline void _send_input_change(RP2040* device, uint8_t input, bool value) {
rp2040_input_message_t message;
message.input = input;
message.state = value;
xQueueSend(device->queue, &message, portMAX_DELAY);
}
void rp2040_intr_task(void *arg) {
RP2040* device = (RP2040*) arg;
uint32_t state;
while (1) {
if (xSemaphoreTake(device->_intr_trigger, portMAX_DELAY)) {
esp_err_t res = i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_INPUT1, (uint8_t*) &state, 4);
if (res != ESP_OK) {
ESP_LOGE(TAG, "RP2040 interrupt task failed to read from RP2040");
continue;
}
ESP_LOGW(TAG, "RP2040 input state %08x", state);
uint16_t interrupt = state >> 16;
uint16_t values = state & 0xFFFF;
for (uint8_t index = 0; index < 16; index++) {
if ((interrupt >> index) & 0x01) {
_send_input_change(device, index, (values >> index) & 0x01);
}
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
}
void rp2040_intr_handler(void *arg) {
/* in interrupt handler context */
RP2040* device = (RP2040*) arg;
xSemaphoreGiveFromISR(device->_intr_trigger, NULL);
}
esp_err_t rp2040_init(RP2040* device) {
esp_err_t res;
uint8_t firmware_version;
res = rp2040_get_firmware_version(device, &firmware_version);
if (res != ESP_OK) return res;
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to read firmware version");
return res;
}
if (firmware_version != 1) {
ESP_LOGE(TAG, "Unsupported RP2040 firmware version (%u) found", firmware_version);
@ -24,10 +64,40 @@ esp_err_t rp2040_init(RP2040* device) {
}
res = i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_GPIO_DIR, &device->_gpio_direction, 1);
if (res != ESP_OK) return res;
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to read GPIO direction");
return res;
}
res = i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_GPIO_OUT, &device->_gpio_value, 1);
if (res != ESP_OK) return res;
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to read GPIO state");
return res;
}
//Create interrupt trigger
device->_intr_trigger = xSemaphoreCreateBinary();
if (device->_intr_trigger == NULL) return ESP_ERR_NO_MEM;
//Attach interrupt to interrupt pin
if (device->pin_interrupt >= 0) {
res = gpio_isr_handler_add(device->pin_interrupt, rp2040_intr_handler, (void*) device);
if (res != ESP_OK) return res;
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_NEGEDGE,
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = 1LL << device->pin_interrupt,
.pull_down_en = 0,
.pull_up_en = 1,
};
res = gpio_config(&io_conf);
if (res != ESP_OK) return res;
xTaskCreate(&rp2040_intr_task, "RP2040 interrupt", 4096, (void*) device, 10, &device->_intr_task_handle);
xSemaphoreGive(device->_intr_trigger);
}
return ESP_OK;
}
@ -68,22 +138,6 @@ esp_err_t rp2040_set_gpio_value(RP2040* device, uint8_t gpio, bool value) {
return i2c_write_reg_n(device->i2c_bus, device->i2c_address, RP2040_REG_GPIO_OUT, &device->_gpio_value, 1);
}
esp_err_t rp2040_get_lcd_mode(RP2040* device, lcd_mode_t* mode) {
return i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_LCD_MODE, (uint8_t*) mode, 1);
}
esp_err_t rp2040_set_lcd_mode(RP2040* device, lcd_mode_t mode) {
esp_err_t res;
lcd_mode_t verification;
do {
res = i2c_write_reg_n(device->i2c_bus, device->i2c_address, RP2040_REG_LCD_MODE, (uint8_t*) &mode, 1);
if (res != ESP_OK) return res;
res = rp2040_get_lcd_mode(device, &verification);
if (res != ESP_OK) return res;
} while (verification != mode);
return res;
}
esp_err_t rp2040_get_lcd_backlight(RP2040* device, uint8_t* brightness) {
return i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_LCD_BACKLIGHT, brightness, 1);
}
@ -92,44 +146,11 @@ esp_err_t rp2040_set_lcd_backlight(RP2040* device, uint8_t brightness) {
return i2c_write_reg_n(device->i2c_bus, device->i2c_address, RP2040_REG_LCD_BACKLIGHT, &brightness, 1);
}
esp_err_t rp2040_get_led_mode(RP2040* device, bool* enabled, bool* automatic_flush) {
uint8_t value;
esp_err_t res = i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_LED_MODE, &value, 1);
if (res != ESP_OK) return res;
*enabled = (value >> 0) & 0x01;
*automatic_flush = (value >> 1) & 0x01;
return ESP_OK;
esp_err_t rp2040_set_fpga(RP2040* device, bool enabled) {
uint8_t value = enabled ? 0x01 : 0x00;
return i2c_write_reg_n(device->i2c_bus, device->i2c_address, RP2040_REG_FPGA, &value, 1);
}
esp_err_t rp2040_set_led_mode(RP2040* device, bool enabled, bool automatic_flush) {
uint8_t value = enabled | (automatic_flush << 1);
return i2c_write_reg_n(device->i2c_bus, device->i2c_address, RP2040_REG_LED_MODE, &value, 1);
}
esp_err_t rp2040_get_led_value(RP2040* device, uint8_t led, uint8_t* red, uint8_t* green, uint8_t* blue) {
if (led > 5) return ESP_ERR_NOT_FOUND;
uint8_t value[3];
esp_err_t res = i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_LED_R0 + (led * 3), value, 3);
if (res != ESP_OK) return res;
*red = value[0];
*green = value[1];
*blue = value[2];
return ESP_OK;
}
esp_err_t rp2040_set_led_value(RP2040* device, uint8_t led, uint8_t red, uint8_t green, uint8_t blue) {
if (led > 5) return ESP_ERR_NOT_FOUND;
uint8_t value[3];
value[0] = red;
value[1] = green;
value[2] = blue;
return i2c_write_reg_n(device->i2c_bus, device->i2c_address, RP2040_REG_LED_R0 + (led * 3), value, 3);
}
esp_err_t rp2040_get_led_values(RP2040* device, uint8_t* buffer) {
return i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_LED_R0, buffer, 15);
}
esp_err_t rp2040_set_led_values(RP2040* device, uint8_t* buffer) {
return i2c_write_reg_n(device->i2c_bus, device->i2c_address, RP2040_REG_LED_R0, buffer, 15);
esp_err_t rp2040_read_buttons(RP2040* device, uint16_t* value) {
return i2c_read_reg(device->i2c_bus, device->i2c_address, RP2040_REG_INPUT1, (uint8_t*) value, 2);
}

View file

@ -6,36 +6,106 @@
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <freertos/queue.h>
enum {
RP2040_REG_FW_VER = 0,
RP2040_REG_GPIO_DIR,
RP2040_REG_GPIO_IN,
RP2040_REG_GPIO_OUT,
RP2040_REG_LCD_MODE,
RP2040_REG_LCD_BACKLIGHT,
RP2040_REG_LED_MODE,
RP2040_REG_LED_R0,
RP2040_REG_LED_G0,
RP2040_REG_LED_B0,
RP2040_REG_LED_R1,
RP2040_REG_LED_G1,
RP2040_REG_LED_B1,
RP2040_REG_LED_R2,
RP2040_REG_LED_G2,
RP2040_REG_LED_B2,
RP2040_REG_LED_R3,
RP2040_REG_LED_G3,
RP2040_REG_LED_B3,
RP2040_REG_LED_R4,
RP2040_REG_LED_G4,
RP2040_REG_LED_B4,
RP2040_REG_FPGA,
RP2040_REG_INPUT1,
RP2040_REG_INPUT2,
RP2040_REG_INTERRUPT1,
RP2040_REG_INTERRUPT2,
RP2040_REG_ADC_TRIGGER,
RP2040_REG_ADC_VALUE_VUSB1,
RP2040_REG_ADC_VALUE_VUSB2,
RP2040_REG_ADC_VALUE_VBAT1,
RP2040_REG_ADC_VALUE_VBAT2,
RP2040_REG_USB,
RP2040_REG_SCRATCH0, // Used by the ESP32 to store boot parameters, can also be read and written to from WebUSB
RP2040_REG_SCRATCH1,
RP2040_REG_SCRATCH2,
RP2040_REG_SCRATCH3,
RP2040_REG_SCRATCH4,
RP2040_REG_SCRATCH5,
RP2040_REG_SCRATCH6,
RP2040_REG_SCRATCH7,
RP2040_REG_SCRATCH8,
RP2040_REG_SCRATCH9,
RP2040_REG_SCRATCH10,
RP2040_REG_SCRATCH11,
RP2040_REG_SCRATCH12,
RP2040_REG_SCRATCH13,
RP2040_REG_SCRATCH14,
RP2040_REG_SCRATCH15,
RP2040_REG_SCRATCH16,
RP2040_REG_SCRATCH17,
RP2040_REG_SCRATCH18,
RP2040_REG_SCRATCH19,
RP2040_REG_SCRATCH20,
RP2040_REG_SCRATCH21,
RP2040_REG_SCRATCH22,
RP2040_REG_SCRATCH23,
RP2040_REG_SCRATCH24,
RP2040_REG_SCRATCH25,
RP2040_REG_SCRATCH26,
RP2040_REG_SCRATCH27,
RP2040_REG_SCRATCH28,
RP2040_REG_SCRATCH29,
RP2040_REG_SCRATCH30,
RP2040_REG_SCRATCH31,
RP2040_REG_SCRATCH32,
RP2040_REG_SCRATCH33,
RP2040_REG_SCRATCH34,
RP2040_REG_SCRATCH35,
RP2040_REG_SCRATCH36,
RP2040_REG_SCRATCH37,
RP2040_REG_SCRATCH38,
RP2040_REG_SCRATCH39,
RP2040_REG_SCRATCH40,
RP2040_REG_SCRATCH41,
RP2040_REG_SCRATCH42,
RP2040_REG_SCRATCH43,
RP2040_REG_SCRATCH44,
RP2040_REG_SCRATCH45,
RP2040_REG_SCRATCH46,
RP2040_REG_SCRATCH47,
RP2040_REG_SCRATCH48,
RP2040_REG_SCRATCH49,
RP2040_REG_SCRATCH50,
RP2040_REG_SCRATCH51,
RP2040_REG_SCRATCH52,
RP2040_REG_SCRATCH53,
RP2040_REG_SCRATCH54,
RP2040_REG_SCRATCH55,
RP2040_REG_SCRATCH56,
RP2040_REG_SCRATCH57,
RP2040_REG_SCRATCH58,
RP2040_REG_SCRATCH59,
RP2040_REG_SCRATCH60,
RP2040_REG_SCRATCH61,
RP2040_REG_SCRATCH62,
RP2040_REG_SCRATCH63
};
typedef enum {
LCD_MODE_SPI = 0,
LCD_MODE_PARALLEL = 1
} lcd_mode_t;
enum {
RP2040_INPUT_BUTTON_HOME = 0,
RP2040_INPUT_BUTTON_MENU,
RP2040_INPUT_BUTTON_START,
RP2040_INPUT_BUTTON_ACCEPT,
RP2040_INPUT_BUTTON_BACK,
RP2040_INPUT_FPGA_CDONE,
RP2040_INPUT_BATTERY_CHARGING,
RP2040_INPUT_BUTTON_SELECT,
RP2040_INPUT_JOYSTICK_LEFT,
RP2040_INPUT_JOYSTICK_PRESS,
RP2040_INPUT_JOYSTICK_DOWN,
RP2040_INPUT_JOYSTICK_UP,
RP2040_INPUT_JOYSTICK_RIGHT
};
typedef void (*rp2040_intr_t)();
@ -43,14 +113,19 @@ typedef struct {
int i2c_bus;
int i2c_address;
int pin_interrupt;
xQueueHandle queue;
rp2040_intr_t _intr_handler;
TaskHandle_t _intr_task_handle;
xSemaphoreHandle _intr_trigger;
xSemaphoreHandle _mux;
uint8_t _gpio_direction;
uint8_t _gpio_value;
} RP2040;
typedef struct _rp2040_input_message {
uint8_t input;
bool state;
} rp2040_input_message_t;
esp_err_t rp2040_init(RP2040* device);
esp_err_t rp2040_get_firmware_version(RP2040* device, uint8_t* version);
@ -61,17 +136,9 @@ esp_err_t rp2040_set_gpio_dir(RP2040* device, uint8_t gpio, bool direction);
esp_err_t rp2040_get_gpio_value(RP2040* device, uint8_t gpio, bool* value);
esp_err_t rp2040_set_gpio_value(RP2040* device, uint8_t gpio, bool value);
esp_err_t rp2040_get_lcd_mode(RP2040* device, lcd_mode_t* mode);
esp_err_t rp2040_set_lcd_mode(RP2040* device, lcd_mode_t mode);
esp_err_t rp2040_get_lcd_backlight(RP2040* device, uint8_t* brightness);
esp_err_t rp2040_set_lcd_backlight(RP2040* device, uint8_t brightness);
esp_err_t rp2040_get_led_mode(RP2040* device, bool* enabled, bool* automatic_flush);
esp_err_t rp2040_set_led_mode(RP2040* device, bool enabled, bool automatic_flush);
esp_err_t rp2040_set_fpga(RP2040* device, bool enabled);
esp_err_t rp2040_get_led_value(RP2040* device, uint8_t led, uint8_t* red, uint8_t* green, uint8_t* blue);
esp_err_t rp2040_set_led_value(RP2040* device, uint8_t led, uint8_t red, uint8_t green, uint8_t blue);
esp_err_t rp2040_get_led_values(RP2040* device, uint8_t* buffer); // Expects a buffer that can fit 15 bytes (R, G, B * 5 LEDs)
esp_err_t rp2040_set_led_values(RP2040* device, uint8_t* buffer); // Expects a buffer that can contains 15 bytes (R, G, B * 5 LEDs)
esp_err_t rp2040_read_buttons(RP2040* device, uint16_t* value);

@ -1 +1 @@
Subproject commit 1c2b5241dd00d2fddb45d20f3c4a426fa379fa95
Subproject commit 4263f4f60d3d410ebf2ae88a7a26b23bd08eedde

@ -1 +1 @@
Subproject commit c83ac9b467d73595ebd6f15f6f444068fe2d3198
Subproject commit 4bc90991a83df4e733705cc7c89906c64756e0f2

View file

@ -0,0 +1,4 @@
idf_component_register(
SRCS "ws2812.c"
INCLUDE_DIRS include
)

View file

@ -0,0 +1,35 @@
#pragma once
#include <stdint.h>
#include <esp_err.h>
#include <driver/gpio.h>
__BEGIN_DECLS
/**
* Initialize the leds driver. (configure SPI bus and GPIO pins)
* @return ESP_OK on success; any other value indicates an error
*/
extern esp_err_t ws2812_init(gpio_num_t aGpioPin);
/**
* Enable power to the leds bar.
* @return ESP_OK on success; any other value indicates an error
*/
extern esp_err_t ws2812_enable(int gpio_pin);
/**
* Disable power to the leds bar.
* @return ESP_OK on success; any other value indicates an error
*/
extern esp_err_t ws2812_disable(void);
/**
* Send color-data to the leds bus.
* @param data the data-bytes to send on the bus.
* @param len the data-length.
* @return ESP_OK on success; any other value indicates an error
*/
extern esp_err_t ws2812_send_data(uint8_t *data, int len);
__END_DECLS

124
components/ws2812/ws2812.c Normal file
View file

@ -0,0 +1,124 @@
//This driver uses the ESP32 RMT peripheral to drive "Neopixel" compatible LEDs
//The usage of the RMT peripheral has been implemented using work by JSchaenzie:
//you can find his work at https://github.com/JSchaenzle/ESP32-NeoPixel-WS2812-RMT
#include <sdkconfig.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_err.h>
#include <esp_log.h>
#include <driver/gpio.h>
#include <driver/rmt.h>
static const char *TAG = "ws2812";
#define WS2812_RMT_CHANNEL RMT_CHANNEL_0
#define T0H 14 // 0 bit high time
#define T1H 52 // 1 bit high time
#define TL 52 // low time for either bit
static bool gActive = false;
rmt_item32_t* gBuffer = NULL;
int gBufferLength = 0;
gpio_num_t gPin;
esp_err_t ws2812_init(gpio_num_t aGpioPin) {
if (gActive) return ESP_OK;
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1LL << aGpioPin,
.pull_down_en = 0,
.pull_up_en = 0,
};
esp_err_t res = gpio_config(&io_conf);
if (res != ESP_OK) return res;
rmt_config_t config;
config.rmt_mode = RMT_MODE_TX;
config.channel = WS2812_RMT_CHANNEL;
config.gpio_num = aGpioPin;
config.mem_block_num = 3;
config.tx_config.loop_en = false;
config.tx_config.carrier_en = false;
config.tx_config.idle_output_en = true;
config.tx_config.idle_level = 0;
config.clk_div = 2;
res = rmt_config(&config);
if (res != ESP_OK) return res;
res = rmt_driver_install(config.channel, 0, 0);
if (res != ESP_OK) return res;
gActive = true;
gPin = aGpioPin;
return ESP_OK;
}
esp_err_t ws2812_deinit(void) {
if (!gActive) return ESP_OK;
esp_err_t res = rmt_driver_uninstall(WS2812_RMT_CHANNEL);
if (res != ESP_OK) return res;
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = 1LL << gPin,
.pull_down_en = 0,
.pull_up_en = 0,
};
res = gpio_config(&io_conf);
if (res != ESP_OK) return res;
gActive = false;
return ESP_OK;
}
esp_err_t ws2812_prepare_data(uint8_t *data, int len)
{
if (gBuffer != NULL) return ESP_FAIL;
gBuffer = calloc(len * 8, sizeof(rmt_item32_t));
if (gBuffer == NULL) return ESP_FAIL;
gBufferLength = len * 8;
for (uint32_t pos = 0; pos < len; pos++) {
uint32_t mask = 1 << 7;
for (uint8_t i = 0; i < 8; i++) {
bool bit = data[pos] & mask;
gBuffer[pos*8 + i] = bit ?
(rmt_item32_t){{{T1H, 1, TL, 0}}} :
(rmt_item32_t){{{T0H, 1, TL, 0}}};
mask >>= 1;
}
}
return ESP_OK;
}
esp_err_t ws2812_free_data() {
if (!gBuffer) return ESP_FAIL;
free(gBuffer);
gBuffer = NULL;
gBufferLength = 0;
return ESP_OK;
}
esp_err_t ws2812_send_data(uint8_t *data, int len)
{
if (!gActive) return ESP_FAIL;
esp_err_t res = ws2812_prepare_data(data, len);
if (res != ESP_OK) return res;
res = rmt_write_items(WS2812_RMT_CHANNEL, gBuffer, gBufferLength, false);
if (res != ESP_OK) {
ws2812_free_data();
return res;
}
res = rmt_wait_tx_done(WS2812_RMT_CHANNEL, portMAX_DELAY);
if (res != ESP_OK) {
ws2812_free_data();
return res;
}
res = ws2812_free_data();
return res;
}

View file

@ -1,4 +1,4 @@
idf_component_register(
SRCS "main.c" "menu.c" "fpga_test.c" "pax_keyboard.c" "button_wrapper.c" "system_wrapper.c" "appfs_wrapper.c" "graphics_wrapper.c" "settings.c" "wifi_connection.c"
SRCS "main.c" "menu.c" "fpga_test.c" "pax_keyboard.c" "system_wrapper.c" "appfs_wrapper.c" "graphics_wrapper.c" "settings.c" "wifi_connection.c"
INCLUDE_DIRS "." "include"
)

View file

@ -1,25 +0,0 @@
#include "button_wrapper.h"
xQueueHandle queue;
void button_handler(uint8_t pin, bool value) {
button_message_t message;
message.button = pin;
message.state = value;
xQueueSend(queue, &message, portMAX_DELAY);
}
void button_init(PCA9555* aPca9555, xQueueHandle aQueue) {
queue = aQueue;
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_START, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_SELECT, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_MENU, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_HOME, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_LEFT, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_PRESS, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_DOWN, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_UP, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_JOY_RIGHT, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_BACK, button_handler);
pca9555_set_interrupt_handler(aPca9555, PCA9555_PIN_BTN_ACCEPT, button_handler);
}

View file

@ -7,8 +7,8 @@
#include "fpga.h"
#include "ili9341.h"
#include "ice40.h"
#include "rp2040.h"
#include "hardware.h"
#include "button_wrapper.h"
static const char *TAG = "fpga_test";
@ -139,12 +139,12 @@ void fpga_test(ILI9341* ili9341, ICE40* ice40, xQueueHandle buttonQueue) {
bool waitForChoice = true;
while (waitForChoice) {
button_message_t buttonMessage = {0};
rp2040_input_message_t buttonMessage = {0};
printf("Waiting for button press...\n");
if (xQueueReceive(buttonQueue, &buttonMessage, portMAX_DELAY) == pdTRUE) {
printf("Button: %u, %u\n", buttonMessage.button, buttonMessage.state);
printf("Button: %u, %u\n", buttonMessage.input, buttonMessage.state);
if (buttonMessage.state) {
switch(buttonMessage.button) {
/*switch(buttonMessage.button) {
case PCA9555_PIN_BTN_HOME:
case PCA9555_PIN_BTN_MENU:
case PCA9555_PIN_BTN_BACK:
@ -156,7 +156,7 @@ void fpga_test(ILI9341* ili9341, ICE40* ice40, xQueueHandle buttonQueue) {
break;
default:
break;
}
}*/
}
}
}

View file

@ -2,7 +2,7 @@
#include "graphics_wrapper.h"
#include "hardware.h"
#include "pax_keyboard.h"
#include "button_wrapper.h"
#include "rp2040.h"
void render_message(pax_buf_t *aBuffer, char* message, float aPosX, float aPosY, float aWidth, float aHeight) {
pax_col_t fgColor = 0xFFFF0000;
@ -70,68 +70,68 @@ bool keyboard(xQueueHandle buttonQueue, pax_buf_t* aBuffer, ILI9341* ili9341, ui
bool running = true;
while (running) {
button_message_t buttonMessage = {0};
rp2040_input_message_t buttonMessage = {0};
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
uint8_t pin = buttonMessage.button;
uint8_t pin = buttonMessage.input;
bool value = buttonMessage.state;
switch(pin) {
case PCA9555_PIN_BTN_JOY_DOWN:
case RP2040_INPUT_JOYSTICK_DOWN:
if (value) {
pkb_press(&kb_ctx, PKB_DOWN);
} else {
pkb_release(&kb_ctx, PKB_DOWN);
}
break;
case PCA9555_PIN_BTN_JOY_UP:
case RP2040_INPUT_JOYSTICK_UP:
if (value) {
pkb_press(&kb_ctx, PKB_UP);
} else {
pkb_release(&kb_ctx, PKB_UP);
}
break;
case PCA9555_PIN_BTN_JOY_LEFT:
case RP2040_INPUT_JOYSTICK_LEFT:
if (value) {
pkb_press(&kb_ctx, PKB_LEFT);
} else {
pkb_release(&kb_ctx, PKB_LEFT);
}
break;
case PCA9555_PIN_BTN_JOY_RIGHT:
case RP2040_INPUT_JOYSTICK_RIGHT:
if (value) {
pkb_press(&kb_ctx, PKB_RIGHT);
} else {
pkb_release(&kb_ctx, PKB_RIGHT);
}
break;
case PCA9555_PIN_BTN_JOY_PRESS:
case RP2040_INPUT_JOYSTICK_PRESS:
if (value) {
pkb_press(&kb_ctx, PKB_SHIFT);
} else {
pkb_release(&kb_ctx, PKB_SHIFT);
}
break;
case PCA9555_PIN_BTN_ACCEPT:
case RP2040_INPUT_BUTTON_ACCEPT:
if (value) {
pkb_press(&kb_ctx, PKB_CHARSELECT);
} else {
pkb_release(&kb_ctx, PKB_CHARSELECT);
}
break;
case PCA9555_PIN_BTN_BACK:
case RP2040_INPUT_BUTTON_BACK:
if (value) {
pkb_press(&kb_ctx, PKB_DELETE_BEFORE);
} else {
pkb_release(&kb_ctx, PKB_DELETE_BEFORE);
}
break;
case PCA9555_PIN_BTN_SELECT:
case RP2040_INPUT_BUTTON_SELECT:
if (value) {
pkb_press(&kb_ctx, PKB_MODESELECT);
} else {
pkb_release(&kb_ctx, PKB_MODESELECT);
}
break;
case PCA9555_PIN_BTN_HOME:
case RP2040_INPUT_BUTTON_HOME:
if (value) {
running = false;
}

View file

@ -1,15 +0,0 @@
#pragma once
#include <stdio.h>
#include <string.h>
#include <sdkconfig.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include "hardware.h"
typedef struct _button_message {
uint8_t button;
bool state;
} button_message_t;
void button_init(PCA9555* aPca9555, xQueueHandle aQueue);

View file

@ -21,13 +21,14 @@
#include "fpga_test.h"
#include "menu.h"
#include "button_wrapper.h"
#include "system_wrapper.h"
#include "graphics_wrapper.h"
#include "appfs_wrapper.h"
#include "settings.h"
#include "wifi_connection.h"
#include "ws2812.h"
static const char *TAG = "main";
typedef enum action {
@ -85,24 +86,24 @@ void menu_launcher(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341* ili
menu_args_t* menuArgs = NULL;
while (1) {
button_message_t buttonMessage = {0};
rp2040_input_message_t buttonMessage = {0};
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
uint8_t pin = buttonMessage.button;
uint8_t pin = buttonMessage.input;
bool value = buttonMessage.state;
switch(pin) {
case PCA9555_PIN_BTN_JOY_DOWN:
case RP2040_INPUT_JOYSTICK_DOWN:
if (value) {
menu_navigate_next(menu);
render = true;
}
break;
case PCA9555_PIN_BTN_JOY_UP:
case RP2040_INPUT_JOYSTICK_UP:
if (value) {
menu_navigate_previous(menu);
render = true;
}
break;
case PCA9555_PIN_BTN_ACCEPT:
case RP2040_INPUT_BUTTON_ACCEPT:
if (value) {
menuArgs = menu_get_callback_args(menu, menu_get_position(menu));
}
@ -155,24 +156,24 @@ void menu_wifi_settings(xQueueHandle buttonQueue, pax_buf_t* pax_buffer, ILI9341
menu_args_t* menuArgs = NULL;
while (1) {
button_message_t buttonMessage = {0};
rp2040_input_message_t buttonMessage = {0};
if (xQueueReceive(buttonQueue, &buttonMessage, 16 / portTICK_PERIOD_MS) == pdTRUE) {
uint8_t pin = buttonMessage.button;
uint8_t pin = buttonMessage.input;
bool value = buttonMessage.state;
switch(pin) {
case PCA9555_PIN_BTN_JOY_DOWN:
case RP2040_INPUT_JOYSTICK_DOWN:
if (value) {
menu_navigate_next(menu);
render = true;
}
break;
case PCA9555_PIN_BTN_JOY_UP:
case RP2040_INPUT_JOYSTICK_UP:
if (value) {
menu_navigate_previous(menu);
render = true;
}
break;
case PCA9555_PIN_BTN_ACCEPT:
case RP2040_INPUT_BUTTON_ACCEPT:
if (value) {
menuArgs = menu_get_callback_args(menu, menu_get_position(menu));
}
@ -204,15 +205,6 @@ void app_main(void) {
esp_err_t res;
/* Initialize memory */
xQueueHandle buttonQueue = xQueueCreate(10, sizeof(button_message_t));
if (buttonQueue == NULL) {
ESP_LOGE(TAG, "Failed to allocate queue");
restart();
}
uint8_t* framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT);
if (framebuffer == NULL) {
ESP_LOGE(TAG, "Failed to allocate framebuffer");
@ -232,9 +224,14 @@ void app_main(void) {
/* Initialize hardware */
res = board_init();
bool lcdReady = false;
res = board_init(&lcdReady);
if (res != ESP_OK) {
if (lcdReady) {
ILI9341* ili9341 = get_ili9341();
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "HARDWARE ERROR");
}
printf("Failed to initialize hardware!\n");
restart();
}
@ -243,13 +240,7 @@ void app_main(void) {
ICE40* ice40 = get_ice40();
BNO055* bno055 = get_bno055();
RP2040* rp2040 = get_rp2040();
PCA9555* pca9555 = get_pca9555();
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "Button init...");
button_init(pca9555, buttonQueue);
rp2040_set_led_mode(rp2040, true, true);
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "AppFS init...");
res = appfs_init();
if (res != ESP_OK) {
@ -275,16 +266,38 @@ void app_main(void) {
if (sdcard_ready) {
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "SD card mounted");
}
ws2812_init(GPIO_LED_DATA);
uint8_t ledBuffer[15] = {50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
ws2812_send_data(ledBuffer, sizeof(ledBuffer));
//fpga_test(ili9341, ice40, rp2040->queue);
/*while (true) {
uint16_t state;
rp2040_read_buttons(rp2040, &state);
printf("Button state: %04X\n", state);
vTaskDelay(100 / portTICK_PERIOD_MS);
ledBuffer[1] = 255;
ws2812_send_data(ledBuffer, sizeof(ledBuffer));
vTaskDelay(200 / portTICK_PERIOD_MS);
ledBuffer[1] = 0;
ledBuffer[0] = 255;
ws2812_send_data(ledBuffer, sizeof(ledBuffer));
fpga_test(ili9341, ice40, rp2040->queue);
ledBuffer[0] = 0;
ws2812_send_data(ledBuffer, sizeof(ledBuffer));
}*/
while (true) {
menu_action_t menu_action;
appfs_handle_t appfs_fd;
menu_launcher(buttonQueue, pax_buffer, ili9341, framebuffer, &menu_action, &appfs_fd);
menu_launcher(rp2040->queue, pax_buffer, ili9341, framebuffer, &menu_action, &appfs_fd);
if (menu_action == ACTION_APPFS) {
appfs_boot_app(appfs_fd);
} else if (menu_action == ACTION_FPGA) {
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "FPGA TEST");
fpga_test(ili9341, ice40, buttonQueue);
fpga_test(ili9341, ice40, rp2040->queue);
} else if (menu_action == ACTION_INSTALLER) {
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "INSTALLER");
//appfs_store_app();
@ -313,7 +326,7 @@ void app_main(void) {
graphics_task(pax_buffer, ili9341, framebuffer, NULL, "Firmware update...");
} else if (menu_action == ACTION_SETTINGS) {
while (true) {
menu_wifi_settings(buttonQueue, pax_buffer, ili9341, framebuffer, &menu_action);
menu_wifi_settings(rp2040->queue, pax_buffer, ili9341, framebuffer, &menu_action);
if (menu_action == ACTION_WIFI_MANUAL) {
nvs_handle_t handle;
nvs_open("system", NVS_READWRITE, &handle);
@ -335,9 +348,9 @@ void app_main(void) {
if (res != ESP_OK) strcpy(password, "");
}
}
bool accepted = keyboard(buttonQueue, pax_buffer, ili9341, framebuffer, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "WiFi SSID", "Press HOME to exit", ssid, sizeof(ssid));
bool accepted = keyboard(rp2040->queue, pax_buffer, ili9341, framebuffer, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "WiFi SSID", "Press HOME to exit", ssid, sizeof(ssid));
if (accepted) {
accepted = keyboard(buttonQueue, pax_buffer, ili9341, framebuffer, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "WiFi password", "Press HOME to exit", password, sizeof(password));
accepted = keyboard(rp2040->queue, pax_buffer, ili9341, framebuffer, 30, 30, pax_buffer->width - 60, pax_buffer->height - 60, "WiFi password", "Press HOME to exit", password, sizeof(password));
}
if (accepted) {
nvs_set_str(handle, "wifi.ssid", ssid);

View file

@ -8,7 +8,6 @@
#include "ili9341.h"
#include "ice40.h"
#include "hardware.h"
#include "button_wrapper.h"
static const char *TAG = "settings";