Generating waveforms using Arduino due PWM

Good morning, Am trying to generate three different signals using Arduino due with given below specifications :
signal 1 : pulse of peak to peak 3.2 V, 200 us delay , 200 ns rise/fall time, 100 us peak, and 300 us pulse period.
signal 2 : pulse of peak to peak 3.2 V, 350 us delay , 200 ns rise/fall time, 100 us peak, and 300 us pulse period.
signal 3 : pulse of peak to peak 3.2 V, 1000 us delay , 200 ns rise/fall time, 1000 us peak, and 2000 us pulse period.
Am getting the signal but with delay other than the specifications, frequency of the signals is also different when it is connected to the oscilloscope for verification. Please help me to resolve the issue, I used below code to generate the waveforms.

void setup()
{ 
  // Set up PWM on digital pins D9, D8, D7, and D6 for channels 4 through to 7 respectively
  PMC->PMC_PCER1 |= PMC_PCER1_PID36; // Enable PWM controller
  PIOC->PIO_ABSR |= PIO_ABSR_P24 | PIO_ABSR_P23 | PIO_ABSR_P22 | PIO_ABSR_P21; // Set the port C PWM pins to peripheral type B
  PIOC->PIO_PDR |= PIO_PDR_P24 | PIO_PDR_P23 | PIO_PDR_P22 | PIO_PDR_P21; // Set the port C PWM pins to outputs
  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1); // Set the PWM clock A rate to 84MHz (84MHz/1)
  PWM->PWM_SCM = PWM_SCM_SYNC7 | PWM_SCM_SYNC6 | PWM_SCM_SYNC5 | PWM_SCM_SYNC4;
  PWM->PWM_CH_NUM[0].PWM_CPRD = 25200; // Set PWM period for channel 0
  
  PWM->PWM_CH_NUM[4].PWM_CMR = PWM_CMR_DTE | PWM_CMR_CPRE_CLKA; // Configure channel 4
  PWM->PWM_CH_NUM[4].PWM_CDTY = 8400; // Set duty cycle for channel 4
  
  PWM->PWM_CH_NUM[5].PWM_CMR = PWM_CMR_DTE | PWM_CMR_CPRE_CLKA; // Configure channel 5
  PWM->PWM_CH_NUM[5].PWM_CDTY = 8400; // Set duty cycle for channel 5
  
  PWM->PWM_CH_NUM[6].PWM_CMR = PWM_CMR_DTE | PWM_CMR_CPRE_CLKA; // Configure channel 6
  PWM->PWM_CH_NUM[6].PWM_CDTY = 84000; // Set duty cycle for channel 6

  // Enable channels with delays
  PWM->PWM_ENA = PWM_ENA_CHID4; // Enable channel 4
  delayMicroseconds(200);       // Delay 200 microseconds
  
  PWM->PWM_ENA = PWM_ENA_CHID5; // Enable channel 5
  delayMicroseconds(350);       // Delay 350 microseconds
  
  PWM->PWM_ENA = PWM_ENA_CHID6; // Enable channel 6
  delayMicroseconds(1000);       // Delay 1000 microseconds
  
  PWM->PWM_SCUC = PWM_SCUC_UPDULOCK; // Set the update unlock bit to trigger an update at the end of the next PWM period
}

void loop()
{
  // Empty loop, no repeated actions
}
type or paste code here

What controller is this written for?
delayMicroseconds takes steps of 4 microSeconds on Uno.
Also, the code itself takes time. This will be added to your wave period.
Using micros() may solve the latter.
The first may be solved with a mcu with higher clock frequency.

This code is to generate a waveform to test some digital circuit. I am using Arduino DUE. As I am new to coding, please help me out to correct my code in the above-mentioned thread and to generate the waveform as below specifications:
signal 1 : pulse of peak to peak 3.2 V, 200 us delay, 200 ns rise/fall time, 100 us peak, and 300 us pulse period.
signal 2 : pulse of peak to peak 3.2 V, 350 us delay, 200 ns rise/fall time, 100 us peak, and 300 us pulse period.
signal 3 : pulse of peak to peak 3.2 V, 1000 us delay, 200 ns rise/fall time, 1000 us peak, and 2000 us pulse period.

are the signals synchronized to each other?
try drawing a diagram showing the timing?
EDIT: is it just a single sequence or does it repeat?
e.g. single sequence

I think that rise and fall time are a property of the pwm output of the mcu.
It would be a coincidence if it was 100 ns..
If it is faster than 100ns you might add an rc filter to add some rise and fall time...
If it is slower you need to find another mcu...

if the signals then repeat you get


the 300mSec period signals are synchronized with each other
the 2000mSec period signal not synchronized with the others

I assume you have some trigger signal which starts the pulses after the delays

