High speed GPIO operations
High speed GPIO operations
I have a question is about the main operation of the clock.The default clock frequency is how much?
while(1) {
gpio_set_level(BLINK_GPIO, 0);
gpio_set_level(BLINK_GPIO, 1);
}
I wrote above, the BLINK_GPIO output frequency only 3Mhz,If the clock is 240 Mhz,I/O output frequency why so low?
while(1) {
gpio_set_level(BLINK_GPIO, 0);
gpio_set_level(BLINK_GPIO, 1);
}
I wrote above, the BLINK_GPIO output frequency only 3Mhz,If the clock is 240 Mhz,I/O output frequency why so low?
Re: Confirmations about ESP32 peripherals
May be because the C function gpio_set_level get translated in multiple assembly instructions by the C compiler ?I wrote above, the BLINK_GPIO output frequency only 3Mhz,If the clock is 240 Mhz,I/O output frequency why so low?
F.
-
- Posts: 9764
- Joined: Thu Nov 26, 2015 4:08 am
Re: Confirmations about ESP32 peripherals
It's mostly because GPIO_SET_LEVEL is somewhat inefficient if you're generating MHz-scale signals, plus because iirc we use the APB bus to control GPIO because of a chip bug (which is going to be fixed in the next rev), causing GPIO to be slower than it can be in general.
Re: Confirmations about ESP32 peripherals
Can you please suggest how do bit banging on gpio at microsecond resolution on esp32?
I would like to send some serial stream of pulses to gpio with 1 to 8 microsecond delay between them.
On ARM CPU I can do some ASM nop and we have ARM_DWT_CYCCNT to count elapsed cycles.
Are there any alternatives to them on ESP32?
You also mention that gpio_set_level are slow on current ESP32 revision. Maybe there any workaround to change gpio level directly?
I would like to send some serial stream of pulses to gpio with 1 to 8 microsecond delay between them.
On ARM CPU I can do some ASM nop and we have ARM_DWT_CYCCNT to count elapsed cycles.
Are there any alternatives to them on ESP32?
You also mention that gpio_set_level are slow on current ESP32 revision. Maybe there any workaround to change gpio level directly?
Re: Confirmations about ESP32 peripherals
Hi Sintech,
There is also ets_delay_us(), which is a ROM function that will give you microsecond-level busy-waiting delays.
Bear in mind that because esp-idf is a preemptive multitasking environment, high-priority tasks and interrupts may prevent you getting cycle-accurate delays of this kind. Placing the WiFi & IP stacks on one core and disabling interrupts on the other core (which runs the task with your bit-banging code) will help, but there's a better solution which I'll explain in a moment.
https://github.com/espressif/esp-idf/bl ... pio.c#L162
The registers themselves (available in "struct" and bare register form) are here:
https://github.com/espressif/esp-idf/bl ... ruct.h#L59
https://github.com/espressif/esp-idf/bl ... _reg.h#L34
There is one thing to keep in mind, the current chip revision's ECO document section 3.3 describes a problem when writing to the same register address repeatedly (ie one CPU cycle after the next), some writes are lost. If you're writing at microsecond-ish intervals, this won't be a problem - but if trying to bit-bang at close to the peripheral bus clock rate (80MHz) then you'll need to either add some "nops" in between each register write, or use the alternative register base address given in the ECO document. The next silicon revision will not have this limitation.
(This is the same "chip bug" Sprite mentioned, although it turns esp-idf hasn't been updated to use the "slower/fixed" addresses yet - it's using the faster/may-lose-writes addresses.)
I suggest taking a look at the RMT ("remote control") peripheral. It's documented in the hardware Technical Reference Manual, and there is also a driver with a documented API. It can generate arbitrary pulse signals of this kind, without needing to bit-bang with the CPU. By using the GPIO Matrix, the RMT peripheral channels can be mapped to any pin (this is true for nearly all ESP32 peripherals.)
The I2S hardware may also be suitable, although it's more complex hardware and the documentation/driver are still forthcoming. If the pulses are very simple, the LEDC (LED controller) pulse generation hardware may be suitable.
Angus
The CCOUNT special register on Xtensa gives you the CPU cycle count (at the current CPU clock speed). You can read it from C code via the "RSR" (read special register) macro:sintech wrote:On ARM CPU I can do some ASM nop and we have ARM_DWT_CYCCNT to count elapsed cycles.
Are there any alternatives to them on ESP32?
Code: Select all
uint32_t ccount;
RSR(CCOUNT, ccount);
Bear in mind that because esp-idf is a preemptive multitasking environment, high-priority tasks and interrupts may prevent you getting cycle-accurate delays of this kind. Placing the WiFi & IP stacks on one core and disabling interrupts on the other core (which runs the task with your bit-banging code) will help, but there's a better solution which I'll explain in a moment.
Once the GPIO is configured, you can write directly to the GPIO W1TS & W1TC ("set" and "clear") registers in the same way gpio_set_level does:sintech wrote: You also mention that gpio_set_level are slow on current ESP32 revision. Maybe there any workaround to change gpio level directly?
https://github.com/espressif/esp-idf/bl ... pio.c#L162
The registers themselves (available in "struct" and bare register form) are here:
https://github.com/espressif/esp-idf/bl ... ruct.h#L59
https://github.com/espressif/esp-idf/bl ... _reg.h#L34
There is one thing to keep in mind, the current chip revision's ECO document section 3.3 describes a problem when writing to the same register address repeatedly (ie one CPU cycle after the next), some writes are lost. If you're writing at microsecond-ish intervals, this won't be a problem - but if trying to bit-bang at close to the peripheral bus clock rate (80MHz) then you'll need to either add some "nops" in between each register write, or use the alternative register base address given in the ECO document. The next silicon revision will not have this limitation.
(This is the same "chip bug" Sprite mentioned, although it turns esp-idf hasn't been updated to use the "slower/fixed" addresses yet - it's using the faster/may-lose-writes addresses.)
Good news! ESP32 has a bunch of dedicated hardware that means you don't need to bit-bang these signals at all.sintech wrote:I would like to send some serial stream of pulses to gpio with 1 to 8 microsecond delay between them.
I suggest taking a look at the RMT ("remote control") peripheral. It's documented in the hardware Technical Reference Manual, and there is also a driver with a documented API. It can generate arbitrary pulse signals of this kind, without needing to bit-bang with the CPU. By using the GPIO Matrix, the RMT peripheral channels can be mapped to any pin (this is true for nearly all ESP32 peripherals.)
The I2S hardware may also be suitable, although it's more complex hardware and the documentation/driver are still forthcoming. If the pulses are very simple, the LEDC (LED controller) pulse generation hardware may be suitable.
Angus
Re: High speed GPIO operations
in some situation i use asm for "counting"
and make a function in c code like this:
( i use it for other esp's too )
and use return value in a wait while,
or perhabs, you have a try CCOMPARE SRs with setting a Value there and use the IRQ
( 4.4.6 Timer interrupt option ) old ISA book
@Angus
txs "special register macro" - have not seen this in the past
best wishes
rudi
and make a function in c code like this:
( i use it for other esp's too )
Code: Select all
static inline unsigned get_ccount(void)
{
unsigned r;
asm volatile ("rsr %0, ccount" : "=r"(r));
return r;
}
or perhabs, you have a try CCOMPARE SRs with setting a Value there and use the IRQ
( 4.4.6 Timer interrupt option ) old ISA book
@Angus
txs "special register macro" - have not seen this in the past
best wishes
rudi
-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪
Re: High speed GPIO operations
Thanks a lot for answers.
ESP_Angus, you mention use of RMT for sending stream of pulses.
I have read manual chapter about RMT and found that I need to allocate memory for pulses description.
According to manual, maximum memory block that I can have for one tx channel is 512x32 bytes, which would be 32 bit per 1 byte of my data. Total amount of data that I need to send are 13000 bytes, so we need to allocate 416k of RAM for this purpose which is not possible.
But as I read from manual, RMT in wraparound mode can generate interrupt after sending configured count of entries. And on this interrupt I should refill the buffer with fresh data before it came to an end of transmission.
I think the speed of CPU and APB bus will be enough to keep the buffer full and avoid interruptions of continuous stream of pulses.
A couple of words about my project: it is a floppy drive simulator for the Apple II clone computer. So I have to generate MFM encoded pulses on GPIO to emulate output from floppy drive. My current sources for Teensy 3.1 dev board https://github.com/sintech/AGAT/tree/ma ... c/agatdisk
ESP_Angus, you mention use of RMT for sending stream of pulses.
I have read manual chapter about RMT and found that I need to allocate memory for pulses description.
According to manual, maximum memory block that I can have for one tx channel is 512x32 bytes, which would be 32 bit per 1 byte of my data. Total amount of data that I need to send are 13000 bytes, so we need to allocate 416k of RAM for this purpose which is not possible.
But as I read from manual, RMT in wraparound mode can generate interrupt after sending configured count of entries. And on this interrupt I should refill the buffer with fresh data before it came to an end of transmission.
I think the speed of CPU and APB bus will be enough to keep the buffer full and avoid interruptions of continuous stream of pulses.
A couple of words about my project: it is a floppy drive simulator for the Apple II clone computer. So I have to generate MFM encoded pulses on GPIO to emulate output from floppy drive. My current sources for Teensy 3.1 dev board https://github.com/sintech/AGAT/tree/ma ... c/agatdisk
Re: High speed GPIO operations
I agree, I think this is the best approach for what you need.sintech wrote: But as I read from manual, RMT in wraparound mode can generate interrupt after sending configured count of entries. And on this interrupt I should refill the buffer with fresh data before it came to an end of transmission.
I think the speed of CPU and APB bus will be enough to keep the buffer full and avoid interruptions of continuous stream of pulses.
Very cool. I look forward to seeing how this goes!sintech wrote: A couple of words about my project: it is a floppy drive simulator for the Apple II clone computer. So I have to generate MFM encoded pulses on GPIO to emulate output from floppy drive. My current sources for Teensy 3.1 dev board https://github.com/sintech/AGAT/tree/ma ... c/agatdisk
-
- Posts: 9764
- Joined: Thu Nov 26, 2015 4:08 am
Re: High speed GPIO operations
I think in general the RMT could benefit from a streaming mode. If I have some time, I can see if I can implement it.
Re: High speed GPIO operations
When experimenting with launching tasks on APP core I have observed that "ets_delay_us" function produces much shorter delays when used in tasks working on APP core, while it is pretty accurate on PRO core. Is it an expected behaviour?
At the same time counting cycles via "CCOUNT" register produce the same results on both cores.
At the same time counting cycles via "CCOUNT" register produce the same results on both cores.
Who is online
Users browsing this forum: Vishna and 147 guests