Hey everyone! I've had a good search but can't find an answer
Using an ESP32 Wroom I need to generate three concurrent pulses with an "on" time of 5us with a frequency of around 1KHz. Programatically I don't see this is a problem (digWrite HIGH, delayMicrosec 5, digWrite Low, rinse and repeat 2 more times then wait about 1ms). Just another point, I would like the pulses to overlap by 1us).
So, I suppose the real question is, is this possible with the ESP32? Is the microsecond resolution good enough? Should I be looking at something other than digitalWrite and delayMicroseconds?
I was looking at the LEDC library - looks like it has a fixed 50% duty cycle whereas I need about 0.5% (5us on every 1ms). In saying that, perhaps the LEDC code would be a good place to start for ideas!
I do need the signal quite accurate. I could probably cope with a 5 to 10% error but not more.
This part of the project is to control a CD4066 bilateral switch to extract a small portion of a signal for analyses. In past iterations of the project we used various astable oscillators with a lot of phase shifting to control the switch but, as we were using the ESP32 for other parts of the circuit, I thought it would be a great idea to use it to control the three gates. Hope that makes sense
// Arduino Uno at 16MHz
// Best accuracy with an Arduino Uno that has a crystal instead of resonator.
// A NOP takes 62.5 ns.
// A change of a pin with writing to PINB also takes 62.5 ns.
// There are 16 NOP in a microsecond.
//
// Does the "while(true)" take time ?
void setup()
{
pinMode( 8, OUTPUT); // pin 8 = PORTB bit 0
pinMode( 9, OUTPUT); // pin 9 = PORTB bit 1
pinMode( 10, OUTPUT); // pin 10 = PORTB bit 2
noInterrupts(); // Stop Arduino services
while( true) // forever
{
PINB = bit( 0); // HIGH
__builtin_avr_delay_cycles (63); // 4 us = 64 NOP (-1 for PINB instruction)
PINB = bit( 1); // HIGH
__builtin_avr_delay_cycles (15); // 1 us = 16 NOP
PINB = bit( 0); // LOW
__builtin_avr_delay_cycles (47); // 3 us = 48 NOP
PINB = bit( 2); // HIGH
__builtin_avr_delay_cycles (15); // 1 us = 16 NOP
PINB = bit( 1); // LOW
__builtin_avr_delay_cycles (63); // 4 us = 64 NOP
PINB = bit( 2); // LOW
__builtin_avr_delay_cycles (15791); // 16000 NOP minus all the other code
}
}
void loop() { }
The accuracy is 100%, it depends on the crystal or resonator of the Arduino Uno.
I have to admit that I only tested it in a simulation.
You can open the link to the Wokwi simulation, but don't start it unless you know how to use the Wokwi Logic Analyzer.
I don't think so. I can turn off all the Arduino services on a Arduino Uno, but you can not turn off the services of a ESP32.
I don't think that the LEDC is flexible enough for the overlap.
The RP2040 has programmable hardware blocks, those should be able to do that.
If you use the RMT peripheral with the APB clock (80Mhz) as source, then you can use 80 as channel divider, giving 1Mhz (1hz per micros). For each 5us pulse you would need the rmt_item32_t.duration[0|1] to be 5 and that makes it quite easy to do with an ESP32. To do the offsetting, the first rmt_item would just need to be low for 4us for the second and 8us for the third pin.
Look at the morse code example for a better understanding of how RMT works.
EDIT 1: If getting all 3 signals to synchronize properly is an issue, then using the ESP32's MCPWM hardware might be an option.
EDIT 2: ... or maybe increase the bit resolution up to 16, then if each signal is one bit-time out of sync, the % timing error wont' be as high as for 10-bit. You'll need to re-calculate the width and phase-shift values.
It doesn't. Too bad we can't get 10-bit resolution at 100kHz, then the interrupts of a 1kHz fourth timer could control the pulse pattern every millisecond (the ESP32's max PWM frequency at 10-bit is 78.125KHz).
@alphabetix, how about using digitalWrite and delayMicroseconds to create a function that produces the required pattern ... it only lasts 13µs. Then use a timer's interrupt to call this function every millisecond.
Yes, I meant to refer to your example in reply#8 ... like that, but have it in a small function called by a timer. I haven't worked with ESP32's timer interrupts yet, so can't elaborate much further.
I ended up using the MCPWM library with a sync delay between two timers
mcpwm_sync_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SELECT_SYNC_INT0, 96);
MCPWM0.timer[0].sync.out_sel = 1;
delayMicroseconds(1000);
MCPWM0.timer[0].sync.out_sel = 0;
Where "96" is the delay between the first two pulses
Thank you everyone for your help. It definitely put me on the correct path.