Generating waveforms using Arduino due PWM

You can, the sequence repeats every 6ms, during which time signals 1 and 2 have completed 20 cycles, and signal 3 has completed 3 cycles.


I had to adjust the 'hold off time' to get a stable oscilloscope trace.

1 Like

Thank you so much @horace for your kind response, as mention in the post 19 with slight modification the mentioned wave forms are generated in the attachement

`// DUE three pulse test using timer interrupts
// Signal 1: 100 µs HIGH, 200 µs LOW
// Signal 2: 150 µs LOW, 100 µs HIGH, 50 µs LOW
// Signal 3: 650 µs LOW, 1000 µs HIGH, 150 µs LOW

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

// Setup pulse patterns - each slot is 50 µs
// Signal 1: 100 µs HIGH, 200 µs LOW
byte signal1[] = { 1, 1, 0, 0, 0, 0 };
int s1index = 0;

// Signal 2: 150 µs LOW, 100 µs HIGH, 50 µs LOW
byte signal2[] = { 0, 0, 0, 1, 1, 0 };
int s2index = 0;

// Signal 3: 650 µs LOW, 1000 µs HIGH, 150 µs LOW
byte signal3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 650 µs LOW
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1000 µs HIGH
1, 1, 1, 1, 1, 1, 1, 1, // (20 × 50 µs)
0, 0, 0 }; // 150 µs LOW
int s3index = 0;

// Timer ISR (Interrupt Service Routine)
void TC3_Handler() {
// Clear the interrupt flag
TC_GetStatus(TC1, 0);

// Output next 50 µs level
digitalWrite(S1pin, signal1[s1index++]);
digitalWrite(S2pin, signal2[s2index++]);
digitalWrite(S3pin, signal3[s3index++]);

// Reset indices when reaching the end of arrays
if (s1index == sizeof(signal1)) s1index = 0;
if (s2index == sizeof(signal2)) s2index = 0;
if (s3index == sizeof(signal3)) s3index = 0;
}

void setup() {
// Initialize pins as outputs
pinMode(S1pin, OUTPUT);
pinMode(S2pin, OUTPUT);
pinMode(S3pin, OUTPUT);

// Configure Timer3 for 50 µs interval
pmc_set_writeprotect(false); // Disable write protection for PMC
pmc_enable_periph_clk(TC3_IRQn); // Enable Timer3 peripheral clock

Tc *tc = TC1; // Timer1 block
uint32_t channel = 0; // Channel 0 for TC3
uint32_t freq = 20000; // Timer frequency (1/50 µs = 20,000 Hz)

TC_Configure(tc, channel,
TC_CMR_TCCLKS_TIMER_CLOCK1 | // Select Timer clock (MCK/2)
TC_CMR_WAVE | // Waveform mode
TC_CMR_WAVSEL_UP_RC); // Count up to RC

uint32_t rc = VARIANT_MCK / 2 / freq; // Set RC for 50 µs
TC_SetRA(tc, channel, rc / 2); // Set RA for duty cycle (50%)
TC_SetRC(tc, channel, rc); // Set RC for 50 µs period

TC_Start(tc, channel); // Start Timer
tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPCS; // Enable RC compare interrupt
tc->TC_CHANNEL[channel].TC_IDR = ~TC_IER_CPCS;
NVIC_EnableIRQ(TC3_IRQn); // Enable interrupt in NVIC
}

void loop() {
// Main loop does nothing - timing is handled by the Timer ISR
}
`

good news!
edit your post 22 to use code tags </> - make code much more readable

as @JohnLincoln mentions in post 20 you can synch a signal of 2000uSec period with signals of 300uSec period over 6mSec
e.g. timing

// 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, 0, 0, 0, 0};                    // 350uSec low
int s3index = 0;

scope display using a hold off of 4.1mSec

I still prefer to use the ESP32S3 RMT to generate complex pulse sequences
set it going and you can forget about it - no ISRs etc

Hello, @horace @JohnLincoln could you help where am going wrong with the code which i used to generate for 6 signals. why the frequency of the signals are degrading ?

#define S1pin 2
#define S2pin 3
#define S3pin 4
#define S4pin 5
#define S5pin 6
#define S6pin 7

// Signal patterns (predefined based on required frequencies)
// Signal 1-4: 12.5 kHz (80 µs period, 8 steps)
byte signal1[] = {0, 0, 0, 1, 0, 0, 0, 0};
byte signal2[] = {0, 0, 0, 0, 0, 1, 0, 0};
byte signal3[] = {0, 0, 0, 0, 0, 0, 0, 1};
byte signal4[] = {0, 1, 0, 0, 0, 0, 0, 0};

