Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

RMandR
Posts: 75
Joined: Mon Oct 29, 2018 3:13 pm

Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

Postby RMandR » Tue Mar 05, 2019 9:20 pm

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

User avatar
fly135
Posts: 606
Joined: Wed Jan 03, 2018 8:33 pm
Location: Orlando, FL

Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

Postby fly135 » Tue Mar 05, 2019 9:39 pm

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

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

Postby ESP_Angus » Wed Mar 06, 2019 12:41 am

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.

ESP_igrr
Posts: 2067
Joined: Tue Dec 01, 2015 8:37 am

Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

Postby ESP_igrr » Wed Mar 06, 2019 2:05 am

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.

RMandR
Posts: 75
Joined: Mon Oct 29, 2018 3:13 pm

Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

Postby RMandR » Wed Mar 06, 2019 5:01 pm

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:

NewFile3.png
NewFile3.png (47.82 KiB) Viewed 26710 times

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

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

Postby ESP_Angus » Thu Mar 07, 2019 2:33 am

Ah, using the carrier wave mode and a single pulse is a much better idea!
RMandR wrote:
Wed Mar 06, 2019 5:01 pm
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?
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?

RMandR
Posts: 75
Joined: Mon Oct 29, 2018 3:13 pm

Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

Postby RMandR » Thu Mar 07, 2019 4:36 pm

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:

NewFile4.png
NewFile4.png (47.19 KiB) Viewed 26653 times

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;
and sending out two items [will change to 1 later]:

Code: Select all

{{{ 128, 0, 1024, 1 }}}, // used to be dot
//
{{{ 32767, 0, 32767, 0 }}}, // SPACE
and ended up with what looks like 7 wave cycles. If I've done the math right:

ABP clock 80Mhz/255 divider = 313725 ticks?
313725 / 2000 freq = 156 counts is a full wave?

-armand

shirogeek
Posts: 15
Joined: Sun Nov 08, 2020 8:07 pm

Re: Using RMT to generate 'n' rising clock edges [faster than gpio twiddling]

Postby shirogeek » Wed Jan 10, 2024 6:10 pm

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

Who is online

Users browsing this forum: No registered users and 137 guests