ESP32 Thing SparkFun RMT Pulse Generator 4 Channels Jitter 100ns

space2021
Posts: 4
Joined: Sun Mar 21, 2021 1:48 pm

ESP32 Thing SparkFun RMT Pulse Generator 4 Channels Jitter 100ns

Postby space2021 » Sun Mar 21, 2021 2:21 pm

Hello

I'm programming with the Arduino IDE and hardware "ESP32 Thing with set to 80Mhz" from SparkFun.
My target application is a Pulse Generator for 4 Channels with low (no) Jitter.

See the following example code:
The code can be uploaded with the Arduino IDE (you have to download also the "driver/rmt.h" file and store it in a folder "driver" in the same project.

The example code configures 4 RMA channels each channel has a different GPIO PIN (16,17,18,19) used to output the synced pulses.
You can check the PINS with a 2 CH oscilloscope.

For repetitive testing I added a PWM which creates a 10 Hz repetition which triggers the RMA cycle.


The code works great!
But I get a Jitter from first RMT channel to the last channel of about 100ns. I would like to eliminate this too.
It would be intrestion to see this code working on a esp32-s3 with 240 Mhz compared tith the 80Mhz does in this case the jitter (delay) result in about 25ns?

I checked the architecture of the RMT but did only find enable signals per channel.

I start the 4 channels one after other with the following code lines. I believe I get some code execution delay from first line to the second or last line which creates this jitter of 100ns. (Maybe its the time to set the bit in the register)

Code: Select all

REG_SET_BIT(RMT_CH0CONF1_REG, RMT_TX_START_CH0);
REG_SET_BIT(RMT_CH1CONF1_REG, RMT_TX_START_CH1);
REG_SET_BIT(RMT_CH2CONF1_REG, RMT_TX_START_CH2);
REG_SET_BIT(RMT_CH3CONF1_REG, RMT_TX_START_CH3);
At first I did use the command rmt_tx_start(RMT_CHANNEL_0, false); but with tis the jitter was even bigger and this makes also in my opinion sense.

Does somebody have a solution to hold the transmission of each RMT channel until a global bit /register or clock has been activated?

Is there a easy solution with a mentioned carrier CLK?

Thank for you Help!

Code: Select all

 
#include <driver/rmt.h>
 
// the number of the LED pin
const int ledPin = 16;  // 16 corresponds to GPIO16

// use first channel of 16 channels (started from zero)
#define LEDC_CHANNEL_0     0

// use 13 bit precission for LEDC timer
#define LEDC_TIMER_13_BIT  13

// use 5000 Hz as a LEDC base frequency
#define LEDC_BASE_FREQ     10

// fade LED PIN (replace with LED_BUILTIN constant for built-in LED)
#define LED_PIN            LED_BUILTIN

int brightness = 0;    // how bright the LED is
int fadeAmount = 5;    // how many points to fade the LED by


// Reference https://esp-idf.readthedocs.io/en/v1.0/api/rmt.html

// Arduino like analogWrite
// value has to be between 0 and valueMax
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
  // calculate duty, 8191 from 2 ^ 13 - 1
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);

  // write duty to LEDC
  ledcWrite(channel, duty);
}

struct Button {
  const uint8_t PIN;
  uint32_t numberKeyPresses;
  bool pressed;
};

Button button1 = {0, 0, false};

void IRAM_ATTR isr() {
  button1.numberKeyPresses += 1;
  button1.pressed = true;
}

rmt_config_t configA;
rmt_item32_t itemsA[3];

rmt_config_t configB;
rmt_item32_t itemsB[3];
 
rmt_config_t configC;
rmt_item32_t itemsC[3];

 rmt_config_t configD;
rmt_item32_t itemsD[3];

