big merge

This commit is contained in:
Philippe G
2021-12-18 21:04:23 -08:00
parent 955692f8ad
commit 898998efb0
583 changed files with 84472 additions and 1965 deletions

View File

@@ -29,12 +29,11 @@
#include "driver/spi_common_internal.h"
#include "esp32/rom/efuse.h"
#include "adac.h"
#include "trace.h"
#include "tools.h"
#include "monitor.h"
#include "messaging.h"
#include "network_ethernet.h"
static const char *TAG = "services";
const char *i2c_name_type="I2C";
const char *spi_name_type="SPI";
@@ -63,7 +62,6 @@ static char * config_spdif_get_string(){
",ws=" STR(CONFIG_SPDIF_WS_IO) ",do=" STR(CONFIG_SPDIF_DO_IO));
}
/****************************************************************************************
*
*/
@@ -110,9 +108,9 @@ bool is_spdif_config_locked(){
static void set_i2s_pin(char *config, i2s_pin_config_t *pin_config) {
char *p;
pin_config->bck_io_num = pin_config->ws_io_num = pin_config->data_out_num = pin_config->data_in_num = -1;
if ((p = strcasestr(config, "bck")) != NULL) pin_config->bck_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "ws")) != NULL) pin_config->ws_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "do")) != NULL) pin_config->data_out_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "bck"))) sscanf(p, "bck%*[^=]=%d", &pin_config->bck_io_num);
if ((p = strcasestr(config, "ws"))) sscanf(p, "ws%*[^=]=%d", &pin_config->ws_io_num);
if ((p = strcasestr(config, "do"))) sscanf(p, "do%*[^=]=%d", &pin_config->data_out_num);
}
/****************************************************************************************
@@ -120,19 +118,20 @@ static void set_i2s_pin(char *config, i2s_pin_config_t *pin_config) {
*/
const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){
static EXT_RAM_ATTR i2s_platform_config_t i2s_dac_pin;
memset(&i2s_dac_pin, 0xFF, sizeof(i2s_dac_pin));
memset(&i2s_dac_pin, 0xff, sizeof(i2s_dac_pin));
set_i2s_pin(dac_config, &i2s_dac_pin.pin);
strcpy(i2s_dac_pin.model, "i2s");
char * p=NULL;
if ((p = strcasestr(dac_config, "i2c")) != NULL) i2s_dac_pin.i2c_addr = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(dac_config, "sda")) != NULL) i2s_dac_pin.sda = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(dac_config, "scl")) != NULL) i2s_dac_pin.scl = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(dac_config, "model")) != NULL) sscanf(p, "%*[^=]=%31[^,]", i2s_dac_pin.model);
if ((p = strcasestr(dac_config, "mute")) != NULL) {
PARSE_PARAM(dac_config, "i2c", '=', i2s_dac_pin.i2c_addr);
PARSE_PARAM(dac_config, "sda", '=', i2s_dac_pin.sda);
PARSE_PARAM(dac_config, "scl", '=', i2s_dac_pin.scl);
PARSE_PARAM_STR(dac_config, "model", '=', i2s_dac_pin.model, 31);
if ((p = strcasestr(dac_config, "mute"))) {
char mute[8] = "";
sscanf(p, "%*[^=]=%7[^,]", mute);
i2s_dac_pin.mute_gpio = atoi(mute);
if ((p = strchr(mute, ':')) != NULL) i2s_dac_pin.mute_level = atoi(p + 1);
PARSE_PARAM(p, "mute", ':', i2s_dac_pin.mute_level);
}
return &i2s_dac_pin;
}
@@ -140,52 +139,56 @@ const i2s_platform_config_t * config_get_i2s_from_str(char * dac_config ){
/****************************************************************************************
* Get eth config structure from config string
*/
const eth_config_t * config_get_eth_from_str(char * eth_config ){
char * p=NULL;
static EXT_RAM_ATTR eth_config_t eth_pin;
memset(&eth_pin, 0xFF, sizeof(eth_pin));
memset(&eth_pin.model, 0x00, sizeof(eth_pin.model));
eth_pin.valid = true;
const eth_config_t * config_get_eth_from_str(char* config ){
static EXT_RAM_ATTR eth_config_t eth_config;
memset(&eth_config, 0xff, sizeof(eth_config));
memset(&eth_config.model, 0x00, sizeof(eth_config.model));
eth_config.valid = true;
if ((p = strcasestr(eth_config, "model")) != NULL) sscanf(p, "%*[^=]=%15[^,]", eth_pin.model);
if ((p = strcasestr(eth_config, "mdc")) != NULL) eth_pin.mdc = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "mdio")) != NULL) eth_pin.mdio = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "rst")) != NULL) eth_pin.rst = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "mosi")) != NULL) eth_pin.mosi = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "miso")) != NULL) eth_pin.miso = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "intr")) != NULL) eth_pin.intr = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "cs")) != NULL) eth_pin.cs = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "speed")) != NULL) eth_pin.speed = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "clk")) != NULL) eth_pin.clk = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(eth_config, "host")) != NULL) eth_pin.host = atoi(strchr(p, '=') + 1);
PARSE_PARAM_STR(config, "model", '=', eth_config.model, 15);
PARSE_PARAM(config, "mdc", '=', eth_config.mdc);
PARSE_PARAM(config, "mdio", '=', eth_config.mdio);
PARSE_PARAM(config, "rst", '=', eth_config.rst);
PARSE_PARAM(config, "mosi", '=', eth_config.mosi);
PARSE_PARAM(config, "miso", '=', eth_config.miso);
PARSE_PARAM(config, "intr", '=', eth_config.intr);
PARSE_PARAM(config, "cs", '=', eth_config.cs);
PARSE_PARAM(config, "speed", '=', eth_config.speed);
PARSE_PARAM(config, "clk", '=', eth_config.clk);
if(!eth_pin.model || strlen(eth_pin.model)==0){
eth_pin.valid = false;
return &eth_pin;
// only system host is available
eth_config.host = spi_system_host;
if(!eth_config.model || strlen(eth_config.model)==0){
eth_config.valid = false;
return &eth_config;
}
network_ethernet_driver_t* network_driver = network_ethernet_driver_autodetect(eth_pin.model);
network_ethernet_driver_t* network_driver = network_ethernet_driver_autodetect(eth_config.model);
if(!network_driver || !network_driver->valid){
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: model %s %s",eth_pin.model,network_driver?"was not compiled in":"was not found");
eth_pin.valid = false;
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: model %s %s",eth_config.model,network_driver?"was not compiled in":"was not found");
eth_config.valid = false;
}
if(network_driver){
eth_pin.rmii = network_driver->rmii;
eth_pin.spi = network_driver->spi;
eth_config.rmii = network_driver->rmii;
eth_config.spi = network_driver->spi;
if(network_driver->rmii){
if(!GPIO_IS_VALID_GPIO(eth_pin.mdio) || !GPIO_IS_VALID_GPIO(eth_pin.mdc)){
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: %s %s",!GPIO_IS_VALID_GPIO(eth_pin.mdc)?"Invalid MDC":"",!GPIO_IS_VALID_GPIO(eth_pin.mdio)?"Invalid mdio":"");
eth_pin.valid = false;
if(!GPIO_IS_VALID_GPIO(eth_config.mdio) || !GPIO_IS_VALID_GPIO(eth_config.mdc)){
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: %s %s",!GPIO_IS_VALID_GPIO(eth_config.mdc)?"Invalid MDC":"",!GPIO_IS_VALID_GPIO(eth_config.mdio)?"Invalid mdio":"");
eth_config.valid = false;
}
}
else if(network_driver->spi){
if(!GPIO_IS_VALID_GPIO(eth_pin.cs)){
if(!GPIO_IS_VALID_GPIO(eth_config.cs)){
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Ethernet config invalid: invalid CS pin");
return false;
}
}
}
return &eth_pin;
return &eth_config;
}
/****************************************************************************************
@@ -468,16 +471,18 @@ const display_config_t * config_display_get(){
sscanf(p, "%*[^:]:%u", &dstruct.depth);
dstruct.drivername = display_conf_get_driver_name(strchr(p, '=') + 1);
}
if ((p = strcasestr(config, "width")) != NULL) dstruct.width = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "height")) != NULL) dstruct.height = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "reset")) != NULL) dstruct.RST_pin = atoi(strchr(p, '=') + 1);
PARSE_PARAM(config, "width", '=', dstruct.width);
PARSE_PARAM(config, "height", '=', dstruct.height);
PARSE_PARAM(config, "reset", '=', dstruct.RST_pin);
PARSE_PARAM(config, "address", '=', dstruct.address);
PARSE_PARAM(config, "cs", '=', dstruct.CS_pin);
PARSE_PARAM(config, "speed", '=', dstruct.speed);
PARSE_PARAM(config, "back", '=', dstruct.back);
if (strstr(config, "I2C") ) dstruct.type=i2c_name_type;
if ((p = strcasestr(config, "address")) != NULL) dstruct.address = atoi(strchr(p, '=') + 1);
if (strstr(config, "SPI") ) dstruct.type=spi_name_type;
if ((p = strcasestr(config, "cs")) != NULL) dstruct.CS_pin = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "speed")) != NULL) dstruct.speed = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "back")) != NULL) dstruct.back = atoi(strchr(p, '=') + 1);
dstruct.hflip= strcasestr(config, "HFlip") ? true : false;
dstruct.vflip= strcasestr(config, "VFlip") ? true : false;
dstruct.rotate= strcasestr(config, "rotate") ? true : false;
@@ -488,7 +493,7 @@ const display_config_t * config_display_get(){
*
*/
const i2c_config_t * config_i2c_get(int * i2c_port) {
char *nvs_item, *p;
char *nvs_item;
static i2c_config_t i2c = {
.mode = I2C_MODE_MASTER,
.sda_io_num = -1,
@@ -502,10 +507,10 @@ const i2c_config_t * config_i2c_get(int * i2c_port) {
nvs_item = config_alloc_get(NVS_TYPE_STR, "i2c_config");
if (nvs_item) {
if ((p = strcasestr(nvs_item, "scl")) != NULL) i2c.scl_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "sda")) != NULL) i2c.sda_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "speed")) != NULL) i2c.master.clk_speed = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "port")) != NULL) i2c_system_port = atoi(strchr(p, '=') + 1);
PARSE_PARAM(nvs_item, "scl", '=', i2c.scl_io_num);
PARSE_PARAM(nvs_item, "sda", '=', i2c.sda_io_num);
PARSE_PARAM(nvs_item, "speed", '=', i2c.master.clk_speed);
PARSE_PARAM(nvs_item, "port", '=', i2c_system_port);
free(nvs_item);
}
if(i2c_port) {
@@ -518,6 +523,46 @@ const i2c_config_t * config_i2c_get(int * i2c_port) {
return &i2c;
}
/****************************************************************************************
* Get IO expander config structure from config string
*/
const gpio_exp_config_t* config_gpio_exp_get(int index) {
char *nvs_item, *item, *p;
static gpio_exp_config_t config;
// re-initialize config every time
memset(&config, 0, sizeof(config));
config.intr = -1; config.count = 16; config.base = GPIO_NUM_MAX; config.phy.port = i2c_system_port; config.phy.host = spi_system_host;
nvs_item = config_alloc_get(NVS_TYPE_STR, "gpio_exp_config");
if (!nvs_item || !*nvs_item) return NULL;
// search index items
for (item = strtok(nvs_item, ";"); index && item; index--) {
if ((item = strtok(NULL, ";")) == NULL) {
free(nvs_item);
return NULL;
}
}
PARSE_PARAM(item, "addr", '=', config.phy.addr);
PARSE_PARAM(item, "cs", '=', config.phy.cs_pin);
PARSE_PARAM(item, "speed", '=', config.phy.speed);
PARSE_PARAM(item, "intr", '=', config.intr);
PARSE_PARAM(item, "base", '=', config.base);
PARSE_PARAM(item, "count", '=', config.count);
PARSE_PARAM_STR(item, "model", '=', config.model, 31);
if ((p = strcasestr(item, "port")) != NULL) {
char port[8] = "";
sscanf(p, "%*[^=]=%7[^,]", port);
if (strcasestr(port, "dac")) config.phy.port = 0;
}
free(nvs_item);
return &config;
}
/****************************************************************************************
*
*/
@@ -596,18 +641,19 @@ const set_GPIO_struct_t * get_gpio_struct(){
*
*/
const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host) {
char *nvs_item, *p;
char *nvs_item;
static EXT_RAM_ATTR spi_bus_config_t spi;
memset(&spi, 0xFF, sizeof(spi));
memset(&spi, 0xff, sizeof(spi));
nvs_item = config_alloc_get_str("spi_config", CONFIG_SPI_CONFIG, NULL);
if (nvs_item) {
if ((p = strcasestr(nvs_item, "data")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "mosi")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "miso")) != NULL) spi.miso_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "clk")) != NULL) spi.sclk_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "dc")) != NULL) spi_system_dc_gpio = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "host")) != NULL) spi_system_host = atoi(strchr(p, '=') + 1);
PARSE_PARAM(nvs_item, "data", '=', spi.mosi_io_num);
PARSE_PARAM(nvs_item, "mosi", '=', spi.mosi_io_num);
PARSE_PARAM(nvs_item, "miso", '=', spi.miso_io_num);
PARSE_PARAM(nvs_item, "clk", '=', spi.sclk_io_num);
PARSE_PARAM(nvs_item, "dc", '=', spi_system_dc_gpio);
// only VSPI (1) can be used as Flash and PSRAM run at 80MHz
// if ((p = strcasestr(nvs_item, "host")) != NULL) spi_system_host = atoi(strchr(p, '=') + 1);
free(nvs_item);
}
if(spi_host) *spi_host = spi_system_host;
@@ -642,11 +688,11 @@ const rotary_struct_t * config_rotary_get() {
char *config = config_alloc_get_default(NVS_TYPE_STR, "rotary_config", NULL, 0);
if (config && *config) {
char *p;
// parse config
if ((p = strcasestr(config, "A")) != NULL) rotary.A = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "B")) != NULL) rotary.B = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "SW")) != NULL) rotary.SW = atoi(strchr(p, '=') + 1);
PARSE_PARAM(config, "A", '=', rotary.A);
PARSE_PARAM(config, "B", '=', rotary.B);
PARSE_PARAM(config, "SW", '=', rotary.SW);
if ((p = strcasestr(config, "knobonly")) != NULL) {
p = strchr(p, '=');
rotary.knobonly = true;
@@ -691,13 +737,10 @@ cJSON * add_gpio_for_value(cJSON * list,const char * name,int gpio, const char *
*/
cJSON * add_gpio_for_name(cJSON * list,const char * nvs_entry,const char * name, const char * prefix, bool fixed){
cJSON * llist = list?list:cJSON_CreateArray();
char *p;
int gpioNum=0;
if ((p = strcasestr(nvs_entry, name)) != NULL) {
gpioNum = atoi(strchr(p, '=') + 1);
if(gpioNum>=0){
cJSON_AddItemToArray(llist,get_gpio_entry(name,prefix,gpioNum,fixed));
}
PARSE_PARAM(nvs_entry, name, '=', gpioNum);
if(gpioNum>=0){
cJSON_AddItemToArray(llist,get_gpio_entry(name,prefix,gpioNum,fixed));
}
return llist;
}
@@ -1059,14 +1102,11 @@ cJSON * get_gpio_list(bool refresh) {
#ifndef CONFIG_BAT_LOCKED
char *bat_config = config_alloc_get_default(NVS_TYPE_STR, "bat_config", NULL, 0);
if (bat_config) {
char *p;
int channel;
if ((p = strcasestr(bat_config, "channel") ) != NULL) {
channel = atoi(strchr(p, '=') + 1);
if(channel != -1){
if(adc1_pad_get_io_num(channel,&gpio_num )==ESP_OK){
cJSON_AddItemToArray(gpio_list,get_gpio_entry("bat","other",gpio_num,false));
}
int channel = -1;
PARSE_PARAM(bat_config, "channel", '=', channel);
if(channel != -1){
if(adc1_pad_get_io_num(channel,&gpio_num )==ESP_OK){
cJSON_AddItemToArray(gpio_list,get_gpio_entry("bat","other",gpio_num,false));
}
}
free(bat_config);

View File

@@ -12,10 +12,11 @@
#include "driver/i2c.h"
#include "driver/i2s.h"
#include "driver/spi_master.h"
#include "freertos/queue.h"
#include "gpio_exp.h"
extern const char *i2c_name_type;
extern const char *spi_name_type;
typedef struct {
int width;
int height;
@@ -97,6 +98,7 @@ esp_err_t config_i2s_set(const i2s_platform_config_t * config, const char *
esp_err_t config_spi_set(const spi_bus_config_t * config, int host, int dc);
const i2c_config_t * config_i2c_get(int * i2c_port);
const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host);
const gpio_exp_config_t * config_gpio_exp_get(int index);
void parse_set_GPIO(void (*cb)(int gpio, char *value));
const i2s_platform_config_t * config_dac_get();
const i2s_platform_config_t * config_spdif_get( );

View File

@@ -67,12 +67,12 @@ static const char * actrls_action_s[ ] = { EP(ACTRLS_POWER),EP(ACTRLS_VOLUP),EP(
static const char * TAG = "audio controls";
static actrls_config_t *json_config;
cJSON * control_profiles = NULL;
static actrls_t default_controls, current_controls;
static EXT_RAM_ATTR actrls_t default_controls, current_controls;
static actrls_hook_t *default_hook, *current_hook;
static bool default_raw_controls, current_raw_controls;
static actrls_ir_handler_t *default_ir_handler, *current_ir_handler;
static struct {
static EXT_RAM_ATTR struct {
bool long_state;
bool volume_lock;
TimerHandle_t timer;
@@ -137,10 +137,10 @@ esp_err_t actrls_init(const char *profile_name) {
int A = -1, B = -1, SW = -1, longpress = 0;
// parse config
if ((p = strcasestr(config, "A")) != NULL) A = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "B")) != NULL) B = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "SW")) != NULL) SW = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "knobonly")) != NULL) {
PARSE_PARAM(config, "A", '=', A);
PARSE_PARAM(config, "B", '=', B);
PARSE_PARAM(config, "SW", '=', SW);
if ((p = strcasestr(config, "knobonly"))) {
p = strchr(p, '=');
int double_press = p ? atoi(p + 1) : 350;
rotary.timer = xTimerCreate("knobTimer", double_press / portTICK_RATE_MS, pdFALSE, NULL, rotary_timer);

View File

@@ -79,13 +79,12 @@ void battery_svc_init(void) {
char *nvs_item = config_alloc_get_default(NVS_TYPE_STR, "bat_config", "n", 0);
if (nvs_item) {
char *p;
#ifndef CONFIG_BAT_LOCKED
if ((p = strcasestr(nvs_item, "channel")) != NULL) battery.channel = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "scale")) != NULL) battery.scale = atof(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "atten")) != NULL) battery.attenuation = atoi(strchr(p, '=') + 1);
PARSE_PARAM(nvs_item, "channel", '=', battery.channel);
PARSE_PARAM(nvs_item, "scale", '=', battery.scale);
PARSE_PARAM(nvs_item, "atten", '=', battery.attenuation);
#endif
if ((p = strcasestr(nvs_item, "cells")) != NULL) battery.cells = atof(strchr(p, '=') + 1);
PARSE_PARAM(nvs_item, "cells", '=', battery.cells);
free(nvs_item);
}

View File

@@ -21,16 +21,17 @@
#include "esp_task.h"
#include "driver/gpio.h"
#include "driver/rmt.h"
#include "gpio_exp.h"
#include "buttons.h"
#include "rotary_encoder.h"
#include "globdefs.h"
static const char * TAG = "buttons";
static int n_buttons = 0;
static EXT_RAM_ATTR int n_buttons;
#define BUTTON_STACK_SIZE 4096
#define MAX_BUTTONS 16
#define MAX_BUTTONS 32
#define DEBOUNCE 50
#define BUTTON_QUEUE_LEN 10
@@ -47,6 +48,7 @@ static EXT_RAM_ATTR struct button_s {
TimerHandle_t timer;
} buttons[MAX_BUTTONS];
// can't use EXT_RAM_ATTR for initialized structure
static struct {
int gpio, level;
struct button_s *button;
@@ -67,10 +69,11 @@ static EXT_RAM_ATTR struct {
infrared_handler handler;
} infrared;
static xQueueHandle button_evt_queue;
static QueueSetHandle_t common_queue_set;
static EXT_RAM_ATTR QueueHandle_t button_queue;
static EXT_RAM_ATTR QueueSetHandle_t common_queue_set;
static void buttons_task(void* arg);
static void buttons_handler(struct button_s *button, int level);
/****************************************************************************************
* Start task needed by button,s rotaty and infrared
@@ -86,40 +89,33 @@ static void common_task_init(void) {
}
/****************************************************************************************
* GPIO low-level handler
* GPIO low-level ISR handler
*/
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
struct button_s *button = (struct button_s*) arg;
BaseType_t woken = pdFALSE;
if (xTimerGetPeriod(button->timer) > button->debounce / portTICK_RATE_MS) xTimerChangePeriodFromISR(button->timer, button->debounce / portTICK_RATE_MS, &woken); // does that restart the timer?
else xTimerResetFromISR(button->timer, &woken);
if (xTimerGetPeriod(button->timer) > pdMS_TO_TICKS(button->debounce)) {
if (button->gpio < GPIO_NUM_MAX) xTimerChangePeriodFromISR(button->timer, pdMS_TO_TICKS(button->debounce), &woken);
else xTimerChangePeriod(button->timer, pdMS_TO_TICKS(button->debounce), pdMS_TO_TICKS(10));
} else {
if (button->gpio < GPIO_NUM_MAX) xTimerResetFromISR(button->timer, &woken);
else xTimerReset(button->timer, portMAX_DELAY);
}
if (woken) portYIELD_FROM_ISR();
ESP_EARLY_LOGD(TAG, "INT gpio %u level %u", button->gpio, button->level);
}
/****************************************************************************************
* Buttons debounce/longpress timer
*/
static void buttons_timer( TimerHandle_t xTimer ) {
static void buttons_timer_handler( TimerHandle_t xTimer ) {
struct button_s *button = (struct button_s*) pvTimerGetTimerID (xTimer);
button->level = gpio_get_level(button->gpio);
if (button->shifter && button->shifter->type == button->shifter->level) button->shifter->shifting = true;
if (button->long_press && !button->long_timer && button->level == button->type) {
// detect a long press, so hold event generation
ESP_LOGD(TAG, "setting long timer gpio:%u level:%u", button->gpio, button->level);
xTimerChangePeriod(xTimer, button->long_press / portTICK_RATE_MS, 0);
button->long_timer = true;
} else {
// send a button pressed/released event (content is copied in queue)
ESP_LOGD(TAG, "sending event for gpio:%u level:%u", button->gpio, button->level);
// queue will have a copy of button's context
xQueueSend(button_evt_queue, button, 0);
button->long_timer = false;
}
// if this is an expanded GPIO, must give cache a chance
buttons_handler(button, gpio_exp_get_level(button->gpio, (button->debounce * 3) / 2, NULL));
}
/****************************************************************************************
@@ -133,11 +129,33 @@ static void buttons_polling( TimerHandle_t xTimer ) {
if (level != polled_gpio[i].level) {
polled_gpio[i].level = level;
buttons_timer(polled_gpio[i].button->timer);
buttons_handler(polled_gpio[i].button, level);
}
}
}
/****************************************************************************************
* Buttons timer handler for press/longpress
*/
static void buttons_handler(struct button_s *button, int level) {
button->level = level;
if (button->shifter && button->shifter->type == button->shifter->level) button->shifter->shifting = true;
if (button->long_press && !button->long_timer && button->level == button->type) {
// detect a long press, so hold event generation
ESP_LOGD(TAG, "setting long timer gpio:%u level:%u", button->gpio, button->level);
xTimerChangePeriod(button->timer, button->long_press / portTICK_RATE_MS, 0);
button->long_timer = true;
} else {
// send a button pressed/released event (content is copied in queue)
ESP_LOGD(TAG, "sending event for gpio:%u level:%u", button->gpio, button->level);
// queue will have a copy of button's context
xQueueSend(button_queue, button, 0);
button->long_timer = false;
}
}
/****************************************************************************************
* Tasks that calls the appropriate functions when buttons are pressed
*/
@@ -150,13 +168,13 @@ static void buttons_task(void* arg) {
// wait on button, rotary and infrared queues
if ((xActivatedMember = xQueueSelectFromSet( common_queue_set, portMAX_DELAY )) == NULL) continue;
if (xActivatedMember == button_evt_queue) {
if (xActivatedMember == button_queue) {
struct button_s button;
button_event_e event;
button_press_e press;
// received a button event
xQueueReceive(button_evt_queue, &button, 0);
xQueueReceive(button_queue, &button, 0);
event = (button.level == button.type) ? BUTTON_PRESSED : BUTTON_RELEASED;
@@ -175,18 +193,18 @@ static void buttons_task(void* arg) {
if (event == BUTTON_RELEASED) {
// early release of a long-press button, send press/release
if (!button.shifting) {
(*button.handler)(button.client, BUTTON_PRESSED, press, false);
(*button.handler)(button.client, BUTTON_RELEASED, press, false);
button.handler(button.client, BUTTON_PRESSED, press, false);
button.handler(button.client, BUTTON_RELEASED, press, false);
}
// button is a copy, so need to go to real context
button.self->shifting = false;
} else if (!button.shifting) {
// normal long press and not shifting so don't discard
(*button.handler)(button.client, BUTTON_PRESSED, press, true);
button.handler(button.client, BUTTON_PRESSED, press, true);
}
} else {
// normal press/release of a button or release of a long-press button
if (!button.shifting) (*button.handler)(button.client, event, press, button.long_press);
if (!button.shifting) button.handler(button.client, event, press, button.long_press);
// button is a copy, so need to go to real context
button.self->shifting = false;
}
@@ -195,12 +213,12 @@ static void buttons_task(void* arg) {
// received a rotary event
xQueueReceive(rotary.queue, &event, 0);
ESP_LOGD(TAG, "Event: position %d, direction %s", event.state.position,
event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
rotary.handler(rotary.client, event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ?
ROTARY_RIGHT : ROTARY_LEFT, false);
ROTARY_RIGHT : ROTARY_LEFT, false);
} else {
// this is IR
infrared_receive(infrared.rb, infrared.handler);
@@ -224,9 +242,9 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
ESP_LOGI(TAG, "Creating button using GPIO %u, type %u, pull-up/down %u, long press %u shifter %d", gpio, type, pull, long_press, shifter_gpio);
if (!n_buttons) {
button_evt_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(struct button_s));
button_queue = xQueueCreate(BUTTON_QUEUE_LEN, sizeof(struct button_s));
common_task_init();
xQueueAddToSet( button_evt_queue, common_queue_set );
xQueueAddToSet( button_queue, common_queue_set );
}
// just in case this structure is allocated in a future release
@@ -240,7 +258,7 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
buttons[n_buttons].long_press = long_press;
buttons[n_buttons].shifter_gpio = shifter_gpio;
buttons[n_buttons].type = type;
buttons[n_buttons].timer = xTimerCreate("buttonTimer", buttons[n_buttons].debounce / portTICK_RATE_MS, pdFALSE, (void *) &buttons[n_buttons], buttons_timer);
buttons[n_buttons].timer = xTimerCreate("buttonTimer", buttons[n_buttons].debounce / portTICK_RATE_MS, pdFALSE, (void *) &buttons[n_buttons], buttons_timer_handler);
buttons[n_buttons].self = buttons + n_buttons;
for (int i = 0; i < n_buttons; i++) {
@@ -257,24 +275,21 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
}
}
gpio_pad_select_gpio(gpio);
gpio_set_direction(gpio, GPIO_MODE_INPUT);
// we need any edge detection
gpio_set_intr_type(gpio, GPIO_INTR_ANYEDGE);
gpio_pad_select_gpio_x(gpio);
gpio_set_direction_x(gpio, GPIO_MODE_INPUT);
// do we need pullup or pulldown
if (pull) {
if (GPIO_IS_VALID_OUTPUT_GPIO(gpio)) {
if (type == BUTTON_LOW) gpio_set_pull_mode(gpio, GPIO_PULLUP_ONLY);
else gpio_set_pull_mode(gpio, GPIO_PULLDOWN_ONLY);
if (GPIO_IS_VALID_OUTPUT_GPIO(gpio) || gpio >= GPIO_NUM_MAX) {
if (type == BUTTON_LOW) gpio_set_pull_mode_x(gpio, GPIO_PULLUP_ONLY);
else gpio_set_pull_mode_x(gpio, GPIO_PULLDOWN_ONLY);
} else {
ESP_LOGW(TAG, "cannot set pull up/down for gpio %u", gpio);
}
}
// and initialize level ...
buttons[n_buttons].level = gpio_get_level(gpio);
buttons[n_buttons].level = gpio_get_level_x(gpio);
// nasty ESP32 bug: fire-up constantly INT on GPIO 36/39 if ADC1, AMP, Hall used which WiFi does when PS is activated
for (int i = 0; polled_gpio[i].gpio != -1; i++) if (polled_gpio[i].gpio == gpio) {
@@ -282,19 +297,21 @@ void button_create(void *client, int gpio, int type, bool pull, int debounce, bu
polled_timer = xTimerCreate("buttonsPolling", 100 / portTICK_RATE_MS, pdTRUE, polled_gpio, buttons_polling);
xTimerStart(polled_timer, portMAX_DELAY);
}
polled_gpio[i].button = buttons + n_buttons;
polled_gpio[i].level = gpio_get_level(gpio);
ESP_LOGW(TAG, "creating polled gpio %u, level %u", gpio, polled_gpio[i].level);
gpio = -1;
break;
}
// only create timers and ISR is this is not a polled gpio
// only create ISR if this is not a polled gpio
if (gpio != -1) {
gpio_isr_handler_add(gpio, gpio_isr_handler, (void*) &buttons[n_buttons]);
gpio_intr_enable(gpio);
// we need any edge detection
gpio_set_intr_type_x(gpio, GPIO_INTR_ANYEDGE);
gpio_isr_handler_add_x(gpio, gpio_isr_handler, buttons + n_buttons);
gpio_intr_enable_x(gpio);
}
n_buttons++;
@@ -362,7 +379,7 @@ void *button_remap(void *client, int gpio, button_handler handler, int long_pres
}
/****************************************************************************************
* Create rotary encoder
* Rotary encoder handler
*/
static void rotary_button_handler(void *id, button_event_e event, button_press_e mode, bool long_press) {
ESP_LOGI(TAG, "Rotary push-button %d", event);

View File

@@ -21,13 +21,3 @@ typedef struct {
int timer, base_channel, max;
} pwm_system_t;
extern pwm_system_t pwm_system;
#ifdef CONFIG_SQUEEZEAMP
#define ADAC dac_tas57xx
#elif defined(CONFIG_A1S)
#define ADAC dac_a1s
#else
#define ADAC dac_external
#endif
void * malloc_init_external(size_t sz);
void * clone_obj_psram(void * source, size_t source_sz);
char * strdup_psram(const char * source);

View File

@@ -0,0 +1,738 @@
/* GDS Example
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.
*/
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include "esp_task.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "driver/spi_master.h"
#include "gpio_exp.h"
#define GPIO_EXP_INTR 0x100
#define GPIO_EXP_WRITE 0x200
/*
shadow register is both output and input, so we assume that reading to the
ports also reads the value set on output
*/
typedef struct gpio_exp_s {
uint32_t first, last;
int intr;
bool intr_pending;
struct {
struct gpio_exp_phy_s phy;
spi_device_handle_t spi_handle;
};
uint32_t shadow, pending;
TickType_t age;
SemaphoreHandle_t mutex;
uint32_t r_mask, w_mask;
uint32_t pullup, pulldown;
struct gpio_exp_isr_s {
gpio_isr_t handler;
void *arg;
TimerHandle_t timer;
} isr[32];
struct gpio_exp_model_s const *model;
} gpio_exp_t;
typedef struct {
enum { ASYNC_WRITE } type;
int gpio;
int level;
gpio_exp_t *expander;
} queue_request_t;
static const char TAG[] = "gpio expander";
static void IRAM_ATTR intr_isr_handler(void* arg);
static gpio_exp_t* find_expander(gpio_exp_t *expander, int *gpio);
static void pca9535_set_direction(gpio_exp_t* self);
static uint32_t pca9535_read(gpio_exp_t* self);
static void pca9535_write(gpio_exp_t* self);
static uint32_t pca85xx_read(gpio_exp_t* self);
static void pca85xx_write(gpio_exp_t* self);
static esp_err_t mcp23017_init(gpio_exp_t* self);
static void mcp23017_set_pull_mode(gpio_exp_t* self);
static void mcp23017_set_direction(gpio_exp_t* self);
static uint32_t mcp23017_read(gpio_exp_t* self);
static void mcp23017_write(gpio_exp_t* self);
static esp_err_t mcp23s17_init(gpio_exp_t* self);
static void mcp23s17_set_pull_mode(gpio_exp_t* self);
static void mcp23s17_set_direction(gpio_exp_t* self);
static uint32_t mcp23s17_read(gpio_exp_t* self);
static void mcp23s17_write(gpio_exp_t* self);
static void service_handler(void *arg);
static void debounce_handler( TimerHandle_t xTimer );
static esp_err_t i2c_write(uint8_t port, uint8_t addr, uint8_t reg, uint32_t data, int len);
static uint32_t i2c_read(uint8_t port, uint8_t addr, uint8_t reg, int len);
static spi_device_handle_t spi_config(struct gpio_exp_phy_s *phy);
static esp_err_t spi_write(spi_device_handle_t handle, uint8_t addr, uint8_t reg, uint32_t data, int len);
static uint32_t spi_read(spi_device_handle_t handle, uint8_t addr, uint8_t reg, int len);
static const struct gpio_exp_model_s {
char *model;
gpio_int_type_t trigger;
esp_err_t (*init)(gpio_exp_t* self);
uint32_t (*read)(gpio_exp_t* self);
void (*write)(gpio_exp_t* self);
void (*set_direction)(gpio_exp_t* self);
void (*set_pull_mode)(gpio_exp_t* self);
} registered[] = {
{ .model = "pca9535",
.trigger = GPIO_INTR_NEGEDGE,
.set_direction = pca9535_set_direction,
.read = pca9535_read,
.write = pca9535_write, },
{ .model = "pca85xx",
.trigger = GPIO_INTR_NEGEDGE,
.read = pca85xx_read,
.write = pca85xx_write, },
{ .model = "mcp23017",
.trigger = GPIO_INTR_NEGEDGE,
.init = mcp23017_init,
.set_direction = mcp23017_set_direction,
.set_pull_mode = mcp23017_set_pull_mode,
.read = mcp23017_read,
.write = mcp23017_write, },
{ .model = "mcp23s17",
.trigger = GPIO_INTR_NEGEDGE,
.init = mcp23s17_init,
.set_direction = mcp23s17_set_direction,
.set_pull_mode = mcp23s17_set_pull_mode,
.read = mcp23s17_read,
.write = mcp23s17_write, },
};
static EXT_RAM_ATTR uint8_t n_expanders;
static EXT_RAM_ATTR QueueHandle_t message_queue;
static EXT_RAM_ATTR gpio_exp_t expanders[4];
static EXT_RAM_ATTR TaskHandle_t service_task;
/******************************************************************************
* Retrieve base from an expander reference
*/
uint32_t gpio_exp_get_base(gpio_exp_t *expander) {
return expander->first;
}
/******************************************************************************
* Retrieve reference from a GPIO
*/
gpio_exp_t *gpio_exp_get_expander(int gpio) {
int _gpio = gpio;
return find_expander(NULL, &_gpio);
}
/******************************************************************************
* Create an I2C expander
*/
gpio_exp_t* gpio_exp_create(const gpio_exp_config_t *config) {
gpio_exp_t *expander = expanders + n_expanders;
if (config->base < GPIO_NUM_MAX || n_expanders == sizeof(expanders)/sizeof(gpio_exp_t)) {
ESP_LOGE(TAG, "Base %d GPIO must be at least %d for %s or too many expanders %d", config->base, GPIO_NUM_MAX, config->model, n_expanders);
return NULL;
}
// See if we know that model (expanders is zero-initialized)
for (int i = 0; !expander->model && i < sizeof(registered)/sizeof(struct gpio_exp_model_s); i++) {
if (strcasestr(config->model, registered[i].model)) expander->model = registered + i;
}
// well... try again
if (!expander->model) {
ESP_LOGE(TAG, "Unknown GPIO expansion chip %s", config->model);
return NULL;
}
memcpy(&expander->phy, &config->phy, sizeof(struct gpio_exp_phy_s));
// try to initialize the expander if required
if (expander->model->init && expander->model->init(expander) != ESP_OK) {
ESP_LOGE(TAG, "Cannot create GPIO expander %s, check i2c/spi configuration", config->model);
return NULL;
}
n_expanders++;
expander->first = config->base;
expander->last = config->base + config->count - 1;
expander->intr = config->intr;
expander->mutex = xSemaphoreCreateMutex();
// create a task to handle asynchronous requests (only write at this time)
if (!message_queue) {
// we allocate TCB but stack is static to avoid SPIRAM fragmentation
StaticTask_t* xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
static EXT_RAM_ATTR StackType_t xStack[4*1024] __attribute__ ((aligned (4)));
message_queue = xQueueCreate(4, sizeof(queue_request_t));
service_task = xTaskCreateStatic(service_handler, "gpio_expander", sizeof(xStack), NULL, ESP_TASK_PRIO_MIN + 1, xStack, xTaskBuffer);
}
// set interrupt if possible
if (config->intr >= 0) {
gpio_pad_select_gpio(config->intr);
gpio_set_direction(config->intr, GPIO_MODE_INPUT);
switch (expander->model->trigger) {
case GPIO_INTR_NEGEDGE:
case GPIO_INTR_LOW_LEVEL:
gpio_set_pull_mode(config->intr, GPIO_PULLUP_ONLY);
break;
case GPIO_INTR_POSEDGE:
case GPIO_INTR_HIGH_LEVEL:
gpio_set_pull_mode(config->intr, GPIO_PULLDOWN_ONLY);
break;
default:
gpio_set_pull_mode(config->intr, GPIO_PULLUP_PULLDOWN);
break;
}
gpio_set_intr_type(config->intr, expander->model->trigger);
gpio_isr_handler_add(config->intr, intr_isr_handler, expander);
gpio_intr_enable(config->intr);
}
ESP_LOGI(TAG, "Create GPIO expander %s at base %u with INT %d at @%x on port/host %d/%d", config->model, config->base, config->intr, config->phy.addr, config->phy.port, config->phy.host);
return expander;
}
/******************************************************************************
* Add ISR handler for a GPIO
*/
esp_err_t gpio_exp_isr_handler_add(int gpio, gpio_isr_t isr_handler, uint32_t debounce, void *arg, struct gpio_exp_s *expander) {
if (gpio < GPIO_NUM_MAX && !expander) return gpio_isr_handler_add(gpio, isr_handler, arg);
if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG;
expander->isr[gpio].handler = isr_handler;
expander->isr[gpio].arg = arg;
if (debounce) expander->isr[gpio].timer = xTimerCreate("gpioExpDebounce", pdMS_TO_TICKS(debounce),
pdFALSE, expander->isr + gpio, debounce_handler );
return ESP_OK;
}
/******************************************************************************
* Remove ISR handler for a GPIO
*/
esp_err_t gpio_exp_isr_handler_remove(int gpio, struct gpio_exp_s *expander) {
if (gpio < GPIO_NUM_MAX && !expander) return gpio_isr_handler_remove(gpio);
if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG;
if (expander->isr[gpio].timer) xTimerDelete(expander->isr[gpio].timer, portMAX_DELAY);
memset(expander->isr + gpio, 0, sizeof(struct gpio_exp_isr_s));
return ESP_OK;
}
/******************************************************************************
* Set GPIO direction
*/
esp_err_t gpio_exp_set_direction(int gpio, gpio_mode_t mode, gpio_exp_t *expander) {
if (gpio < GPIO_NUM_MAX && !expander) return gpio_set_direction(gpio, mode);
if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG;
xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(portMAX_DELAY));
if (mode == GPIO_MODE_INPUT) {
expander->r_mask |= 1 << gpio;
expander->shadow = expander->model->read(expander);
expander->age = ~xTaskGetTickCount();
} else {
expander->w_mask |= 1 << gpio;
}
if (expander->r_mask & expander->w_mask) {
xSemaphoreGive(expander->mutex);
ESP_LOGE(TAG, "GPIO %d on expander base %u can't be r/w", gpio, expander->first);
return ESP_ERR_INVALID_ARG;
}
// most expanders want unconfigured GPIO to be set to output
if (expander->model->set_direction) expander->model->set_direction(expander);
xSemaphoreGive(expander->mutex);
return ESP_OK;
}
/******************************************************************************
* Get GPIO level with cache
*/
int gpio_exp_get_level(int gpio, int age, gpio_exp_t *expander) {
if (gpio < GPIO_NUM_MAX && !expander) return gpio_get_level(gpio);
if ((expander = find_expander(expander, &gpio)) == NULL) return -1;
uint32_t now = xTaskGetTickCount();
// return last thing we had if we can't get the mutex
if (xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(50)) == pdFALSE) {
ESP_LOGW(TAG, "Can't get mutex for GPIO %d", expander->first + gpio);
return (expander->shadow >> gpio) & 0x01;
}
// re-read the expander if data is too old
if (age >= 0 && now - expander->age >= pdMS_TO_TICKS(age)) {
uint32_t value = expander->model->read(expander);
expander->pending |= (expander->shadow ^ value) & expander->r_mask;
expander->shadow = value;
expander->age = now;
}
// clear pending bit
expander->pending &= ~(1 << gpio);
xSemaphoreGive(expander->mutex);
ESP_LOGD(TAG, "Get level for GPIO %u => read %x", expander->first + gpio, expander->shadow);
return (expander->shadow >> gpio) & 0x01;
}
/******************************************************************************
* Set GPIO level with cache
*/
esp_err_t gpio_exp_set_level(int gpio, int level, bool direct, gpio_exp_t *expander) {
if (gpio < GPIO_NUM_MAX && !expander) return gpio_set_level(gpio, level);
if ((expander = find_expander(expander, &gpio)) == NULL) return ESP_ERR_INVALID_ARG;
uint32_t mask = 1 << gpio;
// very limited risk with lack of semaphore here
if ((expander->w_mask & mask) == 0) {
ESP_LOGW(TAG, "GPIO %d is not set for output", expander->first + gpio);
return ESP_ERR_INVALID_ARG;
}
if (direct) {
xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(portMAX_DELAY));
level = level ? mask : 0;
mask &= expander->shadow;
// only write if shadow not up to date
if ((mask ^ level) && expander->model->write) {
expander->shadow = (expander->shadow & ~(mask | level)) | level;
expander->model->write(expander);
}
xSemaphoreGive(expander->mutex);
ESP_LOGD(TAG, "Set level %x for GPIO %u => wrote %x", level, expander->first + gpio, expander->shadow);
} else {
queue_request_t request = { .gpio = gpio, .level = level, .type = ASYNC_WRITE, .expander = expander };
if (xQueueSend(message_queue, &request, 0) == pdFALSE) return ESP_ERR_INVALID_RESPONSE;
// notify service task that will write it when it can
xTaskNotify(service_task, GPIO_EXP_WRITE, eSetValueWithoutOverwrite);
}
return ESP_OK;
}
/******************************************************************************
* Set GPIO pullmode
*/
esp_err_t gpio_exp_set_pull_mode(int gpio, gpio_pull_mode_t mode, gpio_exp_t *expander) {
if (gpio < GPIO_NUM_MAX && !expander) return gpio_set_pull_mode(gpio, mode);
if ((expander = find_expander(expander, &gpio)) != NULL && expander->model->set_pull_mode) {
expander->pullup &= ~(1 << gpio);
expander->pulldown &= ~(1 << gpio);
if (mode == GPIO_PULLUP_ONLY || mode == GPIO_PULLUP_PULLDOWN) expander->pullup |= 1 << gpio;
if (mode == GPIO_PULLDOWN_ONLY || mode == GPIO_PULLUP_PULLDOWN) expander->pulldown |= 1 << gpio;
expander->model->set_pull_mode(expander);
return ESP_OK;
}
return ESP_ERR_INVALID_ARG;
}
/******************************************************************************
* Wrapper function
*/
esp_err_t gpio_set_pull_mode_x(int gpio, gpio_pull_mode_t mode) {
if (gpio < GPIO_NUM_MAX) return gpio_set_pull_mode(gpio, mode);
return gpio_exp_set_pull_mode(gpio, mode, NULL);
}
esp_err_t gpio_set_direction_x(int gpio, gpio_mode_t mode) {
if (gpio < GPIO_NUM_MAX) return gpio_set_direction(gpio, mode);
return gpio_exp_set_direction(gpio, mode, NULL);
}
int gpio_get_level_x(int gpio) {
if (gpio < GPIO_NUM_MAX) return gpio_get_level(gpio);
return gpio_exp_get_level(gpio, 10, NULL);
}
esp_err_t gpio_set_level_x(int gpio, int level) {
if (gpio < GPIO_NUM_MAX) return gpio_set_level(gpio, level);
return gpio_exp_set_level(gpio, level, false, NULL);
}
esp_err_t gpio_isr_handler_add_x(int gpio, gpio_isr_t isr_handler, void* args) {
if (gpio < GPIO_NUM_MAX) return gpio_isr_handler_add(gpio, isr_handler, args);
return gpio_exp_isr_handler_add(gpio, isr_handler, 0, args, NULL);
}
esp_err_t gpio_isr_handler_remove_x(int gpio) {
if (gpio < GPIO_NUM_MAX) return gpio_isr_handler_remove(gpio);
return gpio_exp_isr_handler_remove(gpio, NULL);
}
/****************************************************************************************
* INTR low-level handler
*/
static void IRAM_ATTR intr_isr_handler(void* arg) {
gpio_exp_t *self = (gpio_exp_t*) arg;
BaseType_t woken = pdFALSE;
// activate all, including ourselves
for (int i = 0; i < n_expanders; i++) if (expanders[i].intr == self->intr) expanders[i].intr_pending = true;
xTaskNotifyFromISR(service_task, GPIO_EXP_INTR, eSetValueWithOverwrite, &woken);
if (woken) portYIELD_FROM_ISR();
ESP_EARLY_LOGD(TAG, "INTR for expander base %d", gpio_exp_get_base(self));
}
/****************************************************************************************
* INTR debounce handler
*/
static void debounce_handler( TimerHandle_t xTimer ) {
struct gpio_exp_isr_s *isr = (struct gpio_exp_isr_s*) pvTimerGetTimerID (xTimer);
isr->handler(isr->arg);
}
/****************************************************************************************
* Service task
*/
void service_handler(void *arg) {
while (1) {
queue_request_t request;
uint32_t notif = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// we have been notified of an interrupt
if (notif == GPIO_EXP_INTR) {
/* If we want a smarter bitmap of expanders with a pending interrupt
we'll have to disable interrupts while clearing that bitmap. For
now, a loop will do */
for (int i = 0; i < n_expanders; i++) {
gpio_exp_t *expander = expanders + i;
// no interrupt for that gpio
if (expander->intr < 0) continue;
// only check expander with pending interrupts
gpio_intr_disable(expander->intr);
if (!expander->intr_pending) {
gpio_intr_enable(expander->intr);
continue;
}
expander->intr_pending = false;
gpio_intr_enable(expander->intr);
xSemaphoreTake(expander->mutex, pdMS_TO_TICKS(50));
// read GPIOs and clear all pending status
uint32_t value = expander->model->read(expander);
uint32_t pending = expander->pending | ((expander->shadow ^ value) & expander->r_mask);
expander->shadow = value;
expander->pending = 0;
expander->age = xTaskGetTickCount();
xSemaphoreGive(expander->mutex);
ESP_LOGD(TAG, "Handling GPIO %d reads 0x%04x and has 0x%04x pending", expander->first, expander->shadow, pending);
for (int gpio = 31, clz; pending; pending <<= (clz + 1)) {
clz = __builtin_clz(pending);
gpio -= clz;
if (expander->isr[gpio].timer) xTimerReset(expander->isr[gpio].timer, 1); // todo 0
else if (expander->isr[gpio].handler) expander->isr[gpio].handler(expander->isr[gpio].arg);
}
}
}
// check if we have some other pending requests
while (xQueueReceive(message_queue, &request, 0) == pdTRUE) {
esp_err_t err = gpio_exp_set_level(request.gpio, request.level, true, request.expander);
if (err != ESP_OK) ESP_LOGW(TAG, "Can't execute async GPIO %d write request (%d)", request.gpio, err);
}
}
}
/****************************************************************************************
* Find the expander related to base
*/
static gpio_exp_t* find_expander(gpio_exp_t *expander, int *gpio) {
// a mutex would be better, but risk is so small...
for (int i = 0; !expander && i < n_expanders; i++) {
if (*gpio >= expanders[i].first && *gpio <= expanders[i].last) expander = expanders + i;
}
// normalize GPIO number
if (expander && *gpio >= expander->first) *gpio -= expander->first;
return expander;
}
/****************************************************************************************
DRIVERS
****************************************************************************************/
/****************************************************************************************
* PCA9535 family : direction, read and write
*/
static void pca9535_set_direction(gpio_exp_t* self) {
i2c_write(self->phy.port, self->phy.addr, 0x06, self->r_mask, 2);
}
static uint32_t pca9535_read(gpio_exp_t* self) {
return i2c_read(self->phy.port, self->phy.addr, 0x00, 2);
}
static void pca9535_write(gpio_exp_t* self) {
i2c_write(self->phy.port, self->phy.addr, 0x02, self->shadow, 2);
}
/****************************************************************************************
* PCA85xx family : read and write
*/
static uint32_t pca85xx_read(gpio_exp_t* self) {
// must return the full set of pins, not just inputs
uint32_t data = i2c_read(self->phy.port, self->phy.addr, 0xff, 2);
return (data & self->r_mask) | (self->shadow & ~self->r_mask);
}
static void pca85xx_write(gpio_exp_t* self) {
/*
There is no good option with this chip: normally, unused pin should be set to input
to avoid any conflict but then they float and create tons of suprious. So option 1 is
to le tthem float and option 2 is to set them as output to 0.
In addition, setting an output pin to 1 equals is making it an input and if this is
use to short a led (e.g.) instead of being the sink, the it generates a spurious
*/
// option 1
// i2c_write(self->phy.port, self->phy.addr, 0xff, (self->shadow & self->w_mask) | ~self->w_mask, 2);
// option 2
i2c_write(self->phy.port, self->phy.addr, 0xff, (self->shadow & self->w_mask) | self->r_mask, 2);
}
/****************************************************************************************
* MCP23017 family : init, direction, read and write
*/
static esp_err_t mcp23017_init(gpio_exp_t* self) {
/*
0111 x10x = same bank, mirrot single int, no sequentµial, open drain, active low
not sure about this funny change of mapping of the control register itself, really?
*/
esp_err_t err = i2c_write(self->phy.port, self->phy.addr, 0x05, 0x74, 1);
err |= i2c_write(self->phy.port, self->phy.addr, 0x0a, 0x74, 1);
// no interrupt on comparison or on change
err |= i2c_write(self->phy.port, self->phy.addr, 0x04, 0x00, 2);
err |= i2c_write(self->phy.port, self->phy.addr, 0x08, 0x00, 2);
return err;
}
static void mcp23017_set_direction(gpio_exp_t* self) {
// default to input and set real input to generate interrupt
i2c_write(self->phy.port, self->phy.addr, 0x00, ~self->w_mask, 2);
i2c_write(self->phy.port, self->phy.addr, 0x04, self->r_mask, 2);
}
static void mcp23017_set_pull_mode(gpio_exp_t* self) {
i2c_write(self->phy.port, self->phy.addr, 0x0c, self->pullup, 2);
}
static uint32_t mcp23017_read(gpio_exp_t* self) {
// read the pins value, not the stored one @interrupt
return i2c_read(self->phy.port, self->phy.addr, 0x12, 2);
}
static void mcp23017_write(gpio_exp_t* self) {
i2c_write(self->phy.port, self->phy.addr, 0x12, self->shadow, 2);
}
/****************************************************************************************
* MCP23s17 family : init, direction, read and write
*/
static esp_err_t mcp23s17_init(gpio_exp_t* self) {
if ((self->spi_handle = spi_config(&self->phy)) == NULL) return ESP_ERR_INVALID_ARG;
/*
0111 x10x = same bank, mirrot single int, no sequentµial, open drain, active low
not sure about this funny change of mapping of the control register itself, really?
*/
esp_err_t err = spi_write(self->spi_handle, self->phy.addr, 0x05, 0x74, 1);
err |= spi_write(self->spi_handle, self->phy.addr, 0x0a, 0x74, 1);
// no interrupt on comparison or on change
err |= spi_write(self->spi_handle, self->phy.addr, 0x04, 0x00, 2);
err |= spi_write(self->spi_handle, self->phy.addr, 0x08, 0x00, 2);
return err;
}
static void mcp23s17_set_direction(gpio_exp_t* self) {
// default to input and set real input to generate interrupt
spi_write(self->spi_handle, self->phy.addr, 0x00, ~self->w_mask, 2);
spi_write(self->spi_handle, self->phy.addr, 0x04, self->r_mask, 2);
}
static void mcp23s17_set_pull_mode(gpio_exp_t* self) {
spi_write(self->spi_handle, self->phy.addr, 0x0c, self->pullup, 2);
}
static uint32_t mcp23s17_read(gpio_exp_t* self) {
// read the pins value, not the stored one @interrupt
return spi_read(self->spi_handle, self->phy.addr, 0x12, 2);
}
static void mcp23s17_write(gpio_exp_t* self) {
spi_write(self->spi_handle, self->phy.addr, 0x12, self->shadow, 2);
}
/***************************************************************************************
I2C low level
***************************************************************************************/
/****************************************************************************************
* I2C write up to 32 bits
*/
static esp_err_t i2c_write(uint8_t port, uint8_t addr, uint8_t reg, uint32_t data, int len) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
if (reg != 0xff) i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
// works with our endianness
if (len > 1) i2c_master_write(cmd, (uint8_t*) &data, len, I2C_MASTER_NACK);
else i2c_master_write_byte(cmd, data, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(port, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C write failed");
}
return ret;
}
/****************************************************************************************
* I2C read up to 32 bits
*/
static uint32_t i2c_read(uint8_t port, uint8_t addr, uint8_t reg, int len) {
uint32_t data = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
// when using a register, write it's value then the device address again
if (reg != 0xff) {
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK);
} else {
i2c_master_write_byte(cmd, (addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK);
}
// works with our endianness
if (len > 1) i2c_master_read(cmd, (uint8_t*) &data, len, I2C_MASTER_LAST_NACK);
else i2c_master_read_byte(cmd, (uint8_t*) &data, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(port, cmd, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C read failed");
}
return data;
}
/***************************************************************************************
SPI low level
***************************************************************************************/
/****************************************************************************************
* SPI device addition
*/
static spi_device_handle_t spi_config(struct gpio_exp_phy_s *phy) {
spi_device_interface_config_t config = { };
spi_device_handle_t handle = NULL;
config.command_bits = config.address_bits = 8;
config.clock_speed_hz = phy->speed ? phy->speed : SPI_MASTER_FREQ_8M;
config.spics_io_num = phy->cs_pin;
config.queue_size = 1;
config.flags = SPI_DEVICE_NO_DUMMY;
spi_bus_add_device( phy->host, &config, &handle );
ESP_LOGI(TAG, "SPI expander initialized on host:%d with cs:%d and speed:%dHz", phy->host, phy->cs_pin, config.clock_speed_hz);
return handle;
}
/****************************************************************************************
* SPI write up to 32 bits
*/
static esp_err_t spi_write(spi_device_handle_t handle, uint8_t addr, uint8_t reg, uint32_t data, int len) {
spi_transaction_t transaction = { };
// rx_buffer is NULL, nothing to receive
transaction.flags = SPI_TRANS_USE_TXDATA;
transaction.cmd = addr << 1;
transaction.addr = reg;
transaction.tx_data[0] = data; transaction.tx_data[1] = data >> 8;
transaction.length = len * 8;
// only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes)
return spi_device_polling_transmit(handle, &transaction);
}
/****************************************************************************************
* SPI read up to 32 bits
*/
static uint32_t spi_read(spi_device_handle_t handle, uint8_t addr, uint8_t reg, int len) {
spi_transaction_t *transaction = heap_caps_calloc(1, sizeof(spi_transaction_t), MALLOC_CAP_DMA);
// tx_buffer is NULL, nothing to transmit except cmd/addr
transaction->flags = SPI_TRANS_USE_RXDATA;
transaction->cmd = (addr << 1) | 0x01;
transaction->addr = reg;
transaction->length = len * 8;
// only do polling as we don't have contention on SPI (otherwise DMA for transfers > 16 bytes)
spi_device_polling_transmit(handle, transaction);
uint32_t data = *(uint32_t*) transaction->rx_data;
free(transaction);
return data;
}

View File

@@ -0,0 +1,61 @@
/* GDS Example
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 "freertos/FreeRTOS.h"
#include "driver/gpio.h"
struct gpio_exp_s;
typedef struct {
char model[32];
int intr;
uint8_t count;
uint32_t base;
struct gpio_exp_phy_s {
uint8_t addr;
struct { // for I2C
uint8_t port;
};
struct { // for SPI
uint32_t speed;
uint8_t host;
uint8_t cs_pin;
};
} phy;
} gpio_exp_config_t;
// set <intr> to -1 and <queue> to NULL if there is no interrupt
struct gpio_exp_s* gpio_exp_create(const gpio_exp_config_t *config);
uint32_t gpio_exp_get_base(struct gpio_exp_s *expander);
struct gpio_exp_s* gpio_exp_get_expander(int gpio);
#define gpio_is_expanded(gpio) (gpio < GPIO_NUM_MAX)
/*
For all functions below when <expander> is provided, GPIO's can be numbered from 0. If <expander>
is NULL, then GPIO must start from base OR be on-chip
*/
esp_err_t gpio_exp_set_direction(int gpio, gpio_mode_t mode, struct gpio_exp_s *expander);
esp_err_t gpio_exp_set_pull_mode(int gpio, gpio_pull_mode_t mode, struct gpio_exp_s *expander);
int gpio_exp_get_level(int gpio, int age, struct gpio_exp_s *expander);
esp_err_t gpio_exp_set_level(int gpio, int level, bool direct, struct gpio_exp_s *expander);
esp_err_t gpio_exp_isr_handler_add(int gpio, gpio_isr_t isr, uint32_t debounce, void *arg, struct gpio_exp_s *expander);
esp_err_t gpio_exp_isr_handler_remove(int gpio, struct gpio_exp_s *expander);
// unified function to use either built-in or expanded GPIO
esp_err_t gpio_set_direction_x(int gpio, gpio_mode_t mode);
esp_err_t gpio_set_pull_mode_x(int gpio, gpio_pull_mode_t mode);
int gpio_get_level_x(int gpio);
esp_err_t gpio_set_level_x(int gpio, int level);
esp_err_t gpio_isr_handler_add_x(int gpio, gpio_isr_t isr_handler, void* args);
esp_err_t gpio_isr_handler_remove_x(int gpio);
#define gpio_set_intr_type_x(gpio, type) do { if (gpio < GPIO_NUM_MAX) gpio_set_intr_type(gpio, type); } while (0)
#define gpio_intr_enable_x(gpio) do { if (gpio < GPIO_NUM_MAX) gpio_intr_enable(gpio); } while (0)
#define gpio_pad_select_gpio_x(gpio) do { if (gpio < GPIO_NUM_MAX) gpio_pad_select_gpio(gpio); } while (0)

View File

@@ -19,6 +19,7 @@
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "platform_config.h"
#include "gpio_exp.h"
#include "led.h"
#include "globdefs.h"
#include "accessors.h"
@@ -40,7 +41,8 @@ static EXT_RAM_ATTR struct led_s {
TimerHandle_t timer;
} leds[MAX_LED];
static EXT_RAM_ATTR struct {
// can't use EXT_RAM_ATTR for initialized structure
static struct {
int gpio;
int active;
int pwm;
@@ -53,7 +55,7 @@ static int led_max = 2;
*
*/
static void set_level(struct led_s *led, bool on) {
if (led->pwm < 0) gpio_set_level(led->gpio, on ? led->onstate : !led->onstate);
if (led->pwm < 0 || led->gpio >= GPIO_NUM_MAX) gpio_set_level_x(led->gpio, on ? led->onstate : !led->onstate);
else {
ledc_set_duty(LEDC_HIGH_SPEED_MODE, led->channel, on ? led->pwm : (led->onstate ? 0 : pwm_system.max));
ledc_update_duty(LEDC_HIGH_SPEED_MODE, led->channel);
@@ -179,9 +181,9 @@ bool led_config(int idx, gpio_num_t gpio, int onstate, int pwm) {
leds[idx].onstate = onstate;
leds[idx].pwm = -1;
if (pwm < 0) {
gpio_pad_select_gpio(gpio);
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
if (pwm < 0 || gpio >= GPIO_NUM_MAX) {
gpio_pad_select_gpio_x(gpio);
gpio_set_direction_x(gpio, GPIO_MODE_OUTPUT);
} else {
leds[idx].channel = pwm_system.base_channel++;
leds[idx].pwm = pwm_system.max * powf(pwm / 100.0, 3);
@@ -232,10 +234,10 @@ void led_svc_init(void) {
parse_set_GPIO(set_led_gpio);
#endif
char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness"), *p;
char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness");
if (nvs_item) {
if ((p = strcasestr(nvs_item, "green")) != NULL) green.pwm = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(nvs_item, "red")) != NULL) red.pwm = atoi(strchr(p, '=') + 1);
PARSE_PARAM(nvs_item, "green", '=', green.pwm);
PARSE_PARAM(nvs_item, "red", '=', red.pwm);
free(nvs_item);
}

View File

@@ -14,8 +14,7 @@
#include "nvs_utilities.h"
#include "platform_esp32.h"
#include "messaging.h"
#include "trace.h"
#include "globdefs.h"
#include "tools.h"
/************************************
* Globals
*/

View File

@@ -23,7 +23,7 @@
#include "accessors.h"
#include "messaging.h"
#include "cJSON.h"
#include "trace.h"
#include "tools.h"
#define MONITOR_TIMER (10*1000)
#define SCRATCH_SIZE 256
@@ -147,7 +147,7 @@ static void monitor_callback(TimerHandle_t xTimer) {
*
*/
static void jack_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) {
ESP_LOGD(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed");
ESP_LOGI(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed");
if (jack_handler_svc) (*jack_handler_svc)(event == BUTTON_PRESSED);
}

View File

@@ -91,6 +91,7 @@
#include "esp_log.h"
#include "driver/gpio.h"
#include "gpio_exp.h"
#define TAG "rotary_encoder"
@@ -148,7 +149,7 @@ static uint8_t _process(rotary_encoder_info_t * info)
if (info != NULL)
{
// Get state of input pins.
uint8_t pin_state = (gpio_get_level(info->pin_b) << 1) | gpio_get_level(info->pin_a);
uint8_t pin_state = (gpio_get_level_x(info->pin_b) << 1) | gpio_get_level_x(info->pin_a);
// Determine new state from the pins and state table.
#ifdef ROTARY_ENCODER_DEBUG
@@ -198,12 +199,18 @@ static void _isr_rotenc(void * args)
.direction = info->state.direction,
},
};
BaseType_t task_woken = pdFALSE;
xQueueOverwriteFromISR(info->queue, &queue_event, &task_woken);
if (task_woken)
{
portYIELD_FROM_ISR();
}
if (info->pin_a < GPIO_NUM_MAX) {
BaseType_t task_woken = pdFALSE;
xQueueOverwriteFromISR(info->queue, &queue_event, &task_woken);
if (task_woken)
{
portYIELD_FROM_ISR();
}
}
else
{
xQueueOverwrite(info->queue, &queue_event);
}
}
}
@@ -220,19 +227,19 @@ esp_err_t rotary_encoder_init(rotary_encoder_info_t * info, gpio_num_t pin_a, gp
info->state.direction = ROTARY_ENCODER_DIRECTION_NOT_SET;
// configure GPIOs
gpio_pad_select_gpio(info->pin_a);
gpio_set_pull_mode(info->pin_a, GPIO_PULLUP_ONLY);
gpio_set_direction(info->pin_a, GPIO_MODE_INPUT);
gpio_set_intr_type(info->pin_a, GPIO_INTR_ANYEDGE);
gpio_pad_select_gpio_x(info->pin_a);
gpio_set_pull_mode_x(info->pin_a, GPIO_PULLUP_ONLY);
gpio_set_direction_x(info->pin_a, GPIO_MODE_INPUT);
gpio_set_intr_type_x(info->pin_a, GPIO_INTR_ANYEDGE);
gpio_pad_select_gpio(info->pin_b);
gpio_set_pull_mode(info->pin_b, GPIO_PULLUP_ONLY);
gpio_set_direction(info->pin_b, GPIO_MODE_INPUT);
gpio_set_intr_type(info->pin_b, GPIO_INTR_ANYEDGE);
gpio_pad_select_gpio_x(info->pin_b);
gpio_set_pull_mode_x(info->pin_b, GPIO_PULLUP_ONLY);
gpio_set_direction_x(info->pin_b, GPIO_MODE_INPUT);
gpio_set_intr_type_x(info->pin_b, GPIO_INTR_ANYEDGE);
// install interrupt handlers
gpio_isr_handler_add(info->pin_a, _isr_rotenc, info);
gpio_isr_handler_add(info->pin_b, _isr_rotenc, info);
gpio_isr_handler_add_x(info->pin_a, _isr_rotenc, info);
gpio_isr_handler_add_x(info->pin_b, _isr_rotenc, info);
}
else
{
@@ -280,8 +287,8 @@ esp_err_t rotary_encoder_uninit(rotary_encoder_info_t * info)
esp_err_t err = ESP_OK;
if (info)
{
gpio_isr_handler_remove(info->pin_a);
gpio_isr_handler_remove(info->pin_b);
gpio_isr_handler_remove_x(info->pin_a);
gpio_isr_handler_remove_x(info->pin_b);
}
else
{

View File

@@ -12,15 +12,13 @@
#include "driver/ledc.h"
#include "driver/i2c.h"
#include "platform_config.h"
#include "gpio_exp.h"
#include "battery.h"
#include "led.h"
#include "monitor.h"
#include "globdefs.h"
#include "accessors.h"
#include "messaging.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
extern void battery_svc_init(void);
extern void monitor_svc_init(void);
@@ -38,48 +36,14 @@ pwm_system_t pwm_system = {
static const char *TAG = "services";
void * malloc_init_external(size_t sz){
void * ptr=NULL;
ptr = heap_caps_malloc(sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if(ptr==NULL){
ESP_LOGE(TAG,"malloc_init_external: unable to allocate %d bytes of PSRAM!",sz);
}
else {
memset(ptr,0x00,sz);
}
return ptr;
}
void * clone_obj_psram(void * source, size_t source_sz){
void * ptr=NULL;
ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if(ptr==NULL){
ESP_LOGE(TAG,"clone_obj_psram: unable to allocate %d bytes of PSRAM!",source_sz);
}
else {
memcpy(ptr,source,source_sz);
}
return ptr;
}
char * strdup_psram(const char * source){
void * ptr=NULL;
size_t source_sz = strlen(source)+1;
ptr = heap_caps_malloc(source_sz, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if(ptr==NULL){
ESP_LOGE(TAG,"strdup_psram: unable to allocate %d bytes of PSRAM! Cannot clone string %s",source_sz,source);
}
else {
memset(ptr,0x00,source_sz);
strcpy(ptr,source);
}
return ptr;
}
/****************************************************************************************
*
*/
void set_power_gpio(int gpio, char *value) {
void set_chip_power_gpio(int gpio, char *value) {
bool parsed = true;
// we only parse on-chip GPIOs
if (gpio >= GPIO_NUM_MAX) return;
if (!strcasecmp(value, "vcc") ) {
gpio_pad_select_gpio(gpio);
@@ -89,9 +53,26 @@ void set_power_gpio(int gpio, char *value) {
gpio_pad_select_gpio(gpio);
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
gpio_set_level(gpio, 0);
} else parsed = false ;
} else parsed = false;
if (parsed) ESP_LOGI(TAG, "set GPIO %u to %s", gpio, value);
}
void set_exp_power_gpio(int gpio, char *value) {
bool parsed = true;
// we only parse on-chip GPIOs
if (gpio < GPIO_NUM_MAX) return;
if (!strcasecmp(value, "vcc") ) {
gpio_exp_set_direction(gpio, GPIO_MODE_OUTPUT, NULL);
gpio_exp_set_level(gpio, 1, true, NULL);
} else if (!strcasecmp(value, "gnd")) {
gpio_exp_set_direction(gpio, GPIO_MODE_OUTPUT, NULL);
gpio_exp_set_level(gpio, 0, true, NULL);
} else parsed = false;
if (parsed) ESP_LOGI(TAG, "set expanded GPIO %u to %s", gpio, value);
}
@@ -109,8 +90,8 @@ void services_init(void) {
}
#endif
// set potential power GPIO
parse_set_GPIO(set_power_gpio);
// set potential power GPIO on chip first in case expanders are power using these
parse_set_GPIO(set_chip_power_gpio);
// shared I2C bus
const i2c_config_t * i2c_config = config_i2c_get(&i2c_system_port);
@@ -140,6 +121,13 @@ void services_init(void) {
ESP_LOGW(TAG, "no SPI configured");
}
// create GPIO expanders
const gpio_exp_config_t* gpio_exp_config;
for (int count = 0; (gpio_exp_config = config_gpio_exp_get(count)); count++) gpio_exp_create(gpio_exp_config);
// now set potential power GPIO on expander
parse_set_GPIO(set_exp_power_gpio);
// system-wide PWM timer configuration
ledc_timer_config_t pwm_timer = {
.duty_resolution = LEDC_TIMER_13_BIT,