With the above-mentioned code, the generated waveforms are in the attachment. whether dont know the waveform are synchronized or not. if it requires please let me know how to synchronize it

i need the waveforms as mention by @horace in the attachement but with delay as mentioned in the specifications

you specify
signal 1 : pulse of peak to peak 3.2 V, 200 us delay, 200 ns rise/fall time, 100 us peak, and 300 us pulse period.
signal 2 : pulse of peak to peak 3.2 V, 350 us delay, 200 ns rise/fall time, 100 us peak, and 300 us pulse period.

how can you have a 300uSec periods and also have the delays?
first signal OK but signal 2 would have a 450uSec period?
e.g.

no synchronization between pulses

I assume during the delays the signal is 0?

Taking reference to the first pulse, the second pulse has a 150-us delay (350-200 = 150), and similarly, the third pulse has a 650-us delay (1000-350 = 650).
Better you share with me the code that you have implemented to generate the above pulses @horace; it will help me out in understanding a better way.

using a ESP32S3 RMT module in attempt to make sense of timing
code

// ESP32S3 RMT three pulse test
// initial state
// 100uSec high 200uSec low
// 150uSec low 100uSec High 50uSec low
// 650uSec low 1000high 350uSec low
// signal 1 and 2 in synchronization


// https://docs.espressif.com/projects/esp-idf/en/v5.3.1/esp32/api-reference/peripherals/rmt.html

