|
19 | 19 |
|
20 | 20 |
|
21 | 21 | // adding a debug toggle pin to measure the time of the interrupt with oscilloscope |
22 | | - |
23 | 22 | // #define SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
24 | 23 |
|
25 | 24 | #ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
26 | 25 | #include "driver/gpio.h" |
27 | 26 |
|
28 | | -#ifdef CONFIG_IDF_TARGET_ESP32S3 |
| 27 | +#ifndef DEBUGPIN |
| 28 | +#ifdef CONFIG_IDF_TARGET_ESP32S3 |
29 | 29 | #define DEBUGPIN 16 |
30 | | -#define GPIO_NUM GPIO_NUM_16 |
31 | 30 | #else |
32 | 31 | #define DEBUGPIN 19 |
33 | | -#define GPIO_NUM GPIO_NUM_19 |
| 32 | +#endif |
34 | 33 | #endif |
35 | 34 |
|
| 35 | +#define GPIO_NUM (gpio_num_t)((int)GPIO_NUM_0 + DEBUGPIN) |
| 36 | +#endif |
| 37 | + |
| 38 | +#ifndef SIMPLEFOC_CS_PRETRIGGER_US |
| 39 | +#define SIMPLEFOC_CS_PRETRIGGER_US 5 // 5 us because ADC read takes around 10us |
36 | 40 | #endif |
37 | 41 |
|
38 | 42 |
|
@@ -120,40 +124,119 @@ static bool IRAM_ATTR _mcpwmTriggerADCCallback(mcpwm_timer_handle_t tim, const m |
120 | 124 | return true; |
121 | 125 | } |
122 | 126 |
|
| 127 | +// Comparator on_reach callback: sample ADC pre-trigger |
| 128 | +// In center-aligned mode, comparator fires twice per cycle (up and down) |
| 129 | +// Only sample on down-count to get one sample per PWM period |
| 130 | +static bool IRAM_ATTR _mcpwmComparatorADCCallback(mcpwm_cmpr_handle_t cmpr, const mcpwm_compare_event_data_t* edata, void* user_data){ |
| 131 | + // Only trigger on down-count direction |
| 132 | + if(edata->direction != MCPWM_TIMER_DIRECTION_UP){ |
| 133 | + return true; |
| 134 | + } |
| 135 | + |
| 136 | + ESP32CurrentSenseParams *p = (ESP32CurrentSenseParams*)user_data; |
| 137 | +#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
| 138 | + gpio_set_level(GPIO_NUM,1); |
| 139 | +#endif |
| 140 | + |
| 141 | + // sample the phase currents one at a time |
| 142 | + // ESP's adc read takes around 10us which is very long |
| 143 | + // so we are sampling one phase per call |
| 144 | + p->adc_buffer[p->buffer_index] = adcRead(p->pins[p->buffer_index]); |
| 145 | + |
| 146 | + // increment buffer index |
| 147 | + p->buffer_index++; |
| 148 | + if(p->buffer_index >= p->no_adc_channels){ |
| 149 | + p->buffer_index = 0; |
| 150 | + } |
| 151 | + |
| 152 | +#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
| 153 | + gpio_set_level(GPIO_NUM,0); |
| 154 | +#endif |
| 155 | + return true; |
| 156 | +} |
| 157 | + |
123 | 158 | void* IRAM_ATTR _driverSyncLowSide(void* driver_params, void* cs_params){ |
124 | 159 | #ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG |
125 | 160 | pinMode(DEBUGPIN, OUTPUT); |
126 | 161 | #endif |
127 | 162 | ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams*)driver_params; |
128 | 163 | mcpwm_timer_t* t = (mcpwm_timer_t*) p->timers[0]; |
| 164 | + int group_id = p->group_id; |
129 | 165 |
|
130 | | - // check if low side callback is already set |
131 | | - // if it is, return error |
132 | | - if(t->on_full != nullptr){ |
133 | | - SIMPLEFOC_ESP32_CS_DEBUG("ERROR: Low side callback is already set. Cannot set it again for timer: "+String(t->timer_id)+", group: "+String(t->group->group_id)); |
| 166 | + |
| 167 | + ESP32CurrentSenseParams *cs = (ESP32CurrentSenseParams*)cs_params; |
| 168 | + if(!cs){ |
| 169 | + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: cs_params is null"); |
134 | 170 | return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; |
135 | 171 | } |
136 | 172 |
|
137 | | - // set the callback for the low side current sensing |
138 | | - // mcpwm_timer_event_callbacks_t can be used to set the callback |
139 | | - // for three timer events |
140 | | - // - on_full - low-side |
141 | | - // - on_empty - high-side |
142 | | - // - on_sync - sync event (not used with simplefoc) |
143 | | - auto cbs = mcpwm_timer_event_callbacks_t{ |
144 | | - .on_full = _mcpwmTriggerADCCallback, |
145 | | - }; |
146 | | - SIMPLEFOC_ESP32_CS_DEBUG("Timer "+String(t->timer_id)+" enable interrupt callback."); |
147 | | - // set the timer state to init (so that we can call the `mcpwm_timer_register_event_callbacks` ) |
148 | | - // this is a hack, as this function is not supposed to be called when the timer is running |
149 | | - // the timer does not really go to the init state! |
150 | | - t->fsm = MCPWM_TIMER_FSM_INIT; |
151 | | - // set the callback |
152 | | - CHECK_CS_ERR(mcpwm_timer_register_event_callbacks(t, &cbs, cs_params), "Failed to set low side callback"); |
153 | | - // set the timer state to enabled again |
154 | | - t->fsm = MCPWM_TIMER_FSM_ENABLE; |
155 | | - SIMPLEFOC_ESP32_CS_DEBUG("Timer "+String(t->timer_id)+" enable interrupts."); |
156 | | - CHECK_CS_ERR(esp_intr_enable(t->intr), "Failed to enable low-side interrupts!"); |
| 173 | + SIMPLEFOC_ESP32_CS_DEBUG("Configuring sync with comparator..."); |
| 174 | + |
| 175 | + // Create a spare comparator on the first operator for ADC pre-trigger |
| 176 | + // This comparator will fire ~5 µs before on_reach (peak) |
| 177 | + mcpwm_comparator_config_t cmp_config = {0}; |
| 178 | + cmp_config.flags.update_cmp_on_tez = true; |
| 179 | + for(int i=2; i>=0; i--){ // start from the end as first operators are more likely to be used fully |
| 180 | + if(p->oper[i] == nullptr) continue; |
| 181 | + if(mcpwm_new_comparator(p->oper[i], &cmp_config, (mcpwm_cmpr_handle_t*)&cs->pretrig_comparator) == ESP_OK){ |
| 182 | + break; |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + // if comparator creation failed, fall back to on_full callback |
| 187 | + if (cs->pretrig_comparator){ |
| 188 | + // Calculate pwm duty cycle ticks for pre-trigger channel |
| 189 | + uint32_t pwm_duty_cycle = p->mcpwm_period * (0.75 - ((float)p->pwm_frequency*SIMPLEFOC_CS_PRETRIGGER_US)/1e6/2.0); |
| 190 | + // set up the comparator duty cycle |
| 191 | + CHECK_CS_ERR(mcpwm_comparator_set_compare_value((mcpwm_cmpr_handle_t)cs->pretrig_comparator, pwm_duty_cycle), |
| 192 | + "Failed to set pretrigger compare value"); |
| 193 | + |
| 194 | + // Register comparator on_reach callback for ADC sampling |
| 195 | + mcpwm_comparator_event_callbacks_t cmp_cbs = { |
| 196 | + .on_reach = _mcpwmComparatorADCCallback |
| 197 | + }; |
| 198 | + // register the callback |
| 199 | + CHECK_CS_ERR(mcpwm_comparator_register_event_callbacks((mcpwm_cmpr_handle_t)cs->pretrig_comparator, &cmp_cbs, cs_params), |
| 200 | + "Failed to register comparator callback"); |
| 201 | + |
| 202 | + SIMPLEFOC_ESP32_CS_DEBUG("MCPWM"+String(group_id)+" Timer "+String(t->timer_id)+" pretrigger comparator configured."); |
| 203 | + // notify the driver code that low side is uses one comparator |
| 204 | + _notifyLowSideUsingComparator(group_id); |
| 205 | + |
| 206 | + }else{ |
| 207 | + |
| 208 | + SIMPLEFOC_ESP32_CS_DEBUG("WARN: Failed to create comparator!"); |
| 209 | + SIMPLEFOC_ESP32_CS_DEBUG("Configuring sync with on_full callback (less accurate)..."); |
| 210 | + |
| 211 | + // check if low side callback is already set |
| 212 | + // if it is, return error |
| 213 | + if(t->on_full != nullptr){ |
| 214 | + SIMPLEFOC_ESP32_CS_DEBUG("ERROR: Low side callback is already set. Cannot set it again for timer: "+String(t->timer_id)+", group: "+String(t->group->group_id)); |
| 215 | + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; |
| 216 | + } |
| 217 | + // set the callback for the low side current sensing |
| 218 | + // mcpwm_timer_event_callbacks_t can be used to set the callback |
| 219 | + // for three timer events |
| 220 | + // - on_full - low-side |
| 221 | + // - on_empty - high-side |
| 222 | + // - on_sync - sync event (not used with simplefoc) |
| 223 | + auto cbs = mcpwm_timer_event_callbacks_t{ |
| 224 | + .on_full = _mcpwmTriggerADCCallback, |
| 225 | + }; |
| 226 | + SIMPLEFOC_ESP32_CS_DEBUG("Timer "+String(t->timer_id)+" enable interrupt callback."); |
| 227 | + // set the timer state to init (so that we can call the `mcpwm_timer_register_event_callbacks` ) |
| 228 | + // this is a hack, as this function is not supposed to be called when the timer is running |
| 229 | + // the timer does not really go to the init state! |
| 230 | + t->fsm = MCPWM_TIMER_FSM_INIT; |
| 231 | + // set the callback |
| 232 | + CHECK_CS_ERR(mcpwm_timer_register_event_callbacks(t, &cbs, cs_params), "Failed to set low side callback"); |
| 233 | + // set the timer state to enabled again |
| 234 | + t->fsm = MCPWM_TIMER_FSM_ENABLE; |
| 235 | + CHECK_CS_ERR(esp_intr_enable(t->intr), "Failed to enable low-side interrupts!"); |
| 236 | + |
| 237 | + SIMPLEFOC_ESP32_CS_DEBUG("MCPWM"+String(group_id)+" Timer "+String(t->timer_id)+" on_full callback configured."); |
| 238 | + } |
| 239 | + |
157 | 240 |
|
158 | 241 | return cs_params; |
159 | 242 | } |
|
0 commit comments