// Signal 5: 2 kHz (500 µs period, 50 steps)
byte signal5[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
                  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                  1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

// Signal 6: 1 kHz (1 ms period, 100 steps)
byte signal6[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                  0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// Indices for signal tracking
volatile int s1index = 0, s2index = 0, s3index = 0, s4index = 0;
volatile int s5index = 0, s6index = 0;

// Timer Interrupt Service Routine (ISR)
void TC3_Handler() {
  TC_GetStatus(TC1, 0); // Clear the interrupt flag

  // Atomic signal update
  digitalWrite(S1pin, signal1[s1index]);
  digitalWrite(S2pin, signal2[s2index]);
  digitalWrite(S3pin, signal3[s3index]);
  digitalWrite(S4pin, signal4[s4index]);
  digitalWrite(S5pin, signal5[s5index]);
  digitalWrite(S6pin, signal6[s6index]);

  // Increment and reset indices
  s1index = (s1index + 1) % sizeof(signal1);
  s2index = (s2index + 1) % sizeof(signal2);
  s3index = (s3index + 1) % sizeof(signal3);
  s4index = (s4index + 1) % sizeof(signal4);
  s5index = (s5index + 1) % sizeof(signal5);
  s6index = (s6index + 1) % sizeof(signal6);
}

void setup() {
  // Configure pins as outputs
  pinMode(S1pin, OUTPUT);
  pinMode(S2pin, OUTPUT);
  pinMode(S3pin, OUTPUT);
  pinMode(S4pin, OUTPUT);
  pinMode(S5pin, OUTPUT);
  pinMode(S6pin, OUTPUT);

  // Configure Timer 3
  pmc_set_writeprotect(false);       // Disable write protection
  pmc_enable_periph_clk(TC3_IRQn);  // Enable peripheral clock for Timer3

  Tc *tc = TC1;                     // Use Timer1 block
  uint32_t channel = 0;             // Channel 0 (TC3)
  uint32_t freq = 125000;           // Timer frequency: 8 µs (125,000 Hz)

  TC_Configure(tc, channel,
               TC_CMR_TCCLKS_TIMER_CLOCK1 |  // Clock source: MCK/2
               TC_CMR_WAVE |                 // Enable waveform mode
               TC_CMR_WAVSEL_UP_RC);         // Count up to RC

  uint32_t rc = VARIANT_MCK / 2 / freq; // Calculate RC for 8 µs period
  TC_SetRA(tc, channel, rc / 2);        // 50% duty cycle
  TC_SetRC(tc, channel, rc);            // Set RC for 8 µs

  TC_Start(tc, channel);                // Start Timer
  tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPCS; // Enable RC compare interrupt
  tc->TC_CHANNEL[channel].TC_IDR = ~TC_IER_CPCS;
  NVIC_EnableIRQ(TC3_IRQn);             // Enable interrupt in NVIC
}

void loop() {
  // Main loop left empty as timing is handled by the ISR
}


could be the Due is not fast enough
will experiment

signals 1 to 6 using digitalWriteFast()

// DUE six pulse test using timer interrupts
// // Signal 1-4: 12.5 kHz (80 µs period, 8 steps)

#include <DueTimer.h>

#define S1pin 2
#define S2pin 3
#define S3pin 4
#define S4pin 5
#define S5pin 6
#define S6pin 7

// setup pulse patterns - each slot is 10uSec
// Signal 1-4: 12.5 kHz (80 µs period, 8 steps)
byte signal1[] = { 0, 0, 0, 1, 0, 0, 0, 0 };
byte signal2[] = { 0, 0, 0, 0, 0, 1, 0, 0 };
byte signal3[] = { 0, 0, 0, 0, 0, 0, 0, 1 };
byte signal4[] = { 0, 1, 0, 0, 0, 0, 0, 0 };
// Signal 5: 2 kHz (500 µs period, 50 steps)
byte signal5[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
                   1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                   1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };

// Signal 6: 1 kHz (1 ms period, 100 steps)
byte signal6[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                   1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                   1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                   1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                   1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

int s1index = 0;
int s2index = 0;
int s3index = 0;
int s4index = 0;
int s5index = 0;
int s6index = 0;

// digitalWriteFast from
//  https://forum.arduino.cc/t/arduino-due-digitalwrite-vs-direct-port-manipulation-speed/666404/31
boolean NonConstantUsed(void) __attribute__((error("")));

#define digitalWriteFast(pin, val) \
  if (__builtin_constant_p(pin)) { \
    _dwfast(pin, val); \
  } else { \
    NonConstantUsed(); \
  }

static inline __attribute__((always_inline)) void _dwfast(int pin, int val) {
  if (val) {
    digitalPinToPort(pin)->PIO_SODR = digitalPinToBitMask(pin);
  } else {
    digitalPinToPort(pin)->PIO_CODR = digitalPinToBitMask(pin);
  }
}
// on timer interrupt output next 50uSec level
void timerISR(void) {
  digitalWriteFast(S1pin, signal1[s1index++]);  // signal 1
  digitalWriteFast(S2pin, signal2[s2index++]);
  digitalWriteFast(S3pin, signal3[s3index++]);
  digitalWriteFast(S4pin, signal4[s4index++]);
  digitalWriteFast(S5pin, signal5[s5index++]);
  digitalWriteFast(S6pin, signal6[s6index++]);
  if (s1index == sizeof(signal1)) s1index = 0;  // increment array index if at end
  if (s2index == sizeof(signal2)) s2index = 0;
  if (s3index == sizeof(signal3)) s3index = 0;
  if (s4index == sizeof(signal4)) s4index = 0;
  if (s5index == sizeof(signal5)) s5index = 0;
  if (s6index == sizeof(signal6)) s6index = 0;
}

void setup() {
  pinMode(S1pin, OUTPUT);  // initialise outputs
  pinMode(S2pin, OUTPUT);
  pinMode(S3pin, OUTPUT);
  pinMode(S4pin, OUTPUT);
  pinMode(S5pin, OUTPUT);
  pinMode(S6pin, OUTPUT);
  Timer3.attachInterrupt(timerISR);  // attached interrupt ISR
  Timer3.start(10);                  // interval is in microseconds.
}

void loop() {}

signals 1 to 4

signal 1, 2, 5, 6