RMT Peripheral to send a variable number of pulses continuously

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

RMT Peripheral to send a variable number of pulses continuously

Postby shirogeek » Wed Jan 10, 2024 1:16 pm

Hi guys,

I am tinkering around with the rmt peripheral and understand it is an amazing tool to generate complex and changing waveform overtime. I need to generate a certain amount of pulses over a GPIO that I would like to vary in terms of number of pulses, frequency and duty cycle. Here is my current code :

Code: Select all

#include <Arduino.h>
#include <driver/rmt.h>

// RMT configuration parameters
#define RMT_TX_CHANNEL RMT_CHANNEL_0         // RMT channel for transmitter
#define RMT_TX_GPIO_NUM 4                   // GPIO number for transmitter signal
#define RMT_CLK_DIV 4                        // Clock divider for RMT
#define RMT_BASE_CLK 80000000                // Base clock frequency in Hz (typically 80 MHz for ESP32)
#define CARRIER_FREQ_HZ 120000                // Carrier frequency in Hz
#define CARRIER_DUTY_PERCENT 30              // Carrier duty cycle in percent

// Calculated RMT ticks for a period of the carrier frequency
uint32_t rmt_tick_period =(RMT_BASE_CLK / RMT_CLK_DIV / CARRIER_FREQ_HZ);
rmt_config_t rmt_tx_config = {};
volatile bool tx_complete = true;

rmt_isr_handle_t my_rmt_isr_handle;

rmt_item32_t item;
uint8_t counter=0;

uint8_t desired_duty[3]={50,10,1};


void setup() {
  Serial.begin(115200);
  
  rmt_tx_config.channel = RMT_TX_CHANNEL;
  rmt_tx_config.gpio_num = (gpio_num_t)RMT_TX_GPIO_NUM;
  rmt_tx_config.mem_block_num = 4;
  rmt_tx_config.clk_div = RMT_CLK_DIV;

  rmt_tx_config.rmt_mode = RMT_MODE_TX;
  rmt_tx_config.tx_config.loop_en = false;
  
  rmt_tx_config.tx_config.carrier_en = true;
  rmt_tx_config.tx_config.carrier_freq_hz = CARRIER_FREQ_HZ; // Carrier frequency
  rmt_tx_config.tx_config.carrier_duty_percent = CARRIER_DUTY_PERCENT; // Duty cycle
  rmt_tx_config.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  rmt_tx_config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  rmt_tx_config.tx_config.idle_output_en = true;

  // Install RMT driver
  rmt_config(&rmt_tx_config);
  rmt_driver_install(rmt_tx_config.channel, 0, 0);
  rmt_register_tx_end_callback(rmt_tx_end_callback, NULL);


}

void loop() {
  if(tx_complete){
    sendPulses(5,CARRIER_FREQ_HZ,desired_duty[counter]);
    counter++;
    if(counter==3)
      counter=0;
  }

}

static void rmt_tx_end_callback(rmt_channel_t channel, void *arg) {
  tx_complete=true;
}
void sendPulses(uint32_t numPulses,uint32_t freq, uint8_t duty_percent) {
  rmt_tx_config.tx_config.carrier_duty_percent = duty_percent; // Duty cycle
  rmt_tx_config.tx_config.carrier_freq_hz = freq; // Carrier frequency
  rmt_tick_period =(RMT_BASE_CLK / RMT_CLK_DIV / freq);
  rmt_config(&rmt_tx_config);
  tx_complete=false;
  item.level0 = 1; // High: Enable carrier signal
  item.duration0 = (rmt_tick_period) * (numPulses); // Duration of all pulses
  item.level1 = 0; // Low: Disable carrier signal
  item.duration1 = 0; // Duration for signal to be off
  rmt_write_items(RMT_TX_CHANNEL, &item, 1, false);

}
Basically what I do is configure the RMT to use a carrier . The carrier is where I define my frequency and duty cycle. I then write an item whose duration allows me to get the number of pulses I would like. Basically, I see it that the item I write is a mask that will ultimately get me the number of pulses I wish for. It works quite well and allows me to have a dynamic number of pulses. However I have two main issues :

1- Sometimes, I can get one more pulse and the one at the beginning and at the end of my wave of pulses are no longer of the width I look for . This tells me my item is not synchronized with the rising edge of my carrier and so my method of masking does not work.

2- When I run the code above, I expect to continuously send a wave of 5 pulses and expect each wave of 5 pulses to have a different duty cycle. The problem is that I have a huge delay between two successive waves that varies between 20 microseconds to 40 microseconds. How can I make it so that this delay is minimized. Ideally, I would like the waves to be sent successively almost indistinguishable from a non interrupted PWM. Is that possible ?

You can see below both problems (supposed to be 5 pulses at 50% duty cycle 120 kHz followed by 5 pulses at 10% duty cycle 120kHz) :

Image

My biggest problem really is the point number 2 because I really cannot afford 20 microseconds of delay in between two consecutive waves of pulses.

Thanks in advance
Attachments
Screenshot 2024-01-10 141031.png
Screenshot 2024-01-10 141031.png (16.42 KiB) Viewed 35596 times

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

Re: RMT Peripheral to send a variable number of pulses continuously

Postby shirogeek » Thu Jan 11, 2024 10:42 am

I managed to fix the delay between two consecutive waves of pulses being sent by disabling the interrupt for the channel I use and simply launching my function sendPulses at regular interval. The delay between the pulses being sent becomes then exactly equal to the interval without the additional delay that I was observing. I suppose the delay is mostly caused by interrupts in the background...

I still however have the problem of my carrier not being synchronized with my items. It is a matter of luck whether I start at the same time as the rising edge of the carrier. Any suggestion in this direction ?

Who is online

Users browsing this forum: No registered users and 45 guests