Hello everyone
I am trying to generate three independent PWM pulses each of 20 KHz with different duty cycles and want to incorporate 1 microsecond delay between each of them. I am using an Arduino Due board. Please help.
To generate three independent 20 KHz PWM signals with different duty cycles on an Arduino Due, you can use the built-in hardware PWM support. The idea is to:
- Configure the built-in timers to generate a 20 KHz PWM signal.
- Set the duty cycle for each PWM signal.
- Use the
delayMicroseconds()
function to introduce a 1 µs delay between the signals.
Below is a simplified version of the Arduino code. This code sets up PWM on pins 6, 7, and 8, with different duty cycles.
void setup(){
analogWriteResolution(8); // 8-bit resolution
analogWriteFrequency(6, 20000); // 20 KHz frequency on Pin 6
analogWriteFrequency(7, 20000); // 20 KHz frequency on Pin 7
analogWriteFrequency(8, 20000); // 20 KHz frequency on Pin 8
}
void loop(){
analogWrite(6, 64); // 25% duty cycle on Pin 6
delayMicroseconds(1);
analogWrite(7, 128); // 50% duty cycle on Pin 7
delayMicroseconds(1);
analogWrite(8, 192); // 75% duty cycle on Pin 8
delayMicroseconds(1);
}
Please note that this is a simplified example and actual implementation may depend on the specific requirements of your project.
Thank you for your response.
I tried running this code, but the analogWriteFrequency function is not defined in the scope.
Do I need to install any particular library for it?
The analogWriteFrequency()
function is specific to some boards like Teensy but it's not available in the standard Arduino library.
For Arduino Due, you might have to use a lower-level approach to directly configure the hardware registers or use a third-party library.
One such library is the DueTimer library that provides more control over the timers, but it doesn't directly support changing the frequency of the PWM signals.
If you are comfortable with lower-level coding, you might want to delve into the datasheet for the microcontroller on the Arduino Due and manually configure the timers and PWM outputs.
Alternatively, you may consider using a different microcontroller that has more flexible timer/PWM functionality, or an external PWM driver chip. This can simplify the coding required.
I ask out of pure curiosity. What will you use these generated signals for?
I am trying to generate the stimulus for testing some digital logic circuits
I tried using the antodom libraries for pwm and timer configuration.
I used the following example code:
#include "pwm_lib.h"
#include "tc_lib.h"
using namespace arduino_due::pwm_lib;
#define PWM_PERIOD_PIN_35 5000 // hundredth of usecs (1e-8 secs)
#define PWM_DUTY_PIN_35 600 // 100 msecs in hundredth of usecs (1e-8 secs)
#define PWM_PERIOD_PIN_42 5000 // 100 usecs in hundredth of usecs (1e-8 secs)
#define PWM_DUTY_PIN_42 1000 // 10 usec in hundredth of usecs (1e-8 secs)
#define CAPTURE_TIME_WINDOW 15000000 // usecs
#define DUTY_KEEPING_TIME 30000 // msecs
// defining pwm object using pin 35, pin PC3 mapped to pin 35 on the DUE
// this object uses PWM channel 0
pwm<pwm_pin::PWMH0_PC3> pwm_pin35;
// defining pwm objetc using pin 42, pin PA19 mapped to pin 42 on the DUE
// this object used PWM channel 1
pwm<pwm_pin::PWMH1_PA19> pwm_pin42;
// To measure PWM signals generated by the previous pwm objects, we will use
// capture objects of tc_lib library as "oscilloscopes" probes. We will need
// a different capture object for measuring each signal: capture_tc0 and
// capture_tc1, respectively.
// IMPORTANT: Take into account that for TC0 (TC0 and channel 0) the TIOA0 is
// PB25, which is pin 2 for Arduino DUE, so capture_tc0's capture pin is pin
// 2. For capture_tc1 (TC0 and channel 1), TIOA1 is PA2 which is pin A7
// (ANALOG IN 7). For the correspondence between all TIOA inputs for the
// different TC module channels, you should consult uC Atmel ATSAM3X8E
// datasheet in section "36. Timer Counter (TC)"), and the Arduino pin mapping
// for the DUE.
// All in all, to meausure pwm outputs in this example you should connect the
// PWM output of pin 35 to capture_tc0 object pin 2, and pwm output pin 42 to
// capture_tc1's pin A7.
capture_tc0_declaration(); // TC0 and channel 0
auto& capture_pin2=capture_tc0;
capture_tc1_declaration(); // TC0 and channel 1
auto& capture_pinA7=capture_tc1;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
// initialization of capture objects
capture_pin2.config(CAPTURE_TIME_WINDOW);
capture_pinA7.config(CAPTURE_TIME_WINDOW);
// starting PWM signals
pwm_pin35.start(PWM_PERIOD_PIN_35,PWM_DUTY_PIN_35);
pwm_pin42.start(PWM_PERIOD_PIN_42,PWM_DUTY_PIN_42);
Serial.println("================================================");
Serial.println("============= pwm_lib - basic_test =============");
Serial.println("================================================");
for(size_t i=0; i<=pwm_core::max_clocks;i++)
{
Serial.print("maximum period - clock "); Serial.print(i); Serial.print(": ");
Serial.print(pwm_core::max_period(i),12); Serial.println(" s.");
}
Serial.println("================================================");
}
// FIX: function template change_duty is defined in
// #define to avoid it to be considered a function
// prototype when integrating all .ino files in one
// whole .cpp file. Without this trick the compiler
// complains about the definition of the template
// function.
#define change_duty_definition \
template<typename pwm_type> void change_duty( \
pwm_type& pwm_obj, \
uint32_t pwm_duty, \
uint32_t pwm_period \
) \
{ \
uint32_t duty=pwm_obj.get_duty()+pwm_duty; \
if(duty>pwm_period) duty=pwm_duty; \
pwm_obj.set_duty(duty); \
}
// FIX: here we instantiate the template definition
// of change_duty
change_duty_definition;
void loop() {
// put your main code here,to run repeatedly:
delay(DUTY_KEEPING_TIME);
uint32_t status,duty,period;
Serial.println("================================================");
status=capture_pin2.get_duty_and_period(duty,period);
Serial.print("[PIN 35 -> PIN 2] duty: ");
Serial.print(
static_cast<double>(duty)/
static_cast<double>(capture_pin2.ticks_per_usec()),
3
);
Serial.print(" usecs. period: ");
Serial.print(period/capture_pin2.ticks_per_usec());
Serial.println(" usecs.");
status=capture_pinA7.get_duty_and_period(duty,period);
Serial.print("[PIN 42 -> PIN A7] duty: ");
Serial.print(
static_cast<double>(duty)/
static_cast<double>(capture_pinA7.ticks_per_usec()),
3
);
Serial.print(" usecs. period: ");
Serial.print(period/capture_pinA7.ticks_per_usec());
Serial.println(" usecs.");
Serial.println("================================================");
// changing duty in pwm output pin 35
change_duty(pwm_pin35,PWM_DUTY_PIN_35,PWM_PERIOD_PIN_35);
// changing duty in pwm output pin 42
change_duty(pwm_pin42,PWM_DUTY_PIN_42,PWM_PERIOD_PIN_42);
}
I am not very good at coding. But can you alter this example to generate 3 independent signals instead of 2.
I don't own an Arduino Due. However, based on my experience with AVR boards, timer registers for PWM generation are mapped to specific pins. This means you will need to consult the datasheet and/or pinout diagram for the Arduino Due to correctly configure the timer registers.
Based on the piece of code that you attached in your latest post, it looks like the registers PWMH0 and PWMH1 are used to produce PWM signals. Since the Arduino Due has 12 PWM pins, there should be four other registers named PWMH2, PWMH3, PWMH4 and PWMH5.
Hi @sagardas007
The following code outputs synchronised, 20kHz PWM signals at 50% duty-cycle on digital pins D9, D8, D7 and D6, which map on to PWMLx channels 4, 5, 6 and 7 respectively. The leading edges of these waveforms are staggered progressively by 1us from each other using the PWM controller's dead-time insertion functionality:
// Enable 4 synchronous PWM channels at 20kHz, stager the leading edges by 1us steps using dead-time insertion
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; // Set the PWM channels as synchronous
PWM->PWM_CH_NUM[0].PWM_CPRD = 4199; // Set the PWM frequency 84MHz/(4199 + 1) = 20kHz for all synchronous channels
for (uint8_t i = 4; i < PWMCH_NUM_NUMBER; i++) // Loop for each PWM channel (4 in total)
{
PWM->PWM_CH_NUM[i].PWM_CMR = PWM_CMR_DTE | PWM_CMR_CPRE_CLKA; // Enable dead-time insertion and set the clock source as CLKA for all synchronous channels
PWM->PWM_CH_NUM[i].PWM_CDTY = 2100; // Set the PWM duty cycle to 50% for all channels
PWM->PWM_CH_NUM[i].PWM_DT = PWM_DT_DTL((i - 4) * 84); // Stager the dead-times: 0, 1us, 2us, 3us from the start of the timer cycle
}
PWM->PWM_ENA = PWM_ENA_CHID0; // Enable all synchronous PWM channels
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() {}
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.