void setup() {
  Serial.begin(115200);
  pinMode(button1.PIN, INPUT_PULLUP);
  attachInterrupt(button1.PIN, isr, FALLING);


   // Setup timer and attach timer to a led pin
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
  ledcAttachPin(LED_PIN, LEDC_CHANNEL_0);

  
  // put your setup code here, to run once:
  configA.rmt_mode = RMT_MODE_TX;
  configA.channel = RMT_CHANNEL_0;
  configA.gpio_num = GPIO_NUM_16;
  configA.mem_block_num = 1;
  configA.tx_config.loop_en = 0;
  configA.tx_config.carrier_en = 0;
  configA.tx_config.idle_output_en = 1;
  configA.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  configA.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  configA.clk_div = 80; // 80MHx / 80 = 1MHz 0r 1uS per count
  rmt_config(&configA);
  rmt_driver_install(RMT_CHANNEL_0, 0, 0);  //  rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)
  
  // put your setup code here, to run once:
  configB.rmt_mode = RMT_MODE_TX;
  configB.channel = RMT_CHANNEL_1;
  configB.gpio_num = GPIO_NUM_17;
  configB.mem_block_num = 1;
  configB.tx_config.loop_en = 0;
  configB.tx_config.carrier_en = 0;
  configB.tx_config.idle_output_en = 1;
  configB.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  configB.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  configB.clk_div = 80; // 80MHx / 80 = 1MHz 0r 1uS per count

  rmt_config(&configB);
  rmt_driver_install(RMT_CHANNEL_1, 0, 0);  //  rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)


  // put your setup code here, to run once:
  configC.rmt_mode = RMT_MODE_TX;
  configC.channel = RMT_CHANNEL_2;
  configC.gpio_num = GPIO_NUM_18;
  configC.mem_block_num = 1;
  configC.tx_config.loop_en = 0;
  configC.tx_config.carrier_en = 0;
  configC.tx_config.idle_output_en = 1;
  configC.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  configC.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  configC.clk_div = 80; // 80MHx / 80 = 1MHz 0r 1uS per count

  rmt_config(&configC);
  rmt_driver_install(RMT_CHANNEL_2, 0, 0);  //  rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)

  // put your setup code here, to run once:
  configD.rmt_mode = RMT_MODE_TX;
  configD.channel = RMT_CHANNEL_3;
  configD.gpio_num = GPIO_NUM_19;
  configD.mem_block_num = 1;
  configD.tx_config.loop_en = 0;
  configD.tx_config.carrier_en = 0;
  configD.tx_config.idle_output_en = 1;
  configD.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  configD.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  configD.clk_div = 80; // 80MHx / 80 = 1MHz 0r 1uS per count

  rmt_config(&configD);
  rmt_driver_install(RMT_CHANNEL_3, 0, 0);  //  rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)

  
  itemsA[0].duration0 = 100;
  itemsA[0].level0 = 1;
  itemsA[0].duration1 = 200;
  itemsA[0].level1 = 0;  
  itemsA[1].duration0 = 100;
  itemsA[1].level0 = 0;
  itemsA[1].duration1 = 50;
  itemsA[1].level1 = 0;
  itemsA[2].duration0 = 10;
  itemsA[2].level0 = 0;
  itemsA[2].duration1 = 0;
  itemsA[2].level1 = 0;


  itemsB[0].duration0 = 100;
  itemsB[0].level0 = 0;
  itemsB[0].duration1 = 200;
  itemsB[0].level1 = 0;  
  itemsB[1].duration0 = 100;
  itemsB[1].level0 = 0;
  itemsB[1].duration1 = 50;
  itemsB[1].level1 = 0;
  itemsB[2].duration0 = 10;
  itemsB[2].level0 = 0;
  itemsB[2].duration1 = 0;
  itemsB[2].level1 = 0;

  
  itemsC[0].duration0 = 100;
  itemsC[0].level0 = 0;
  itemsC[0].duration1 = 200;
  itemsC[0].level1 = 0;  
  itemsC[1].duration0 = 100;
  itemsC[1].level0 = 0;
  itemsC[1].duration1 = 50;
  itemsC[1].level1 = 0;
  itemsC[2].duration0 = 10;
  itemsC[2].level0 = 0;
  itemsC[2].duration1 = 0;
  itemsC[2].level1 = 0;

  
  itemsD[0].duration0 = 100;
  itemsD[0].level0 = 0;
  itemsD[0].duration1 = 200;
  itemsD[0].level1 = 0;  
  itemsD[1].duration0 = 100;
  itemsD[1].level0 = 1;
  itemsD[1].duration1 = 10;
  itemsD[1].level1 = 0;
  itemsD[2].duration0 = 0;
  itemsD[2].level0 = 0;
  itemsD[2].duration1 = 0;
  itemsD[2].level1 = 0;
    
  rmt_fill_tx_items(RMT_CHANNEL_0,itemsA,3,0);
  rmt_fill_tx_items(RMT_CHANNEL_1,itemsB,3,0);
  rmt_fill_tx_items(RMT_CHANNEL_2,itemsC,3,0);
  rmt_fill_tx_items(RMT_CHANNEL_3,itemsD,3,0);

  

}
 
void loop() {
  if (button1.pressed) {
      Serial.printf("Button 1 has been pressed %u times\n", button1.numberKeyPresses);
      button1.pressed = false;
      // esp_err_t rmt_write_items(rmt_channel_t channel, rmt_item32_t *rmt_item, int item_num, bool wait_tx_done)
      //rmt_tx_start(RMT_CHANNEL_0, false);
      //rmt_tx_start(RMT_CHANNEL_1, false);
      REG_SET_BIT(RMT_CH0CONF1_REG, RMT_TX_START_CH0);
      REG_SET_BIT(RMT_CH1CONF1_REG, RMT_TX_START_CH1);
      REG_SET_BIT(RMT_CH2CONF1_REG, RMT_TX_START_CH2);
      REG_SET_BIT(RMT_CH3CONF1_REG, RMT_TX_START_CH3);

  }

 // set the brightness on LEDC channel 0
  ledcAnalogWrite(LEDC_CHANNEL_0, 1);

     
 // rmt_write_items(RMT_CHANNEL_0, items, 2, 0);
 // rmt_write_items(RMT_CHANNEL_1, itemsA, 2, 0);
 
  
  delay(10);
}

RickRac
Posts: 1
Joined: Thu Apr 29, 2021 8:10 pm

Re: ESP32 Thing SparkFun RMT Pulse Generator 4 Channels Jitter 100ns

Postby RickRac » Thu Apr 29, 2021 8:21 pm

Where do I find the "driver/rmt.h" file? This is new to me.
Thanks

space2021
Posts: 4
Joined: Sun Mar 21, 2021 1:48 pm

Re: ESP32 Thing SparkFun RMT Pulse Generator 4 Channels Jitter 100ns

Postby space2021 » Sat May 01, 2021 4:52 pm

I attached the rmt.h file I used and is working. You have to place it inside the Arduino folder with the *.ino file under a folder named driver so the arduino can fin it with the inline driver/rmt.h.
Attachments
rmt.h
(27.37 KiB) Downloaded 418 times

Langelot
Posts: 12
Joined: Tue Jun 01, 2021 9:57 am

