Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • flow3r/flow3r-firmware
  • Vespasian/flow3r-firmware
  • alxndr42/flow3r-firmware
  • pl/flow3r-firmware
  • Kari/flow3r-firmware
  • raimue/flow3r-firmware
  • grandchild/flow3r-firmware
  • mu5tach3/flow3r-firmware
  • Nervengift/flow3r-firmware
  • arachnist/flow3r-firmware
  • TheNewCivilian/flow3r-firmware
  • alibi/flow3r-firmware
  • manuel_v/flow3r-firmware
  • xeniter/flow3r-firmware
  • maxbachmann/flow3r-firmware
  • yGifoom/flow3r-firmware
  • istobic/flow3r-firmware
  • EiNSTeiN_/flow3r-firmware
  • gnudalf/flow3r-firmware
  • 999eagle/flow3r-firmware
  • toerb/flow3r-firmware
  • pandark/flow3r-firmware
  • teal/flow3r-firmware
  • x42/flow3r-firmware
  • alufers/flow3r-firmware
  • dos/flow3r-firmware
  • yrlf/flow3r-firmware
  • LuKaRo/flow3r-firmware
  • ThomasElRubio/flow3r-firmware
  • ai/flow3r-firmware
  • T_X/flow3r-firmware
  • highTower/flow3r-firmware
  • beanieboi/flow3r-firmware
  • Woazboat/flow3r-firmware
  • gooniesbro/flow3r-firmware
  • marvino/flow3r-firmware
  • kressnerd/flow3r-firmware
  • quazgar/flow3r-firmware
  • aoid/flow3r-firmware
  • jkj/flow3r-firmware
  • naomi/flow3r-firmware
