From 37d246e86c6235cede9e34fa1621a43a90cd9c90 Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Thu, 20 Apr 2023 11:21:54 +0200 Subject: [PATCH] scripted synth running in parallel with micropython, bugs/restrictions galore --- ports/esp32/badge23/CMakeLists.txt | 6 + ports/esp32/badge23/Kconfig.projbuild | 21 + ports/esp32/badge23/apa102LEDStrip.c | 50 ++ ports/esp32/badge23/apa102LEDStrip.h | 17 + ports/esp32/badge23/audio.c | 266 ++++++++ ports/esp32/badge23/audio.h | 14 + ports/esp32/badge23/captouch.c | 417 ++++++++++++ ports/esp32/badge23/captouch.h | 6 + .../badge23/components/gc9a01/CMakeLists.txt | 1 + ports/esp32/badge23/components/gc9a01/Kconfig | 121 ++++ ports/esp32/badge23/components/gc9a01/LICENSE | 29 + .../esp32/badge23/components/gc9a01/gc9a01.c | 641 ++++++++++++++++++ .../esp32/badge23/components/gc9a01/gc9a01.h | 61 ++ .../esp32/badge23/components/gc9a01/readme.md | 51 ++ ports/esp32/badge23/decode_image.h | 33 + ports/esp32/badge23/display.c | 121 ++++ ports/esp32/badge23/display.h | 6 + ports/esp32/badge23/espan.c | 178 +++++ ports/esp32/badge23/espan.h | 2 + ports/esp32/badge23/idf_component.yml | 17 + ports/esp32/badge23/instruments/instrument.c | 73 ++ ports/esp32/badge23/instruments/instrument.h | 130 ++++ .../badge23/instruments/minimal_example.c | 89 +++ ports/esp32/badge23/leds.c | 348 ++++++++++ ports/esp32/badge23/leds.h | 4 + ports/esp32/badge23/scope.c | 63 ++ ports/esp32/badge23/scope.h | 15 + ports/esp32/badge23/synth.c | 167 +++++ ports/esp32/badge23/synth.h | 48 ++ ports/esp32/badge23/tags | 162 +++++ .../esp32/boards/GENERIC_S3_BADGE/board.json | 18 + .../GENERIC_S3_BADGE/mpconfigboard.cmake | 9 + .../boards/GENERIC_S3_BADGE/mpconfigboard.h | 10 + .../boards/GENERIC_S3_BADGE/sdkconfig.board | 12 + ports/esp32/boards/sdkconfig.badge23 | 21 + ports/esp32/main.c | 7 +- ports/esp32/main/CMakeLists.txt | 13 + 37 files changed, 3246 insertions(+), 1 deletion(-) create mode 100644 ports/esp32/badge23/CMakeLists.txt create mode 100644 ports/esp32/badge23/Kconfig.projbuild create mode 100644 ports/esp32/badge23/apa102LEDStrip.c create mode 100644 ports/esp32/badge23/apa102LEDStrip.h create mode 100644 ports/esp32/badge23/audio.c create mode 100644 ports/esp32/badge23/audio.h create mode 100644 ports/esp32/badge23/captouch.c create mode 100644 ports/esp32/badge23/captouch.h create mode 100644 ports/esp32/badge23/components/gc9a01/CMakeLists.txt create mode 100644 ports/esp32/badge23/components/gc9a01/Kconfig create mode 100644 ports/esp32/badge23/components/gc9a01/LICENSE create mode 100644 ports/esp32/badge23/components/gc9a01/gc9a01.c create mode 100644 ports/esp32/badge23/components/gc9a01/gc9a01.h create mode 100644 ports/esp32/badge23/components/gc9a01/readme.md create mode 100644 ports/esp32/badge23/decode_image.h create mode 100644 ports/esp32/badge23/display.c create mode 100644 ports/esp32/badge23/display.h create mode 100644 ports/esp32/badge23/espan.c create mode 100644 ports/esp32/badge23/espan.h create mode 100644 ports/esp32/badge23/idf_component.yml create mode 100644 ports/esp32/badge23/instruments/instrument.c create mode 100644 ports/esp32/badge23/instruments/instrument.h create mode 100644 ports/esp32/badge23/instruments/minimal_example.c create mode 100644 ports/esp32/badge23/leds.c create mode 100644 ports/esp32/badge23/leds.h create mode 100644 ports/esp32/badge23/scope.c create mode 100644 ports/esp32/badge23/scope.h create mode 100644 ports/esp32/badge23/synth.c create mode 100644 ports/esp32/badge23/synth.h create mode 100644 ports/esp32/badge23/tags create mode 100644 ports/esp32/boards/GENERIC_S3_BADGE/board.json create mode 100644 ports/esp32/boards/GENERIC_S3_BADGE/mpconfigboard.cmake create mode 100644 ports/esp32/boards/GENERIC_S3_BADGE/mpconfigboard.h create mode 100644 ports/esp32/boards/GENERIC_S3_BADGE/sdkconfig.board create mode 100644 ports/esp32/boards/sdkconfig.badge23 diff --git a/ports/esp32/badge23/CMakeLists.txt b/ports/esp32/badge23/CMakeLists.txt new file mode 100644 index 0000000000..a4cf9845e1 --- /dev/null +++ b/ports/esp32/badge23/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register(SRCS "espan.c" "apa102LEDStrip.c" "audio.c" "captouch.c" "decode_image.c" "leds.c" "display.c" "synth.c" "scope.c" + EMBED_FILES + ${project_dir}/resources/boot.snd + ${project_dir}/resources/pan.s16 + ${project_dir}/resources/image.jpg + INCLUDE_DIRS ".") diff --git a/ports/esp32/badge23/Kconfig.projbuild b/ports/esp32/badge23/Kconfig.projbuild new file mode 100644 index 0000000000..62f89be8f9 --- /dev/null +++ b/ports/esp32/badge23/Kconfig.projbuild @@ -0,0 +1,21 @@ +menu "Example Configuration" + + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + config I2C_MASTER_SCL + int "SCL GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 19 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 6 + help + GPIO number for I2C Master clock line. + + config I2C_MASTER_SDA + int "SDA GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 18 if IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 + default 5 + help + GPIO number for I2C Master data line. + +endmenu diff --git a/ports/esp32/badge23/apa102LEDStrip.c b/ports/esp32/badge23/apa102LEDStrip.c new file mode 100644 index 0000000000..32179d1f9f --- /dev/null +++ b/ports/esp32/badge23/apa102LEDStrip.c @@ -0,0 +1,50 @@ +#include "apa102LEDStrip.h" +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +void initLEDs(struct apa102LEDStrip *ledObject, short int numLEDs, unsigned char bytesPerLED, unsigned char globalBrightness) +{ + ledObject->_numLEDs = numLEDs; + ledObject->_bytesPerLED = bytesPerLED; + ledObject->_endFrameLength = 1;//round( (numLEDs/2)/8 ); + ledObject->_frameLength = (1+numLEDs+ledObject->_endFrameLength)*bytesPerLED; + ledObject->_globalBrightness = globalBrightness; + ledObject->LEDs = (unsigned char *)malloc(ledObject->_frameLength*sizeof(unsigned char)); + + //Start Frame + ledObject->LEDs[0] = 0; + ledObject->LEDs[1] = 0; + ledObject->LEDs[2] = 0; + ledObject->LEDs[3] = 0; + //Driver frame+PIXEL frames + for(ledObject->_counter=ledObject->_bytesPerLED; ledObject->_counter<ledObject->_frameLength-(ledObject->_endFrameLength*ledObject->_bytesPerLED); ledObject->_counter+=ledObject->_bytesPerLED) + { + ledObject->LEDs[ledObject->_counter] = ledObject->_globalBrightness; + ledObject->LEDs[ledObject->_counter+1] = 0; + ledObject->LEDs[ledObject->_counter+2] = 0; + ledObject->LEDs[ledObject->_counter+3] = 0; + } + //END frames + for(ledObject->_counter=ledObject->_frameLength-(ledObject->_endFrameLength*ledObject->_bytesPerLED); ledObject->_counter<ledObject->_frameLength; ledObject->_counter+=ledObject->_bytesPerLED) + { + ledObject->LEDs[ledObject->_counter] = 255; + ledObject->LEDs[ledObject->_counter+1] = 255; + ledObject->LEDs[ledObject->_counter+2] = 255; + ledObject->LEDs[ledObject->_counter+3] = 255; + } +} +void setPixel(struct apa102LEDStrip *ledObject, short int pixelIndex, unsigned char *pixelColour) +{ + ledObject->_counter = 4*(pixelIndex+1); + ledObject->LEDs[ ledObject->_counter + 1 ] = pixelColour[2]; + ledObject->LEDs[ ledObject->_counter + 2 ] = pixelColour[1]; + ledObject->LEDs[ ledObject->_counter + 3 ] = pixelColour[0]; +} +void getPixel(struct apa102LEDStrip *ledObject, short int pixelIndex, unsigned char *pixelColour) +{ + ledObject->_counter = 4*(pixelIndex+1); + pixelColour[2] = ledObject->LEDs[ ledObject->_counter + 1 ]; + pixelColour[1] = ledObject->LEDs[ ledObject->_counter + 2 ]; + pixelColour[0] = ledObject->LEDs[ ledObject->_counter + 3 ]; +} diff --git a/ports/esp32/badge23/apa102LEDStrip.h b/ports/esp32/badge23/apa102LEDStrip.h new file mode 100644 index 0000000000..da495abf62 --- /dev/null +++ b/ports/esp32/badge23/apa102LEDStrip.h @@ -0,0 +1,17 @@ +#ifndef DEF_apa102LEDStrip +#define DEF_apa102LEDStrip + +struct apa102LEDStrip +{ + unsigned char *LEDs; + short int _frameLength; + short int _endFrameLength; + short int _numLEDs; + unsigned char _bytesPerLED; + short int _counter; + unsigned char _globalBrightness; +}; +void initLEDs(struct apa102LEDStrip *ledObject, short int numLEDs, unsigned char bytesPerLED, unsigned char globalBrightness); +void setPixel(struct apa102LEDStrip *ledObject, short int pixelIndex, unsigned char *pixelColour); +void getPixel(struct apa102LEDStrip *ledObject, short int pixelIndex, unsigned char *pixelColour); +#endif diff --git a/ports/esp32/badge23/audio.c b/ports/esp32/badge23/audio.c new file mode 100644 index 0000000000..9bd88dd066 --- /dev/null +++ b/ports/esp32/badge23/audio.c @@ -0,0 +1,266 @@ +#include "audio.h" +#include "synth.h" +#include "scope.h" + +#include "driver/i2s.h" + +#include <freertos/FreeRTOS.h> +#include <freertos/task.h> +#include <freertos/queue.h> +#include <stdio.h> +#include <math.h> +#include <string.h> + +#define DRUMS_TOP 0 + +#define NUM_SYNTH 10 + +static trad_osc_t synths[NUM_SYNTH]; +static void audio_player_task(void* arg); + +#define DMA_BUFFER_SIZE 64 +#define DMA_BUFFER_COUNT 2 +#define I2S_PORT 0 + +#if 0 +static i2s_chan_handle_t tx_chan; // I2S tx channel handler +static i2s_chan_handle_t rx_chan; // I2S rx channel handler + +static void i2s_init_std_duplex(void) +{ + /* Setp 1: Determine the I2S channel configuration and allocate both channels + * The default configuration can be generated by the helper macro, + * it only requires the I2S controller id and I2S role */ + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); + chan_cfg.dma_desc_num = DMA_BUFFER_COUNT; + chan_cfg.dma_frame_num = DMA_BUFFER_SIZE; + + // Play silence after all DMA buffers are empty + chan_cfg.auto_clear = true; + + ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan)); + + /* Step 2: Setting the configurations of standard mode, and initialize rx & tx channels + * The slot configuration and clock configuration can be generated by the macros + * These two helper macros is defined in 'i2s_std.h' which can only be used in STD mode. + * They can help to specify the slot and clock configurations for initialization or re-configuring */ + i2s_std_config_t std_cfg = { + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE), + //.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), + .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), + .gpio_cfg = { + .mclk = 11, + .bclk = 13, + .ws = 12, + .dout = 14, + .din = I2S_GPIO_UNUSED, + .invert_flags = { + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false, + }, + }, + }; + /* Initialize the channels */ + ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg)); + ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_chan, &std_cfg)); +} +#endif + +static void i2s_init_idk_lol(void){ + + static const i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, + .sample_rate = SAMPLE_RATE, + .bits_per_sample = 16, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + //.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + + .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + .intr_alloc_flags = 0, // default interrupt priority + .dma_buf_count = DMA_BUFFER_COUNT, + .dma_buf_len = DMA_BUFFER_SIZE, + .use_apll = false + }; + static const i2s_pin_config_t pin_config = { + .bck_io_num = 13, + .mck_io_num = 11, + .ws_io_num = 12, + .data_out_num = 14, + .data_in_num = I2S_PIN_NO_CHANGE + }; + i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); + + i2s_set_pin(I2S_PORT, &pin_config); + +} + +static float FREQ_TABLE[] = { + 523.25, + 587.33, + 659.25 , + 698.46 , + 783.99 , + 880.00 , + 987.77 , + 1046.50, + 1174.66, + 1318.51, +}; + +#if 0 +static float FREQ_TABLE[] = { + 783.99, + 415.30, + 440.0, + 493.88, + 523.25, + 587.33, + 659.25, + 698.46, + 830.61, + 880.00, +}; +#endif + +static void _audio_init(void) { + init_scope(241); + //i2s_init_std_duplex(); + i2s_init_idk_lol(); + //ESP_ERROR_CHECK(i2s_channel_enable(tx_chan)); + + for(int i = 0; i < NUM_SYNTH; i++){ + if((i%2) || (!DRUMS_TOP) ){ //bottom leaves + synths[i].decay_steps = 50; + synths[i].attack_steps = 3; + if(i == 2 || i == 8) synths[i].attack_steps = 15; + synths[i].vol = 1.; + synths[i].gate = 0.01; + synths[i].freq = FREQ_TABLE[i]/2; + synths[i].counter = 0; + synths[i].bend = 1; + synths[i].noise_reg = 1; + synths[i].waveform = 1; + synths[i].skip_hold = !(i % 2); + if(DRUMS_TOP) synths[i].waveform = 4; + } else { //top leaves + synths[i].decay_steps = 50; + synths[i].attack_steps = 0; + synths[i].bend = 1; + synths[i].vol = 0.0; + synths[i].skip_hold = 1; + switch(i){ + case 0: //kick + synths[i].freq = 1800; + break; + case 2: //snare 1 + synths[i].freq = 5000; + break; + case 4: //snare 2 + synths[i].freq = 6000; + break; + case 6: //hihat + synths[i].freq = 12000; + break; + case 8: //crash + synths[i].freq = 16000; + synths[i].gate = 0.1; + break; + } + synths[i].counter = 0; + synths[i].waveform = 8; + synths[i].noise_reg = 1; + } + } + + TaskHandle_t handle; + xTaskCreate(&audio_player_task, "Audio player", 20000, NULL, configMAX_PRIORITIES - 1, &handle); +} + +#define MIN(a,b) ((a > b) ? b : a) +#define GLOBAL_VOL 3000 +#define LR_PHASE -1 + +static void audio_player_task(void* arg) { + int16_t buffer[DMA_BUFFER_SIZE * 2]; + //memset(buffer, 0, sizeof(buffer)); + + while(true) { + + for(int i = 0; i < (DMA_BUFFER_SIZE * 2); i += 2){ + float sample = 0; + for(int j = 0; j<NUM_SYNTH; j++){ + sample += trad_osc(&(synths[j])); + } + write_to_scope((int16_t) (1600. * sample)); + sample = GLOBAL_VOL * sample; + if(sample > 32767) sample = 32767; + if(sample < -32767) sample = -32767; + buffer[i] = (int16_t) sample; + buffer[i+1] = LR_PHASE * buffer[i]; + } + + size_t count = 0; + //i2s_channel_write(tx_chan, buffer, sizeof(buffer), &count, 1000); + i2s_write(I2S_PORT, buffer, sizeof(buffer), &count, 1000); + if (count != sizeof(buffer)) { + printf("i2s_write_bytes: count (%d) != length (%d)\n", count, sizeof(buffer)); + abort(); + } + } +} + +void audio_init() { _audio_init(); } + +extern const int16_t boot_snd_start[] asm("_binary_boot_snd_start"); +extern const int16_t boot_snd_end[] asm("_binary_boot_snd_end"); + +extern const int16_t pan_s16_start[] asm("_binary_pan_s16_start"); +extern const int16_t pan_s16_end[] asm("_binary_pan_s16_end"); + +void play_bootsound() { + /* + sound_cfg_t sound = {0,}; + sound.buffer = boot_snd_start, sound.size = boot_snd_end - boot_snd_start; + //sound.buffer = pan_s16_start, sound.size = pan_s16_end - pan_s16_start; + sound.slot = 10; + xQueueSend(sound_queue, &sound, 0); + */ +} + +void synth_set_freq(int i, float freq){ + synths[i].freq = freq; +} + +void synth_set_vol(int i, float vol){ + synths[i].vol = vol; +} + +#define NAT_LOG_SEMITONE 0.05776226504666215 + +void synth_set_bend(int i, float bend){ + if(bend != bend) return; + if((bend > -0.0001) && (bend < 0.0001)){ + synths[i].bend = 1; + } else { + synths[i].bend = exp(bend * NAT_LOG_SEMITONE); + } +} + +void synth_stop(int i){ + if(synths[i].env_phase){ + synths[i].env_phase = 3; + } +} + +void synth_fullstop(int i){ + synths[i].env_phase = 0; +} + +void synth_start(int i){ + synths[i].env_phase = 1; //put into attack phase; +} + +float synth_get_env(int i){ + return synths[i].env; +} diff --git a/ports/esp32/badge23/audio.h b/ports/esp32/badge23/audio.h new file mode 100644 index 0000000000..3ab9a98ae8 --- /dev/null +++ b/ports/esp32/badge23/audio.h @@ -0,0 +1,14 @@ +#pragma once +#include <stdint.h> + +#define SAMPLE_RATE 16000 + +void audio_init(); +void play_bootsound(); +void synth_set_freq(int i, float freq); +void synth_set_vol(int i, float vol); +void synth_set_bend(int i, float bend); +void synth_start(int i); +void synth_stop(int i); +void synth_fullstop(int i); +float synth_get_env(int i); diff --git a/ports/esp32/badge23/captouch.c b/ports/esp32/badge23/captouch.c new file mode 100644 index 0000000000..2a133699bb --- /dev/null +++ b/ports/esp32/badge23/captouch.c @@ -0,0 +1,417 @@ +//#include <stdio.h> +//#include <string.h> +#include "esp_log.h" +#include "driver/i2c.h" + + +static const char *TAG = "captouch"; + +#define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */ + +#define AD7147_BASE_ADDR 0x2C + +#define AD7147_REG_PWR_CONTROL 0x00 +#define AD7147_REG_STAGE_CAL_EN 0x01 +#define AD7147_REG_STAGE_HIGH_INT_ENABLE 0x06 +#define AD7147_REG_DEVICE_ID 0x17 + +#define TIMEOUT_MS 1000 + +struct ad714x_chip { + uint8_t addr; + uint8_t gpio; + int afe_offsets[13]; + int stages; +}; + +static const struct ad714x_chip chip_top = {.addr = AD7147_BASE_ADDR + 1, .gpio = 48, .afe_offsets = {24, 12, 16, 33, 30, 28, 31, 27, 22, 24, 18, 19, }, .stages=12}; +static const struct ad714x_chip chip_bot = {.addr = AD7147_BASE_ADDR, .gpio = 3, .afe_offsets = {3, 2, 1, 1 ,1, 1, 1, 1, 2, 3}, .stages=10}; + +static esp_err_t ad714x_i2c_write(const struct ad714x_chip *chip, const uint16_t reg, const uint16_t data) +{ + const uint8_t tx[] = {reg >> 8, reg & 0xFF, data >> 8, data & 0xFF}; + ESP_LOGI(TAG, "AD7147 write reg %X-> %X", reg, data); + return i2c_master_write_to_device(I2C_MASTER_NUM, chip->addr, tx, sizeof(tx), TIMEOUT_MS / portTICK_PERIOD_MS); +} + +static esp_err_t ad714x_i2c_read(const struct ad714x_chip *chip, const uint16_t reg, uint16_t *data, const size_t len) +{ + const uint8_t tx[] = {reg >> 8, reg & 0xFF}; + uint8_t rx[len * 2]; + esp_err_t ret = i2c_master_write_read_device(I2C_MASTER_NUM, chip->addr, tx, sizeof(tx), rx, sizeof(rx), TIMEOUT_MS / portTICK_PERIOD_MS); + for(int i = 0; i < len; i++) { + data[i] = (rx[i * 2] << 8) | rx[i * 2 + 1]; + } + return ret; +} + +struct ad7147_stage_config { + unsigned int cinX_connection_setup[13]; + unsigned int se_connection_setup:2; + unsigned int neg_afe_offset_disable:1; + unsigned int pos_afe_offset_disable:1; + unsigned int neg_afe_offset:6; + unsigned int neg_afe_offset_swap:1; + unsigned int pos_afe_offset:6; + unsigned int pos_afe_offset_swap:1; + unsigned int neg_threshold_sensitivity:4; + unsigned int neg_peak_detect:3; + unsigned int pos_threshold_sensitivity:4; + unsigned int pos_peak_detect:3; +}; + +#define CIN CDC_NONE 0 +#define CIN_CDC_NEG 1 +#define CIN_CDC_POS 2 +#define CIN_BIAS 3 + +static const uint16_t bank2 = 0x80; + +static void ad714x_set_stage_config(const struct ad714x_chip *chip, const uint8_t stage, const struct ad7147_stage_config * config) +{ + const uint16_t connection_6_0 = (config->cinX_connection_setup[6] << 12) | (config->cinX_connection_setup[5] << 10) | (config->cinX_connection_setup[4] << 8) | (config->cinX_connection_setup[3] << 6) | (config->cinX_connection_setup[2] << 4) | (config->cinX_connection_setup[1] << 2) | (config->cinX_connection_setup[0] << 0); + const uint16_t connection_12_7 = (config->pos_afe_offset_disable << 15) | (config->neg_afe_offset_disable << 14) | (config->se_connection_setup << 12) | (config->cinX_connection_setup[12] << 10) | (config->cinX_connection_setup[11] << 8) | (config->cinX_connection_setup[10] << 6) | (config->cinX_connection_setup[9] << 4) | (config->cinX_connection_setup[8] << 2) | (config->cinX_connection_setup[7] << 0); + const uint16_t afe_offset = (config->pos_afe_offset_swap << 15) | (config->pos_afe_offset << 8) | (config->neg_afe_offset_swap << 7) | (config->neg_afe_offset << 0); + const uint16_t sensitivity = (config->pos_peak_detect << 12) | (config->pos_threshold_sensitivity << 8) | (config->neg_peak_detect << 4) | (config->neg_threshold_sensitivity << 0); + + //ESP_LOGI(TAG, "Stage %d config-> %X %X %X %X", stage, connection_6_0, connection_12_7, afe_offset, sensitivity); + //ESP_LOGI(TAG, "Config: %X %X %X %X %X %X %X %X %X", config->pos_afe_offset_disable, config->pos_afe_offset_disable, config->se_connection_setup, config->cinX_connection_setup[12], config->cinX_connection_setup[11], config->cinX_connection_setup[10], config->cinX_connection_setup[9], config->cinX_connection_setup[8], config->cinX_connection_setup[7]); + + ad714x_i2c_write(chip, bank2 + stage * 8, connection_6_0); + ad714x_i2c_write(chip, bank2 + stage * 8 + 1, connection_12_7); + ad714x_i2c_write(chip, bank2 + stage * 8 + 2, afe_offset); + ad714x_i2c_write(chip, bank2 + stage * 8 + 3, sensitivity); +} + +struct ad7147_device_config { + unsigned int power_mode:2; + unsigned int lp_conv_delay:2; + unsigned int sequence_stage_num:4; + unsigned int decimation:2; + unsigned int sw_reset:1; + unsigned int int_pol:1; + unsigned int ext_source:1; + unsigned int cdc_bias:2; + + unsigned int stage0_cal_en:1; + unsigned int stage1_cal_en:1; + unsigned int stage2_cal_en:1; + unsigned int stage3_cal_en:1; + unsigned int stage4_cal_en:1; + unsigned int stage5_cal_en:1; + unsigned int stage6_cal_en:1; + unsigned int stage7_cal_en:1; + unsigned int stage8_cal_en:1; + unsigned int stage9_cal_en:1; + unsigned int stage10_cal_en:1; + unsigned int stage11_cal_en:1; + unsigned int avg_fp_skip:2; + unsigned int avg_lp_skip:2; + + unsigned int stage0_high_int_enable:1; + unsigned int stage1_high_int_enable:1; + unsigned int stage2_high_int_enable:1; + unsigned int stage3_high_int_enable:1; + unsigned int stage4_high_int_enable:1; + unsigned int stage5_high_int_enable:1; + unsigned int stage6_high_int_enable:1; + unsigned int stage7_high_int_enable:1; + unsigned int stage8_high_int_enable:1; + unsigned int stage9_high_int_enable:1; + unsigned int stage10_high_int_enable:1; + unsigned int stage11_high_int_enable:1; +}; + + +static void ad714x_set_device_config(const struct ad714x_chip *chip, const struct ad7147_device_config * config) +{ + const uint16_t pwr_control = (config->cdc_bias << 14) | (config->ext_source << 12) | (config->int_pol << 11) | (config->sw_reset << 10) | (config->decimation << 8) | (config->sequence_stage_num << 4) | (config->lp_conv_delay << 2) | (config->power_mode << 0); + const uint16_t stage_cal_en = (config->avg_lp_skip << 14) | (config->avg_fp_skip << 12) | (config->stage11_cal_en << 11) | (config->stage10_cal_en << 10) | (config->stage9_cal_en << 9) | (config->stage8_cal_en << 8) | (config->stage7_cal_en << 7) | (config->stage6_cal_en << 6) | (config->stage5_cal_en << 5) | (config->stage4_cal_en << 4) | (config->stage3_cal_en << 3) | (config->stage2_cal_en << 2) | (config->stage1_cal_en << 1) | (config->stage0_cal_en << 0); + const uint16_t stage_high_int_enable = (config->stage11_high_int_enable << 11) | (config->stage10_high_int_enable << 10) | (config->stage9_high_int_enable << 9) | (config->stage8_high_int_enable << 8) | (config->stage7_high_int_enable << 7) | (config->stage6_high_int_enable << 6) | (config->stage5_high_int_enable << 5) | (config->stage4_high_int_enable << 4) | (config->stage3_high_int_enable << 3) | (config->stage2_high_int_enable << 2) | (config->stage1_high_int_enable << 1) | (config->stage0_high_int_enable << 0); + + ad714x_i2c_write(chip, AD7147_REG_PWR_CONTROL, pwr_control); + ad714x_i2c_write(chip, AD7147_REG_STAGE_CAL_EN, stage_cal_en); + ad714x_i2c_write(chip, AD7147_REG_STAGE_HIGH_INT_ENABLE, stage_high_int_enable); +} + +static struct ad7147_stage_config ad714x_default_config(void) +{ + return (struct ad7147_stage_config) { + .cinX_connection_setup={CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS}, + .se_connection_setup=0b01, + .pos_afe_offset=0, + }; +} + +#define ESP_INTR_FLAG_DEFAULT 0 + +static QueueHandle_t gpio_evt_queue = NULL; + +static void IRAM_ATTR gpio_isr_handler(void* arg) +{ + struct ad714x_chip* chip = (struct ad714x_chip *) arg; + xQueueSendFromISR(gpio_evt_queue, &chip, NULL); +} + +void espan_handle_captouch(uint16_t pressed_top, uint16_t pressed_bot); + +static uint16_t pressed_top, pressed_bot; +void gpio_event_handler(void* arg) +{ + static unsigned long counter = 0; + struct ad714x_chip* chip; + uint16_t pressed; + while(true) { + /* + if(xQueueReceive(gpio_evt_queue, &chip, portMAX_DELAY)) { + ad714x_i2c_read(chip, 9, &pressed, 1); + ESP_LOGI(TAG, "Addr %x, High interrupt %X", chip->addr, pressed); + + pressed &= ((1 << chip->stages) - 1); + + if(chip == &chip_top) pressed_top = pressed; + if(chip == &chip_bot) pressed_bot = pressed; + espan_handle_captouch(pressed_top, pressed_bot); + } + */ + vTaskDelay(1000/portTICK_PERIOD_MS); + counter++; + pressed = counter & ((1 << 5) - 1); + pressed_bot = pressed; + pressed_top = pressed; + espan_handle_captouch(pressed_top, pressed_bot); + } +} + +static void captouch_init_chip(const struct ad714x_chip* chip, const struct ad7147_device_config device_config) +{ + uint16_t data; + ad714x_i2c_read(chip, AD7147_REG_DEVICE_ID, &data, 1); + ESP_LOGI(TAG, "DEVICE ID = %X", data); + + ad714x_set_device_config(chip, &device_config); + + for(int i=0; i<chip->stages; i++) { + struct ad7147_stage_config stage_config; + stage_config = ad714x_default_config(); + stage_config.cinX_connection_setup[i] = CIN_CDC_POS; + stage_config.pos_afe_offset=chip->afe_offsets[i]; + ad714x_set_stage_config(chip, i, &stage_config); + } + + // Force calibration + ad714x_i2c_write(chip, 2, (1 << 14)); + + gpio_config_t io_conf = {}; + io_conf.intr_type = GPIO_INTR_NEGEDGE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = (1ULL << chip->gpio); + io_conf.pull_up_en = 1; + io_conf.pull_down_en = 0; + gpio_config(&io_conf); + + gpio_evt_queue = xQueueCreate(10, sizeof(const struct ad714x_chip*)); + xTaskCreate(gpio_event_handler, "gpio_event_handler", 2048 * 2, NULL, configMAX_PRIORITIES - 2, NULL); + gpio_isr_handler_add(chip->gpio, gpio_isr_handler, (void *)chip); + +} + +void captouch_init(void) +{ + gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); + captouch_init_chip(&chip_top, (struct ad7147_device_config){.sequence_stage_num = 11, + .decimation = 1, + .stage0_cal_en = 1, + .stage1_cal_en = 1, + .stage2_cal_en = 1, + .stage3_cal_en = 1, + .stage4_cal_en = 1, + .stage5_cal_en = 1, + .stage6_cal_en = 1, + .stage7_cal_en = 1, + .stage8_cal_en = 1, + .stage9_cal_en = 1, + .stage10_cal_en = 1, + .stage11_cal_en = 1, + + .stage0_high_int_enable = 1, + .stage1_high_int_enable = 1, + .stage2_high_int_enable = 1, + .stage3_high_int_enable = 1, + .stage4_high_int_enable = 1, + .stage5_high_int_enable = 1, + .stage6_high_int_enable = 1, + .stage7_high_int_enable = 1, + .stage8_high_int_enable = 1, + .stage9_high_int_enable = 1, + .stage10_high_int_enable = 1, + .stage11_high_int_enable = 1, + }); + + captouch_init_chip(&chip_bot, (struct ad7147_device_config){.sequence_stage_num = 11, + .decimation = 1, + .stage0_cal_en = 1, + .stage1_cal_en = 1, + .stage2_cal_en = 1, + .stage3_cal_en = 1, + .stage4_cal_en = 1, + .stage5_cal_en = 1, + .stage6_cal_en = 1, + .stage7_cal_en = 1, + .stage8_cal_en = 1, + .stage9_cal_en = 1, + + .stage0_high_int_enable = 1, + .stage1_high_int_enable = 1, + .stage2_high_int_enable = 1, + .stage3_high_int_enable = 1, + .stage4_high_int_enable = 1, + .stage5_high_int_enable = 1, + .stage6_high_int_enable = 1, + .stage7_high_int_enable = 1, + .stage8_high_int_enable = 1, + .stage9_high_int_enable = 1, + }); +} + +static void captouch_print_debug_info_chip(const struct ad714x_chip* chip) +{ + uint16_t data[12] = {0,}; + uint16_t ambient[12] = {0,}; + const int stages = chip->stages; +#if 1 + for(int stage=0; stage<stages; stage++) { + ad714x_i2c_read(chip, 0x0FA + stage * (0x104 - 0xE0), data, 1); + ESP_LOGI(TAG, "stage %d threshold: %X", stage, data[0]); + } + + ad714x_i2c_read(chip, 0xB, data, stages); + ESP_LOGI(TAG, "CDC results: %X %X %X %X %X %X %X %X %X %X %X %X", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]); + + for(int stage=0; stage<stages; stage++) { + ad714x_i2c_read(chip, 0x0F1 + stage * (0x104 - 0xE0), &ambient[stage], 1); + ESP_LOGI(TAG, "stage %d ambient: %X diff: %d", stage, ambient[stage], data[stage] - ambient[stage]); + } + +#endif +#if 1 + ad714x_i2c_read(chip, 8, data, 1); + ESP_LOGI(TAG, "Low interrupt %X", data[0]); + ad714x_i2c_read(chip, 9, data, 1); + ESP_LOGI(TAG, "High interrupt %X", data[0]); + ad714x_i2c_read(chip, 0x42, data, 1); + ESP_LOGI(TAG, "Proximity %X", data[0]); + //ESP_LOGI(TAG, "CDC result = %X", data[0]); + //if(data[0] > 0xa000) { + //ESP_LOGI(TAG, "Touch! %X", data[0]); + //} +#endif +} + +void captouch_print_debug_info(void) +{ + captouch_print_debug_info_chip(&chip_top); + captouch_print_debug_info_chip(&chip_bot); +} + +void captouch_get_cross(int paddle, int *x, int *y) +{ + uint16_t data[12] = {0,}; + uint16_t ambient[12] = {0,}; + + int result[2] = {0, 0}; + float total = 0; + +#if 0 + if(paddle == 2) { + ad714x_i2c_read(&chip_top, 0xB, data, 3); + //ESP_LOGI(TAG, "CDC results: %X %X %X %X %X %X %X %X %X %X %X %X", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]); + + for(int stage=0; stage<3; stage++) { + ad714x_i2c_read(&chip_top, 0x0F1 + stage * (0x104 - 0xE0), &ambient[stage], 1); + //ESP_LOGI(TAG, "stage %d ambient: %X diff: %d", stage, ambient[stage], data[stage] - ambient[stage]); + } + + int vectors[][2] = {{0, 0}, {0,240}, {240, 120}}; + total = (data[0] - ambient[0]) + (data[1] - ambient[1]) + (data[2] - ambient[2]); + + result[0] = vectors[0][0] * (data[0] - ambient[0]) + vectors[1][0] * (data[1] - ambient[1]) + vectors[2][0] * (data[2] - ambient[2]); + result[1] = vectors[0][1] * (data[0] - ambient[0]) + vectors[1][1] * (data[1] - ambient[1]) + vectors[2][1] * (data[2] - ambient[2]); + } + + if(paddle == 8) { + ad714x_i2c_read(&chip_top, 0xB + 5, data + 5, 3); + //ESP_LOGI(TAG, "CDC results: %X %X %X %X %X %X %X %X %X %X %X %X", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]); + + for(int stage=5; stage<8; stage++) { + ad714x_i2c_read(&chip_top, 0x0F1 + stage * (0x104 - 0xE0), &ambient[stage], 1); + //ESP_LOGI(TAG, "stage %d ambient: %X diff: %d", stage, ambient[stage], data[stage] - ambient[stage]); + } + + int vectors[][2] = {{240, 240}, {240, 0}, {0, 120}}; + total = (data[5] - ambient[5]) + (data[6] - ambient[6]) + (data[7] - ambient[7]); + + result[0] = vectors[0][0] * (data[5] - ambient[5]) + vectors[1][0] * (data[6] - ambient[6]) + vectors[2][0] * (data[7] - ambient[7]); + result[1] = vectors[0][1] * (data[5] - ambient[5]) + vectors[1][1] * (data[6] - ambient[6]) + vectors[2][1] * (data[7] - ambient[7]); + } + + *x = result[0] / total; + *y = result[1] / total; + + //ESP_LOGI(TAG, "x=%d y=%d\n", *x, *y); +#endif + + const int paddle_info_1[] = { + 4, + 0, + 1, + 2, + 11, + 4, + 9, + 7, + 6, + 9, + }; + const int paddle_info_2[] = { + 3, + 1, + 0, + 3, + 10, + 5, + 8, + 6, + 5, + 8, + }; + + struct ad714x_chip* chip; + if (paddle % 2 == 0) { + chip = &chip_top; + } else { + chip = &chip_bot; + } + + ad714x_i2c_read(chip, 0xB, data, 12); + //ESP_LOGI(TAG, "CDC results: %X %X %X %X %X %X %X %X %X %X %X %X", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]); + + for(int stage=0; stage<12; stage++) { + ad714x_i2c_read(chip, 0x0F1 + stage * (0x104 - 0xE0), &ambient[stage], 1); + //ESP_LOGI(TAG, "stage %d ambient: %X diff: %d", stage, ambient[stage], data[stage] - ambient[stage]); + } + + int diff1 = data[paddle_info_1[paddle]] - ambient[paddle_info_1[paddle]]; + int diff2 = data[paddle_info_2[paddle]] - ambient[paddle_info_2[paddle]]; + + ESP_LOGI(TAG, "%10d %10d", diff1, diff2); + + int vectors[][2] = {{240, 240}, {240, 0}, {0, 120}}; + total = ((diff1) + (diff2)); + + result[0] = vectors[0][0] * (diff1) + vectors[1][0] * (diff2); + result[1] = vectors[0][1] * (diff1) + vectors[1][1] * (diff2); + + *x = result[0] / total; + *y = result[1] / total; +} diff --git a/ports/esp32/badge23/captouch.h b/ports/esp32/badge23/captouch.h new file mode 100644 index 0000000000..8c3ff2aaf6 --- /dev/null +++ b/ports/esp32/badge23/captouch.h @@ -0,0 +1,6 @@ +#pragma once + +void captouch_init(void); +void captouch_print_debug_info(void); +void gpio_event_handler(void * arg); +void captouch_get_cross(int paddle, int * x, int * y); diff --git a/ports/esp32/badge23/components/gc9a01/CMakeLists.txt b/ports/esp32/badge23/components/gc9a01/CMakeLists.txt new file mode 100644 index 0000000000..cb20ec6a4e --- /dev/null +++ b/ports/esp32/badge23/components/gc9a01/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "gc9a01.c" REQUIRES driver INCLUDE_DIRS ".") diff --git a/ports/esp32/badge23/components/gc9a01/Kconfig b/ports/esp32/badge23/components/gc9a01/Kconfig new file mode 100644 index 0000000000..5d8d28900b --- /dev/null +++ b/ports/esp32/badge23/components/gc9a01/Kconfig @@ -0,0 +1,121 @@ +menu "GC9A01 LCD Config" + config GC9A01_Width + int "GC9A01 LCD Width" + range 64 1024 + default 240 + + config GC9A01_Height + int "GC9A01 LCD Height" + range 64 1024 + default 240 + #--------------------------------------------- + choice GC9A01_SPI_HOST + prompt "GC9A01 SPI HOST" + default USE_SPI3_HOST + help + Hardware SPI , HSPI=SPI2 , VSPI=SPI3 + + config USE_SPI1_HOST + bool "USE SPI1 HOST" + config USE_SPI2_HOST + bool "USE SPI2 HOST" + config USE_SPI3_HOST + bool "USE SPI3 HOST" + config USE_SPI4_HOST + bool "USE SPI4 HOST" + endchoice + + config GC9A01_SPI_HOST + int + default 0 if USE_SPI1_HOST + default 1 if USE_SPI2_HOST + default 2 if USE_SPI3_HOST + default 3 if USE_SPI4_HOST + #--------------------------------------------- + + config GC9A01_PIN_NUM_SCK + int "LCD SPI SCK Pin" + range 0 48 + default 18 + help + Must Support SPI SCK + + config GC9A01_PIN_NUM_MOSI + int "LCD SPI MOSI Pin" + range 0 48 + default 23 + help + Must Support SPI MOSI + + config GC9A01_PIN_NUM_CS + int "LCD SPI CS Pin" + range 0 48 + default 05 + + config GC9A01_PIN_NUM_DC + int "LCD DC(Data or Command) GPIO Pin Number" + range 0 48 + default 21 + + config GC9A01_SPI_SCK_FREQ_M + int "SPI Clock Freq in MHz" + range 1 80 + default 40 + + config GC9A01_CONTROL_BACK_LIGHT_USED + bool "LCD Control Back Light" + default y + + config GC9A01_PIN_NUM_BCKL + int "LCD BL(Back Light) GPIO Pin Number" + depends on GC9A01_CONTROL_BACK_LIGHT_USED + range 0 48 + default 19 + + #--------------------------------------------- + choice GC9A01_CONTROL_BACK_LIGHT_MODE + prompt "GC9A01b Control Back Light Mode" + default GC9A01_BACK_LIGHT_MODE_PWM + depends on GC9A01_CONTROL_BACK_LIGHT_USED + help + PWM use LEDC_TIMER_0 and LEDC_CHANNEL_0 + + config GC9A01_BACK_LIGHT_MODE_On_OFF + bool "USE GPIO ON/OF" + config GC9A01_BACK_LIGHT_MODE_PWM + bool "USE PWM" + endchoice + + config GC9A01_CONTROL_BACK_LIGHT_MODE + int + default 0 if GC9A01_BACK_LIGHT_MODE_On_OFF + default 1 if GC9A01_BACK_LIGHT_MODE_PWM + #--------------------------------------------- + + config GC9A01_RESET_USED + bool "GC9A01 RESET Pin Used" + default y + help + Use GC9A01 Hard Reset Pin + + config GC9A01_PIN_NUM_RST + int "LCD RST GPIO Pin Number" + depends on GC9A01_RESET_USED + range 0 48 + default 22 + + config GC9A01_BUFFER_MODE + bool "Enable Buffer Mode" + default y + help + Disable for Direct Mode + + config GC9A01_BUFFER_SCREEN_FAST_MODE + bool "Don't Convert Buffer for Screen_Load() & Screen_Shot()" + default n + depends on GC9A01_BUFFER_MODE + help + If Enabled , the Screen_load() & Screen_Shot() don't run SwapBytes() for Buffer + & Direct Save Data to SPI Buffer,So the data must manually SwapBytes before Send to LCD. + +endmenu diff --git a/ports/esp32/badge23/components/gc9a01/LICENSE b/ports/esp32/badge23/components/gc9a01/LICENSE new file mode 100644 index 0000000000..9fd35d36ac --- /dev/null +++ b/ports/esp32/badge23/components/gc9a01/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2021, liyanboy74 +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ports/esp32/badge23/components/gc9a01/gc9a01.c b/ports/esp32/badge23/components/gc9a01/gc9a01.c new file mode 100644 index 0000000000..ac5770dc9a --- /dev/null +++ b/ports/esp32/badge23/components/gc9a01/gc9a01.c @@ -0,0 +1,641 @@ +//-------------------------------------------------------------------------------------------------------- +// Nadyrshin Ruslan - [YouTube-channel: https://www.youtube.com/channel/UChButpZaL5kUUl_zTyIDFkQ] +// Liyanboy74 +//-------------------------------------------------------------------------------------------------------- +#include <stdio.h> +#include <string.h> + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "driver/ledc.h" + +#include "sdkconfig.h" +#include "gc9a01.h" + +#if (CONFIG_GC9A01_RESET_USED) +#define RESET_HIGH() gpio_set_level(CONFIG_GC9A01_PIN_NUM_RST,1) +#define RESET_LOW() gpio_set_level(CONFIG_GC9A01_PIN_NUM_RST,0) +#endif + +#if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED) +#define BLK_HIGH() gpio_set_level(CONFIG_GC9A01_PIN_NUM_BCKL,1) +#define BLK_LOW() gpio_set_level(CONFIG_GC9A01_PIN_NUM_BCKL,0) +#endif + +#define Cmd_SLPIN 0x10 +#define Cmd_SLPOUT 0x11 +#define Cmd_INVOFF 0x20 +#define Cmd_INVON 0x21 +#define Cmd_DISPOFF 0x28 +#define Cmd_DISPON 0x29 +#define Cmd_CASET 0x2A +#define Cmd_RASET 0x2B +#define Cmd_RAMWR 0x2C +#define Cmd_TEON 0x35 // Tearing effect line ON +#define Cmd_MADCTL 0x36 // Memory data access control +#define Cmd_COLMOD 0x3A // Pixel format set + +#define Cmd_DisplayFunctionControl 0xB6 +#define Cmd_PWCTR1 0xC1 // Power control 1 +#define Cmd_PWCTR2 0xC3 // Power control 2 +#define Cmd_PWCTR3 0xC4 // Power control 3 +#define Cmd_PWCTR4 0xC9 // Power control 4 +#define Cmd_PWCTR7 0xA7 // Power control 7 + +#define Cmd_FRAMERATE 0xE8 +#define Cmd_InnerReg1Enable 0xFE +#define Cmd_InnerReg2Enable 0xEF + +#define Cmd_GAMMA1 0xF0 // Set gamma 1 +#define Cmd_GAMMA2 0xF1 // Set gamma 2 +#define Cmd_GAMMA3 0xF2 // Set gamma 3 +#define Cmd_GAMMA4 0xF3 // Set gamma 4 + +#define ColorMode_RGB_16bit 0x50 +#define ColorMode_RGB_18bit 0x60 +#define ColorMode_MCU_12bit 0x03 +#define ColorMode_MCU_16bit 0x05 +#define ColorMode_MCU_18bit 0x06 + +#define MADCTL_MY 0x80 +#define MADCTL_MX 0x40 +#define MADCTL_MV 0x20 +#define MADCTL_ML 0x10 +#define MADCTL_BGR 0x08 +#define MADCTL_MH 0x04 + +uint8_t GC9A01_X_Start = 0, GC9A01_Y_Start = 0; + +#if (CONFIG_GC9A01_BUFFER_MODE) +DMA_ATTR uint16_t ScreenBuff[GC9A01_Height * GC9A01_Width]; +#endif + +//SPI Config +spi_device_handle_t spi; +spi_host_device_t LCD_HOST=CONFIG_GC9A01_SPI_HOST; + +//LEDC Config +#if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED) +#if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_MODE) +ledc_channel_config_t ledc_cConfig; +ledc_timer_config_t ledc_tConfig; +void LEDC_PWM_Duty_Set(uint8_t DutyP); +#endif +#endif + +/* + The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. +*/ +typedef struct { + uint8_t cmd; + uint8_t data[16]; + uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds. +} lcd_init_cmd_t; + +static const lcd_init_cmd_t lcd_init_cmds[]={ + {0xef,{0},0}, + {0xeb,{0x14},1}, + {0xfe,{0},0}, + {0xef,{0},0}, + {0xeb,{0x14},1}, + {0x84,{0x40},1}, + {0x85,{0xff},1}, + {0x86,{0xff},1}, + {0x87,{0xff},1}, + {0x88,{0x0a},1}, + {0x89,{0x21},1}, + {0x8a,{0x00},1}, + {0x8b,{0x80},1}, + {0x8c,{0x01},1}, + {0x8d,{0x01},1}, + {0x8e,{0xff},1}, + {0x8f,{0xff},1}, + {Cmd_DisplayFunctionControl,{0x00,0x20},2},// Scan direction S360 -> S1 + //{Cmd_MADCTL,{0x08},1},//MemAccessModeSet(0, 0, 0, 1); + //{Cmd_COLMOD,{ColorMode_MCU_16bit&0x77},1}, + {0x90,{0x08,0x08,0x08,0x08},4}, + {0xbd,{0x06},1}, + {0xbc,{0x00},1}, + {0xff,{0x60,0x01,0x04},3}, + {Cmd_PWCTR2,{0x13},1}, + {Cmd_PWCTR3,{0x13},1}, + {Cmd_PWCTR4,{0x22},1}, + {0xbe,{0x11},1}, + {0xe1,{0x10,0x0e},2}, + {0xdf,{0x21,0x0c,0x02},3}, + {Cmd_GAMMA1,{0x45,0x09,0x08,0x08,0x26,0x2a},6}, + {Cmd_GAMMA2,{0x43,0x70,0x72,0x36,0x37,0x6f},6}, + {Cmd_GAMMA3,{0x45,0x09,0x08,0x08,0x26,0x2a},6}, + {Cmd_GAMMA4,{0x43,0x70,0x72,0x36,0x37,0x6f},6}, + {0xed,{0x1b,0x0b},2}, + {0xae,{0x77},1}, + {0xcd,{0x63},1}, + {0x70,{0x07,0x07,0x04,0x0e,0x0f,0x09,0x07,0x08,0x03},9}, + {Cmd_FRAMERATE,{0x34},1},// 4 dot inversion + {0x62,{0x18,0x0D,0x71,0xED,0x70,0x70,0x18,0x0F,0x71,0xEF,0x70,0x70},12}, + {0x63,{0x18,0x11,0x71,0xF1,0x70,0x70,0x18,0x13,0x71,0xF3,0x70,0x70},12}, + {0x64,{0x28,0x29,0xF1,0x01,0xF1,0x00,0x07},7}, + {0x66,{0x3C,0x00,0xCD,0x67,0x45,0x45,0x10,0x00,0x00,0x00},10}, + {0x67,{0x00,0x3C,0x00,0x00,0x00,0x01,0x54,0x10,0x32,0x98},10}, + {0x74,{0x10,0x85,0x80,0x00,0x00,0x4E,0x00},7}, + {0x98,{0x3e,0x07},2}, + {Cmd_TEON,{0},0},// Tearing effect line on + {0, {0}, 0xff},//END +}; + +//This function is called (in irq context!) just before a transmission starts. It will +//set the D/C line to the value indicated in the user field. +static IRAM_ATTR void lcd_spi_pre_transfer_callback(spi_transaction_t *t) +{ + int dc=(int)t->user; + gpio_set_level(CONFIG_GC9A01_PIN_NUM_DC, dc); +} + +/* Send a command to the LCD. Uses spi_device_polling_transmit, which waits + * until the transfer is complete. + * + * Since command transactions are usually small, they are handled in polling + * mode for higher speed. The overhead of interrupt transactions is more than + * just waiting for the transaction to complete. + */ +void lcd_cmd(uint8_t cmd) +{ + esp_err_t ret; + spi_transaction_t t; + memset(&t, 0, sizeof(t)); //Zero out the transaction + t.length=8; //Command is 8 bits + t.tx_buffer=&cmd; //The data is the cmd itself + t.user=(void*)0; //D/C needs to be set to 0 + ret=spi_device_polling_transmit(spi, &t); //Transmit! + assert(ret==ESP_OK); //Should have had no issues. +} + +/* Send data to the LCD. Uses spi_device_polling_transmit, which waits until the + * transfer is complete. + * + * Since data transactions are usually small, they are handled in polling + * mode for higher speed. The overhead of interrupt transactions is more than + * just waiting for the transaction to complete. + */ +void lcd_data(const uint8_t *data, int len) +{ + esp_err_t ret; + if (len==0) return; //no need to send anything + + /* + On certain MC's the max SPI DMA transfer length might be smaller than the buffer. We then have to split the transmissions. + */ + int offset = 0; + do { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); //Zero out the transaction + + int tx_len = ((len - offset) < SPI_MAX_DMA_LEN) ? (len - offset) : SPI_MAX_DMA_LEN; + t.length=tx_len * 8; //Len is in bytes, transaction length is in bits. + t.tx_buffer= data + offset; //Data + t.user=(void*)1; //D/C needs to be set to 1 + ret=spi_device_polling_transmit(spi, &t); //Transmit! + assert(ret==ESP_OK); //Should have had no issues. + offset += tx_len; // Add the transmission length to the offset + } + while (offset < len); +} + +void lcd_send_byte(uint8_t Data) +{ + lcd_data(&Data,1); +} + +void delay_ms (uint32_t Delay_ms) +{ + vTaskDelay(Delay_ms/portTICK_PERIOD_MS); +} + +uint16_t GC9A01_GetWidth() { + return GC9A01_Width; +} + +uint16_t GC9A01_GetHeight() { + return GC9A01_Height; +} + +void GC9A01_HardReset(void) { + #if (CONFIG_GC9A01_RESET_USED) + RESET_LOW(); + delay_ms(10); + RESET_HIGH(); + delay_ms(150); + #endif +} + +void GC9A01_SleepMode(uint8_t Mode) { + if (Mode) + lcd_cmd(Cmd_SLPIN); + else + lcd_cmd(Cmd_SLPOUT); + + delay_ms(500); +} + +void GC9A01_InversionMode(uint8_t Mode) { + if (Mode) + lcd_cmd(Cmd_INVON); + else + lcd_cmd(Cmd_INVOFF); +} + +void GC9A01_DisplayPower(uint8_t On) { + if (On) + lcd_cmd(Cmd_DISPON); + else + lcd_cmd(Cmd_DISPOFF); +} + +static void ColumnSet(uint16_t ColumnStart, uint16_t ColumnEnd) { + if (ColumnStart > ColumnEnd) + return; + if (ColumnEnd > GC9A01_Width) + return; + + ColumnStart += GC9A01_X_Start; + ColumnEnd += GC9A01_X_Start; + + lcd_cmd(Cmd_CASET); + lcd_send_byte(ColumnStart >> 8); + lcd_send_byte(ColumnStart & 0xFF); + lcd_send_byte(ColumnEnd >> 8); + lcd_send_byte(ColumnEnd & 0xFF); +} + +static void RowSet(uint16_t RowStart, uint16_t RowEnd) { + if (RowStart > RowEnd) + return; + if (RowEnd > GC9A01_Height) + return; + + RowStart += GC9A01_Y_Start; + RowEnd += GC9A01_Y_Start; + + lcd_cmd(Cmd_RASET); + lcd_send_byte(RowStart >> 8); + lcd_send_byte(RowStart & 0xFF); + lcd_send_byte(RowEnd >> 8); + lcd_send_byte(RowEnd & 0xFF); +} + +void GC9A01_SetWindow(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { + ColumnSet(x0, x1); + RowSet(y0, y1); + + lcd_cmd(Cmd_RAMWR); +} + +static void ColorModeSet(uint8_t ColorMode) { + lcd_cmd(Cmd_COLMOD); + lcd_send_byte(ColorMode & 0x77); +} + +static void MemAccessModeSet(uint8_t Rotation, uint8_t VertMirror, + uint8_t HorizMirror, uint8_t IsBGR) { + uint8_t Ret=0; + Rotation &= 7; + + lcd_cmd(Cmd_MADCTL); + + switch (Rotation) { + case 0: + Ret = 0; + break; + case 1: + Ret = MADCTL_MX; + break; + case 2: + Ret = MADCTL_MY; + break; + case 3: + Ret = MADCTL_MX | MADCTL_MY; + break; + case 4: + Ret = MADCTL_MV; + break; + case 5: + Ret = MADCTL_MV | MADCTL_MX; + break; + case 6: + Ret = MADCTL_MV | MADCTL_MY; + break; + case 7: + Ret = MADCTL_MV | MADCTL_MX | MADCTL_MY; + break; + } + + if (VertMirror) + Ret = MADCTL_ML; + if (HorizMirror) + Ret = MADCTL_MH; + + if (IsBGR) + Ret |= MADCTL_BGR; + + lcd_send_byte(Ret); +} + +void GC9A01_SetBL(uint8_t Value) +{ + if (Value > 100) Value = 100; + #if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED) + #if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_MODE) + LEDC_PWM_Duty_Set(Value); + #else + if (Value) BLK_HIGH(); + else BLK_LOW(); + #endif + #endif +} + +//Direct Mode +#if (!CONFIG_GC9A01_BUFFER_MODE) + + void GC9A01_RamWrite(uint16_t *pBuff, uint16_t Len) + { + while (Len--) + { + lcd_send_byte(*pBuff >> 8); + lcd_send_byte(*pBuff & 0xFF); + } + } + + void GC9A01_DrawPixel(int16_t x, int16_t y, uint16_t color) + { + if ((x < 0) ||(x >= GC9A01_Width) || (y < 0) || (y >= GC9A01_Height)) + return; + + GC9A01_SetWindow(x, y, x, y); + GC9A01_RamWrite(&color, 1); + } + + void GC9A01_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) + { + if ((x >= GC9A01_Width) || (y >= GC9A01_Height)) + return; + + if ((x + w) > GC9A01_Width) + w = GC9A01_Width - x; + + if ((y + h) > GC9A01_Height) + h = GC9A01_Height - y; + + GC9A01_SetWindow(x, y, x + w - 1, y + h - 1); + + for (uint32_t i = 0; i < (h * w); i++) + GC9A01_RamWrite(&color, 1); + } + +//Buffer mode +#else + + static void SwapBytes(uint16_t *color) { + uint8_t temp = *color >> 8; + *color = (*color << 8) | temp; + } + + uint16_t GC9A01_GetPixel(int16_t x, int16_t y) { + if ((x < 0) || (x >= GC9A01_Width) || (y < 0) || (y >= GC9A01_Height)) + return 0; + + uint16_t color = ScreenBuff[y * GC9A01_Width + x]; + SwapBytes(&color); + return color; + } + + void GC9A01_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color) { + if ((w <= 0) || (h <= 0) || (x >= GC9A01_Width) || (y >= GC9A01_Height)) + return; + + if (x < 0) { + w += x; + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + + if ((w <= 0) || (h <= 0)) + return; + + if ((x + w) > GC9A01_Width) + w = GC9A01_Width - x; + if ((y + h) > GC9A01_Height) + h = GC9A01_Height - y; + + SwapBytes(&color); + + for (uint16_t row = 0; row < h; row++) { + for (uint16_t col = 0; col < w; col++) + //GC9A01_DrawPixel(col, row, color); + ScreenBuff[(y + row) * GC9A01_Width + x + col] = color; + } + } + + void GC9A01_Update() + { + int len = GC9A01_Width * GC9A01_Height; + GC9A01_SetWindow(0, 0, GC9A01_Width - 1, GC9A01_Height - 1); + lcd_data((uint8_t*) &ScreenBuff[0], len*2); + } + + void GC9A01_Clear(void) + { + GC9A01_FillRect(0, 0, GC9A01_Width, GC9A01_Height, 0x0000); + } + +#endif + +static void gc9a01_GPIO_init(void) +{ + gpio_config_t gpiocfg={ + .pin_bit_mask= ((uint64_t)1UL<<CONFIG_GC9A01_PIN_NUM_DC), + .mode=GPIO_MODE_OUTPUT, + .pull_up_en=GPIO_PULLUP_DISABLE, + .pull_down_en=GPIO_PULLDOWN_DISABLE, + .intr_type=GPIO_INTR_DISABLE, + }; + + gpio_config(&gpiocfg); + gpio_set_level(CONFIG_GC9A01_PIN_NUM_DC,0); + + #if(CONFIG_GC9A01_RESET_USED) + gpiocfg.pin_bit_mask|=((uint64_t)1UL<<CONFIG_GC9A01_PIN_NUM_RST); + gpio_config(&gpiocfg); + gpio_set_level(CONFIG_GC9A01_PIN_NUM_RST,1); + #endif + + #if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED) + #if(!CONFIG_GC9A01_CONTROL_BACK_LIGHT_MODE) + gpiocfg.pin_bit_mask|=((uint64_t)1UL<<CONFIG_GC9A01_PIN_NUM_BCKL); + gpio_config(&gpiocfg); + gpio_set_level(CONFIG_GC9A01_PIN_NUM_BCKL,0); + #endif + #endif + +} + +void gc9a01_SPI_init(void) +{ + esp_err_t ret; + spi_bus_config_t buscfg={ + .mosi_io_num=CONFIG_GC9A01_PIN_NUM_MOSI, + .miso_io_num=GPIO_NUM_NC, + .sclk_io_num=CONFIG_GC9A01_PIN_NUM_SCK, + .quadwp_io_num=-1, + .quadhd_io_num=-1, + .max_transfer_sz=250*250*2, + }; + spi_device_interface_config_t devcfg={ + .clock_speed_hz=CONFIG_GC9A01_SPI_SCK_FREQ_M*1000*1000, + .mode=0, + .spics_io_num=CONFIG_GC9A01_PIN_NUM_CS, + .queue_size=7, + .pre_cb=lcd_spi_pre_transfer_callback, + }; + + ret=spi_bus_initialize(LCD_HOST,&buscfg,SPI_DMA_CH_AUTO); + ESP_ERROR_CHECK(ret); + + ret=spi_bus_add_device(LCD_HOST,&devcfg,&spi); + ESP_ERROR_CHECK(ret); +} + +#if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED) +#if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_MODE) +void LEDC_PWM_Duty_Set(uint8_t DutyP) +{ + uint16_t Duty,MaxD; + + MaxD=(1<<(int)ledc_tConfig.duty_resolution)-1; + + if(DutyP>=100)Duty=MaxD; + else + { + Duty=DutyP*(MaxD/(float)100); + } + ledc_cConfig.duty=Duty; + ledc_channel_config(&ledc_cConfig); +} + +void LEDC_Channel_Config(void) +{ + ledc_cConfig.gpio_num=CONFIG_GC9A01_PIN_NUM_BCKL; + ledc_cConfig.speed_mode=LEDC_LOW_SPEED_MODE; + ledc_cConfig.channel=LEDC_CHANNEL_0; + ledc_cConfig.intr_type=LEDC_INTR_DISABLE; + ledc_cConfig.timer_sel=LEDC_TIMER_0; + ledc_cConfig.duty=0; + ledc_cConfig.hpoint=0; + ledc_channel_config(&ledc_cConfig); +} + +void LEDC_Timer_Config(void) +{ + ledc_tConfig.speed_mode=LEDC_LOW_SPEED_MODE ; + ledc_tConfig.duty_resolution=LEDC_TIMER_8_BIT; + //ledc_tConfig.bit_num=LEDC_TIMER_8_BIT; + ledc_tConfig.timer_num=LEDC_TIMER_0; + ledc_tConfig.freq_hz=1000; + ledc_tConfig.clk_cfg=LEDC_AUTO_CLK; + ledc_timer_config(&ledc_tConfig); +} +#endif +#endif + +void GC9A01_Init() +{ + int cmd=0; + + GC9A01_X_Start = 0; + GC9A01_Y_Start = 0; + + gc9a01_GPIO_init(); + gc9a01_SPI_init(); + + #if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED) + #if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_MODE) + LEDC_Timer_Config(); + LEDC_Channel_Config(); + #endif + #endif + + #if(CONFIG_GC9A01_RESET_USED) + GC9A01_HardReset(); + #endif + + //Send all the commands + while (lcd_init_cmds[cmd].databytes!=0xff) + { + lcd_cmd(lcd_init_cmds[cmd].cmd); + lcd_data(lcd_init_cmds[cmd].data, lcd_init_cmds[cmd].databytes&0x1F); + if (lcd_init_cmds[cmd].databytes&0x80) + { + delay_ms(100); + } + cmd++; + } + + MemAccessModeSet(0,0,0,1); + ColorModeSet(ColorMode_MCU_16bit); + + GC9A01_InversionMode(1); + GC9A01_SleepMode(0); + + delay_ms(120); + GC9A01_DisplayPower(1); + delay_ms(20); + + #if(CONFIG_GC9A01_BUFFER_MODE) + GC9A01_Clear(); + GC9A01_Update(); + delay_ms(30); + #endif + + #if(CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED) + GC9A01_SetBL(100); + #endif +} + +#if(CONFIG_GC9A01_BUFFER_MODE) +void GC9A01_Screen_Shot(uint16_t x,uint16_t y,uint16_t width ,uint16_t height,uint16_t * Buffer) +{ + uint16_t i,j; + for (i=0;i<height;i++) + { + for(j=0;j<width;j++) + { + #if(!CONFIG_GC9A01_BUFFER_SCREEN_FAST_MODE) + Buffer[i*width+j]=GC9A01_GetPixel(x+j,y+i); + #else + Buffer[i*width+j]=ScreenBuff[((y+i) * GC9A01_Width )+ (x+j)]; + #endif + } + } +} +void GC9A01_Screen_Load(uint16_t x,uint16_t y,uint16_t width ,uint16_t height,uint16_t * Buffer) +{ + uint16_t i,j; + for (i=0;i<height;i++) + { + for(j=0;j<width;j++) + { + #if(!CONFIG_GC9A01_BUFFER_SCREEN_FAST_MODE) + GC9A01_DrawPixel(x+j,y+i,Buffer[i*width+j]); + #else + ScreenBuff[((y+i) * GC9A01_Width )+ (x+j)] = Buffer[i*width+j]; + #endif + } + } +} +#endif diff --git a/ports/esp32/badge23/components/gc9a01/gc9a01.h b/ports/esp32/badge23/components/gc9a01/gc9a01.h new file mode 100644 index 0000000000..aa587a2f07 --- /dev/null +++ b/ports/esp32/badge23/components/gc9a01/gc9a01.h @@ -0,0 +1,61 @@ +//-------------------------------------------------------------------------------------------------------- +// Nadyrshin Ruslan - [YouTube-channel: https://www.youtube.com/channel/UChButpZaL5kUUl_zTyIDFkQ] +// Liyanboy74 +//-------------------------------------------------------------------------------------------------------- +#ifndef _GC9A01_H +#define _GC9A01_H + +#include "sdkconfig.h" +#include <stdint.h> + +#define CONFIG_GC9A01_Width 240 +#define CONFIG_GC9A01_Height 240 +//#define # CONFIG_USE_SPI1_HOST is not set +//#define # CONFIG_USE_SPI2_HOST is not set +#define CONFIG_USE_SPI3_HOST 1 +//#define # CONFIG_USE_SPI4_HOST is not set +#define CONFIG_GC9A01_SPI_HOST 2 +#define CONFIG_GC9A01_PIN_NUM_SCK 39 +#define CONFIG_GC9A01_PIN_NUM_MOSI 41 +#define CONFIG_GC9A01_PIN_NUM_CS 40 +#define CONFIG_GC9A01_PIN_NUM_DC 42 +#define CONFIG_GC9A01_SPI_SCK_FREQ_M 80 +//#define # CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED is not set +#define CONFIG_GC9A01_RESET_USED 1 +#define CONFIG_GC9A01_PIN_NUM_RST 38 +#define CONFIG_GC9A01_BUFFER_MODE 1 +//#define # CONFIG_GC9A01_BUFFER_SCREEN_FAST_MODE is not set +//#define # end of GC9A01 LCD Config + + +#define GC9A01_Width CONFIG_GC9A01_Width +#define GC9A01_Height CONFIG_GC9A01_Height + +extern uint16_t ScreenBuff[GC9A01_Height * GC9A01_Width]; + +static inline void GC9A01_DrawPixel(int16_t x, int16_t y, uint16_t color) { + if ((x < 0) || (x >= GC9A01_Width) || (y < 0) || (y >= GC9A01_Height)) + return; + + //SwapBytes(&color); + + ScreenBuff[y * GC9A01_Width + x] = color; + } + + +uint16_t GC9A01_GetWidth(); +uint16_t GC9A01_GetHeight(); + +void GC9A01_Init(); +void GC9A01_SleepMode(uint8_t Mode); +void GC9A01_DisplayPower(uint8_t On); +void GC9A01_DrawPixel(int16_t x, int16_t y, uint16_t color); +void GC9A01_FillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); +void GC9A01_Update(); +void GC9A01_SetBL(uint8_t Value); +uint16_t GC9A01_GetPixel(int16_t x, int16_t y); + +void GC9A01_Screen_Shot(uint16_t x,uint16_t y,uint16_t width ,uint16_t height,uint16_t * Buffer); +void GC9A01_Screen_Load(uint16_t x,uint16_t y,uint16_t width ,uint16_t height,uint16_t * Buffer); + +#endif diff --git a/ports/esp32/badge23/components/gc9a01/readme.md b/ports/esp32/badge23/components/gc9a01/readme.md new file mode 100644 index 0000000000..46156f31f4 --- /dev/null +++ b/ports/esp32/badge23/components/gc9a01/readme.md @@ -0,0 +1,51 @@ +# GC9A01 ESP-IDF Component + +Clone to `components` folder and run `idf.py menuconfig` + + + +**Add as submodule:** + +`git submodule add https://github.com/liyanboy74/gc9a01-esp-idf.git components/gc9a01` + +**Example Test:** + +```c +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "sdkconfig.h" + +#include "gc9a01.h" + +#define STACK_SIZE 2048 + +void LCD(void * arg) +{ + uint16_t Color; + GC9A01_Init(); + for(;;) + { + Color=rand(); + GC9A01_FillRect(0,0,239,239,Color); + GC9A01_Update(); + vTaskDelay(1000/portTICK_PERIOD_MS); + } +} + +void app_main(void) +{ + TaskHandle_t LCDHandle; + + xTaskCreate(LCD,"Test LCD",STACK_SIZE,NULL,tskIDLE_PRIORITY,&LCDHandle); + configASSERT(LCDHandle); +} + +``` + +- If you succeed, it's time to go one layer higher! Try [Dispcolor](https://github.com/liyanboy74/dispcolor) +- You can also use the [BMP24 to RGB565](https://github.com/liyanboy74/bmp24-to-rgb565) tools to convert and display images + diff --git a/ports/esp32/badge23/decode_image.h b/ports/esp32/badge23/decode_image.h new file mode 100644 index 0000000000..6100703afc --- /dev/null +++ b/ports/esp32/badge23/decode_image.h @@ -0,0 +1,33 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once +#include <stdint.h> +#include "esp_err.h" + +#define IMAGE_W 240 +#define IMAGE_H 240 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Decode the jpeg ``image.jpg`` embedded into the program file into pixel data. + * + * @param pixels A pointer to a pointer for an array of rows, which themselves are an array of pixels. + * Effectively, you can get the pixel data by doing ``decode_image(&myPixels); pixelval=myPixels[ypos][xpos];`` + * @return - ESP_ERR_NOT_SUPPORTED if image is malformed or a progressive jpeg file + * - ESP_ERR_NO_MEM if out of memory + * - ESP_OK on succesful decode + */ +esp_err_t decode_image(uint16_t **pixels); + +#ifdef __cplusplus +} +#endif diff --git a/ports/esp32/badge23/display.c b/ports/esp32/badge23/display.c new file mode 100644 index 0000000000..2b627c664c --- /dev/null +++ b/ports/esp32/badge23/display.c @@ -0,0 +1,121 @@ +#include "display.h" +#include "components/gc9a01/gc9a01.h" + +#include "esp_log.h" +#include <freertos/FreeRTOS.h> +#include <freertos/task.h> +#include <freertos/timers.h> +#include <freertos/queue.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +//#include <audio.h> + +#include "scope.h" +#include "esp_system.h" + +uint16_t *pixels; +#include "decode_image.h" + +typedef struct leds_cfg { + bool active_paddles[10]; +} display_cfg_t; + +static QueueHandle_t display_queue = NULL; +static void display_task(TimerHandle_t aaaaa); +//static void display_task(void* arg); + +static void _display_init() { + GC9A01_Init(); + +#if 0 + uint16_t Color; + for(;;) + { + Color=rand(); + GC9A01_FillRect(0,0,239,239,Color); + GC9A01_Update(); + vTaskDelay(1000/portTICK_PERIOD_MS); + } +#endif + + //decode_image(&pixels); + GC9A01_Screen_Load(0,0,240,240,pixels); + GC9A01_Update(); + + /* + display_queue = xQueueCreate(1, sizeof(display_cfg_t)); + TaskHandle_t handle; + xTaskCreate(&display_task, "Display", 4096, NULL, configMAX_PRIORITIES - 3, &handle); + */ + TimerHandle_t aa = xTimerCreate("Display", pdMS_TO_TICKS(100), pdTRUE, (void *) 0, *display_task); + if( xTimerStart(aa, 0 ) != pdPASS ) + { + } +} + +//static void display_task(void* arg) { +static void display_task(TimerHandle_t aaaaa) { + display_cfg_t display_; + // printf("hewo!"); + + //static const int paddle_pos[10][2] = {{120, 240}, {190, 217}, {234, 157}, {234, 82}, {190, 22}, {120, 0}, {49, 22}, {5, 82}, {5, 157}, {49, 217}}; + uint16_t line[240]; + //while(true) { + /* + printf("waiting...\n"); + xQueueReceive(display_queue, &display_, portMAX_DELAY); + printf("go...\n"); + + bool any_active = false; + for(int i=0; i<10; i++) { + any_active |= display_.active_paddles[i]; + } + + if(any_active) { + */ + // printf("hewwo!"); + if(1) { + uint32_t t0 = esp_log_timestamp(); + /* + for(int y=0; y<240; y++) { + for(int x=0; x<240; x++) { + uint16_t Color=0; + for(int i=0; i<10; i++) { + if(display_.active_paddles[i]) { + int x_d = x - paddle_pos[i][0]; + int y_d = y - paddle_pos[i][1]; + + int dist = (x_d * x_d + y_d * y_d) / 64; + Color += dist; + } + } + line[x] = Color; + } + memcpy(&ScreenBuff[y * 240], line, sizeof(line)); + } + */ + begin_scope_read(); + for(int y=0; y<240; y++){ + read_line_from_scope(&(line[0]), y); + memcpy(&ScreenBuff[y * 240], line, sizeof(line)); + } + end_scope_read(); + uint32_t td = esp_log_timestamp() - t0; + // printf("it took %lu\n", td); + + } else { + GC9A01_Screen_Load(0,0,240,240,pixels); + } + GC9A01_Update(); + //} +} + +void display_init() { _display_init(); } + +void display_show(bool active_paddles[10]) { + display_cfg_t display = {0,}; + + memcpy(display.active_paddles, active_paddles, sizeof(active_paddles[0]) * 10); + xQueueOverwrite(display_queue, &display); +} diff --git a/ports/esp32/badge23/display.h b/ports/esp32/badge23/display.h new file mode 100644 index 0000000000..cd319d1be7 --- /dev/null +++ b/ports/esp32/badge23/display.h @@ -0,0 +1,6 @@ +#pragma once + +#include <stdbool.h> + +void display_init(); +void display_show(bool active_paddles[10]); diff --git a/ports/esp32/badge23/espan.c b/ports/esp32/badge23/espan.c new file mode 100644 index 0000000000..b3dcd3c99a --- /dev/null +++ b/ports/esp32/badge23/espan.c @@ -0,0 +1,178 @@ +#include "captouch.h" +#include "audio.h" +#include "leds.h" +#include "../../py/mphal.h" +#include "display.h" + +#include "esp_log.h" +#include "driver/i2c.h" +#include "driver/spi_master.h" + +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <stdint.h> + +static const char *TAG = "espan"; + +#define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */ +#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */ +#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ +#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ + +#define CONFIG_I2C_MASTER_SDA 10 +#define CONFIG_I2C_MASTER_SCL 9 + + +static esp_err_t i2c_master_init(void) +{ + int i2c_master_port = I2C_MASTER_NUM; + + i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = CONFIG_I2C_MASTER_SDA, + .scl_io_num = CONFIG_I2C_MASTER_SCL, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = I2C_MASTER_FREQ_HZ, + }; + + i2c_param_config(i2c_master_port, &conf); + + return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); +} + +// channel -> paddle +uint8_t top_map[] = {2, 2, 2, 0, 0, 8, 8, 8, 6, 6, 4, 4}; +uint8_t bot_map[] = {1, 1, 3, 3, 5, 5, 7, 7, 9, 9}; + +static bool active_paddles[10]; +void espan_handle_captouch(uint16_t pressed_top, uint16_t pressed_bot) +{ + + bool paddles[10] = {0,}; + for(int i=0; i<12; i++) { + if(pressed_top & (1 << i)) { + paddles[top_map[i]] = true; + } + } + + for(int i=0; i<10; i++) { + if(pressed_bot & (1 << i)) { + paddles[bot_map[i]] = true; + } + } + + bool changed = false; + for(int i=0; i<10; i++) { + if(active_paddles[i] == false && paddles[i] == true) { + if(!(i == 2 || i == 8)) synth_start(i); + leds_animate(i); + active_paddles[i] = true; + changed = true; + } else if(active_paddles[i] == true && paddles[i] == false) { + active_paddles[i] = false; + changed = true; + if(!(i == 2 || i == 8)) synth_stop(i); + } + } + + if(changed) { + //display_show(active_paddles); + } +} + +#define VIB_DEPTH 0.01 +#define VIB 0 +#define VIOLIN 1 +#define VIOLIN_DECAY 10 +#define VIOLIN_VOL_BOOST 0.004 +#define VIOLIN_SENS_POW 2 + +void old_app_main(void) +{ + ESP_ERROR_CHECK(i2c_master_init()); + ESP_LOGI(TAG, "I2C initialized successfully"); + + vTaskDelay(1000 / portTICK_PERIOD_MS); + mp_hal_stdout_tx_str("test\n\r"); + + audio_init(); + leds_init(); + //display_init(); + captouch_init(); + + mp_hal_stdout_tx_str("test2\n\r"); + play_bootsound(); + //not sure how slow captouch_get_cross is so duplicating edge detection here; + bool prev_petals[10] = {0}; + //pitch bend as movement relative to inital touch pos to make intonation easier + int petal_refs[10] = {0}; + int petal_tot_prev[10] = {0}; + int petal_min[10] = {0}; + int petal_max[10] = {0}; + int petal_dir_prev[10] = {0}; + int i = 0; + void * asdasd = &i; + while(1) { + gpio_event_handler(asdasd); + + i = (i + 1) % 10; + if(!(i == 2 || i == 8)) continue; + if(VIB){ + if(active_paddles[i]){ + int x = 0; + int y = 0; + captouch_get_cross(i, &x, &y); + int tot = (x >> 1) + (y >> 1); + if(!prev_petals[i]){ + prev_petals[i] = 1; + petal_refs[i] = tot; + synth_set_bend(i, 0); + } else { + float bend = tot - petal_refs[i]; + bend *= VIB_DEPTH; + synth_set_bend(i, bend); + } + } else { + prev_petals[i] = 0; + } + } + if(VIOLIN){ + if(active_paddles[i]){ + int x = 0; + int y = 0; + captouch_get_cross(i, &x, &y); + int tot = (x >> 1) + (y >> 1); + + uint8_t dir = tot > petal_tot_prev[i]; + + if(dir != petal_dir_prev[i]){ + if(dir){ + petal_min[i] = petal_tot_prev[i]; + } else { + petal_max[i] = petal_tot_prev[i]; + } + petal_refs[i] = tot; + float vol = (VIOLIN_VOL_BOOST) * (petal_max[i] - petal_min[i]); + for(int i = 1; i < VIOLIN_SENS_POW; i++){ + vol *= vol; + } + vol += synth_get_env(i); + if(vol > 1.) vol = 1; + if(vol < 0.) vol = 0; + synth_set_vol(i, vol); + synth_start(i); + } + petal_tot_prev[i] = tot; + petal_dir_prev[i] = dir; + } + } + + vTaskDelay(10 / portTICK_PERIOD_MS); + //captouch_print_debug_info(); + } + + ESP_ERROR_CHECK(i2c_driver_delete(I2C_MASTER_NUM)); + ESP_LOGI(TAG, "I2C de-initialized successfully"); +} diff --git a/ports/esp32/badge23/espan.h b/ports/esp32/badge23/espan.h new file mode 100644 index 0000000000..85fa85ef1f --- /dev/null +++ b/ports/esp32/badge23/espan.h @@ -0,0 +1,2 @@ +#pragma once +void old_app_main(void); diff --git a/ports/esp32/badge23/idf_component.yml b/ports/esp32/badge23/idf_component.yml new file mode 100644 index 0000000000..6766c35944 --- /dev/null +++ b/ports/esp32/badge23/idf_component.yml @@ -0,0 +1,17 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_jpeg: "==1.0.0" + ## Required IDF version + idf: + version: ">=4.1.0" + # # Put list of dependencies here + # # For components maintained by Espressif: + # component: "~1.0.0" + # # For 3rd party components: + # username/component: ">=1.0.0,<2.0.0" + # username2/component2: + # version: "~1.0.0" + # # For transient dependencies `public` flag can be set. + # # `public` flag doesn't have an effect dependencies of the `main` component. + # # All dependencies of `main` are public by default. + # public: true diff --git a/ports/esp32/badge23/instruments/instrument.c b/ports/esp32/badge23/instruments/instrument.c new file mode 100644 index 0000000000..2b703921d5 --- /dev/null +++ b/ports/esp32/badge23/instruments/instrument.c @@ -0,0 +1,73 @@ +void render_audio( + instrument_descriptor_t instrument_descriptor, + instrument_t instance, + unsigned int sample_count, + float * output_vector +){ + for(unsigned int i = 0; i < sample_count; i++){ + instrument_descriptor->render_audio_sample(instance, sample_count, output_vector[2*i]); + } +} + +void render_audio_adding( + instrument_descriptor_t instrument_descriptor, + instrument_t instance, + unsigned int sample_count, + float * output_vector, + float gain +){ + if(gain <= 0.0000001) return; + float temp[2]; + for(unsigned int i = 0; i < sample_count; i++){ + instrument_descriptor->render_audio_sample(instance, sample_count, temp); + output_vector[2*i] += gain * buffer[0]; + output_vector[2*i+1] += gain * buffer[1]; + } +} + +void append_instrument_descriptor( + instrument_descriptor_list_t * list, + void * construct_instrument_descriptor +){ + lle_t * element = list; + while(element->next != NULL){ + element = element->next; + } + element->next = malloc(sizeof(lle_t); + if(element->next == NULL) return; + element = element->next; + element->next = NULL; + + element->content = construct_instrument_descriptor(); +} + +instrument_descriptor_list_t * list_builtin_instrument_descriptors(){ + //really hope we can make this more elegant some day + instrument_descriptor_list_t * list = NULL; + + //add your instrument here! + append_instrument_descriptor(list, &minimal_example_descriptor); + + return list; +} + +void instantiate_instrument(active_instrument_list_t instruments, + instrument_descriptor_t descriptor +){ + descriptor->new_instrument +} + +void mix_instruments_audio(active_instrument_list_t instruments, + unsigned int sample_count, + float * output_vector +){ + active_instrument_t instrument = instruments; + while(instrument->next != NULL){ + render_audio_adding(instrument->descriptor, + instrument->instrument, + sample_count, + output_vector, + instrument->gain) + instrument = instrument->next; + } +} diff --git a/ports/esp32/badge23/instruments/instrument.h b/ports/esp32/badge23/instruments/instrument.h new file mode 100644 index 0000000000..77ad119847 --- /dev/null +++ b/ports/esp32/badge23/instruments/instrument.h @@ -0,0 +1,130 @@ +/* wip instrument api for badge23 (in large parts inspired by ladspa) + +current status: none of this is hooked up or functional or compiles, just drafting things rn + +some core concepts: + +- several instruments can run at the same time (e.g., drum computer running in background), +with only one of them being in foreground and using buttons and display + +- instruments are as "self contained" as possible, i.e. they access buttons and display with +minimal host involvement and pretty much just produce audio "on their own". aside from +scheduling the host mostly does some cap touch preprocessing and the audio output mixdown + +- different timing requirements -> different "threads" for audio, buttons, display each +(leds: special case, see below) + +open questions: + +- led animations: instruments should be able to output led patterns. maybe keeping a dummy +led array in ram for each running instrument and allowing users to create and run "shaders" +would be a desirable functional mode; do we want this/does this need any extra api? + +- for performance reasons: instruments are expected to behave themselves, i.e. not access hw +without permission or write to read-only locations, can we do better than that without +excessive overhead? and if we can, do we _want_ to? (devices that make electronic musical +instruments malfunction on purpose are not uncommon in general) + +- badge link/cv data input/output: can probably be added to the descriptor easily? shouldn't +freeze api before tho +*/ + +//=========================================================================================== +//some hardware dummy definitions, move somewhere else someday maybe +typedef struct { + int intensity; //touch strength, considered as no touch if below 0 + int rad; //radial position + int az; //cw azimuthal position (only meaningful for top petals); +} petal_t; + +typedef int button_t; //0: no press, -1: left, 1: right, 2: down + +typedef struct { //read-only (shared between all instruments, unprotected) + petal_t petals[10]; //even: top, odd: bottom, 0 at usb-c jack, count ccw + button_t button; //can be either left or right, depending on host + //handedness settings. the other button is reserved for + //host use and is not exposed here. +} hardware_inputs_t; +//=========================================================================================== + +typedef void * instrument_t; //contains instrument instance data, not to be interpreted by host + +typedef struct _instrument_descriptor_t { + unsigned int unique_id; + //null terminated instrument name + const char * name; + + //allocates memory for new instance data and returns pointer to it (mandatory) + instrument_t (* new_instrument)(const struct _instrument_descriptor * descriptor, + unsigned int sample_rate, + hardware_inputs_t * hw); + + //frees instance data memory (mandatory) + void (* delete_instrument) (instrument_t instance); + + //renders a single stereo audio sample (optional, NULL if not supported) + void (* render_audio_sample) (instrument_t instance); + + //handles petal/button/sensor input, ideally runs every 3-8ms (optional, NULL if not supported) + void (* process_user_input) (instrument_t instance); + + //only runs when instrument is in foreground (optional, NULL if not supported) + void (* update_display) (instrument_t instance); + + //(optional, NULL if not supported) + void (* update_leds) (instrument_t instance); +} instrument_descriptor_t; + +// this function is called once per instrument type before use (i.e. around boot) and returns a +// filled out instrument descriptor struct to be used by the host for creating instrument instances +// returns null if allocation fails +const instrument_descriptor_t * contruct_instrument_descriptor(); + +//=========================================================================================== +//host-side helper functions + +void render_audio( + instrument_descriptor_t instrument_descriptor, + instrument_t instance, + unsigned int sample_count, + float * output_vector +); + +void render_audio_adding( + instrument_descriptor_t instrument_descriptor, + instrument_t instance, + unsigned int sample_count, + float * output_vector, + float gain +); + +typedef struct { + void * content; + lle_t * next; +} lle_t; //linked list element + +typedef lle_t instrument_descriptor_list_t; + +typedef struct { + instrument_t * instrument; + instrument_descriptor_t descriptor; + char is_foreground; + char is_rendering_leds; + float gain; +} active_instrument_t; + +void append_instrument_descriptor( + instrument_descriptor_list_t * list, + void * construct_instrument_descriptor +); + +instrument_descriptor_list_t * list_builtin_instrument_descriptors(); + +typedef lle_t active_instrument_list_t; +void mix_instruments_audio(active_instrument_list_t instruments, + unsigned int sample_count, + float * output_vector +); + + +//=========================================================================================== diff --git a/ports/esp32/badge23/instruments/minimal_example.c b/ports/esp32/badge23/instruments/minimal_example.c new file mode 100644 index 0000000000..7673f22e17 --- /dev/null +++ b/ports/esp32/badge23/instruments/minimal_example.c @@ -0,0 +1,89 @@ +//simple instrument example implementation + +//trad_osc is made up rn, didn't check how the existing implementation works + +typedef struct{ + unsigned int * sample_rate; + hardware_inputs_t * hw; + trad_osc_t[30] osc; + unsigned int sample_rate; + hardware_inputs_t * hw; + float last_freq; //frequency of last note played to write to display +} minimal_example_t; + +void minimal_example_render_sample(instrument_handle inst, float * stereo_output){ + float acc = 0; + for(int i = 0; i < 30; i++){ + acc += trad_osc_run(&inst.osc[i], inst.sample_rate); + } + stereo_output[0] = acc; // both channels the same -> mono + stereo_output[1] = acc; +} + +void minimal_example_process_user_input(instrument_handle inst){ + static int petal_prev[10]; + for(int i = 0; i < 10; i++){ + if(inst->hw.petals[i].intensity > 0){ //if the pad is touched... + if(!petal_prev[i]){ //and it's a new touch... + if(button != 2){ //if button isn't pressed: play single note in different octaves + int j = i + (inst->hw.button + 1) * 10; //choose osc + trad_osc_start(&inst.osc[j]); //play a tone + inst->last_freq = inst.osc[j].freq; + } else { //for button center: all the octaves at once + trad_osc_start(&inst.osc[i]); //play a tone + trad_osc_start(&inst.osc[i+10]); //play a tone + trad_osc_start(&inst.osc[i+20]); //play a tone + inst->last_freq = inst.osc[i+10].freq; + } + } + petal_prev[i] = 1; + } else { + petal_prev[i] = 0; + } + } +} + +void minimal_example_update_display(instrument_handle inst){ + display_print("%f", inst->last_freq); +} + +static float pad_to_freq(int pad){ + int j = pad % 10; + if(j) j++; //skip min 2nd + if(j>8) j++; //skip maj 6th + j += (pad/10) * 12; //add octaves + float freq = 440 * pow(2., j/12.); +} + +instrument_t new_minimal_example( + const struct _instrument_descriptor * descriptor, + unsigned int sample_rate, + hardware_inputs_t * hw) +{ + instrument_t inst = malloc(sizeof(minimal_example_t)); + if(inst == NULL) return NULL; + inst->sample_rate = sample_rate; + inst->hw = hw; + for(int i = 0; i < 30; i++){ + inst->osc[i] = trad_osc_new(pad_to_freq(i), sample_rate); + //other osc parameters (wave, envelope, etc.) omitted for clarity + } + return inst; +} + +void delete_minimal_example(instrument_t inst){ + free(inst); +} + +const instrument_descriptor_t * minimal_example_descriptor(){ + instrument_descriptor_t * inst = malloc(sizeof(instrument_descriptor_t)); + if(inst == NULL) return NULL; + inst->unique_id = 0; + inst->name = "simple instrument"; + inst->render_audio_sample = &minimal_example_render_sample; + inst->new_instrument = &new_minimal_example; + inst->delete_instrument = &delete_minimal_example; + inst->update_display = NULL; + inst->process_user_input = minimal_example_user_input; + inst->update_leds = NULL; +} diff --git a/ports/esp32/badge23/leds.c b/ports/esp32/badge23/leds.c new file mode 100644 index 0000000000..f314192269 --- /dev/null +++ b/ports/esp32/badge23/leds.c @@ -0,0 +1,348 @@ +#include "leds.h" + +#include "driver/spi_master.h" + +#include <freertos/FreeRTOS.h> +#include <freertos/task.h> +#include <freertos/queue.h> +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include "esp_system.h" + +typedef struct leds_cfg { + int leaf; + size_t size; + size_t position; + size_t slot; + int timer; +} leds_cfg_t; + +static QueueHandle_t leds_queue = NULL; +static leds_cfg_t active_leds[11]; +static void leds_task(void* arg); + +#include "apa102LEDStrip.h" +static struct apa102LEDStrip leds; + +//SPI Vars +static spi_device_handle_t spi_led; +static spi_transaction_t spiTransObject; +static esp_err_t ret; +static spi_bus_config_t buscfg; +static spi_device_interface_config_t devcfg; + +#define totalPixels 40 +#define bytesPerPixel 4 +#define maxValuePerColour 255 +#define maxSPIFrameInBytes 8000 +#define maxSPIFrequency 10000000 + +struct RGB +{ + unsigned char R; + unsigned char G; + unsigned char B; +}; + +struct HSV +{ + double H; + double S; + double V; +}; + +struct RGB HSVToRGB(struct HSV hsv) { + double r = 0, g = 0, b = 0; + + if (hsv.S == 0) + { + r = hsv.V; + g = hsv.V; + b = hsv.V; + } + else + { + int i; + double f, p, q, t; + + if (hsv.H == 360) + hsv.H = 0; + else + hsv.H = hsv.H / 60; + + i = (int)trunc(hsv.H); + f = hsv.H - i; + + p = hsv.V * (1.0 - hsv.S); + q = hsv.V * (1.0 - (hsv.S * f)); + t = hsv.V * (1.0 - (hsv.S * (1.0 - f))); + + switch (i) + { + case 0: + r = hsv.V; + g = t; + b = p; + break; + + case 1: + r = q; + g = hsv.V; + b = p; + break; + + case 2: + r = p; + g = hsv.V; + b = t; + break; + + case 3: + r = p; + g = q; + b = hsv.V; + break; + + case 4: + r = t; + g = p; + b = hsv.V; + break; + + default: + r = hsv.V; + g = p; + b = q; + break; + } + + } + + struct RGB rgb; + rgb.R = r * 255; + rgb.G = g * 255; + rgb.B = b * 255; + + return rgb; +} + +static void renderLEDs() +{ + spi_device_queue_trans(spi_led, &spiTransObject, portMAX_DELAY); +} + +static int setupSPI() +{ + //Set up the Bus Config struct + buscfg.miso_io_num=-1; + buscfg.mosi_io_num=18; + buscfg.sclk_io_num=8; + buscfg.quadwp_io_num=-1; + buscfg.quadhd_io_num=-1; + buscfg.max_transfer_sz=maxSPIFrameInBytes; + + //Set up the SPI Device Configuration Struct + devcfg.clock_speed_hz=maxSPIFrequency; + devcfg.mode=0; + devcfg.spics_io_num=-1; + devcfg.queue_size=1; + + //Initialize the SPI driver + ret=spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO); + ESP_ERROR_CHECK(ret); + //Add SPI port to bus + ret=spi_bus_add_device(SPI2_HOST, &devcfg, &spi_led); + ESP_ERROR_CHECK(ret); + return ret; +} + +static uint8_t black[] = {0,0,0}; + +static const uint8_t pl[][9] = { + {39, 0, 1, 2, 3, 4, 5, 6, 7}, + {3, 4, 5, 6, 7, 8, 9, 10, 11}, + {7, 8, 9, 10, 11, 12, 13, 14, 15}, + {11, 12, 13, 14, 15, 16, 17, 18, 19}, + {15, 16, 17, 18, 19, 20, 21, 22, 23}, + {19, 20, 21, 22, 23, 24, 25, 26, 27}, + {23, 24, 25, 26, 27, 28, 29, 30, 31}, + {27, 28, 29, 30, 31, 32, 33, 34, 35}, + {31, 32, 33, 34, 35, 36, 37, 38, 39}, + {35, 36, 37, 38, 39, 0, 1, 2, 3} + }; + +static const int8_t pan_leds[][11][2] = { + { + {pl[0][4], pl[0][4]}, {pl[0][3], pl[0][5]}, {pl[0][2], pl[0][6]}, {pl[0][1], pl[0][7]}, {pl[0][0], pl[0][8]}, + {pl[4][0], pl[6][8]}, {pl[4][1], pl[6][7]}, {pl[4][2], pl[6][6]}, {pl[4][3], pl[6][5]}, {pl[4][4], pl[6][4]} + ,{-1, -1} + }, + { + {pl[2][4], pl[0][4]}, {pl[2][3], pl[0][5]}, {pl[2][2], pl[0][6]}, {pl[2][1], pl[0][7]}, {pl[2][0], pl[0][8]}, + {pl[6][8], pl[6][0]}, {pl[6][6], pl[6][1]}, {pl[6][6], pl[6][2]}, {pl[6][5], pl[6][3]}, {pl[6][4], pl[6][4]} + ,{-1, -1} + }, + { + {pl[2][4], pl[2][4]}, {pl[2][3], pl[2][5]}, {pl[2][2], pl[2][6]}, {pl[2][1], pl[2][7]}, {pl[2][0], pl[2][8]}, + {pl[6][0], pl[8][8]}, {pl[6][1], pl[8][7]}, {pl[6][2], pl[8][6]}, {pl[6][3], pl[8][5]}, {pl[6][4], pl[8][4]} + ,{-1, -1} + }, + { + {pl[4][4], pl[2][4]}, {pl[4][3], pl[2][5]}, {pl[4][2], pl[2][6]}, {pl[4][1], pl[2][7]}, {pl[4][0], pl[2][8]}, + {pl[8][8], pl[8][0]}, {pl[8][6], pl[8][1]}, {pl[8][6], pl[8][2]}, {pl[8][5], pl[8][3]}, {pl[8][4], pl[8][4]} + ,{-1, -1} + }, + { + {pl[4][4], pl[4][4]}, {pl[4][3], pl[4][5]}, {pl[4][2], pl[4][6]}, {pl[4][1], pl[4][7]}, {pl[4][0], pl[4][8]}, + {pl[8][0], pl[0][8]}, {pl[8][1], pl[0][7]}, {pl[8][2], pl[0][6]}, {pl[8][3], pl[0][5]}, {pl[8][4], pl[0][4]} + ,{-1, -1} + }, + { + {pl[6][4], pl[4][4]}, {pl[6][3], pl[4][5]}, {pl[6][2], pl[4][6]}, {pl[6][1], pl[4][7]}, {pl[6][0], pl[4][8]}, + {pl[0][8], pl[0][0]}, {pl[0][6], pl[0][1]}, {pl[0][6], pl[0][2]}, {pl[0][5], pl[0][3]}, {pl[0][4], pl[0][4]} + ,{-1, -1} + }, + { + {pl[6][4], pl[6][4]}, {pl[6][3], pl[6][5]}, {pl[6][2], pl[6][6]}, {pl[6][1], pl[6][7]}, {pl[6][0], pl[6][8]}, + {pl[0][0], pl[6][2]}, {pl[0][1], pl[2][7]}, {pl[0][2], pl[2][6]}, {pl[0][3], pl[2][5]}, {pl[0][4], pl[2][4]} + ,{-1, -1} + }, + { + {pl[8][4], pl[6][4]}, {pl[8][3], pl[6][5]}, {pl[8][2], pl[6][6]}, {pl[8][1], pl[6][7]}, {pl[8][0], pl[6][8]}, + {pl[2][8], pl[2][0]}, {pl[2][6], pl[2][1]}, {pl[2][6], pl[2][2]}, {pl[2][5], pl[2][3]}, {pl[2][4], pl[2][4]} + ,{-1, -1} + }, + { + {pl[8][4], pl[8][4]}, {pl[8][3], pl[8][5]}, {pl[8][2], pl[8][6]}, {pl[8][1], pl[8][7]}, {pl[8][0], pl[8][8]}, + {pl[2][0], pl[4][4]}, {pl[2][1], pl[4][7]}, {pl[2][2], pl[4][6]}, {pl[2][3], pl[4][5]}, {pl[2][4], pl[4][4]} + ,{-1, -1} + }, + { + {pl[0][4], pl[8][4]}, {pl[0][3], pl[8][5]}, {pl[0][2], pl[8][6]}, {pl[0][1], pl[8][7]}, {pl[0][0], pl[8][8]}, + {pl[4][8], pl[4][0]}, {pl[4][6], pl[4][1]}, {pl[4][6], pl[4][2]}, {pl[4][5], pl[4][3]}, {pl[4][4], pl[4][4]} + ,{-1, -1} + }, + + }; + + +static void _leds_init() { + leds_queue = xQueueCreate(10, sizeof(leds_cfg_t)); + memset(active_leds, 0 , sizeof(active_leds)); + + setupSPI(); + initLEDs(&leds, totalPixels, bytesPerPixel, 255); + + //Set up SPI tx/rx storage Object + memset(&spiTransObject, 0, sizeof(spiTransObject)); + spiTransObject.length = leds._frameLength*8; + spiTransObject.tx_buffer = leds.LEDs; + + + TaskHandle_t handle; + xTaskCreate(&leds_task, "LEDs player", 4096, NULL, configMAX_PRIORITIES - 2, &handle); +} + +static bool leds_active(void) +{ + for(size_t i=0; i<sizeof(active_leds) / sizeof(active_leds[0]); i++) { + if(active_leds[i].size != active_leds[i].position) { + return true; + } + } + return false; +} + + +static void leds_task(void* arg) { + leds_cfg_t leds_; + + static int timer = 0; + + while(true) { + if(xQueueReceive(leds_queue, &leds_, 1)) { + leds_.position = 0; + active_leds[leds_.slot] = leds_; + } + + if(leds_active()) { + for(int i=0; i<40; i++) { + setPixel(&leds, i, black); + } + + for(size_t i=0; i<sizeof(active_leds) / sizeof(active_leds[0]); i++) { + if(active_leds[i].size == active_leds[i].position) continue; + + int leaf = active_leds[i].leaf; + int position = active_leds[i].position; + + uint8_t c[] = {0,0,0}; + if(leaf % 2 == 0) + c[0] = 32; + else + c[2] = 32; + + struct RGB rgb = HSVToRGB((struct HSV){leaf/9.*360,1,1}); + c[0] = rgb.R / 4; + c[1] = rgb.G / 4; + c[2] = rgb.B / 4; + + if(pan_leds[leaf][position][0] >= 0) + setPixel(&leds, pan_leds[leaf][position][0], c); + if(pan_leds[leaf][position][1] >= 0) + setPixel(&leds, pan_leds[leaf][position][1], c); + + if(active_leds[i].timer-- == 0) { + active_leds[i].timer = 2; + active_leds[i].position += 1; + } + + } + + renderLEDs(); + timer = 30; + } + + static int phase = 0; + + if(!leds_active()) { + // background anim + + if(timer-- == 0) { + timer = 60; + for(int i=0; i<40; i++) { + setPixel(&leds, i, black); + } + + if(phase++ == 360) { + phase = 0; + } + + uint8_t c[] = {0,0,0}; + + for(int i=0; i<40; i++) { + struct RGB rgb = HSVToRGB((struct HSV){(phase+i*3) % 360,1,0.125}); + c[0] = rgb.R / 1; + c[1] = rgb.G / 1; + c[2] = rgb.B / 1; + setPixel(&leds, i, c); + } + + renderLEDs(); + } + } + } +} + +void leds_init() { _leds_init(); } + +void leds_animate(int leaf) { + leds_cfg_t leds = {0,}; + + leds.leaf = leaf; + leds.slot = leaf; + leds.size = 11; + xQueueSend(leds_queue, &leds, 0); +} diff --git a/ports/esp32/badge23/leds.h b/ports/esp32/badge23/leds.h new file mode 100644 index 0000000000..a14fc13bd1 --- /dev/null +++ b/ports/esp32/badge23/leds.h @@ -0,0 +1,4 @@ +#pragma once + +void leds_init(); +void leds_animate(int leaf); diff --git a/ports/esp32/badge23/scope.c b/ports/esp32/badge23/scope.c new file mode 100644 index 0000000000..689c691a1f --- /dev/null +++ b/ports/esp32/badge23/scope.c @@ -0,0 +1,63 @@ +#include "scope.h" +#include <string.h> + +scope_t * scope; + +void init_scope(uint16_t size){ + scope_t * scp = malloc(sizeof(scope_t)); + if(scp == NULL) scope = NULL; + scp->buffer_size = size; + scp->buffer = malloc(sizeof(int16_t) * scp->buffer_size); + if(scp->buffer == NULL){ + free(scp); + scope = NULL; + } else { + memset(scp->buffer, 0, sizeof(int16_t) * scp->buffer_size); + scope = scp; + } +} + +void write_to_scope(int16_t value){ + if(scope->is_being_read) return; + if(scope == NULL) return; + scope->write_head_position++; + if(scope->write_head_position >= scope->buffer_size) scope->write_head_position = 0; + scope->buffer[scope->write_head_position] = value; +} + +void begin_scope_read(){ + if(scope == NULL) return; + scope->is_being_read = 1; //best mutex +} + +void read_line_from_scope(uint16_t * line, int16_t point){ + memset(line, 0, 480); + int16_t startpoint = scope->write_head_position - point; + while(startpoint < 0){ + startpoint += scope->buffer_size; + } + int16_t stoppoint = startpoint - 1; + if(stoppoint < 0){ + stoppoint += scope->buffer_size; + } + int16_t start = (scope->buffer[point]/32 + 120); + int16_t stop = (scope->buffer[point+1]/32 + 120); + if(start>240) start = 240; + if(start<0) start = 0; + if(stop>240) stop = 240; + if(stop<0) stop = 0; + + if(start > stop){ + int16_t inter = start; + start = stop; + stop = inter; + } + for(int16_t i = start; i <= stop; i++){ + line[i] = 255; + } +} + +void end_scope_read(){ + if(scope == NULL) return; + scope->is_being_read = 0; //best mutex +} diff --git a/ports/esp32/badge23/scope.h b/ports/esp32/badge23/scope.h new file mode 100644 index 0000000000..302bea447a --- /dev/null +++ b/ports/esp32/badge23/scope.h @@ -0,0 +1,15 @@ +#pragma once +#include <stdint.h> + +typedef struct { + int16_t * buffer; + int16_t buffer_size; + int16_t write_head_position; // last written value + volatile uint8_t is_being_read; +} scope_t; + +void init_scope(uint16_t size); +void write_to_scope(int16_t value); +void begin_scope_read(); +void end_scope_read(); +void read_line_from_scope(uint16_t * line, int16_t point); diff --git a/ports/esp32/badge23/synth.c b/ports/esp32/badge23/synth.c new file mode 100644 index 0000000000..1c87a89b11 --- /dev/null +++ b/ports/esp32/badge23/synth.c @@ -0,0 +1,167 @@ +#include "synth.h" +#include "audio.h" +#include <math.h> + +float ks_osc(ks_osc_t * ks, float input){ + //TODO: FIX THIS + ks->real_feedback = ks->feedback; + + float delay_time = ((float) (SAMPLE_RATE))/ks->freq; + if(delay_time >= (KS_BUFFER_SIZE)) delay_time = (KS_BUFFER_SIZE) - 1; + + + //ks->tape[0] = input + real_feedback * ks->tape[delay_time]; + return ks->tape[0]; +} + +float waveshaper(uint8_t shape, float in); +float nes_noise(uint16_t * reg, uint8_t mode, uint8_t run); + +void trad_env(trad_osc_t * osc){ + switch(osc->env_phase){ + case 0: + osc->env = 0; osc->counter = 0; osc->env_counter = 0; + break; + case 1: + if(osc->attack_steps){ + if(osc->env == 0){ + osc->env = (TRAD_OSC_MIN_ATTACK_ENV); + } else { + osc->env_counter++; + if(osc->env_counter > osc->attack_steps){ + osc->env *= (1. + (TRAD_OSC_ATTACK_STEP)); + osc->env_counter = 0; + } + } + } else { + osc->env += osc->vol/TRAD_OSC_ATTACK_POP_BLOCK; + } + if(osc->env > osc->vol){ + osc->env_phase = 2; + osc->env = osc->vol; + } + break; + case 2: + osc->env = osc->vol; + osc->env_counter = 0; + if(osc->skip_hold) osc->env_phase = 3; + break; + case 3: + if(osc->decay_steps){ + osc->env_counter++; + if(osc->env_counter > osc->decay_steps){ + osc->env *= (1. - (TRAD_OSC_DECAY_STEP)); + osc->env_counter = 0; + } + if(osc->env < osc->gate){ + osc->env_phase = 0; osc->env = 0; osc->counter = 0; + } + } else { + osc->env_phase = 0; osc->env = 0; osc->counter = 0; + } + break; + } +} + +float trad_osc(trad_osc_t * osc){ + trad_env(osc); + if(!osc->env_phase) return 0; + float ret = 0; + + //run core sawtooth + float freq = osc->freq * osc->bend; + if(freq > 10000) freq = 10000; + if(freq < -10000) freq = -10000; + if(freq != freq) freq = 0; + osc->counter += 2. * freq / ((float)(SAMPLE_RATE)); + if(osc->counter != osc->counter){ + printf("trad_osc counter is NaN"); + abort(); + } + while(osc->counter > 1.){ + osc->counter -= 2.; + osc->overflow_event = 1; + } + while(osc->counter < -1.){ + osc->counter += 2.; + osc->overflow_event = -1; + } + + if(osc->waveform >= 7){ + ret = nes_noise(&(osc->noise_reg), osc->waveform == 7, osc->overflow_event); + osc->overflow_event = 0; + } else { + //apply waveshaper + ret = waveshaper(osc->waveform, osc->counter); + } + + //apply volume + ret *= osc->env; + return ret; +} + +float nes_noise(uint16_t * reg, uint8_t mode, uint8_t run){ + if(run) { + uint8_t fb = *reg; + if(mode){ + fb = fb>>6; + } else { + fb = fb>>1; + } + fb = (fb ^ (*reg)) & 1; + *reg = (*reg >> 1); + *reg = (*reg) | (((uint16_t) fb) << 14); + } + return ((float) ((*reg) & 1)) * 2 - 1; +} + +float waveshaper(uint8_t shape, float in){ + //expects sawtooth input in [-1..1] range + switch(shape){ + case 0: // TODO: implement proper sine + in = sin(in * 3.1415); + break; + case 1: //fast sine + in = waveshaper(2, in); + if(in > 0.){ + in = 1. - in; + in *= in; + in = 1. - in; + } else { + in = 1. + in; + in *= in; + in = in - 1.; + } + break; + case 2: //triangle + in += 0.5; + if(in > 1.0) in -= 2; + if(in > 0.) in = -in; + in = (2. * in) + 1.; + break; + case 3: //sawtooth + break; + case 4: //square + if(in > 0){ + in = 1.; + } else { + in = -1.; + } + break; + case 5: //33% pulse + if(in > 0.33){ + in = 1.; + } else { + in = -1.; + } + break; + case 6: //25% pulse + if(in > 0.5){ + in = 1.; + } else { + in = -1.; + } + break; + } + return in; +} diff --git a/ports/esp32/badge23/synth.h b/ports/esp32/badge23/synth.h new file mode 100644 index 0000000000..5d498731cd --- /dev/null +++ b/ports/esp32/badge23/synth.h @@ -0,0 +1,48 @@ +#pragma once +#include <stdint.h> +#include <stdio.h> + +#define TRAD_OSC_DECAY_STEP 0.01 +#define TRAD_OSC_ATTACK_STEP 0.01 +#define TRAD_OSC_MIN_ATTACK_ENV 0.01 +#define TRAD_OSC_ATTACK_POP_BLOCK 16 +typedef struct { + //user variables + float freq; //in hertz, negative frequencies for linFM allowed + float bend; + float vol; //output volume + float env; + uint8_t env_phase; //0: off, 1: attack, 2: hold, 3: decay + uint8_t skip_hold; + float gate; //below what volume the oscillator stops and returns 0 + uint16_t decay_steps; //after how many sample rate steps the volume reduces + //by factor TRAD_OSC_DECAY_STEP, set 0 for no decay + uint16_t attack_steps; + uint8_t waveform; //0: sine, 1: fast sine, 2: tri, 3: saw, + //4: square, 5: 33% pulse, 6: 25% pulse + + //internal data storage, not for user access + float counter; //state of central sawtooth oscillator, [-1..1] typ. + uint16_t env_counter; + int8_t overflow_event; //set to -1 when counter underflows (below -1), + //set to +1 when counter overflows (above 1) + //not reset or used by anything so far + uint16_t noise_reg; +} trad_osc_t; + +//#define KS_BUFFER_SIZE (SAMPLE_RATE)/20 +#define KS_BUFFER_SIZE 800 + +typedef struct { + //user variables + float freq; //frequency in hertz, negative frequencies are rectified, + //minimum freq determined by KS_BUFFER_SIZE + float feedback; //feedback value, will be compensated with frequency + //for equal decay across spectrum, [-1..1] without + + //internal data storage, not for user access + float tape[KS_BUFFER_SIZE]; //the delay chain + float real_feedback; //compensated feedback value +} ks_osc_t; //karplus strong + +float trad_osc(trad_osc_t * osc); diff --git a/ports/esp32/badge23/tags b/ports/esp32/badge23/tags new file mode 100644 index 0000000000..afb47d0f15 --- /dev/null +++ b/ports/esp32/badge23/tags @@ -0,0 +1,162 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ +!_TAG_OUTPUT_FILESEP slash /slash or backslash/ +!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ +!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/ +!_TAG_PROC_CWD /home/zjfms/fluff/badge2023/bootstrap/software/espan/main/ // +!_TAG_PROGRAM_AUTHOR Universal Ctags Team // +!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ +!_TAG_PROGRAM_URL https://ctags.io/ /official site/ +!_TAG_PROGRAM_VERSION 5.9.0 /p5.9.20220828.0/ +AD7147_BASE_ADDR captouch.c /^#define AD7147_BASE_ADDR /;" d file: +AD7147_REG_DEVICE_ID captouch.c /^#define AD7147_REG_DEVICE_ID /;" d file: +AD7147_REG_PWR_CONTROL captouch.c /^#define AD7147_REG_PWR_CONTROL /;" d file: +AD7147_REG_STAGE_CAL_EN captouch.c /^#define AD7147_REG_STAGE_CAL_EN /;" d file: +AD7147_REG_STAGE_HIGH_INT_ENABLE captouch.c /^#define AD7147_REG_STAGE_HIGH_INT_ENABLE /;" d file: +CIN captouch.c /^#define CIN /;" d file: +CIN_BIAS captouch.c /^#define CIN_BIAS /;" d file: +CIN_CDC_NEG captouch.c /^#define CIN_CDC_NEG /;" d file: +CIN_CDC_POS captouch.c /^#define CIN_CDC_POS /;" d file: +CONFIG_I2C_MASTER_SCL Kconfig.projbuild /^ config I2C_MASTER_SCL$/;" c menu:Example Configuration +CONFIG_I2C_MASTER_SCL_MODULE Kconfig.projbuild /^ config I2C_MASTER_SCL$/;" c menu:Example Configuration +CONFIG_I2C_MASTER_SDA Kconfig.projbuild /^ config I2C_MASTER_SDA$/;" c menu:Example Configuration +CONFIG_I2C_MASTER_SDA_MODULE Kconfig.projbuild /^ config I2C_MASTER_SDA$/;" c menu:Example Configuration +DEF_apa102LEDStrip apa102LEDStrip.h /^#define DEF_apa102LEDStrip$/;" d +DMA_BUFFER_COUNT audio.c /^#define DMA_BUFFER_COUNT /;" d file: +DMA_BUFFER_SIZE audio.c /^#define DMA_BUFFER_SIZE /;" d file: +ESP_INTR_FLAG_DEFAULT captouch.c /^#define ESP_INTR_FLAG_DEFAULT /;" d file: +Example Configuration Kconfig.projbuild /^menu "Example Configuration"$/;" m +I2C_MASTER_FREQ_HZ espan.c /^#define I2C_MASTER_FREQ_HZ /;" d file: +I2C_MASTER_NUM captouch.c /^#define I2C_MASTER_NUM /;" d file: +I2C_MASTER_NUM espan.c /^#define I2C_MASTER_NUM /;" d file: +I2C_MASTER_RX_BUF_DISABLE espan.c /^#define I2C_MASTER_RX_BUF_DISABLE /;" d file: +I2C_MASTER_SCL Kconfig.projbuild /^ config I2C_MASTER_SCL$/;" c menu:Example Configuration +I2C_MASTER_SCL_IO espan.c /^#define I2C_MASTER_SCL_IO /;" d file: +I2C_MASTER_SDA Kconfig.projbuild /^ config I2C_MASTER_SDA$/;" c menu:Example Configuration +I2C_MASTER_SDA_IO espan.c /^#define I2C_MASTER_SDA_IO /;" d file: +I2C_MASTER_TX_BUF_DISABLE espan.c /^#define I2C_MASTER_TX_BUF_DISABLE /;" d file: +LEDs apa102LEDStrip.h /^ unsigned char *LEDs;$/;" m struct:apa102LEDStrip typeref:typename:unsigned char * +MIN audio.c /^#define MIN(/;" d file: +SAMPLE_RATE audio.c /^#define SAMPLE_RATE /;" d file: +TAG captouch.c /^static const char *TAG = "captouch";$/;" v typeref:typename:const char * file: +TAG espan.c /^static const char *TAG = "espan";$/;" v typeref:typename:const char * file: +TIMEOUT_MS captouch.c /^#define TIMEOUT_MS /;" d file: +_audio_init audio.c /^static void _audio_init(int i2s_num) {$/;" f typeref:typename:void file: +_bytesPerLED apa102LEDStrip.h /^ unsigned char _bytesPerLED;$/;" m struct:apa102LEDStrip typeref:typename:unsigned char +_counter apa102LEDStrip.h /^ short int _counter;$/;" m struct:apa102LEDStrip typeref:typename:short int +_endFrameLength apa102LEDStrip.h /^ short int _endFrameLength;$/;" m struct:apa102LEDStrip typeref:typename:short int +_frameLength apa102LEDStrip.h /^ short int _frameLength;$/;" m struct:apa102LEDStrip typeref:typename:short int +_globalBrightness apa102LEDStrip.h /^ unsigned char _globalBrightness;$/;" m struct:apa102LEDStrip typeref:typename:unsigned char +_numLEDs apa102LEDStrip.h /^ short int _numLEDs;$/;" m struct:apa102LEDStrip typeref:typename:short int +active_paddles espan.c /^static bool active_paddles[10];$/;" v typeref:typename:bool[10] file: +active_sounds audio.c /^static sound_cfg_t active_sounds[11];$/;" v typeref:typename:sound_cfg_t[11] file: +ad7147_device_config captouch.c /^struct ad7147_device_config {$/;" s file: +ad7147_stage_config captouch.c /^struct ad7147_stage_config {$/;" s file: +ad714x_chip captouch.c /^struct ad714x_chip {$/;" s file: +ad714x_default_config captouch.c /^static struct ad7147_stage_config ad714x_default_config(void)$/;" f typeref:struct:ad7147_stage_config file: +ad714x_i2c_read captouch.c /^static esp_err_t ad714x_i2c_read(const struct ad714x_chip *chip, const uint16_t reg, uint16_t *d/;" f typeref:typename:esp_err_t file: +ad714x_i2c_write captouch.c /^static esp_err_t ad714x_i2c_write(const struct ad714x_chip *chip, const uint16_t reg, const uint/;" f typeref:typename:esp_err_t file: +ad714x_set_device_config captouch.c /^static void ad714x_set_device_config(const struct ad714x_chip *chip, const struct ad7147_device_/;" f typeref:typename:void file: +ad714x_set_stage_config captouch.c /^static void ad714x_set_stage_config(const struct ad714x_chip *chip, const uint8_t stage, const s/;" f typeref:typename:void file: +addr captouch.c /^ uint8_t addr;$/;" m struct:ad714x_chip typeref:typename:uint8_t file: +afe_offsets captouch.c /^ int afe_offsets[13];$/;" m struct:ad714x_chip typeref:typename:int[13] file: +apa102LEDStrip apa102LEDStrip.h /^struct apa102LEDStrip$/;" s +app_main espan.c /^void app_main(void)$/;" f typeref:typename:void +audio_init audio.c /^void audio_init() { _audio_init(0); }$/;" f typeref:typename:void +audio_player_task audio.c /^static void audio_player_task(void* arg) {$/;" f typeref:typename:void file: +avg_fp_skip captouch.c /^ unsigned int avg_fp_skip:2;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:2 file: +avg_lp_skip captouch.c /^ unsigned int avg_lp_skip:2;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:2 file: +bank2 captouch.c /^static const uint16_t bank2 = 0x80;$/;" v typeref:typename:const uint16_t file: +black espan.c /^uint8_t black[] = {0,0,0};$/;" v typeref:typename:uint8_t[] +blue espan.c /^uint8_t blue[] = {0,0,32};$/;" v typeref:typename:uint8_t[] +bot_map espan.c /^uint8_t bot_map[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4};$/;" v typeref:typename:uint8_t[] +buffer audio.c /^ const int16_t* buffer;$/;" m struct:sound_cfg typeref:typename:const int16_t * file: +buscfg espan.c /^spi_bus_config_t buscfg;$/;" v typeref:typename:spi_bus_config_t +bytesPerPixel espan.c /^#define bytesPerPixel /;" d file: +captouch_init captouch.c /^void captouch_init(void)$/;" f typeref:typename:void +captouch_init_chip captouch.c /^static void captouch_init_chip(const struct ad714x_chip* chip)$/;" f typeref:typename:void file: +captouch_print_debug_info captouch.c /^void captouch_print_debug_info(void)$/;" f typeref:typename:void +captouch_print_debug_info_chip captouch.c /^static void captouch_print_debug_info_chip(const struct ad714x_chip* chip)$/;" f typeref:typename:void file: +cdc_bias captouch.c /^ unsigned int cdc_bias:2;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:2 file: +chip_bot captouch.c /^static const struct ad714x_chip chip_bot = {.addr = AD7147_BASE_ADDR, .gpio = 3, .afe_offsets = /;" v typeref:typename:const struct ad714x_chip file: +chip_top captouch.c /^static const struct ad714x_chip chip_top = {.addr = AD7147_BASE_ADDR + 1, .gpio = 48, .afe_offse/;" v typeref:typename:const struct ad714x_chip file: +cinX_connection_setup captouch.c /^ unsigned int cinX_connection_setup[13];$/;" m struct:ad7147_stage_config typeref:typename:unsigned int[13] file: +decimation captouch.c /^ unsigned int decimation:2;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:2 file: +devcfg espan.c /^spi_device_interface_config_t devcfg;$/;" v typeref:typename:spi_device_interface_config_t +espan_handle_captouch espan.c /^void espan_handle_captouch(uint16_t pressed_top, uint16_t pressed_bot)$/;" f typeref:typename:void +ext_source captouch.c /^ unsigned int ext_source:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +free_buffer audio.c /^ bool free_buffer;$/;" m struct:sound_cfg typeref:typename:bool file: +getPixel apa102LEDStrip.c /^void getPixel(struct apa102LEDStrip *ledObject, short int pixelIndex, unsigned char *pixelColour/;" f typeref:typename:void +gpio captouch.c /^ uint8_t gpio;$/;" m struct:ad714x_chip typeref:typename:uint8_t file: +gpio_event_handler captouch.c /^static void gpio_event_handler(void* arg)$/;" f typeref:typename:void file: +gpio_evt_queue captouch.c /^static QueueHandle_t gpio_evt_queue = NULL;$/;" v typeref:typename:QueueHandle_t file: +gpio_isr_handler captouch.c /^static void IRAM_ATTR gpio_isr_handler(void* arg)$/;" f typeref:typename:void IRAM_ATTR file: +i2c_master_init espan.c /^static esp_err_t i2c_master_init(void)$/;" f typeref:typename:esp_err_t file: +initLEDs apa102LEDStrip.c /^void initLEDs(struct apa102LEDStrip *ledObject, short int numLEDs, unsigned char bytesPerLED, un/;" f typeref:typename:void +int_pol captouch.c /^ unsigned int int_pol:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +leds espan.c /^struct apa102LEDStrip leds;$/;" v typeref:struct:apa102LEDStrip +lp_conv_delay captouch.c /^ unsigned int lp_conv_delay:2;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:2 file: +maxSPIFrameInBytes espan.c /^#define maxSPIFrameInBytes /;" d file: +maxSPIFrequency espan.c /^#define maxSPIFrequency /;" d file: +maxValuePerColour espan.c /^#define maxValuePerColour /;" d file: +neg_afe_offset captouch.c /^ unsigned int neg_afe_offset:6;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:6 file: +neg_afe_offset_disable captouch.c /^ unsigned int neg_afe_offset_disable:1;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:1 file: +neg_afe_offset_swap captouch.c /^ unsigned int neg_afe_offset_swap:1;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:1 file: +neg_peak_detect captouch.c /^ unsigned int neg_peak_detect:3;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:3 file: +neg_threshold_sensitivity captouch.c /^ unsigned int neg_threshold_sensitivity:4;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:4 file: +paddle_leds espan.c /^static const uint8_t paddle_leds[][9] = {$/;" v typeref:typename:const uint8_t[][9] file: +play_bootsound audio.c /^void play_bootsound() {$/;" f typeref:typename:void +play_pan audio.c /^void play_pan(int pan) {$/;" f typeref:typename:void +pos_afe_offset captouch.c /^ unsigned int pos_afe_offset:6;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:6 file: +pos_afe_offset_disable captouch.c /^ unsigned int pos_afe_offset_disable:1;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:1 file: +pos_afe_offset_swap captouch.c /^ unsigned int pos_afe_offset_swap:1;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:1 file: +pos_peak_detect captouch.c /^ unsigned int pos_peak_detect:3;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:3 file: +pos_threshold_sensitivity captouch.c /^ unsigned int pos_threshold_sensitivity:4;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:4 file: +position audio.c /^ size_t position;$/;" m struct:sound_cfg typeref:typename:size_t file: +power_mode captouch.c /^ unsigned int power_mode:2;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:2 file: +pressed_bot captouch.c /^static uint16_t pressed_top, pressed_bot;$/;" v typeref:typename:uint16_t file: +pressed_top captouch.c /^static uint16_t pressed_top, pressed_bot;$/;" v typeref:typename:uint16_t file: +red espan.c /^uint8_t red[] = {32,0,0};$/;" v typeref:typename:uint8_t[] +renderLEDs espan.c /^void renderLEDs()$/;" f typeref:typename:void +ret espan.c /^esp_err_t ret;$/;" v typeref:typename:esp_err_t +se_connection_setup captouch.c /^ unsigned int se_connection_setup:2;$/;" m struct:ad7147_stage_config typeref:typename:unsigned int:2 file: +sequence_stage_num captouch.c /^ unsigned int sequence_stage_num:4;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:4 file: +setPixel apa102LEDStrip.c /^void setPixel(struct apa102LEDStrip *ledObject, short int pixelIndex, unsigned char *pixelColour/;" f typeref:typename:void +setupSPI espan.c /^int setupSPI()$/;" f typeref:typename:int +size audio.c /^ size_t size;$/;" m struct:sound_cfg typeref:typename:size_t file: +slot audio.c /^ size_t slot;$/;" m struct:sound_cfg typeref:typename:size_t file: +sound_active audio.c /^static bool sound_active(void)$/;" f typeref:typename:bool file: +sound_cfg audio.c /^typedef struct sound_cfg {$/;" s file: +sound_cfg_t audio.c /^} sound_cfg_t;$/;" t typeref:struct:sound_cfg file: +sound_queue audio.c /^static QueueHandle_t sound_queue = NULL;$/;" v typeref:typename:QueueHandle_t file: +spiTransObject espan.c /^spi_transaction_t spiTransObject;$/;" v typeref:typename:spi_transaction_t +spi_led espan.c /^spi_device_handle_t spi_led;$/;" v typeref:typename:spi_device_handle_t +stage0_cal_en captouch.c /^ unsigned int stage0_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage0_high_int_enable captouch.c /^ unsigned int stage0_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage10_cal_en captouch.c /^ unsigned int stage10_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage10_high_int_enable captouch.c /^ unsigned int stage10_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage11_cal_en captouch.c /^ unsigned int stage11_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage11_high_int_enable captouch.c /^ unsigned int stage11_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage1_cal_en captouch.c /^ unsigned int stage1_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage1_high_int_enable captouch.c /^ unsigned int stage1_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage2_cal_en captouch.c /^ unsigned int stage2_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage2_high_int_enable captouch.c /^ unsigned int stage2_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage3_cal_en captouch.c /^ unsigned int stage3_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage3_high_int_enable captouch.c /^ unsigned int stage3_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage4_cal_en captouch.c /^ unsigned int stage4_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage4_high_int_enable captouch.c /^ unsigned int stage4_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage5_cal_en captouch.c /^ unsigned int stage5_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage5_high_int_enable captouch.c /^ unsigned int stage5_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage6_cal_en captouch.c /^ unsigned int stage6_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage6_high_int_enable captouch.c /^ unsigned int stage6_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage7_cal_en captouch.c /^ unsigned int stage7_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage7_high_int_enable captouch.c /^ unsigned int stage7_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage8_cal_en captouch.c /^ unsigned int stage8_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage8_high_int_enable captouch.c /^ unsigned int stage8_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage9_cal_en captouch.c /^ unsigned int stage9_cal_en:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stage9_high_int_enable captouch.c /^ unsigned int stage9_high_int_enable:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +stages captouch.c /^ int stages;$/;" m struct:ad714x_chip typeref:typename:int file: +sw_reset captouch.c /^ unsigned int sw_reset:1;$/;" m struct:ad7147_device_config typeref:typename:unsigned int:1 file: +top_map espan.c /^uint8_t top_map[] = {1, 1, 1, 0, 0, 4, 4, 4, 3, 3, 2, 2};$/;" v typeref:typename:uint8_t[] +totalPixels espan.c /^#define totalPixels /;" d file: diff --git a/ports/esp32/boards/GENERIC_S3_BADGE/board.json b/ports/esp32/boards/GENERIC_S3_BADGE/board.json new file mode 100644 index 0000000000..0db9b32503 --- /dev/null +++ b/ports/esp32/boards/GENERIC_S3_BADGE/board.json @@ -0,0 +1,18 @@ +{ + "deploy": [ + "../deploy_s3.md" + ], + "docs": "", + "features": [ + "BLE", + "WiFi" + ], + "images": [ + "generic_s3.jpg" + ], + "mcu": "esp32s3", + "product": "ESP32-S3", + "thumbnail": "", + "url": "https://www.espressif.com/en/products/modules", + "vendor": "Espressif" +} diff --git a/ports/esp32/boards/GENERIC_S3_BADGE/mpconfigboard.cmake b/ports/esp32/boards/GENERIC_S3_BADGE/mpconfigboard.cmake new file mode 100644 index 0000000000..cb32e3ceea --- /dev/null +++ b/ports/esp32/boards/GENERIC_S3_BADGE/mpconfigboard.cmake @@ -0,0 +1,9 @@ +set(IDF_TARGET esp32s3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.badge23 + boards/sdkconfig.usb + boards/sdkconfig.ble + boards/GENERIC_S3/sdkconfig.board +) diff --git a/ports/esp32/boards/GENERIC_S3_BADGE/mpconfigboard.h b/ports/esp32/boards/GENERIC_S3_BADGE/mpconfigboard.h new file mode 100644 index 0000000000..b8f5fb2560 --- /dev/null +++ b/ports/esp32/boards/GENERIC_S3_BADGE/mpconfigboard.h @@ -0,0 +1,10 @@ +#define MICROPY_HW_BOARD_NAME "ESP32S3 module" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_PY_MACHINE_DAC (0) + +// Enable UART REPL for modules that have an external USB-UART and don't use native USB. +#define MICROPY_HW_ENABLE_UART_REPL (1) + +#define MICROPY_HW_I2C0_SCL (9) +#define MICROPY_HW_I2C0_SDA (8) diff --git a/ports/esp32/boards/GENERIC_S3_BADGE/sdkconfig.board b/ports/esp32/boards/GENERIC_S3_BADGE/sdkconfig.board new file mode 100644 index 0000000000..c9726d4232 --- /dev/null +++ b/ports/esp32/boards/GENERIC_S3_BADGE/sdkconfig.board @@ -0,0 +1,12 @@ +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_AFTER_NORESET=y + +CONFIG_SPIRAM_MEMTEST= + +CONFIG_ESPTOOLPY_FLASHSIZE_4MB= +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB= +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" diff --git a/ports/esp32/boards/sdkconfig.badge23 b/ports/esp32/boards/sdkconfig.badge23 new file mode 100644 index 0000000000..7d93f2b3ee --- /dev/null +++ b/ports/esp32/boards/sdkconfig.badge23 @@ -0,0 +1,21 @@ +CONFIG_GC9A01_Width=240 +CONFIG_GC9A01_Height=240 +# CONFIG_USE_SPI1_HOST is not set +# CONFIG_USE_SPI2_HOST is not set +CONFIG_USE_SPI3_HOST=y +# CONFIG_USE_SPI4_HOST is not set +CONFIG_GC9A01_SPI_HOST=2 +CONFIG_GC9A01_PIN_NUM_SCK=39 +CONFIG_GC9A01_PIN_NUM_MOSI=41 +CONFIG_GC9A01_PIN_NUM_CS=40 +CONFIG_GC9A01_PIN_NUM_DC=42 +CONFIG_GC9A01_SPI_SCK_FREQ_M=80 +# CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED is not set +CONFIG_GC9A01_RESET_USED=y +CONFIG_GC9A01_PIN_NUM_RST=38 +CONFIG_GC9A01_BUFFER_MODE=y +# CONFIG_GC9A01_BUFFER_SCREEN_FAST_MODE is not set +# end of GC9A01 LCD Config + +# + diff --git a/ports/esp32/main.c b/ports/esp32/main.c index e2a803fcb6..490a931fd4 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -62,6 +62,7 @@ #include "modmachine.h" #include "modnetwork.h" #include "mpthreadport.h" +#include "badge23/espan.h" #if MICROPY_BLUETOOTH_NIMBLE #include "extmod/modbluetooth.h" @@ -233,12 +234,16 @@ void boardctrl_startup(void) { } void app_main(void) { + printf("die my darling"); // Hook for a board to run code at start up. // This defaults to initialising NVS. MICROPY_BOARD_STARTUP(); // Create and transfer control to the MicroPython task. - xTaskCreatePinnedToCore(mp_task, "mp_task", MP_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID); + //xTaskCreatePinnedToCore(mp_task, "mp_task", MP_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID); + xTaskCreate(mp_task, "mp_task", (MP_TASK_STACK_SIZE / sizeof(StackType_t)) / 2, NULL, configMAX_PRIORITIES-3, &mp_main_task_handle); + printf("don't utter a single word"); + old_app_main(); // ./badge23/ entry point } void nlr_jump_fail(void *val) { diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt index f8acfa9052..77ff07204c 100644 --- a/ports/esp32/main/CMakeLists.txt +++ b/ports/esp32/main/CMakeLists.txt @@ -19,6 +19,18 @@ set(MICROPY_QSTRDEFS_PORT ${PROJECT_DIR}/qstrdefsport.h ) +set(BADGE23_LIB + ${PROJECT_DIR}/badge23/apa102LEDStrip.c + ${PROJECT_DIR}/badge23/audio.c + ${PROJECT_DIR}/badge23/captouch.c + ${PROJECT_DIR}/badge23/display.c + ${PROJECT_DIR}/badge23/espan.c + ${PROJECT_DIR}/badge23/leds.c + ${PROJECT_DIR}/badge23/scope.c + ${PROJECT_DIR}/badge23/synth.c + ${PROJECT_DIR}/badge23/components/gc9a01/gc9a01.c +) + set(MICROPY_SOURCE_SHARED ${MICROPY_DIR}/shared/readline/readline.c ${MICROPY_DIR}/shared/netutils/netutils.c @@ -164,6 +176,7 @@ idf_component_register( ${MICROPY_SOURCE_DRIVERS} ${MICROPY_SOURCE_PORT} ${MICROPY_SOURCE_BOARD} + ${BADGE23_LIB} INCLUDE_DIRS ${MICROPY_INC_CORE} ${MICROPY_INC_USERMOD} -- GitLab