Re: ESP32 Thing SparkFun RMT Pulse Generator 4 Channels Jitter 100ns

Postby Langelot » Thu Jun 24, 2021 4:03 pm

Hello space2021, I have the same need as yours, I would like to create 2 RMT signals that start at the exact same time (or with a small and repetitive jitter). Did you found a way to solve this? Thank you.

space2021
Posts: 4
Joined: Sun Mar 21, 2021 1:48 pm

Re: ESP32 Thing SparkFun RMT Pulse Generator 4 Channels Jitter 100ns

Postby space2021 » Fri Aug 13, 2021 8:08 pm

In the meantime I created a small user interface with a rotating push button encoder and OLED display "Feather OLED 128x32" to config 4 channels with 4 pulses. But this needs still some debugging, the code is not so tidy.

The delay is still there but I did improve the jitter by using the correct core.

Code: Select all

// Reaction time with ESP32 THING PLUS is about 2.5us for a PIN Interrupt
// With RMT we can have 8 channels with as example pulse resolution of 100ns longest pulse is 
// The RMT channels have a delay / jitter of about 100ns
// Reference https://esp-idf.readthedocs.io/en/v1.0/api/rmt.html
//
//  ESP32 Thing Plus WROOM 
//
//  PINOUT LEFT Side
//  23  SDA for OLED as I2C
//  22  SCL for OLED as I2C
//  14  OLED Button C
//  32  OLED Button B
//  15  OLED Button A
//  33  RMT_CHD
//  27  RMT_CHC
//  12  RMT_CHB
//  13  RMT_CHA
//  VUSB
//  EN
//  VBAT
//
//  JST LiPO
//
//  Qwiic Con.
// ***********************************
//  PINOUT RIGHT Side
//  21  Interrupt Toggel pin 4
//  17  ENCODER A
//  16  ENCODER B
//  19  PWM Generator Output (connect to 21 Interrupt Toggle pin)
//  18  
//  5   
//  A5
//  A4
//  A3
//  A2
//  A1
//  A0
//  GND
//  NC
//  3V3
//  RESET




#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <driver/rmt.h>
#include <ESP32Encoder.h>


ESP32Encoder encoder;
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire);

TaskHandle_t Task1;
TaskHandle_t Task2;


// Display variable
int32_t jog_val = 0;
int32_t Set_Val = 0;
int32_t Set_Val_old = 0;

float Act_val;
float Act_val_old;

// Channel parameter is at the beginning so position cursor at 16
int Set_X_pos = 16;
int Set_X_pos_old = 16;

int Param_line_y = 3;
int Param_line_y_old = 3;
int Channel = 0;
int Channel_old = 0;


const int Menu_lenght = 14;

char line[Menu_lenght][22] ={"PULSE GEN V1 by R.L. ",
                    "CHAN : 0             ",
                    "                     ",
                    "CHAN :        0.0    ",
                    "Delay:        0.0 us ",
                    "PulH1:        0.0 us ",
                    "PulL1:        0.0 us ",
                    "PulH2:        0.0 us ",
                    "PulL2:        0.0 us ",
                    "PulH3:        0.0 us ",
                    "PulL3:        0.0 us ",
                    "PulH4:        0.0 us ",
                    "PulL4:        0.0 us ",
                    "PGen :       10.0 Hz "};

struct Pulse_Pattern {
  int del;
  int pulse_del_L;
  int pulseH1;
  int pulseH1_L;
  int pulseL1;
  int pulseL1_L;
  int pulseH2;
  int pulseH2_L;
  int pulseL2;
  int pulseL2_L;
  int pulseH3;
  int pulseH3_L;
  int pulseL3;
  int pulseL3_L;
  int pulseH4;
  int pulseH4_L;
  int pulseL4;
  int pulseL4_L;
};
// Timing in x of 100ns
 
Pulse_Pattern PULS[4] = {{50,1,23,0,32,1,40,0,50,1,00,0,00,1,00,0,00},
                         {60,1,35,0,32,1,40,0,50,1,00,0,00,1,00,0,00},
                         {70,1,30,0,32,1,40,0,50,1,00,0,00,1,00,0,00},
                         {80,1,40,0,32,1,40,0,50,1,00,0,00,1,00,0,00}};

// 
static portMUX_TYPE my_mutex;

//gpio_num_t pin21 = (gpio_num_t)(21 & 0x1F);
//gpio_num_t pin4 = (gpio_num_t)(4 & 0x1F);

int state=0;
uint32_t numberPinHigh;


// use first channel of 16 channels (started from zero)
#define LEDC_CHANNEL_0     0

// use 13 bit precission for LEDC timer
#define LEDC_TIMER_13_BIT  13

// use 5000 Hz as a LEDC base frequency

int LEDC_BASE_FREQ = 10;
int LEDC_BASE_FREQ_old = 10;

int brightness = 1;    // how bright the LED is

struct Button {
  const uint8_t PIN;
  uint32_t numberKeyPresses;
  bool pressed;
};

Button Trig1 = {18, 0, false};  // GPIO 12 is Input Trigger pin.


//int OUT_PIN = 4;

// fade LED PIN (replace with LED_BUILTIN constant for built-in LED)
#define Master_Gen_PIN            19

#define BUTTON_A 15
#define BUTTON_B 32
#define BUTTON_C 14

#define RMT_CHA  GPIO_NUM_13 
#define RMT_CHB  GPIO_NUM_12 
#define RMT_CHC  GPIO_NUM_27 
#define RMT_CHD  GPIO_NUM_33



