Skip to content

Commit 877699b

Browse files
committed
esp32 comparator is possible
1 parent b5557db commit 877699b

File tree

4 files changed

+121
-29
lines changed

4 files changed

+121
-29
lines changed

src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp

Lines changed: 111 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,24 @@
1919

2020

2121
// adding a debug toggle pin to measure the time of the interrupt with oscilloscope
22-
2322
// #define SIMPLEFOC_ESP32_INTERRUPT_DEBUG
2423

2524
#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG
2625
#include "driver/gpio.h"
2726

28-
#ifdef CONFIG_IDF_TARGET_ESP32S3
27+
#ifndef DEBUGPIN
28+
#ifdef CONFIG_IDF_TARGET_ESP32S3
2929
#define DEBUGPIN 16
30-
#define GPIO_NUM GPIO_NUM_16
3130
#else
3231
#define DEBUGPIN 19
33-
#define GPIO_NUM GPIO_NUM_19
32+
#endif
3433
#endif
3534

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
3640
#endif
3741

3842

@@ -120,40 +124,119 @@ static bool IRAM_ATTR _mcpwmTriggerADCCallback(mcpwm_timer_handle_t tim, const m
120124
return true;
121125
}
122126

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+
123158
void* IRAM_ATTR _driverSyncLowSide(void* driver_params, void* cs_params){
124159
#ifdef SIMPLEFOC_ESP32_INTERRUPT_DEBUG
125160
pinMode(DEBUGPIN, OUTPUT);
126161
#endif
127162
ESP32MCPWMDriverParams *p = (ESP32MCPWMDriverParams*)driver_params;
128163
mcpwm_timer_t* t = (mcpwm_timer_t*) p->timers[0];
164+
int group_id = p->group_id;
129165

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");
134170
return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED;
135171
}
136172

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+
157240

158241
return cs_params;
159242
}

src/current_sense/hardware_specific/esp32/esp32_mcu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ typedef struct ESP32CurrentSenseParams {
1717
int adc_buffer[3] = {};
1818
int buffer_index = 0;
1919
int no_adc_channels = 0;
20+
void* pretrig_comparator = nullptr; // MCPWM comparator handle for ADC pre-trigger
2021
} ESP32CurrentSenseParams;
2122

2223
// macros for debugging wuing the simplefoc debug system

src/drivers/hardware_specific/esp32/esp32_driver_mcpwm.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ uint8_t group_pins_used[2] = {0};
121121
// last operator in the group
122122
mcpwm_oper_handle_t last_operator[2];
123123

124-
124+
void _notifyLowSideUsingComparator(int group_id){
125+
group_pins_used[group_id] +=1;
126+
}
125127

126128
// checking if group has pins available
127129
bool _hasAvailablePins(int group, int no_pins){

src/drivers/hardware_specific/esp32/esp32_driver_mcpwm.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,5 +158,11 @@ void _setDutyCycle(mcpwm_cmpr_handle_t cmpr, uint32_t mcpwm_period, float duty_c
158158
*/
159159
void _forcePhaseState(mcpwm_gen_handle_t generator_high, mcpwm_gen_handle_t generator_low, PhaseState phase_state);
160160

161+
/**
162+
* function notifying the driver that low side is used with comparator
163+
* @param group_id - mcpwm group id
164+
*/
165+
void _notifyLowSideUsingComparator(int group_id);
166+
161167
#endif
162168
#endif

0 commit comments

Comments
 (0)