|
| 1 | +#include "../../hardware_api.h" |
| 2 | +#include "../../../drivers/hardware_api.h" |
| 3 | +#include "../../../drivers/hardware_specific/esp32/esp32_driver_mcpwm.h" |
| 4 | +#include "../../../drivers/hardware_specific/esp32/mcpwm_private.h" |
| 5 | + |
| 6 | +#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && defined(SOC_MCPWM_SUPPORTED) && !defined(SIMPLEFOC_ESP32_USELEDC) |
| 7 | + |
| 8 | +// check the version of the ESP-IDF |
| 9 | +#include "esp_idf_version.h" |
| 10 | + |
| 11 | +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) |
| 12 | +#error SimpleFOC: ESP-IDF version 4 or lower detected. Please update to ESP-IDF 5.x and Arduino-esp32 3.0 (or higher) |
| 13 | +#endif |
| 14 | + |
| 15 | + |
| 16 | +#include "esp32_adc_driver.h" |
| 17 | + |
| 18 | +#include "driver/mcpwm_prelude.h" |
| 19 | +#include "soc/mcpwm_reg.h" |
| 20 | +#include "soc/mcpwm_struct.h" |
| 21 | + |
| 22 | +#include <soc/sens_reg.h> |
| 23 | +#include <soc/sens_struct.h> |
| 24 | + |
| 25 | +#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
| 26 | +#include "driver/gpio.h" |
| 27 | +#endif |
| 28 | + |
| 29 | +#define _ADC_VOLTAGE 3.3f |
| 30 | +#define _ADC_RESOLUTION 4095.0f |
| 31 | + |
| 32 | + |
| 33 | + |
| 34 | +#define SIMPLEFOC_ESP32_CS_DEBUG(str)\ |
| 35 | + SIMPLEFOC_ESP32_DEBUG("CS", str);\ |
| 36 | + |
| 37 | +#define CHECK_CS_ERR(func_call, message) \ |
| 38 | + if ((func_call) != ESP_OK) { \ |
| 39 | + SIMPLEFOC_ESP32_CS_DEBUG("ERROR - " + String(message)); \ |
| 40 | + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; \ |
| 41 | + } |
| 42 | + |
| 43 | +typedef struct ESP32MCPWMCurrentSenseParams { |
| 44 | + int pins[3]; |
| 45 | + float adc_voltage_conv; |
| 46 | + int adc_buffer[3] = {}; |
| 47 | + int buffer_index = 0; |
| 48 | + int no_adc_channels = 0; |
| 49 | +} ESP32MCPWMCurrentSenseParams; |
| 50 | + |
| 51 | + |
| 52 | +/** |
| 53 | + * Inline adc reading implementation |
| 54 | +*/ |
| 55 | +// function reading an ADC value and returning the read voltage |
| 56 | +float _readADCVoltageInline(const int pinA, const void* cs_params){ |
| 57 | + uint32_t raw_adc = adcRead(pinA); |
| 58 | + return raw_adc * ((ESP32MCPWMCurrentSenseParams*)cs_params)->adc_voltage_conv; |
| 59 | +} |
| 60 | + |
| 61 | +// function reading an ADC value and returning the read voltage |
| 62 | +void* _configureADCInline(const void* driver_params, const int pinA, const int pinB, const int pinC){ |
| 63 | + |
| 64 | + |
| 65 | + if( _isset(pinA) ) pinMode(pinA, INPUT); |
| 66 | + if( _isset(pinB) ) pinMode(pinB, INPUT); |
| 67 | + if( _isset(pinC) ) pinMode(pinC, INPUT); |
| 68 | + |
| 69 | + ESP32MCPWMCurrentSenseParams* params = new ESP32MCPWMCurrentSenseParams { |
| 70 | + .pins = { pinA, pinB, pinC }, |
| 71 | + .adc_voltage_conv = (_ADC_VOLTAGE)/(_ADC_RESOLUTION) |
| 72 | + }; |
| 73 | + |
| 74 | + return params; |
| 75 | +} |
| 76 | + |
| 77 | + |
| 78 | +/** |
| 79 | + * Low side adc reading implementation |
| 80 | +*/ |
| 81 | + |
| 82 | + |
| 83 | +// function reading an ADC value and returning the read voltage |
| 84 | +float _readADCVoltageLowSide(const int pin, const void* cs_params){ |
| 85 | + ESP32MCPWMCurrentSenseParams* p = (ESP32MCPWMCurrentSenseParams*)cs_params; |
| 86 | + int no_channel = 0; |
| 87 | + for(int i=0; i < 3; i++){ |
| 88 | + if(!_isset(p->pins[i])) continue; |
| 89 | + if(pin == p->pins[i]) // found in the buffer |
| 90 | + return p->adc_buffer[no_channel] * p->adc_voltage_conv; |
| 91 | + else no_channel++; |
| 92 | + } |
| 93 | + // not found |
| 94 | + return 0; |
| 95 | +} |
| 96 | + |
| 97 | + |
| 98 | +// function configuring low-side current sensing |
| 99 | +void* _configureADCLowSide(const void* driver_params, const int pinA,const int pinB,const int pinC){ |
| 100 | + // check if driver timer is already running |
| 101 | + // fail if it is |
| 102 | + // the easiest way that I've found to check if timer is running |
| 103 | + // is to start it and stop it |
| 104 | + ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams*)driver_params; |
| 105 | + mcpwm_timer_t* t = (mcpwm_timer_t*) p->timers[0]; |
| 106 | + |
| 107 | + // check if low side callback is already set |
| 108 | + // if it is, return error |
| 109 | + if(t->on_full != nullptr){ |
| 110 | + SIMPLEFOC_ESP32_CS_DEBUG("Low side callback is already set. Cannot set it again for timer: "+String(t->timer_id)+", group: "+String(t->group->group_id)); |
| 111 | + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; |
| 112 | + } |
| 113 | + |
| 114 | + ESP32MCPWMCurrentSenseParams* params = new ESP32MCPWMCurrentSenseParams{}; |
| 115 | + int no_adc_channels = 0; |
| 116 | + if( _isset(pinA) ){ |
| 117 | + pinMode(pinA, INPUT); |
| 118 | + params->pins[no_adc_channels++] = pinA; |
| 119 | + } |
| 120 | + if( _isset(pinB) ){ |
| 121 | + pinMode(pinB, INPUT); |
| 122 | + params->pins[no_adc_channels++] = pinB; |
| 123 | + } |
| 124 | + if( _isset(pinC) ){ |
| 125 | + pinMode(pinC, INPUT); |
| 126 | + params->pins[no_adc_channels++] = pinC; |
| 127 | + } |
| 128 | + |
| 129 | + t->user_data = params; |
| 130 | + params->adc_voltage_conv = (_ADC_VOLTAGE)/(_ADC_RESOLUTION); |
| 131 | + params->no_adc_channels = no_adc_channels; |
| 132 | + return params; |
| 133 | +} |
| 134 | + |
| 135 | +void* _driverSyncLowSide(void* driver_params, void* cs_params){ |
| 136 | +#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
| 137 | + pinMode(19, OUTPUT); |
| 138 | +#endif |
| 139 | + ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams*)driver_params; |
| 140 | + mcpwm_timer_t* t = (mcpwm_timer_t*) p->timers[0]; |
| 141 | + |
| 142 | + // check if low side callback is already set |
| 143 | + // if it is, return error |
| 144 | + if(t->on_full != nullptr){ |
| 145 | + SIMPLEFOC_ESP32_CS_DEBUG("Low side callback is already set. Cannot set it again for timer: "+String(t->timer_id)+", group: "+String(t->group->group_id)); |
| 146 | + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; |
| 147 | + } |
| 148 | + |
| 149 | + // set the callback for the low side current sensing |
| 150 | + // mcpwm_timer_event_callbacks_t can be used to set the callback |
| 151 | + // for three timer events |
| 152 | + // - on_full - low-side |
| 153 | + // - on_empty - high-side |
| 154 | + // - on_sync - sync event (not used with simplefoc) |
| 155 | + auto cbs = mcpwm_timer_event_callbacks_t{ |
| 156 | + .on_full = [](mcpwm_timer_handle_t tim, const mcpwm_timer_event_data_t* edata, void* user_data){ |
| 157 | + ESP32MCPWMCurrentSenseParams *p = (ESP32MCPWMCurrentSenseParams*)user_data; |
| 158 | +#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
| 159 | + gpio_set_level(GPIO_NUM_19,1); //cca 250ns for on+off |
| 160 | +#endif |
| 161 | + // increment buffer index |
| 162 | + p->buffer_index = (p->buffer_index + 1) % p->no_adc_channels; |
| 163 | + // sample the phase currents one at a time |
| 164 | + // adc read takes around 10us which is very long |
| 165 | + // so we are sampling one phase per call |
| 166 | + p->adc_buffer[p->buffer_index] = adcRead(p->pins[p->buffer_index]); |
| 167 | +#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
| 168 | + gpio_set_level(GPIO_NUM_19,0); //cca 250ns for on+off |
| 169 | +#endif |
| 170 | + return true; |
| 171 | + }, |
| 172 | + }; |
| 173 | + SIMPLEFOC_ESP32_CS_DEBUG("Timer "+String(t->timer_id)+" enable interrupt callback."); |
| 174 | + // set the timer state to init (so that we can call the `mcpwm_timer_register_event_callbacks` ) |
| 175 | + // this is a hack, as this function is not supposed to be called when the timer is running |
| 176 | + // the timer does not really go to the init state! |
| 177 | + t->fsm = MCPWM_TIMER_FSM_INIT; |
| 178 | + // set the callback |
| 179 | + CHECK_CS_ERR(mcpwm_timer_register_event_callbacks(t, &cbs, cs_params), "Failed to set low side callback"); |
| 180 | + // set the timer state to enabled again |
| 181 | + t->fsm = MCPWM_TIMER_FSM_ENABLE; |
| 182 | + SIMPLEFOC_ESP32_CS_DEBUG("Timer "+String(t->timer_id)+" enable interrupts."); |
| 183 | + CHECK_CS_ERR(esp_intr_enable(t->intr), "Failed to enable low-side interrupts!"); |
| 184 | + |
| 185 | + return cs_params; |
| 186 | +} |
| 187 | + |
| 188 | + |
| 189 | +#endif |
0 commit comments