41 results
Show changes
Commits on Source (5)
Showing
with 179 additions and 807 deletions
......@@ -219,7 +219,7 @@ $ idf.py menuconfig
Then, either save into the temporary sdkconfig by using 'S', or save into a
defconfig by using 'D'. The resulting `build/defconfig` file can then be copied
into `sdkconfig` to change the defaults for a given generation (be sure to
remove BADGE23_* options that are usually generated by `idf_ext.py`).
remove FLOW3R_* options that are usually generated by `idf_ext.py`).
### Badge link
......
idf_component_register(
SRCS
captouch.c
espan.c
spio.c
INCLUDE_DIRS
include
REQUIRES
flow3r_bsp
st3m
bl00mbox
)
......@@ -18,7 +18,7 @@
#define AFE_INCR_CAP 1000
#if defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
#if defined(CONFIG_FLOW3R_HW_GEN_P3) || defined(CONFIG_FLOW3R_HW_GEN_P4) || defined(CONFIG_FLOW3R_HW_GEN_P6)
static const uint8_t top_map[] = {0, 0, 0, 2, 2, 2, 6, 6, 6, 4, 4, 4};
static const uint8_t top_stages = 12;
static const uint8_t bot_map[] = {1, 1, 3, 3, 5, 7, 7, 9, 9, 8, 8, 8};
......@@ -27,7 +27,7 @@ static const uint8_t bot_stage_config[] = {0,1,2,3,5,6,7,8,9,10,11,12};
#define DEFAULT_THRES_TOP 8000
#define DEFAULT_THRES_BOT 12000
#elif defined(CONFIG_BADGE23_HW_GEN_P1)
#elif defined(CONFIG_FLOW3R_HW_GEN_P1)
static const uint8_t top_map[] = {2, 2, 2, 0, 0, 8, 8, 8, 6, 6, 4, 4};
static const uint8_t top_stages = 12;
static const uint8_t bot_map[] = {1, 1, 3, 3, 5, 5, 7, 7, 9, 9};
......@@ -42,13 +42,13 @@ static const uint8_t bot_stage_config[] = {0,1,2,3,4,5,6,7,8,9,10,11};
#error "captouch not implemented for this badge generation"
#endif
#if defined(CONFIG_BADGE23_HW_GEN_P4)
#if defined(CONFIG_FLOW3R_HW_GEN_P4)
static const uint8_t top_segment_map[] = {1,3,2,2,3,1,1,3,2,1,3,2}; //PETAL_PAD_*
static const uint8_t bot_segment_map[] = {3,0,3,0,0,0,3,0,3,1,2,3}; //PETAL_PAD_*
#elif defined(CONFIG_BADGE23_HW_GEN_P6)
#elif defined(CONFIG_FLOW3R_HW_GEN_P6)
static const uint8_t top_segment_map[] = {1,3,2,2,3,1,1,3,2,1,3,2}; //PETAL_PAD_*
static const uint8_t bot_segment_map[] = {3,0,3,0,0,0,3,0,3,1,2,3}; //PETAL_PAD_*
#elif defined(CONFIG_BADGE23_HW_GEN_P3)
#elif defined(CONFIG_FLOW3R_HW_GEN_P3)
static const uint8_t top_segment_map[] = {0,1,2, 2,1,0, 0,1,2, 2,1,0}; //PETAL_PAD_*
static const uint8_t bot_segment_map[] = {3,0,3,0,0,0,3,0,3, 0,2,1}; //PETAL_PAD_*
#endif
......@@ -284,7 +284,7 @@ static void captouch_init_petals(){
int32_t captouch_get_petal_rad(uint8_t petal){
if(petal > 9) petal = 9;
uint8_t cf = petals[petal].config_mask;
#if defined(CONFIG_BADGE23_TOP_BOARD_SPIKES)
#if defined(CONFIG_FLOW3R_TOP_BOARD_SPIKES)
if(cf == 0b1110){ //CCW, CW, BASE
int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
left -= petals[petal].amb_values[PETAL_PAD_CCW];
......@@ -294,7 +294,7 @@ int32_t captouch_get_petal_rad(uint8_t petal){
base -= petals[petal].amb_values[PETAL_PAD_BASE];
return (left + right)/2 - base;
}
#elif defined(CONFIG_BADGE23_TOP_BOARD_SPIRALS)
#elif defined(CONFIG_FLOW3R_TOP_BOARD_SPIRALS)
if(cf == 0b1110){ //CCW, CW, BASE
int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
left -= petals[petal].amb_values[PETAL_PAD_CCW];
......@@ -332,7 +332,7 @@ int32_t captouch_get_petal_rad(uint8_t petal){
int32_t captouch_get_petal_phi(uint8_t petal){
if(petal > 9) petal = 9;
uint8_t cf = petals[petal].config_mask;
#if defined(CONFIG_BADGE23_TOP_BOARD_SPIKES)
#if defined(CONFIG_FLOW3R_TOP_BOARD_SPIKES)
if((cf == 0b1110) || (cf == 0b110) || (cf == 0b111)){ //CCW, CW, (BASE)
int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
left -= petals[petal].amb_values[PETAL_PAD_CCW];
......@@ -340,7 +340,7 @@ int32_t captouch_get_petal_phi(uint8_t petal){
right -= petals[petal].amb_values[PETAL_PAD_CW];
return left - right;
}
#elif defined(CONFIG_BADGE23_TOP_BOARD_SPIRALS)
#elif defined(CONFIG_FLOW3R_TOP_BOARD_SPIRALS)
if((cf == 0b1110) || (cf == 0b110) || (cf == 0b111)){ //CCW, CW, (BASE)
int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
left -= petals[petal].amb_values[PETAL_PAD_CCW];
......
#include "badge23/captouch.h"
#include "badge23/spio.h"
#include "flow3r_bsp.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char *TAG = "espan";
static uint8_t hw_init_done = 0;
static void io_fast_task(void * data){
TickType_t last_wake = xTaskGetTickCount();
while(1) {
vTaskDelayUntil(&last_wake, pdMS_TO_TICKS(10)); // 100 Hz
captouch_read_cycle();
update_button_state();
}
}
void badge23_main(void)
{
ESP_LOGI(TAG, "Starting on %s...", flow3r_bsp_hw_name);
init_buttons();
captouch_init();
spio_badge_link_disable(255);
captouch_force_calibration();
xTaskCreatePinnedToCore(&io_fast_task, "iofast", 4096, NULL, configMAX_PRIORITIES-1, NULL, 0);
hw_init_done = 1;
}
uint8_t hardware_is_initialized(){
return hw_init_done;
}
#pragma once
#include <stdint.h>
void os_app_early_init(void);
void os_app_main(void);
uint8_t hardware_is_initialized(void);
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;
}
}
/* 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
);
//===========================================================================================
//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;
}
//special purpose input outputs
#include "driver/gpio.h"
#include "stdint.h"
#include "badge23/spio.h"
#include "st3m_audio.h"
#include "flow3r_bsp_i2c.h"
#include "driver/i2c.h"
#define TIMEOUT_MS 1000
#if defined(CONFIG_BADGE23_HW_GEN_P1)
#define BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN 5
#define BADGE_LINK_LINE_OUT_RING_ENABLE_PIN 6
#define BADGE_LINK_LINE_IN_TIP_ENABLE_PIN 3
#define BADGE_LINK_LINE_IN_RING_ENABLE_PIN 4
#elif defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P3)
#define BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN 6
#define BADGE_LINK_LINE_OUT_RING_ENABLE_PIN 7
#define BADGE_LINK_LINE_IN_TIP_ENABLE_PIN 5
#define BADGE_LINK_LINE_IN_RING_ENABLE_PIN 4
//on ESP32
#define LEFT_BUTTON_LEFT 3
#define LEFT_BUTTON_MID 0
//on PORTEXPANDER
#define LEFT_BUTTON_RIGHT (0+8)
#define RIGHT_BUTTON_LEFT (6+8)
#define RIGHT_BUTTON_MID (5+8)
#define RIGHT_BUTTON_RIGHT (7+8)
#elif defined(CONFIG_BADGE23_HW_GEN_P6)
#define BADGE_LINK_LINE_OUT_RING_ENABLE_PIN 5
#define BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN 6
#define BADGE_LINK_LINE_IN_TIP_ENABLE_PIN 4
#define BADGE_LINK_LINE_IN_RING_ENABLE_PIN 3
#define ENABLE_INVERTED
//on ESP32
#define RIGHT_BUTTON_MID 3
#define LEFT_BUTTON_MID 0
//on PORTEXPANDER
#define LEFT_BUTTON_RIGHT (0+8)
#define RIGHT_BUTTON_LEFT (4+8)
#define LEFT_BUTTON_LEFT (7+8)
#define RIGHT_BUTTON_RIGHT (5+8)
#define LINE_IN_JACKSENSE (6+8)
#define CHARGER_STATE (2+8)
#endif
static int8_t leftbutton = 0;
static int8_t rightbutton = 0;
static bool line_in_jacksense = 1;
static bool charger_state;
static bool menu_button_left = 0;
static uint8_t badge_link_enabled = 0;
/* The MAX7321 doesn't have any input/output pin configuration methods. Instead,
* it has a ~40k pullup resistor to VCC and a programmable shunt transistor to VEE.
* Writing a byte to the IC closes each shunt with a LO at its index.
* Reading a byte returns the state of each pin.
*
* This means that changing a single output bit cannot be changed without some
* information about the other pins. Also a output pin set to HI can read as LO
* if there's an outside shunt.
*/
typedef struct{
flow3r_i2c_address *addr;
uint8_t is_output_pin; // mask for pins we wish to use as outputs
uint8_t read_state; //
uint8_t output_state; // goal output state
} max7321_t;
max7321_t port_expanders[2];
void _max7321_update(max7321_t *max){
uint8_t rx = 0;
uint8_t tx = (~(max->is_output_pin)) | max->output_state;
flow3r_bsp_i2c_write_read_device(*max->addr, &tx, sizeof(tx), &rx, sizeof(rx), TIMEOUT_MS / portTICK_PERIOD_MS);
// TODO(q3k): handle error
max->read_state = rx;
}
void max7321s_update(){
_max7321_update(&port_expanders[0]);
_max7321_update(&port_expanders[1]);
}
bool max7321s_get_pin(uint8_t pin){
if(pin>15) return 0;
max7321_t * pe = &port_expanders[0];
if(pin>7){
pe = &port_expanders[1];
pin -= 8;
}
return ((pe->read_state) >> pin) & 1;
}
bool max7321s_set_pin(uint8_t pin, bool on){
if(pin>15) return 0;
max7321_t * pe = &port_expanders[0];
if(pin>7){
pe = &port_expanders[1];
pin -= 8;
}
if(((pe->is_output_pin) >> pin) & 1){
if(on){
pe->output_state |= 1<<pin;
} else {
pe->output_state &= ~(1<<pin);
}
return 1;
} else {
return 0;
}
}
void max7321s_set_pinmode_output(uint8_t pin, bool output){
if(pin>15) return 0;
max7321_t * pe = &port_expanders[0];
if(pin>7){
pe = &port_expanders[1];
pin -= 8;
}
if(output){
pe->is_output_pin |= (1<<pin);
} else {
pe->is_output_pin &= ~(1<<pin);
}
}
#if defined(CONFIG_BADGE23_HW_GEN_P1)
#define RIGHT_BUTTON_LEFT 37
#define RIGHT_BUTTON_MID 0
#define RIGHT_BUTTON_RIGHT 35
#define LEFT_BUTTON_LEFT 7
#define LEFT_BUTTON_MID 6
#define LEFT_BUTTON_RIGHT 5
static void _init_buttons(){
//configure all buttons as pullup
uint64_t mask = 0;
mask |= (1ULL << RIGHT_BUTTON_LEFT);
mask |= (1ULL << RIGHT_BUTTON_RIGHT);
mask |= (1ULL << LEFT_BUTTON_LEFT);
mask |= (1ULL << LEFT_BUTTON_MID);
mask |= (1ULL << LEFT_BUTTON_RIGHT);
gpio_config_t cfg = {
.pin_bit_mask = mask,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
ESP_ERROR_CHECK(gpio_config(&cfg));
cfg.pin_bit_mask = 1;
cfg.pull_up_en = GPIO_PULLUP_DISABLE;
ESP_ERROR_CHECK(gpio_config(&cfg));
}
void update_button_state(){
if(!gpio_get_level(RIGHT_BUTTON_LEFT)){
rightbutton = BUTTON_PRESSED_LEFT;
} else if(!gpio_get_level(RIGHT_BUTTON_MID)){
rightbutton = BUTTON_PRESSED_DOWN;
} else if(!gpio_get_level(RIGHT_BUTTON_RIGHT)){
rightbutton = BUTTON_PRESSED_RIGHT;
} else {
rightbutton = BUTTON_NOT_PRESSED;
}
if(!gpio_get_level(LEFT_BUTTON_LEFT)){
leftbutton = BUTTON_PRESSED_LEFT;
} else if(!gpio_get_level(LEFT_BUTTON_MID)){
leftbutton = BUTTON_PRESSED_DOWN;
} else if(!gpio_get_level(LEFT_BUTTON_RIGHT)){
leftbutton = BUTTON_PRESSED_RIGHT;
} else {
leftbutton = BUTTON_NOT_PRESSED;
}
}
#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4)
max7321_t port_expanders[] = { {&flow3r_i2c_addresses.portexp[0], 0, 255, 255},
{&flow3r_i2c_addresses.portexp[1], 0, 255, 255} };
static void _init_buttons(){
//configure all buttons as pullup
gpio_config_t cfg = {
.pin_bit_mask = 1 << LEFT_BUTTON_LEFT,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
ESP_ERROR_CHECK(gpio_config(&cfg));
cfg.pin_bit_mask = 1;
cfg.pull_up_en = GPIO_PULLUP_DISABLE;
ESP_ERROR_CHECK(gpio_config(&cfg));
max7321s_set_pinmode_output(RIGHT_BUTTON_RIGHT, 0);
max7321s_set_pinmode_output(RIGHT_BUTTON_MID, 0);
max7321s_set_pinmode_output(RIGHT_BUTTON_LEFT, 0);
max7321s_set_pinmode_output(LEFT_BUTTON_RIGHT, 0);
}
int8_t process_button_state(bool r, bool m, bool l){
if(!l){
return BUTTON_PRESSED_LEFT;
} else if(!m){
return BUTTON_PRESSED_DOWN;
} else if(!r){
return BUTTON_PRESSED_RIGHT;
} else {
return BUTTON_NOT_PRESSED;
}
}
void update_button_state(){
max7321s_update();
uint8_t rr = max7321s_get_pin(RIGHT_BUTTON_RIGHT);
uint8_t rm = max7321s_get_pin(RIGHT_BUTTON_MID);
uint8_t rl = max7321s_get_pin(RIGHT_BUTTON_LEFT);
uint8_t lr = max7321s_get_pin(LEFT_BUTTON_RIGHT);
uint8_t ll = gpio_get_level(LEFT_BUTTON_LEFT);
uint8_t lm = gpio_get_level(LEFT_BUTTON_MID);
int8_t new_rightbutton = process_button_state(rr, rm, rl);
int8_t new_leftbutton = process_button_state(lr, lm, ll);
if(new_rightbutton != rightbutton){
//TODO: CALLBACK button_state_has_changed_to(new_rightbutton)
//note: consider menubutton/application button config option
}
if(new_leftbutton != leftbutton){
//TODO: CALLBACK button_state_has_changed_to(new_leftbutton)
}
rightbutton = new_rightbutton;
leftbutton = new_leftbutton;
}
#elif defined(CONFIG_BADGE23_HW_GEN_P6)
max7321_t port_expanders[] = { {&flow3r_i2c_addresses.portexp[0], 0, 255, 255},
{&flow3r_i2c_addresses.portexp[1], 0, 255, 255} };
static void _init_buttons(){
//configure all buttons as pullup
gpio_config_t cfg = {
.pin_bit_mask = 1 << RIGHT_BUTTON_MID,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
ESP_ERROR_CHECK(gpio_config(&cfg));
cfg.pin_bit_mask = 1;
cfg.pull_up_en = GPIO_PULLUP_DISABLE;
ESP_ERROR_CHECK(gpio_config(&cfg));
max7321s_set_pinmode_output(RIGHT_BUTTON_RIGHT, 0);
max7321s_set_pinmode_output(LEFT_BUTTON_LEFT, 0);
max7321s_set_pinmode_output(RIGHT_BUTTON_LEFT, 0);
max7321s_set_pinmode_output(LEFT_BUTTON_RIGHT, 0);
}
int8_t process_button_state(bool r, bool m, bool l){
if(!l){
return BUTTON_PRESSED_LEFT;
} else if(!m){
return BUTTON_PRESSED_DOWN;
} else if(!r){
return BUTTON_PRESSED_RIGHT;
} else {
return BUTTON_NOT_PRESSED;
}
}
void update_button_state(){
max7321s_update();
uint8_t rr = max7321s_get_pin(RIGHT_BUTTON_RIGHT);
uint8_t ll = max7321s_get_pin(LEFT_BUTTON_LEFT);
uint8_t rl = max7321s_get_pin(RIGHT_BUTTON_LEFT);
uint8_t lr = max7321s_get_pin(LEFT_BUTTON_RIGHT);
uint8_t rm = gpio_get_level(RIGHT_BUTTON_MID);
uint8_t lm = gpio_get_level(LEFT_BUTTON_MID);
line_in_jacksense = max7321s_get_pin(LINE_IN_JACKSENSE);
charger_state = max7321s_get_pin(CHARGER_STATE);
int8_t new_rightbutton = process_button_state(rr, rm, rl);
int8_t new_leftbutton = process_button_state(lr, lm, ll);
if(new_rightbutton != rightbutton){
//TODO: CALLBACK button_state_has_changed_to(new_rightbutton)
//note: consider menubutton/application button config option
}
if(new_leftbutton != leftbutton){
//TODO: CALLBACK button_state_has_changed_to(new_leftbutton)
}
rightbutton = new_rightbutton;
leftbutton = new_leftbutton;
}
#else
#error "spio not implemented for this badge generation"
#endif
void init_buttons(){
_init_buttons();
}
//#define ALWAYS_UPDATE_BUTTON
int8_t get_button_state(bool left){
#ifdef ALWAYS_UPDATE_BUTTON
update_button_state();
#endif
if(left) return leftbutton;
return rightbutton;
}
bool spio_charger_state_get(){
#ifdef ALWAYS_UPDATE_BUTTON
update_button_state();
#endif
return charger_state;
}
bool spio_line_in_jacksense_get(){
#ifdef ALWAYS_UPDATE_BUTTON
update_button_state();
#endif
return line_in_jacksense;
}
void spio_menu_button_set_left(bool left){
menu_button_left = 1;
}
int8_t spio_menu_button_get(){
return get_button_state(menu_button_left);
}
int8_t spio_application_button_get(){
return get_button_state(!menu_button_left);
}
int8_t spio_left_button_get(){
return get_button_state(1);
}
int8_t spio_right_button_get(){
return get_button_state(0);
}
int8_t spio_menu_button_get_left(){
return menu_button_left;
}
uint8_t spio_badge_link_get_active(uint8_t pin_mask){
return badge_link_enabled & pin_mask;
}
#if defined(CONFIG_BADGE23_HW_GEN_P1)
static uint8_t spio_badge_link_set(uint8_t pin_mask, uint8_t state){
return 0; // no badge link here (yet)
}
#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
#define USER_WARNINGS_ENABLED
static int8_t spio_badge_link_set(uint8_t pin_mask, bool state){
if(state) {
if((pin_mask & BADGE_LINK_PIN_MASK_LINE_OUT_RING) || (pin_mask & BADGE_LINK_PIN_MASK_LINE_OUT_TIP)){
if(!st3m_audio_headphones_are_connected()) {
pin_mask &= ~BADGE_LINK_PIN_MASK_LINE_OUT_RING;
pin_mask &= ~BADGE_LINK_PIN_MASK_LINE_OUT_TIP;
#ifdef USER_WARNINGS_ENABLED
printf("cannot enable line out badge link without cable plugged in for safety reasons\n");
} else {
printf("badge link enabled on line out. please make sure not to have headphones or sound sources connected before transmitting data.\n");
#endif
}
}
}
#ifdef ENABLE_INVERTED
uint8_t hw_state = state;
#else
uint8_t hw_state = !state;
#endif
if(pin_mask & BADGE_LINK_PIN_MASK_LINE_IN_RING) max7321s_set_pinmode_output(BADGE_LINK_LINE_IN_RING_ENABLE_PIN, 1);
if(pin_mask & BADGE_LINK_PIN_MASK_LINE_IN_TIP) max7321s_set_pinmode_output(BADGE_LINK_LINE_IN_TIP_ENABLE_PIN, 1);
if(pin_mask & BADGE_LINK_PIN_MASK_LINE_OUT_RING) max7321s_set_pinmode_output(BADGE_LINK_LINE_OUT_RING_ENABLE_PIN, 1);
if(pin_mask & BADGE_LINK_PIN_MASK_LINE_OUT_TIP) max7321s_set_pinmode_output(BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN, 1);
if(pin_mask & BADGE_LINK_PIN_MASK_LINE_IN_RING) max7321s_set_pin(BADGE_LINK_LINE_IN_RING_ENABLE_PIN, hw_state);
if(pin_mask & BADGE_LINK_PIN_MASK_LINE_IN_TIP) max7321s_set_pin(BADGE_LINK_LINE_IN_TIP_ENABLE_PIN, hw_state);
if(pin_mask & BADGE_LINK_PIN_MASK_LINE_OUT_RING) max7321s_set_pin(BADGE_LINK_LINE_OUT_RING_ENABLE_PIN, hw_state);
if(pin_mask & BADGE_LINK_PIN_MASK_LINE_OUT_TIP) max7321s_set_pin(BADGE_LINK_LINE_OUT_TIP_ENABLE_PIN, hw_state);
max7321s_update();
badge_link_enabled = (badge_link_enabled & (~pin_mask)) | (pin_mask & (hw_state ? 255 : 0));
return spio_badge_link_get_active(pin_mask);
}
#endif
uint8_t spio_badge_link_disable(uint8_t pin_mask){
return spio_badge_link_set(pin_mask, 0);
}
uint8_t spio_badge_link_enable(uint8_t pin_mask){
return spio_badge_link_set(pin_mask, 1);
}
......@@ -6,5 +6,5 @@ idf_component_register(
include
buds/tinysynth
REQUIRES
badge23
st3m
)
......@@ -5,10 +5,12 @@ idf_component_register(
flow3r_bsp_gc9a01.c
flow3r_bsp_hwconfig.c
flow3r_bsp_i2c.c
flow3r_bsp_max7321.c
flow3r_bsp_max98091.c
flow3r_bsp_leds.c
flow3r_bsp_rmtled.c
flow3r_bsp_spiled.c
flow3r_bsp_spio.c
INCLUDE_DIRS
.
)
menu "Badge23 Config"
choice BADGE23_HW_GEN
menu "Flow3r Config"
choice FLOW3R_HW_GEN
prompt "Badge23 Hardware Generation"
default BADGE23_HW_GEN_P4
config BADGE23_HW_GEN_P1
default FLOW3R_HW_GEN_P4
config FLOW3R_HW_GEN_P1
help
Protoype version 1, a.k.a. proto1. Very early protoype.
Visual identifiers:
......@@ -10,33 +10,33 @@ menu "Badge23 Config"
- White bottom board
- USB-C jack points side of leaf
bool "Prototype 1"
config BADGE23_HW_GEN_P3
config FLOW3R_HW_GEN_P3
help
Prototype version 3, a.k.a. proto3
Visual identifiers:
- Sticker with B3xx (xx being arbitrary digits) on the back
bool "Prototype 3"
config BADGE23_HW_GEN_P4
config FLOW3R_HW_GEN_P4
help
Prototype version 4, a.k.a. proto4
Visual identifiers:
- Sticker with B4xx (xx being arbitrary digits) on the back
bool "Prototype 4"
config BADGE23_HW_GEN_P6
config FLOW3R_HW_GEN_P6
help
Prototype version 6, a.k.a. proto6
- Sticker with B6xx (xx being arbitrary digits) on the back
bool "Prototype 6"
endchoice
choice BADGE23_TOP_BOARD_STYLE
choice FLOW3R_TOP_BOARD_STYLE
prompt "Badge23 top board style (mainly for proto6)"
default BADGE23_TOP_BOARD_SPIRALS
config BADGE23_TOP_BOARD_SPIKES
default FLOW3R_TOP_BOARD_SPIRALS
config FLOW3R_TOP_BOARD_SPIKES
help
Spikes between captouch pads
bool "Spikes"
config BADGE23_TOP_BOARD_SPIRALS
config FLOW3R_TOP_BOARD_SPIRALS
help
Spirals between captouch pads
bool "Spirals"
......
......@@ -137,4 +137,60 @@ void flow3r_bsp_leds_set_pixel(uint32_t index, uint32_t red, uint32_t green, uin
// Transmit from internal buffer into LEDs. This will block in case there
// already is a previous transmission happening.
esp_err_t flow3r_bsp_leds_refresh(TickType_t timeout_ms);
\ No newline at end of file
esp_err_t flow3r_bsp_leds_refresh(TickType_t timeout_ms);
// A 'tripos' button is what we're calling the shoulder buttons. As the name
// indicates, it has three positions: left, middle (a.k.a. down/press) and right.
typedef enum {
// Not pressed.
flow3r_bsp_tripos_none = 0,
// Pressed towards the left.
flow3r_bsp_tripos_left = -1,
// Pressed down.
flow3r_bsp_tripos_mid = 2,
// Pressed towards the right.
flow3r_bsp_tripos_right = 1,
} flow3r_bsp_tripos_state_t;
// Initialize 'special purpose i/o', which is like gpio, but more :). This means
// effectively all the buttons and on-board detect/enable signals.
esp_err_t flow3r_bsp_spio_init(void);
// Refresh the state of I/O: update outputs based on last called _set functions,
// and make _get return the newest hardware state.
esp_err_t flow3r_bsp_spio_update(void);
// Return the latest updated position of the left shoulder/tripos button.
//
// flow3r_bsp_spio_update must be called for this data to be up to date.
flow3r_bsp_tripos_state_t flow3r_bsp_spio_left_button_get(void);
// Return the latest updated position of the right shoulder/tripos button.
//
// flow3r_bsp_spio_update must be called for this data to be up to date.
flow3r_bsp_tripos_state_t flow3r_bsp_spio_right_button_get(void);
// Returns true if the device is charging.
//
// flow3r_bsp_spio_update must be called for this data to be up to date.
bool flow3r_bsp_spio_charger_state_get(void);
// Returns true if the device has something plugged into the right 3.5mm jack.
//
// flow3r_bsp_spio_update must be called for this data to be up to date.
bool flow3r_bsp_spio_jacksense_right_get(void);
// Switch tip/ring muxes to 'badgelink' digital I/O data pins on the left hand
// jack. As this is the same as the headphone jack, please observe caution when
// enabling this! See the comment at the bottom of st3m_audio.h.
//
// flow3r_bsp_spio_update must be called for this setting to actually propagate
// to hardware.
void flow3r_bsp_spio_badgelink_left_enable(bool tip_on, bool ring_on);
// Switch tip/ring muxes to 'badgelink' digital I/O data pins on the right hand
// jack.
//
// flow3r_bsp_spio_update must be called for this setting to actually propagate
// to hardware.
void flow3r_bsp_spio_badgelink_right_enable(bool tip_on, bool ring_on);
\ No newline at end of file
......@@ -11,7 +11,7 @@ esp_err_t flow3r_bsp_audio_write(const void *src, size_t size, size_t *bytes_wri
return i2s_write(0, src, size, bytes_written, ticks_to_wait);
}
#if defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
#if defined(CONFIG_FLOW3R_HW_GEN_P3) || defined(CONFIG_FLOW3R_HW_GEN_P4) || defined(CONFIG_FLOW3R_HW_GEN_P6)
#include "flow3r_bsp_max98091.h"
......@@ -83,7 +83,7 @@ esp_err_t flow3r_bsp_audio_read(void *dest, size_t size, size_t *bytes_read, Tic
return i2s_read(0, dest, size, bytes_read, ticks_to_wait);
}
#elif defined(CONFIG_BADGE23_HW_GEN_P1)
#elif defined(CONFIG_FLOW3R_HW_GEN_P1)
void flow3r_bsp_audio_init(void) {
static const i2s_config_t i2s_config = {
......
......@@ -9,7 +9,7 @@
static const char *TAG = "flow3r-bsp-display";
#if defined(CONFIG_BADGE23_HW_GEN_P1)
#if defined(CONFIG_FLOW3R_HW_GEN_P1)
#define FLOW3R_BSP_GC9A01
flow3r_bsp_gc9a01_config_t gc9a01_config = {
.reset_used = 1,
......@@ -23,7 +23,7 @@ flow3r_bsp_gc9a01_config_t gc9a01_config = {
.host = 2,
};
#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
#elif defined(CONFIG_FLOW3R_HW_GEN_P3) || defined(CONFIG_FLOW3R_HW_GEN_P4) || defined(CONFIG_FLOW3R_HW_GEN_P6)
#define FLOW3R_BSP_GC9A01
flow3r_bsp_gc9a01_config_t gc9a01_config = {
.reset_used = 0,
......
#include "sdkconfig.h"
#if defined(CONFIG_BADGE23_HW_GEN_P1)
#if defined(CONFIG_FLOW3R_HW_GEN_P1)
const char *flow3r_bsp_hw_name = "proto1";
#elif defined(CONFIG_BADGE23_HW_GEN_P3)
#elif defined(CONFIG_FLOW3R_HW_GEN_P3)
const char *flow3r_bsp_hw_name = "proto3";
#elif defined(CONFIG_BADGE23_HW_GEN_P4)
#elif defined(CONFIG_FLOW3R_HW_GEN_P4)
const char *flow3r_bsp_hw_name = "proto4";
#elif defined(CONFIG_BADGE23_HW_GEN_P6)
#elif defined(CONFIG_FLOW3R_HW_GEN_P6)
const char *flow3r_bsp_hw_name = "proto6";
#else
#error "Badge23 Hardware Generation must be set!"
......
......@@ -9,28 +9,28 @@ static SemaphoreHandle_t mutex;
static const char *TAG = "flow3r-bsp-i2c";
#if defined(CONFIG_BADGE23_HW_GEN_P1)
#if defined(CONFIG_FLOW3R_HW_GEN_P1)
const flow3r_i2c_addressdef flow3r_i2c_addresses = {
.codec = 0, // p1 has no i2c control channel to codec.
.touch_top = 0x2d,
.touch_bottom = 0x2c,
.portexp = { 0x6e, 0x6d },
};
#elif defined(CONFIG_BADGE23_HW_GEN_P3)
#elif defined(CONFIG_FLOW3R_HW_GEN_P3)
const flow3r_i2c_addressdef flow3r_i2c_addresses = {
.codec = 0x10,
.touch_top = 0x2d,
.touch_bottom = 0x2c,
.portexp = { 0x6e, 0x6d },
};
#elif defined(CONFIG_BADGE23_HW_GEN_P4)
#elif defined(CONFIG_FLOW3R_HW_GEN_P4)
const flow3r_i2c_addressdef flow3r_i2c_addresses = {
.codec = 0x10,
.touch_top = 0x2c,
.touch_bottom = 0x2d,
.portexp = { 0x6e, 0x6d },
};
#elif defined(CONFIG_BADGE23_HW_GEN_P6)
#elif defined(CONFIG_FLOW3R_HW_GEN_P6)
const flow3r_i2c_addressdef flow3r_i2c_addresses = {
.codec = 0x10,
.touch_top = 0x2c,
......@@ -41,7 +41,7 @@ const flow3r_i2c_addressdef flow3r_i2c_addresses = {
#error "i2c not implemented for this badge generation"
#endif
#if defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
#if defined(CONFIG_FLOW3R_HW_GEN_P3) || defined(CONFIG_FLOW3R_HW_GEN_P4) || defined(CONFIG_FLOW3R_HW_GEN_P6)
static i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 2,
......@@ -50,7 +50,7 @@ static i2c_config_t i2c_conf = {
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
#elif defined(CONFIG_BADGE23_HW_GEN_P1)
#elif defined(CONFIG_FLOW3R_HW_GEN_P1)
static i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 10,
......
#include "flow3r_bsp.h"
#if defined(CONFIG_BADGE23_HW_GEN_P1)
#if defined(CONFIG_FLOW3R_HW_GEN_P1)
#include "flow3r_bsp_spiled.h"
......@@ -16,7 +16,7 @@ esp_err_t flow3r_bsp_leds_refresh(TickType_t timeout_ms) {
return flow3r_bsp_spiled_refresh(timeout_ms);
}
#elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
#elif defined(CONFIG_FLOW3R_HW_GEN_P3) || defined(CONFIG_FLOW3R_HW_GEN_P4) || defined(CONFIG_FLOW3R_HW_GEN_P6)
#include "flow3r_bsp_rmtled.h"
......
#include "flow3r_bsp_max7321.h"
#include "flow3r_bsp_i2c.h"
/* The MAX7321 doesn't have any input/output pin configuration methods. Instead,
* it has a ~40k pullup resistor to VCC and a programmable shunt transistor to VEE.
* Writing a byte to the IC closes each shunt with a LO at its index.
* Reading a byte returns the state of each pin.
*
* This means that changing a single output bit cannot be changed without some
* information about the other pins. Also a output pin set to HI can read as LO
* if there's an outside shunt.
*/
// TODO(q3k): unhardcode
#define TIMEOUT_MS 1000
esp_err_t flow3r_bsp_max7321_init(flow3r_bsp_max7321_t *max, flow3r_i2c_address addr) {
max->addr = addr;
max->is_output_pin = 0;
max->read_state = 0xff;
max->output_state = 0xff;
return flow3r_bsp_max7321_update(max);
}
void flow3r_bsp_max7321_set_pinmode_output(flow3r_bsp_max7321_t *max, uint32_t pin, bool output) {
if(output) {
max->is_output_pin |= (1<<pin);
} else {
max->is_output_pin &= ~(1<<pin);
}
}
bool flow3r_bsp_max7321_get_pin(flow3r_bsp_max7321_t *max, uint32_t pin) {
return ((max->read_state) >> pin) & 1;
}
void flow3r_bsp_max7321_set_pin(flow3r_bsp_max7321_t *max, uint32_t pin, bool on) {
if(((max->is_output_pin) >> pin) & 1){
if(on){
max->output_state |= 1<<pin;
} else {
max->output_state &= ~(1<<pin);
}
}
}
esp_err_t flow3r_bsp_max7321_update(flow3r_bsp_max7321_t *max) {
uint8_t rx = 0;
uint8_t tx = (~(max->is_output_pin)) | max->output_state;
esp_err_t ret = flow3r_bsp_i2c_write_read_device(max->addr, &tx, sizeof(tx), &rx, sizeof(rx), TIMEOUT_MS / portTICK_PERIOD_MS);
if (ret != ESP_OK) {
return ret;
}
max->read_state = rx;
return ESP_OK;
}
\ No newline at end of file
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "flow3r_bsp_i2c.h"
#include "esp_err.h"
typedef struct {
flow3r_i2c_address addr;
// mask for pins we wish to use as outputs
uint8_t is_output_pin;
uint8_t read_state;
// goal output state
uint8_t output_state;
} flow3r_bsp_max7321_t;
esp_err_t flow3r_bsp_max7321_init(flow3r_bsp_max7321_t *max, flow3r_i2c_address addr);
void flow3r_bsp_max7321_set_pinmode_output(flow3r_bsp_max7321_t *max, uint32_t pin, bool output);
bool flow3r_bsp_max7321_get_pin(flow3r_bsp_max7321_t *max, uint32_t pin);
void flow3r_bsp_max7321_set_pin(flow3r_bsp_max7321_t *max, uint32_t pin, bool on);
esp_err_t flow3r_bsp_max7321_update(flow3r_bsp_max7321_t *max);
\ No newline at end of file