rmt_config_t configA;
rmt_item32_t itemsA[5];

rmt_config_t configB;
rmt_item32_t itemsB[5];
 
rmt_config_t configC;
rmt_item32_t itemsC[5];

rmt_config_t configD;
rmt_item32_t itemsD[5];

// Arduino like analogWrite
// value has to be between 0 and valueMax
void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
  // calculate duty, 8191 from 2 ^ 13 - 1
  uint32_t duty = (8191 / valueMax) * min(value, valueMax);

  // write duty to LEDC
  ledcWrite(channel, duty);
}


// Interrupt routine when RMT to start
void IRAM_ATTR isr() {
  portENTER_CRITICAL(&my_mutex);
  //timing sensitive stuff
  REG_SET_BIT(RMT_CH0CONF1_REG, RMT_TX_START_CH0);
  REG_SET_BIT(RMT_CH1CONF1_REG, RMT_TX_START_CH1);
  REG_SET_BIT(RMT_CH2CONF1_REG, RMT_TX_START_CH2);
  REG_SET_BIT(RMT_CH3CONF1_REG, RMT_TX_START_CH3);
  portEXIT_CRITICAL(&my_mutex);
  
  Trig1.numberKeyPresses += 1;
  Trig1.pressed = true;
}

// Interrupt routine for fast GPIO changes
void IRAM_ATTR isr1() {
  GPIO.out_w1ts = 0b10000;    // Toggle PIN 4
  GPIO.out_w1tc = 0b10000;  
}







void setup() {
  Serial.begin(115200);
  
  // Enable the weak pull up resistors
  ESP32Encoder::useInternalWeakPullResistors=UP;

  // use pin 17 and 16 for the second encoder
  encoder.attachSingleEdge(17, 16);
  encoder.setFilter(1023);
  Serial.println("Encoder Start = " + String((int32_t)encoder.getCount()));
 
  // set starting count value after attaching
  encoder.setCount(Set_X_pos);

 // clear the encoder's raw count and set the tracked count to zero
 // encoder.clearCount();
  
  Serial.println("OLED FeatherWing test");
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Address 0x3C for 128x32
  Serial.println("OLED begun");

  UI_clear();

  Serial.println("IO test");

  pinMode(BUTTON_A, INPUT_PULLUP);  //15
  pinMode(BUTTON_B, INPUT);  //32
  pinMode(BUTTON_C, INPUT_PULLUP);  //14
  
  pinMode(Trig1.PIN, INPUT_PULLUP);   // When no signal is applied, it will be at a voltage level of VCC instead of floating, avoiding the detection of non existing external interrupts.
 
  pinMode(RMT_CHA, INPUT_PULLUP); // RMT Channel0 A
  pinMode(RMT_CHB, INPUT_PULLUP); // RMT Channel1 B
  pinMode(RMT_CHC, INPUT_PULLUP); // RMT Channel2 C
  pinMode(RMT_CHD, INPUT_PULLUP); // RMT Channel3 D
  pinMode(GPIO_NUM_21, INPUT);  // Connected to 18
  pinMode(GPIO_NUM_2, OUTPUT);
  //pinMode(GPIO_NUM_4, OUTPUT);


  //disableCore0WDT();
  disableCore1WDT();
  
  vPortCPUInitializeMutex(&my_mutex);
   
  attachInterrupt(Trig1.PIN, isr, RISING);    // FALLING  RISING   LOW HIGH  CHANGE 
  //attachInterrupt(21, isr1, RISING); 
   
  //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 1
 
  xTaskCreatePinnedToCore(
                    Task_core0,   /* Task function. */
                    "Task_core0",     /* name of task. */
                    10000,       /* Stack size of task */
                    NULL,        /* parameter of the task */
                    1,           /* priority of the task */
                    &Task1,      /* Task handle to keep track of created task */
                    1);          /* pin task to core 0 */                  
  
  delay(500); 

 // Setup timer and attach timer to a led pin
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
  ledcAttachPin(Master_Gen_PIN, LEDC_CHANNEL_0);

  RMT_Init();
  Fill_pulse_pattern();
 
  // set the brightness on LEDC channel 0
  ledcAnalogWrite(LEDC_CHANNEL_0, brightness);     // PWM with duty of 1 from 255

  UI_Init();
}

void PWM_init()
{
    // Setup timer and attach timer to a led pin
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
  //ledcAttachPin(Master_Gen_PIN, LEDC_CHANNEL_0);

}

//Task1code: blinks an LED every 1000 ms
void Task_core0( void * pvParameters ){
  for(;;){
    //Serial.print("Task_Core0 runs on Core ");
    //Serial.println(xPortGetCoreID());
    vTaskDelay(100 / portTICK_PERIOD_MS);
      int time3 = millis();
  UI_update();
  //Serial.println(millis()-time3);
  yield();
  }
}


void loop() {
  
  // This loop Taks runs on core 1
  //GPIO.out_w1ts = 0b10000;    // Toggle PIN 4
  //GPIO.out_w1tc = 0b10000;

}

The code for the user interface File UI_Interface

Code: Select all

// Display 128 x 64
// Line 1 0,0
// Line 2 16,0

int edit_from     =    6;
int edit_to       =   16;

int32_t jog_from  =    6;
int32_t jog_to    =   16;
int  Char_num = 48;

boolean forceUpdate = false;

boolean Edit = false;
boolean Edit_old = false;




