some questions on due pwm

The pwm section is quite large in the data sheet and I was hoping some one with some experience can help me decide if pwm would cut it for my needs or not. I would prefer to use registers instead of a library for performance reasons. These are some of my key concerns I have after a brief reading:

Can it handle the timings. 3us to 2sec off time or on time and any combination in between for all 8 channels. Whats the best frequency it can do with all 8 channels operational.

Is it possible to enable/disable the pwm on a given channel. Is it a single register operation to manipulate the 8ch enable/disable. I did see PWM_ENA and PWM_DIS in the ds and I think thats the one to use. Is there any other register operation required to start stop the pwm. all the configuration is one time option I guess. I dont need pwm output running all the time, basically I want to be able to stop it and restart with minimal memory operations.

Does it use interrupts, not for additional features but for basic operation, meaning I can still get the pwm to work without interrupts and processor support.

Another quick question, I was going through the PWM channel list of the sam, I dont find PWMH7. I found PWML7, but no H7. Does t mean that the complimentary pwm channels that are usually available are not available for channel 8

I am running this program to test the pwm. Once I disable a pwm channel, its output goes haywire. It completes the current cycle, but then instead of it becoming 0v, it starts reducing very slowly. I even tried a 4.7k pulldown resistor, but it did not help. The outputs are connected to pin 34 and 35, only one of them is working. these are supposed to be complimentary but only one is working so far.

// include all arduino libraries here.. these are only accepted from ino files.
#include <Arduino.h>
#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>

void setup() {
  // put your setup code here, to run once:
  pmc_enable_periph_clk (PWM_INTERFACE_ID) ;  // turn on clocking to PWM unit

  PWMC_ConfigureChannel (PWM, 0, 1, 0, PWM_CMR_CPOL) ; // PWM channel 0, clock = MCLK/2 = 42MHz
  PWMC_SetPeriod (PWM, 0, 700) ;  // period = 700 pwm clocks (60kHz)
  PWMC_SetDutyCycle (PWM, 0, 80*700/100) ;  // duty set to 80%
  PWMC_EnableChannel (PWM, 0) ;   // enable

  // Configure pin 34 (PC2) to be driven by peripheral B (PWM channel 0 L)
  // enable pin PC02 and PC03, they are complimentary
  PIOC->PIO_PDR = 0xC ;  // disable PIO control
  PIOC->PIO_IDR = 0xC ;   // disable PIO interrupts
  PIOC->PIO_ABSR |= 0xC ;  // switch to B peripheral


}

void loop() {
  // put your main code here, to run repeatedly:


// From these settings, I got these numbers from the scope - 13.3us on time and 3.32us off time
//

  PWMC_EnableChannel (PWM, 0) ;   // enable
  delayMicroseconds(100);
  PWMC_DisableChannel (PWM, 0) ;   // enable
  delayMicroseconds(100);
	
}

scope image is attached.

Edit: I figured why the complimentary channel doesnt work, its because that output is not enabled. Once I do that, it works fine. Edited the program for this.

photo.JPG

could someone please verify for me if its the same issue on their due also and not specific to mine. Looks like a processor bug though, I opened a ticket with atmel. Is it possible to tame this waveform with some external components?

The SAM3X is a true PWM powerhouse. I can get 14 PWM channels at high speed (dozens of kHz) with 8 bit. No problem. The highest speed you can get at 8 bit is 330 kHz afaik. And pulses can be extremely short (1 MCU clockcycle if necessary). You can increase frequency if you sacrifice PWM resolution. 7 bit will yield 660 kHz, for instance.

You can do PWM with the true PWM pins, but also with the TC pins. Complementary PWM is very powerful. Deadtime can be set independently basically creating twice the amount of independent PWM, although resolution gets halved. But then you have at least 22 independent PWMs. Maybe more.. but I forgot if TC could also do complementary mode (i thought it could). What I know for sure is that TC uses half the MCU clock.. so only 42 MHz instead of 84.

