How does one go about setting up the RMT to generate arbitrary number of clock edges?
In my application I have to twiddle a gpio [rising clock edge] arbitrary number of times (e.g. 53, or 800).
Setting up a memory buffer similar to the neo_pixel library doesn't seem right since creating and filling the buffer containing 'n' edges takes longer than just twiddling the gpio.
tia
-a
Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]
Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]
I'm pretty sure programming the RMT takes less time than manually twiddling the bits. You program the RMT with pulse duration and level, then go back to whatever other business your code has.
https://docs.espressif.com/projects/esp ... s/rmt.html
https://docs.espressif.com/projects/esp ... s/rmt.html
Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]
Hi RMandR,
So it's a different number of clock edges each time? That does mean that you'll need to do something more complex than just writing N RMT memory block entries, where N is the number of edges.
I think you can probably make this work by programming the RMT peripheral directly, with reference to the TRM. Pay particular attention to:
- Wraparound aka continuous transmission mode (RMT_REG_TX_CONTI_MODE), which means that the RMT will keep re-transmitting the same memory block(s) over and over, wrapping around.
- RMT_CHn_TX_LIM_REG, which will produce an interrupt after N entries have been transmitted.
- That transmission will end as soon as it encounters a zero-length block.
So you can do something like:
- Initialize at least one (maybe more) memory blocks with a square wave pattern once (at boot time).
- Configure RMT channel for the correct clock divider, output pins, number of memory blocks, etc.
Each time you need to send N clock cycles:
- Set RMT_CHn_TX_LIM_REG to N minus some overhead for interrupt latency. (This may mean you need to loop over more than one memory block to have enough interrupt latency, depending on your clock speed.)
- Start RMT transmission
- In the interrupt handler for the RMT_CHn_TX_THR_EVENT_INT interrupt, set the memory block entry for (N % number_of_entries_in_each_loop) to 0. This will cause RMT peripheral to stop immediately when it reaches that point.
- In the interrupt handler for RMT_CHn_TX_END_EVENT_IN interrupt, set this 0 memory block entry back to the original square wave pattern so the memory block is ready for next time.
This way, each square wave transmission should only take a few CPU cycles.
So it's a different number of clock edges each time? That does mean that you'll need to do something more complex than just writing N RMT memory block entries, where N is the number of edges.
I think you can probably make this work by programming the RMT peripheral directly, with reference to the TRM. Pay particular attention to:
- Wraparound aka continuous transmission mode (RMT_REG_TX_CONTI_MODE), which means that the RMT will keep re-transmitting the same memory block(s) over and over, wrapping around.
- RMT_CHn_TX_LIM_REG, which will produce an interrupt after N entries have been transmitted.
- That transmission will end as soon as it encounters a zero-length block.
So you can do something like:
- Initialize at least one (maybe more) memory blocks with a square wave pattern once (at boot time).
- Configure RMT channel for the correct clock divider, output pins, number of memory blocks, etc.
Each time you need to send N clock cycles:
- Set RMT_CHn_TX_LIM_REG to N minus some overhead for interrupt latency. (This may mean you need to loop over more than one memory block to have enough interrupt latency, depending on your clock speed.)
- Start RMT transmission
- In the interrupt handler for the RMT_CHn_TX_THR_EVENT_INT interrupt, set the memory block entry for (N % number_of_entries_in_each_loop) to 0. This will cause RMT peripheral to stop immediately when it reaches that point.
- In the interrupt handler for RMT_CHn_TX_END_EVENT_IN interrupt, set this 0 memory block entry back to the original square wave pattern so the memory block is ready for next time.
This way, each square wave transmission should only take a few CPU cycles.
Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]
It is also possible to enable RMT carrier generation, and then fill RMT memory with exactly one pulse, long enough to contain the correct number of carrier frequency pulses.
Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]
Thank you for the responses!
I think setting up a buffer + interrupt could work, I'm not sure if the interrupt latency is deterministic especially if there are other events or interrupts occurring the transmission.
The single item/pulse seems like the right idea, I'm not sure if the carrier wave generator is in sync/phase with the pulse. A scope capture shows the first and the last waves may or may not produce a valid clock edge:
Is there a way to make sure the wave and the pulse are in-phase? The docs refer to the item duration as "ticks" how does one calculate that with respect to the carrier freq?
thx,
-armand
I think setting up a buffer + interrupt could work, I'm not sure if the interrupt latency is deterministic especially if there are other events or interrupts occurring the transmission.
The single item/pulse seems like the right idea, I'm not sure if the carrier wave generator is in sync/phase with the pulse. A scope capture shows the first and the last waves may or may not produce a valid clock edge:
Is there a way to make sure the wave and the pulse are in-phase? The docs refer to the item duration as "ticks" how does one calculate that with respect to the carrier freq?
thx,
-armand
Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]
Ah, using the carrier wave mode and a single pulse is a much better idea!
The values written to RMT_CARRIER_HIGH_CHn and RMT_CARRIER_LOW_CHn both use this unit, as well as the memory block entries. So if you write a single entry with one "on" pulse, you will want the "on" entry length to be N * (RMT_CARRIER_HIGH_CHn + RMT_CARRIER_LOW_CHn), where N is the number of clock cycles. Followed immediately by a zero-length "off" pulse to terminate the RMT state machine.
If you've programmed the RMT like this and you're still getting the slightly short first & last carrier wave edges shown in the scope trace, can you please post the full code you're using?
The "clock ticks" used for both carrier wave periods and RMT transmission entry lengths are determined from the RMT clock frequency, which can be sourced from the APB bus clock or the REF_TICK clock. See TRM section 15.2.3 "Clock" in the RMT chapter.
The values written to RMT_CARRIER_HIGH_CHn and RMT_CARRIER_LOW_CHn both use this unit, as well as the memory block entries. So if you write a single entry with one "on" pulse, you will want the "on" entry length to be N * (RMT_CARRIER_HIGH_CHn + RMT_CARRIER_LOW_CHn), where N is the number of clock cycles. Followed immediately by a zero-length "off" pulse to terminate the RMT state machine.
If you've programmed the RMT like this and you're still getting the slightly short first & last carrier wave edges shown in the scope trace, can you please post the full code you're using?
Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]
ESP_Angus wrote: ↑Thu Mar 07, 2019 2:33 am
The "clock ticks" used for both carrier wave periods and RMT transmission entry lengths are determined from the RMT clock frequency, which can be sourced from the APB bus clock or the REF_TICK clock. See TRM section 15.2.3 "Clock" in the RMT chapter.
The values written to RMT_CARRIER_HIGH_CHn and RMT_CARRIER_LOW_CHn both use this unit, as well as the memory block entries. So if you write a single entry with one "on" pulse, you will want the "on" entry length to be N * (RMT_CARRIER_HIGH_CHn + RMT_CARRIER_LOW_CHn), where N is the number of clock cycles. Followed immediately by a zero-length "off" pulse to terminate the RMT state machine.
If you've programmed the RMT like this and you're still getting the slightly short first & last carrier wave edges shown in the scope trace, can you please post the full code you're using?
Thanks Angus!
I think I see RMT_CARRIER_HIGH_CHn being referenced as RMT.carrier_duty_ch[channel].high.
But I'm not sure if changing that will solve the phase issue. I tried changing the 'items' and placing a 1/4 wave worth of 0 before the pulse of '1', and changing the pulse length and ended up with a fairly regular set of clock edges:
Code: Select all
config.tx_config.carrier_duty_percent = 50;
config.tx_config.carrier_freq_hz = 2000;
config.tx_config.carrier_level = 1;
config.clk_div = 255;
Code: Select all
{{{ 128, 0, 1024, 1 }}}, // used to be dot
//
{{{ 32767, 0, 32767, 0 }}}, // SPACE
ABP clock 80Mhz/255 divider = 313725 ticks?
313725 / 2000 freq = 156 counts is a full wave?
-armand
Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]
Hey guys,
Reviving this topic to see if you could share your full solution with me ... I actually have a piece of working code myself for the same purpose but my problem is that successive calls to send a wave of pulses yields at least a 15 us delay between the two waves and I cannot afford that in my apppplication (see my post for the code and picture of the issue : https://esp32.com/viewtopic.php?f=19&t=37725)
I was wondering if there wasn't a better way to do this...
Thanks
Reviving this topic to see if you could share your full solution with me ... I actually have a piece of working code myself for the same purpose but my problem is that successive calls to send a wave of pulses yields at least a 15 us delay between the two waves and I cannot afford that in my apppplication (see my post for the code and picture of the issue : https://esp32.com/viewtopic.php?f=19&t=37725)
I was wondering if there wasn't a better way to do this...
Thanks
Who is online
Users browsing this forum: ESP_Sprite and 327 guests