void UI_Init()
  {
    // text display tests
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0,0);
    display.println(line[0]);
    display.println(line[1]);
    display.println(line[2]);

    //updateParameter(false);
     for (Channel = 3 ;Channel>=0;Channel--) 
     {
      for (Param_line_y = 3 ;Param_line_y<11;Param_line_y++)
      {
        //Serial.println(Param_line_y);  
        updateParameter(true);
      }
     }
     Param_line_y = 3;
     updateParameter(true);
    Serial.println("Loaded");
    display.println(line[Param_line_y]);
    delay(2000);
    display.display(); // actually display all of the above
  }


void UI_clear()
{
   display.clearDisplay();
   display.display(); // actually display all of the above
}


void setParam(float val1)
{
   strncpy(line[Param_line_y]+6, "         ", 6);
   dtostrf(val1, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
}

void setParamI(int val1)
{
   strncpy(line[Param_line_y]+6, "         ", 6);
   dtostrf((float)val1, 9, 0, line[Param_line_y]+8); // Leave room for too large numbers!
}

void loadALLParameter()
{
  
}

void updateParameter(boolean load)
{

      
       switch (Param_line_y) {
         case 3: //Channel
            
            if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf((float)Channel, 9, 0, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("Channel: ");
              Serial.println(Channel);
            }
            else
            {
             
             Act_val = constrain(atoi(line[Param_line_y]+6),0,3);
             Channel  = Act_val;
             
             //setParamI(Channel);

             //strncpy(line[Param_line_y]+6, "         ", 6);
             //dtostrf((float)val1, 9, 0, line[Param_line_y]+8); // Leave room for too large numbers!
            
             strncpy(line[Param_line_y]+6, "         ", 12);
             dtostrf((float)Act_val, 9, 0, line[Param_line_y]+8); // Leave room for too large numbers!
             strncpy(line[1], "CHAN :              ", 20);
             dtostrf(Act_val, 1, 0, line[1]+8); // Leave room for too large numbers!
             
            }
          break;
          case 4: // Delay
            if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              Act_val = constrain(PULS[Channel].del,0,32767);
              dtostrf(Act_val*0.1, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("PULS[x].del: ");
              Serial.println(PULS[Channel].del);
            }
            else
            {
              Act_val = atof(line[Param_line_y]+6);
              PULS[Channel].del  = constrain((Act_val *10),0,32767);
              setParam(0.1*(float)PULS[Channel].del);
              Act_val = atof(line[Param_line_y]+6);
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(Act_val, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
            }
            if(Act_val>0)
             {
              PULS[Channel].pulse_del_L = 0 ;
             }
             else
             {
              PULS[Channel].pulse_del_L = 0 ;
             }
 
          break;
         case 5:  // H1
            if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              Act_val = constrain(PULS[Channel].pulseH1,0,32767);
              dtostrf(Act_val*0.1, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("PULS[x].pulseH1: ");
              Serial.println(PULS[Channel].pulseH1);
            }
            else
            {
              Act_val = atof(line[Param_line_y]+6);
              PULS[Channel].pulseH1 = constrain((Act_val *10),0,32767);
              setParam(0.1*(float)PULS[Channel].pulseH1);
              Act_val = atof(line[Param_line_y]+6);
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(Act_val, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers! 
              if(Act_val>0)
              {
                PULS[Channel].pulseH1_L = 1 ;
              }
              else
              {
                PULS[Channel].pulseH1_L = 0 ;
              }
            }
           break;
         case 6:   // L1         
          if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              Act_val = constrain(PULS[Channel].pulseL1,0,32767); 
              dtostrf(Act_val*0.1, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("PULS[x].pulseL1: ");
              Serial.println(PULS[Channel].pulseL1);
            }
            else
            {
              Act_val = atof(line[Param_line_y]+6);
              PULS[Channel].pulseL1 = constrain((Act_val *10),0,32767);
              setParam(0.1*(float)PULS[Channel].pulseL1);
              Act_val = atof(line[Param_line_y]+6);
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(Act_val, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
            }
            if(Act_val>0)
             {
              PULS[Channel].pulseL1_L = 0 ;
             }
             else
             {
               PULS[Channel].pulseL1_L = 0 ;
             }
           break;
         case 7:  // H2
            if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              Act_val = constrain(PULS[Channel].pulseH2,0,32767);
              dtostrf(Act_val *0.1, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("PULS[x].pulseH2: ");
              Serial.println(PULS[Channel].pulseH2);
            }
            else
            {
              Act_val = atof(line[Param_line_y]+6);
              PULS[Channel].pulseH2 = constrain((Act_val *10),0,32767);
              setParam(0.1*(float)PULS[Channel].pulseH2);
              Act_val = atof(line[Param_line_y]+6);
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(Act_val, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers! 
            }
            if(Act_val>0)
             {
              PULS[Channel].pulseH2_L = 1 ;
             }
             else
             {
               PULS[Channel].pulseH2_L = 0 ;
             }
 
           break;
         case 8:   // L2         
          if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              Act_val = constrain(PULS[Channel].pulseL2,0,32767);
              dtostrf(Act_val *0.1, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("PULS[x].pulseL2: ");
              Serial.println(PULS[Channel].pulseL2);
            }
            else
            {
              Act_val = atof(line[Param_line_y]+6);
              PULS[Channel].pulseL2 = constrain((Act_val *10),0,32767);
              setParam(0.1*(float)PULS[Channel].pulseL2);
              Act_val = atof(line[Param_line_y]+6);
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(Act_val, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
            }
            if(Act_val>0)
             {
              PULS[Channel].pulseL2_L = 0 ;
             }
             else
             {
               PULS[Channel].pulseL2_L = 0 ;
             }
 
           break;
         case 9:  // H3
            if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              Act_val = constrain(PULS[Channel].pulseH3,0,32767);
              dtostrf(Act_val*0.1, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("PULS[x].pulseH3: ");
              Serial.println(PULS[Channel].pulseH3);
            }
            else
            {
              Act_val = atof(line[Param_line_y]+6);
              PULS[Channel].pulseH3 = constrain((Act_val *10),0,32767);
              setParam(0.1*(float)PULS[Channel].pulseH3);
              Act_val = atof(line[Param_line_y]+6);
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(Act_val, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers! 
            }
            if(Act_val>0)
             {
              PULS[Channel].pulseH3_L = 1 ;
             }
             else
             {
              PULS[Channel].pulseH3_L = 0 ;
             }
           break;
         case 10:   // L3         
          if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              Act_val = constrain(PULS[Channel].pulseL3,0,32767);
              dtostrf(Act_val*0.1, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("PULS[x].pulseL3: ");
              Serial.println(PULS[Channel].pulseL3);
            }
            else
            {
              Act_val = atof(line[Param_line_y]+6);
              PULS[Channel].pulseL3 = constrain((Act_val *10),0,32767);
              setParam(0.1*(float)PULS[Channel].pulseL3);
              Act_val = atof(line[Param_line_y]+6);
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(Act_val, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
            }
            if(Act_val>0)
             {
              PULS[Channel].pulseL3_L = 0 ;
             }
             else
             {
               PULS[Channel].pulseL3_L = 0 ;
             }
 
           break;
      
          case 13:            
          if (load==true)
            {
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(LEDC_BASE_FREQ, 9, 1, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("LEDC_BASE_FREQ: ");
              Serial.println(LEDC_BASE_FREQ);
            }
            else
            {          
              Act_val = atoi(line[Param_line_y]+6);
              LEDC_BASE_FREQ = Act_val;
              setParamI(LEDC_BASE_FREQ);
              Act_val = atof(line[Param_line_y]+6);
              strncpy(line[Param_line_y]+6, "         ", 6);
              dtostrf(Act_val, 9, 0, line[Param_line_y]+8); // Leave room for too large numbers!
              Serial.print("LEDC_BASE_FREQ");
              Serial.println(LEDC_BASE_FREQ);
            }
            break;
          default:
          break;
      }
     //Serial.println(Param_line_y); 
}



void Incr()
{
   float rat;
   Act_val = atof(line[Param_line_y]+8);
  
   if (Set_X_pos== 16)
   {
     rat = pow(10,16-Set_X_pos);
   }
   else
   {
     rat = pow(10,15-Set_X_pos);
   }
   
   Act_val = constrain(Act_val + (float)rat,0,32767);
  
   //Serial.println(rat);    
   setParam(Act_val);
  
}

void Decr()
{
   float rat;
   Act_val = atof(line[Param_line_y]+8);
  
   if (Set_X_pos== 16)
   {
     rat = pow(10,16-Set_X_pos);
   }
   else
   {
     rat = pow(10,15-Set_X_pos);
   }
   
   Act_val = constrain(Act_val - (float)rat,0,32767);
   
   //Serial.println(rat);    
   setParam(Act_val);
}


void UI_update()
  {
    jog_val = constrain(encoder.getCount(),jog_from-1,jog_to+1);
    Set_Val = constrain(jog_val,jog_from,jog_to);
    encoder.setCount(Set_Val);

    
    if (Edit==true)
    {
     
     if (Set_Val==47)
      {
        line[Param_line_y][Set_X_pos] = (char)46;
      }
      else
      {
        line[Param_line_y][Set_X_pos] = (char)Set_Val;
       
        forceUpdate==true;
      }
      
      if (jog_val > Set_Val)
      {
        Incr();
        forceUpdate = true;
        line[Param_line_y][Set_X_pos] = (char)48;
        Set_Val = 48;
        encoder.setCount(Set_Val);
      }
      if (jog_val < Set_Val and not Act_val <=0)
      {
        Decr();
        forceUpdate = true;
        line[Param_line_y][Set_X_pos] = (char)57;
        Set_Val = 57;
        encoder.setCount(Set_Val);
      }
      //Serial.println("Encoder count = " + String(Set_Val));
      
      
    }
    else
    {
         Set_X_pos = constrain(Set_Val ,edit_from,edit_to);
    }

    
    updateParameter(false);

     

    // Check Buttons
    if(!digitalRead(BUTTON_A)) 
     {
        if (Edit==true)  
        {
          UI_edit(); // Leave Edit mode
          Edit_old=true;
        }
        Param_line_y = constrain(Param_line_y-1,3,Menu_lenght-1);
      
        if (Param_line_y==3)
        {
          Set_X_pos = 16;
          Set_X_pos_old = 16;
          encoder.setCount(16);
          //Serial.println("Test 16");
        }
        else
        {
          Set_X_pos = 14;
          Set_X_pos_old = 14;
          encoder.setCount(14);
          //Serial.println("Test 14");
        }
        updateParameter(true);
        delay(200);
        if (Edit_old==true) UI_edit(); // Go back to old Edit mode
       
     }
     if(!digitalRead(BUTTON_B) ) 
     {
       UI_edit();
       delay(200);
     }
     if(!digitalRead(BUTTON_C)) 
     {
        if (Edit==true)  
        {
          UI_edit(); // Leave Edit mode
          Edit_old=true;
        }
        Param_line_y = constrain(Param_line_y+1,3,Menu_lenght-1);
        
        if (Param_line_y==3)
        {
          Set_X_pos = 16;
          Set_X_pos_old = 16;
          encoder.setCount(16);
          //Serial.println("Test 16");
        }
        else
        {
          Set_X_pos = 14;
          Set_X_pos_old = 14;
          encoder.setCount(14);
          //Serial.println("Test 14");
        }
        updateParameter(true);
        delay(200);   
        if (Edit_old==true) UI_edit(); // Go back to old Edit mode
       
    
     }
  
    if (Set_Val_old != Set_Val or Param_line_y != Param_line_y_old or Channel != Channel_old or forceUpdate==true)
    {
      UI_Main_update();
      Fill_pulse_pattern();
      
      //forceUpdate = false;
    }
    if (LEDC_BASE_FREQ_old != LEDC_BASE_FREQ)
    {
      PWM_init();
    }


    // Store actual params
    Param_line_y_old = Param_line_y;
    Channel_old = Channel;
    Set_Val_old = Set_Val;
    Set_X_pos_old = Set_X_pos;
    Edit_old = Edit;


  }


  
void UI_edit()
{
   //display.setTextSize(1);
   //display.setTextColor(SSD1306_WHITE,SSD1306_BLACK);
   int Char_num = line[Param_line_y][Set_X_pos];
   //Serial.println(Char_num);
   
   if (Edit == false and Set_Val != 15 and Char_num!=32)
   {
    strncpy(line[2],"EDIT                 ", 22);
    Edit = true;
    encoder.setCount(Char_num);
    jog_from  =    48;
    jog_to    =    57;
   }
   else
   {
    strncpy(line[2],"                     ", 22);
    Edit = false;
    encoder.setCount(Set_X_pos);
    jog_from  =    6;
    jog_to    =   16;
   }
    //Serial.println(Set_X_pos);
   //display.display(); // actually display all of the above
}




void UI_Main_update()
{
  //Serial.println(Param_line_y);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE,SSD1306_BLACK);
  display.setCursor(0,0*8);
  display.println(line[0]);
  display.println(line[1]);
  display.println(line[2]);
  display.println(line[Param_line_y]);
  display.display(); // actually display all of the above
 
  showCursor();
}

void showCursor()
{
  display.setCursor(Set_X_pos_old*6,3*8);
  display.print(line[Param_line_y_old][Set_X_pos_old]);
  display.setTextColor(SSD1306_BLACK,SSD1306_WHITE);
  display.setCursor(Set_X_pos*6,3*8);
  display.print(line[Param_line_y][Set_X_pos]);
  display.display(); // actually display all of the above
}
and of course the code for the RMT File name RMT_Gen

Code: Select all

void RMT_Init()
{
   // Channel A put your setup code here, to run once:
  configA.rmt_mode = RMT_MODE_TX;
  configA.channel = RMT_CHANNEL_0;
  configA.gpio_num = GPIO_NUM_13;   // RMT_CHA;
  configA.mem_block_num = 1;
  configA.tx_config.loop_en = 0;
  configA.tx_config.carrier_en = 0;
  configA.tx_config.idle_output_en = 1;
  configA.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  configA.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  configA.clk_div = 8; // 80MHx / 80 = 1MHz 0r 1uS per count
  rmt_config(&configA);
  rmt_driver_install(RMT_CHANNEL_0, 0, 0);  //  rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)
  
  // Channel B put your setup code here, to run once:
  configB.rmt_mode = RMT_MODE_TX;
  configB.channel = RMT_CHANNEL_1;
  configB.gpio_num = RMT_CHB;
  configB.mem_block_num = 1;
  configB.tx_config.loop_en = 0;
  configB.tx_config.carrier_en = 0;
  configB.tx_config.idle_output_en = 1;
  configB.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  configB.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  configB.clk_div = 8; // 80MHx / 80 = 1MHz 0r 1uS per count

  rmt_config(&configB);
  rmt_driver_install(RMT_CHANNEL_1, 0, 0);  //  rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)


  // Channel C put your setup code here, to run once:
  configC.rmt_mode = RMT_MODE_TX;
  configC.channel = RMT_CHANNEL_2;
  configC.gpio_num = RMT_CHC;  // GPIO_NUM_15
  configC.mem_block_num = 1;
  configC.tx_config.loop_en = 0;
  configC.tx_config.carrier_en = 0;
  configC.tx_config.idle_output_en = 1;
  configC.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  configC.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  configC.clk_div = 8; // 80MHx / 80 = 1MHz 0r 1uS per count

  rmt_config(&configC);
  rmt_driver_install(RMT_CHANNEL_2, 0, 0);  //  rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)

  // CHannel D put your setup code here, to run once:
  configD.rmt_mode = RMT_MODE_TX;
  configD.channel = RMT_CHANNEL_3;
  configD.gpio_num = RMT_CHD;
  configD.mem_block_num = 1;
  configD.tx_config.loop_en = 0;
  configD.tx_config.carrier_en = 0;
  configD.tx_config.idle_output_en = 1;
  configD.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
  configD.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH;
  configD.clk_div = 8; // 80MHx / 80 = 1MHz 0r 1uS per count

  rmt_config(&configD);
  rmt_driver_install(RMT_CHANNEL_3, 0, 0);  //  rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num)

}


void Fill_pulse_pattern()
{
 //Serial.println("RMT Reinit");
  //PIN 27
  //Serial.println(PULS[1].pulseH1);
  itemsA[0].duration0 = PULS[0].del;
  itemsA[0].level0 = PULS[0].pulse_del_L;;
  itemsA[0].duration1 = PULS[0].pulseH1; //PULS[1].pulseL;
  itemsA[0].level1 = PULS[0].pulseH1_L;  
  itemsA[1].duration0 = PULS[0].pulseL1;
  itemsA[1].level0 = PULS[0].pulseL1_L;;
  itemsA[1].duration1 = PULS[0].pulseH2;
  itemsA[1].level1 = PULS[0].pulseH2_L;;
  itemsA[2].duration0 = PULS[0].pulseL2;
  itemsA[2].level0 = PULS[0].pulseL2_L;;
  itemsA[2].duration1 = PULS[0].pulseH3;      // End of pattern with 0 time to stop pulse train
  itemsA[2].level1 = PULS[0].pulseH3_L;;
  itemsA[3].duration0 = PULS[0].pulseL3;      // End of pattern with 0 time to stop pulse train
  itemsA[3].level0 = PULS[0].pulseL3_L;;
  itemsA[3].duration1 = 0;      // End of pattern with 0 time to stop pulse train
  itemsA[3].level1 = 0;

  //PIN 33
  itemsB[0].duration0 = PULS[1].del;
  itemsB[0].level0 = PULS[1].pulse_del_L;;
  itemsB[0].duration1 = PULS[1].pulseH1; //PULS[1].pulseL;
  itemsB[0].level1 = PULS[1].pulseH1_L;  
  itemsB[1].duration0 = PULS[1].pulseL1;
  itemsB[1].level0 = PULS[1].pulseL1_L;;
  itemsB[1].duration1 = PULS[1].pulseH2;
  itemsB[1].level1 = PULS[1].pulseH2_L;;
  itemsB[2].duration0 = PULS[1].pulseL2;
  itemsB[2].level0 = PULS[1].pulseL2_L;;
  itemsB[2].duration1 = PULS[1].pulseH3;      // End of pattern with 0 time to stop pulse train
  itemsB[2].level1 = PULS[1].pulseH3_L;;
  itemsB[3].duration0 = PULS[1].pulseL3;      // End of pattern with 0 time to stop pulse train
  itemsB[3].level0 = PULS[1].pulseL3_L;;
  itemsB[3].duration1 = 0;      // End of pattern with 0 time to stop pulse train
  itemsB[3].level1 = 0;
  


  //PIN 15
  itemsC[0].duration0 = PULS[2].del;
  itemsC[0].level0 = PULS[2].pulse_del_L;;
  itemsC[0].duration1 = PULS[2].pulseH1; //PULS[1].pulseL;
  itemsC[0].level1 = PULS[2].pulseH1_L;  
  itemsC[1].duration0 = PULS[2].pulseL1;
  itemsC[1].level0 = PULS[2].pulseL1_L;;
  itemsC[1].duration1 = PULS[2].pulseH2;
  itemsC[1].level1 = PULS[2].pulseH2_L;;
  itemsC[2].duration0 = PULS[2].pulseL2;
  itemsC[2].level0 = PULS[2].pulseL2_L;;
  itemsC[2].duration1 = PULS[2].pulseH3;      // End of pattern with 0 time to stop pulse train
  itemsC[2].level1 = PULS[2].pulseH3_L;;
  itemsC[3].duration0 = PULS[2].pulseL3;      // End of pattern with 0 time to stop pulse train
  itemsC[3].level0 = PULS[2].pulseL3_L;;
  itemsC[3].duration1 = 0;      // End of pattern with 0 time to stop pulse train
  itemsC[3].level1 = 0;


  //PIN 32
  itemsD[0].duration0 = PULS[3].del;
  itemsD[0].level0 = PULS[3].pulse_del_L;;
  itemsD[0].duration1 = PULS[3].pulseH1; //PULS[1].pulseL;
  itemsD[0].level1 = PULS[3].pulseH1_L;  
  itemsD[1].duration0 = PULS[3].pulseL1;
  itemsD[1].level0 = PULS[3].pulseL1_L;;
  itemsD[1].duration1 = PULS[3].pulseH2;
  itemsD[1].level1 = PULS[3].pulseH2_L;;
  itemsD[2].duration0 = PULS[3].pulseL2;
  itemsD[2].level0 = PULS[3].pulseL2_L;;
  itemsD[2].duration1 = PULS[3].pulseH3;      // End of pattern with 0 time to stop pulse train
  itemsD[2].level1 = PULS[3].pulseH3_L;;
  itemsD[3].duration0 = PULS[3].pulseL3;      // End of pattern with 0 time to stop pulse train
  itemsD[3].level0 = PULS[3].pulseL3_L;;
  itemsD[3].duration1 = 0;      // End of pattern with 0 time to stop pulse train
  itemsD[3].level1 = 0;
  
  rmt_fill_tx_items(RMT_CHANNEL_0,itemsA,4,0);
  rmt_fill_tx_items(RMT_CHANNEL_1,itemsB,4,0);
  rmt_fill_tx_items(RMT_CHANNEL_2,itemsC,4,0);
  rmt_fill_tx_items(RMT_CHANNEL_3,itemsD,4,0);
}

Who is online

Users browsing this forum: No registered users and 50 guests