#include "Arduino.h"
#include "driver/rmt_tx.h"

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("ESP32S3 RMT !KHz three pulse sync test");
  delay(2000);
  // setup Tx channels
  rmt_channel_handle_t tx_channels[3] = { NULL };
  gpio_num_t tx_gpio_number[3] = { GPIO_NUM_16, GPIO_NUM_17, GPIO_NUM_18 };  // pin numbers
  for (int i = 0; i < 3; i++) {
    Serial.printf("setting up channel %d\n", i);
    rmt_tx_channel_config_t tx_chan_config = {
      .gpio_num = tx_gpio_number[i],
      .clk_src = RMT_CLK_SRC_DEFAULT,
      .resolution_hz = 10 * 1000 * 1000,  // 10MHz clock
// fix for problem with more than two Tx channels see
//  https://esp32.com/viewtopic.php?f=13&t=42301&p=139024#p139024
#if 0
      .mem_block_symbols = 64,
#endif
      .mem_block_symbols = 48,     
      .trans_queue_depth = 1,
    };
    ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_channels[i]));
  }
  rmt_transmit_config_t transmitConfig = {
    .loop_count = -1
  };
  rmt_encoder_handle_t copyEncoder{ NULL };
  rmt_copy_encoder_config_t copyEncoderConfig = {};
  assert(rmt_new_copy_encoder(&copyEncoderConfig, &copyEncoder) == ESP_OK && "Failed to Create Copy Encoder");
  ESP_ERROR_CHECK(rmt_enable(tx_channels[2]));
  ESP_ERROR_CHECK(rmt_enable(tx_channels[1]));
  ESP_ERROR_CHECK(rmt_enable(tx_channels[0]));

  // setup sync manage for the three pulses
  rmt_sync_manager_handle_t synchro = NULL;
  rmt_sync_manager_config_t synchro_config = {
    .tx_channel_array = tx_channels,
    .array_size = sizeof(tx_channels) / sizeof(tx_channels[0]),
  };
  ESP_ERROR_CHECK(rmt_new_sync_manager(&synchro_config, &synchro));

  Serial.println("Starting Transmitters");
  // setup pulse patterns - clock is 10MHz
  // 100uSec high 200uSec low
  const rmt_symbol_word_t pulsePattern1[] = {
    { .duration0 = 1000, .level0 = 1, .duration1 = 2000, .level1 = 0 },
  };
  // 150uSec low 100uSec High 50uSec low
  const rmt_symbol_word_t pulsePattern2[] = {
    { .duration0 = 1500, .level0 = 0, .duration1 = 1000, .level1 = 1 },
    { .duration0 = 250, .level0 = 0, .duration1 =250, .level1 = 0 },
  };
  // 650uSec low 1000high 350uSec low
   const rmt_symbol_word_t pulsePattern3[] = {
    { .duration0 = 6500, .level0 = 0, .duration1 = 10000, .level1 = 1 },
    { .duration0 = 3000, .level0 = 0, .duration1 = 500, .level1 = 0 },
  };

  assert(rmt_transmit(tx_channels[0], copyEncoder, &pulsePattern1, sizeof(pulsePattern1), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
  assert(rmt_transmit(tx_channels[1], copyEncoder, &pulsePattern2, sizeof(pulsePattern2), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
  assert(rmt_transmit(tx_channels[2], copyEncoder, &pulsePattern3, sizeof(pulsePattern3), &transmitConfig) == ESP_OK && "Failed to begin transmitting");
  Serial.println("Transmitters Running");
}
void loop() {
}

result

signals 1 and 2 in synchronization

EDIT: single pulse sequence showing initial timing

Thanks for your continuous efforts and support. @horace , But I am using "Arduino DUE" currently. As I am new to coding it, will you help me out to code it for Arduino DUE?
The refernce code what i generated is as follows:

void setup()
{ 
  // Set up PWM on digital pins D9, D8, D7, and D6 for channels 4 through to 7 respectively
  PMC->PMC_PCER1 |= PMC_PCER1_PID36; // Enable PWM controller
  PIOC->PIO_ABSR |= PIO_ABSR_P24 | PIO_ABSR_P23 | PIO_ABSR_P22 | PIO_ABSR_P21; // Set the port C PWM pins to peripheral type B
  PIOC->PIO_PDR |= PIO_PDR_P24 | PIO_PDR_P23 | PIO_PDR_P22 | PIO_PDR_P21; // Set the port C PWM pins to outputs
  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1); // Set the PWM clock A rate to 84MHz (84MHz/1)
  PWM->PWM_SCM = PWM_SCM_SYNC7 | PWM_SCM_SYNC6 | PWM_SCM_SYNC5 | PWM_SCM_SYNC4;
  PWM->PWM_CH_NUM[0].PWM_CPRD = 25200; // Set PWM period for channel 0
  
  PWM->PWM_CH_NUM[4].PWM_CMR = PWM_CMR_DTE | PWM_CMR_CPRE_CLKA; // Configure channel 4
  PWM->PWM_CH_NUM[4].PWM_CDTY = 8400; // Set duty cycle for channel 4
  
  PWM->PWM_CH_NUM[5].PWM_CMR = PWM_CMR_DTE | PWM_CMR_CPRE_CLKA; // Configure channel 5
  PWM->PWM_CH_NUM[5].PWM_CDTY = 8400; // Set duty cycle for channel 5
  
  PWM->PWM_CH_NUM[6].PWM_CMR = PWM_CMR_DTE | PWM_CMR_CPRE_CLKA; // Configure channel 6
  PWM->PWM_CH_NUM[6].PWM_CDTY = 84000; // Set duty cycle for channel 6

  // Enable channels with delays
  PWM->PWM_ENA = PWM_ENA_CHID4; // Enable channel 4
  delayMicroseconds(200);       // Delay 200 microseconds
  
  PWM->PWM_ENA = PWM_ENA_CHID5; // Enable channel 5
  delayMicroseconds(350);       // Delay 350 microseconds
  
  PWM->PWM_ENA = PWM_ENA_CHID6; // Enable channel 6
  delayMicroseconds(1000);       // Delay 1000 microseconds
  
  PWM->PWM_SCUC = PWM_SCUC_UPDULOCK; // Set the update unlock bit to trigger an update at the end of the next PWM period
}

void loop()
{
  // Empty loop, no repeated actions
}
type or paste code here

Here in the above code, even after changing the pwm period, the output frequency for each signal remains the same as 3.33 kHz.
The first 2 signals have pwm period of 25200 = 84 MHz * 300us, and the third signal is 168000 = 84 MHz * 2ms.
Frequency of the signals first = 3.33 kHz, second = 3.33 kHz, and third = 500 Hz

I have used the Due but not for PWM
are the pulse sequences of post 11 what you are looking for? or even close to it?
e.g. although signal 1 and 2 are in synchronization do you expect signal 3 to be free running
photo shows signal 1 and 2 in synch signal 3 not in synch

Yes, it looks fine in post 14, but I am unable to trace the delay from the picture. I want to implement it in the Arduino DUE. Is it possible to synch all three signals?

don't see how you can synch a signal of 2000uSec period with signals of 300mSec period
try signal 3 at period of 1800uSec (which is a multiple of 300uSec), e.g.

all in synch - scope triggered off signal 3 (purple trace)

on the Due you could try a simple delay
e.g. on an ESP32S3

// ESP32S3 three pulse test using delayMicroseconds()
// signal 1 100uSec high 200uSec low
// signal 2 150uSec low 100uSec High 50uSec low
// signal 3 650uSec low 1000uSec high 150uSec low

#define S1pin 16
#define S2pin 17
#define S3pin 18

// setup pulse patterns - each slot is 50uSec 
// signal 1 100uSec high 200uSec low
byte signal1[] = { 1, 1, 0, 0, 0, 0 };
int s1index = 0;
// signal 2 150uSec low 100uSec High 50uSec low
byte signal2[] = { 0, 0, 0, 1, 1, 0 };
int s2index = 0;
// signal 3 650uSec low 1000uSec high 150uSec low
byte signal3[]={0,0,0,0,0,0,0,0,0,0,0,0,0,          // 650uSec low
           1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 1000uSec high
           0, 0, 0};                                // 150uSec low
int s3index = 0;

void setup() {
  pinMode(S1pin, OUTPUT); // initialise outputs
  pinMode(S2pin, OUTPUT);
  pinMode(S3pin, OUTPUT);
}

void loop() {
  // output next 50uSec level
  digitalWrite(S1pin, signal1[s1index++]);     // signal 1
  if (s1index == sizeof(signal1)) s1index = 0; // increment array index if at end
  digitalWrite(S2pin, signal2[s2index++]);
  if (s2index == sizeof(signal2)) s2index = 0;
  digitalWrite(S3pin, signal3[s3index++]);
  if (s3index == sizeof(signal3)) s3index = 0;
  delayMicroseconds(47);    // 50uSec delay allowing for other instruction delays
}

result

main problem is the processor is just outputting the signals - difficult to do anything else
could use timer interrupts

will see if I can find a DUE

running on a DUE

// DUE three pulse test using delayMicroseconds()
// signal 1 100uSec high 200uSec low
// signal 2 150uSec low 100uSec High 50uSec low
// signal 3 650uSec low 1000uSec high 150uSec low

#define S1pin 2
#define S2pin 3
#define S3pin 4

// setup pulse patterns - each slot is 50uSec 
// signal 1 100uSec high 200uSec low
byte signal1[] = { 1, 1, 0, 0, 0, 0 };
int s1index = 0;
// signal 2 150uSec low 100uSec High 50uSec low
byte signal2[] = { 0, 0, 0, 1, 1, 0 };
int s2index = 0;
// signal 3 650uSec low 1000uSec high 150uSec low
byte signal3[]={0,0,0,0,0,0,0,0,0,0,0,0,0,          // 650uSec low
           1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 1000uSec high
           0, 0, 0};                                // 150uSec low
int s3index = 0;

void setup() {
  pinMode(S1pin, OUTPUT); // initialise outputs
  pinMode(S2pin, OUTPUT);
  pinMode(S3pin, OUTPUT);
}

void loop() {
  // output next 50uSec level
  digitalWrite(S1pin, signal1[s1index++]);     // signal 1
  digitalWrite(S2pin, signal2[s2index++]);
  digitalWrite(S3pin, signal3[s3index++]);
  if (s1index == sizeof(signal1)) s1index = 0; // increment array index if at end
  if (s2index == sizeof(signal2)) s2index = 0;
  if (s3index == sizeof(signal3)) s3index = 0;
  delayMicroseconds(41);    // 50uSec delay allowing for other instruction delays
}

pulses all in synch

pulse start timing will be a few uSec out due to time taken to execute digitalWrite() functions

1 Like

the problem with post 18 code is the processor is just outputting the signals in loop()
difficult to do anything else

this version uses timer interrupts

// DUE three pulse test using timer interrupts
// signal 1 100uSec high 200uSec low
// signal 2 150uSec low 100uSec High 50uSec low
// signal 3 650uSec low 1000uSec high 150uSec low

#include <DueTimer.h>

#define S1pin 2
#define S2pin 3
#define S3pin 4

// setup pulse patterns - each slot is 50uSec 
// signal 1 100uSec high 200uSec low
byte signal1[] = { 1, 1, 0, 0, 0, 0 };
int s1index = 0;
// signal 2 150uSec low 100uSec High 50uSec low
byte signal2[] = { 0, 0, 0, 1, 1, 0 };
int s2index = 0;
// signal 3 650uSec low 1000uSec high 150uSec low
byte signal3[]={0,0,0,0,0,0,0,0,0,0,0,0,0,          // 650uSec low
           1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 1000uSec high
           0, 0, 0};                                // 150uSec low
int s3index = 0;

// on timer interrupt output next 50uSec level
void timerISR(void) {
  digitalWrite(S1pin, signal1[s1index++]);     // signal 1
  digitalWrite(S2pin, signal2[s2index++]);
  digitalWrite(S3pin, signal3[s3index++]);
  if (s1index == sizeof(signal1)) s1index = 0; // increment array index if at end
  if (s2index == sizeof(signal2)) s2index = 0;
  if (s3index == sizeof(signal3)) s3index = 0;
}

void setup() {
  pinMode(S1pin, OUTPUT); // initialise outputs
  pinMode(S2pin, OUTPUT);
  pinMode(S3pin, OUTPUT);
  Timer3.attachInterrupt(timerISR);  // attach interrupt ISR
  Timer3.start(50);               // interval is in microseconds.
}

void loop() {}

you can then do other things in loop()

Horace,
You're coming up with good solutions.

I've had your code from post #18 running on an Uno R3.
I also modified it to use millis() for the timing.