PWMH7 is only present on the SAM3 217 pin package and the DUE only has 144 pins. And PIO_PC20B_PWMH4 is not even connected. to the pins.. oh well. still enough of these buggers.

That a pin starts reducing very slowly smells like it's being set to high impedance to me. That means it's configured as an input, not as an output. You might have to call PIO_Configure() for that pin.. Also, it's probably better to configure the duty cycle from a TC / PWM interrupt as this can be done completely bumpless.

The SAM3X is an amazing piece of hardware.

its very powerful indeed. tc pwm might be even better, but it has only 6 channels available and I need 8 pwm channels.

I could solve the issue by putting a 10k resistor as a pull down. Dont know why previously the 4.7k didnt work, it should have, probably a loose connection on the breadboard.

I of course ran into a different problem now.

My application is like this. I monitor a input trigger line. When it goes up, I need to generate a single pulse of predetermined freq on an output pin.

So what I do is like this -

do all the setup for the pwm configuration, including setting the timings etc.
infinite loop:
read trigger
if (trigger has gone up)
{
enable pwm channel
disable pwm channel
}
goto infinite loop

As per the documentation, when I enable a pwm channel, its supposed to start immediately. and when I do disable on a pwm channel, it will complete the current cycle and then stop. So if I do a enable and disable immediately, it should give me exactly one pulse.

The code is attached here.

#include <Arduino.h>
#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>

// we use pin 34 and 35, which is pc02 and pc03
#define BITMAP 0x0C

// lets use PA0 for the trigger, which is pin CANTX0 or D69
#define TRIGGER_MASK 0x1

#define PWM_TOTAL_PERIOD 7000
#define PWM_DUTY_CYCLE 100
// this sets the clock. e.g. prescalar = 1 is MCK/2, prescalar = 6 is MCK/64
#define CLOCK_PRESCALAR 0x6

void setup()
{
	Serial.begin(115200);
	Serial.println("inside setup");

	// enable clock for inputs or it wont work
	pmc_enable_periph_clk(ID_PIOA);
	// turn on clocking to PWM unit
	pmc_enable_periph_clk (ID_PWM) ;  

	// lets set the trigger input register for gpio and input
	REG_PIOA_PER = TRIGGER_MASK;
	REG_PIOA_ODR = TRIGGER_MASK;

	// Configure pin 34 (PC2) to be driven by peripheral B (PWM channel 0 L)
	// enable pin PC02 and PC03 (pin 35), they are complimentary
	REG_PIOC_OER = 0xC;
	REG_PIOC_PDR = 0xC ;  // disable PIO control
	REG_PIOC_ABSR |= 0xC ;  // switch to B peripheral

	REG_PWM_DIS = 0x1; // disable channel 0

	// set polarity to 1 and clock to PRESCALAR
	// set DTHI to 1, so that both complimentary outputs are identical.
	REG_PWM_CMR0 = CLOCK_PRESCALAR | PWM_CMR_DTHI | PWM_CMR_CPOL;

	// set period and duty cycle
	REG_PWM_CDTY0 = PWM_DUTY_CYCLE;
	REG_PWM_CPRD0 = PWM_TOTAL_PERIOD;

	/*
	unsigned int pwm_pin = 34;
	PIO_Configure( g_APinDescription[pwm_pin].pPort,  PIO_OUTPUT_0,  g_APinDescription[pwm_pin].ulPin,  g_APinDescription[pwm_pin].ulPinConfiguration);
	pwm_pin = 35;
	PIO_Configure( g_APinDescription[pwm_pin].pPort,  PIO_OUTPUT_0, g_APinDescription[pwm_pin].ulPin,  g_APinDescription[pwm_pin].ulPinConfiguration);
	*/

	/*
	unsigned int channel = 0;
	PWMC_ConfigureChannelExt( PWM, channel, CLOCK_PRESCALAR, 0, PWM_CMR_CPOL,
                               0, 0, PWM_CMR_DTHI, 0 );

	PWMC_SetPeriod (PWM, channel, PWM_TOTAL_PERIOD) ;  // period = 700 pwm clocks (60kHz)
	*/

	Serial.println("leaving setup");
}

void loop()
{
	unsigned int old_trigger=0;

	INFINITE_LOOP:
		unsigned int new_trigger = (REG_PIOA_PDSR & TRIGGER_MASK);

		// find trigger going from 0 to 1
		boolean ch_triggered = false;
		if (old_trigger == 0 && new_trigger == 1)
			ch_triggered = true;

		if (ch_triggered == true)
			REG_PWM_ENA = 0x1;

		// commenting out this delay has impact
		delayMicroseconds(5);

		old_trigger = new_trigger;

		if (ch_triggered == true)
			REG_PWM_DIS = 0x1;

	goto INFINITE_LOOP;
}

I generate the trigger on uno and feed it to due (thru a voltage divider for 3.3v).

The issue I face is that I dont see one output per trigger. It goes up only once in a while. then I added the 5us delay in the loop. Now it functions better. If I change the timings of the pwm pulse (duty and period) and make it very large (say 100ms, 200ms), the 5us delay is not enough. It keeps missing outputs.

I also tried the code in comments, but didnt make a difference.

How do i make it stable, i.e. whenever I get a trigger, I show a single pulse. Without a delay.

i would not do this in the main loop.. the due has functionality to generate interrupts for every input pin. attachInterrupt() does that for you. it works on any GPIO pin.

by controlling PWM duty cycle you can also turn it off (dutycycle = 0). you can do this in the PWM interrupt handler to set this for the next period.

in short, use interrupts.. everywhere.

for level shifting you use a voltage divider followed by an amplifier?

interrupts are slow for my needs. Some function calls, jumps, pushing or popping some 8 registers etc. My main loop needs to be fast so I cant use interrupts for the trigger.

controlling pwm by modifying the ducty cycle has performance implications in my app. I can switch on and off all 8 channels by using two memory writes. But If I want to control the duty cycle, I will need to read these for all 8 channels from memory and then write it to registers. is there a way to directly enable/disable all 8 channels together. I dont want to control them individually, but by keeping a bitmap and using it to update the registers.

If you describe exactly what you are trying to achieve it might help find a solution
(ie say what you want, not just how you think it can be done....)

I monitor an input line. As soon as it goes up, I wait for a predetermined amount of time and then raise an output line for another different predetermined amount of time. I need to do this for 8 output lines and I need to do it fast.

Do the signals overlap in time or is only one at a time going to happen?

they overlap in time.

doors666:
I monitor an input line. As soon as it goes up, I wait for a predetermined amount of time and then raise an output line for another different predetermined amount of time. I need to do this for 8 output lines and I need to do it fast.

That sounds like it is ideally suited for timer channels. PWM is normally used for continuous output.

Although depending on what "fast" means, it might be better to do in hardware.

i have an 16 channel PWM program running in interrupt @ 50 kHz. this takes about 10% CPU. but this is all internally generated data.. what doors wants is a program that responds to an input.. to be honest i don't know the interrupt latency at all.

but one more thing that is important for doors is PWM enable. is it some kind of masking or does it actually trigger the pwm to enable at that very moment? if it is masking, then the required delay is logical. there may be another way to trigger pwm.. although i don't know it (sorry). the SAM3X user guide is your friend, i guess.

btw.. one very simple solution to your problem would be to just leave PWM and do everything using GPIO. using direct access to the hardware registers is extremely fast (write upto 32 pins in a single instruction!) and also writing your own timing loop (i'm even talking stacking NOPs here) might yield minimum jitter on your output pulses. if your program isn't supposed to do anything else, this may be a fine solution..