Controlling PWM duty cycle on SAM D20 - ASF Programming

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everyone,

 

I want to control the duty cycle of each PWM pulse (PWM frequency 1-4 MHz) generated by SAM D20. Currently the SAM D20 clocked-up to 48Mhz via DFLL. 

Please help...

here is the code


#include "samd20.h"    // board specific header file
#include "asf.h"   // asf header
/* pin definitions for PWM pins defined in the SRC->ASF->SAM0->utils->cmsis->samd20->include->pio->samd20j18.h for TC channel 6 and w(0) */
#define PWM_MODULE  TC3
#define PWM_OUT_PIN  PIN_PA18F_TC3_WO0
#define PWM_OUT_MUX  MUX_PA18F_TC3_WO0
struct tc_module tc_instance;			// Create a module software instance structure for the TC module to store the TC driver state while it is in use

void configure_tc(void)
{
	struct tc_config config_tc;
	tc_get_config_defaults(&config_tc);
	config_tc.counter_size = TC_COUNTER_SIZE_8BIT;
	system_clock_init();
	config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_PWM;
	  config_tc.counter_8_bit.compare_capture_channel[0] = 06;
		config_tc.pwm_channel[0].enabled = true;
		config_tc.pwm_channel[0].pin_out = PWM_OUT_PIN;
		config_tc.pwm_channel[0].pin_mux = PWM_OUT_MUX;
		//config_tc.double_buffering_enabled=true;
		tc_init(&tc_instance, PWM_MODULE, &config_tc);
		tc_set_top_value(&tc_instance,48);
		tc_enable(&tc_instance);
}	

void tc_callback_to_change_duty_cycle(struct tc_module *const module_inst)
{
	static uint16_t i = 0;
	if (i%2 == 0){
		tc_set_compare_value(module_inst, TC_COMPARE_CAPTURE_CHANNEL_0, 12);
	}
	else{
		tc_set_compare_value(module_inst, TC_COMPARE_CAPTURE_CHANNEL_0, 36);
	}
	i += 1;

}

void configure_tc_callbacks(void)
{
	tc_register_callback(&tc_instance,tc_callback_to_change_duty_cycle,TC_CALLBACK_CC_CHANNEL0);
	tc_enable_callback(&tc_instance, TC_CALLBACK_CC_CHANNEL0);
}

int main(void)
{
	configure_tc();
	configure_tc_callbacks();
	while (true) {
	}
}

But I am not able to get control over every pulse. I intend to generate alternate pulses of 25% and 75% duty cycle. But what I am getting is like 3 pulses with 25% then another 2-3 pulses with 75% duty cycle.

 

Thanks

Last Edited: Mon. Oct 9, 2017 - 08:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

The problem here is the callback is being called only once in four cycles.. any solution to correct it???

 

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How many clocks do you estimate it takes to enter the isr, do the processing and exit? I'd suggest you measure it and that should answer the question as to why you can't control the pwm cycle by cycle at >1MHz.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yes it is because of the isr cycles, as per my measurements it is taking around 70 cycles to change the duty cycle. 

Is there any other way to control the duty cycle of each PWM pulse or get over this problem??..

 

Thanks

Last Edited: Fri. Oct 13, 2017 - 10:15 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Currently I am able to control the pulses upto 430 KHz, but not for higher frequency,

 

#include "samd20.h"    // board specific header file
#include "asf.h"   // asf header 
/* pin definitions for PWM pins defined in the SRC->ASF->SAM0->utils->cmsis->samd20->include->pio->samd20j18.h for TC channel 6 and w(0) */
#define PWM_MODULE  TC3   
#define PWM_OUT_PIN  PIN_PA18F_TC3_WO0  
#define PWM_OUT_MUX  MUX_PA18F_TC3_WO0   
struct tc_module tc_instance;			// Create a module software instance structure for the TC module to store the TC driver state while it is in use
static uint16_t i = 0;
void configure_tc(void)
{
	struct tc_config config_tc;
	tc_get_config_defaults(&config_tc);
	config_tc.counter_size = TC_COUNTER_SIZE_8BIT;
	system_clock_init();
	config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_PWM;
	  config_tc.counter_8_bit.compare_capture_channel[0] = 0;
		config_tc.pwm_channel[0].enabled = true;
		config_tc.pwm_channel[0].pin_out = PWM_OUT_PIN;
		config_tc.pwm_channel[0].pin_mux = PWM_OUT_MUX;
		tc_init(&tc_instance, PWM_MODULE, &config_tc);
		tc_set_top_value(&tc_instance,130);
		tc_enable(&tc_instance);			
}	

void tc_callback_to_change_duty_cycle(struct tc_module *const module_inst)
{
	if (i==0){
		tc_set_compare_value(module_inst, TC_COMPARE_CAPTURE_CHANNEL_0, 32);
		i++;
	}
	else{
		tc_set_compare_value(module_inst, TC_COMPARE_CAPTURE_CHANNEL_0, 65);
		i--;
	}
}
	
void configure_tc_callbacks(void)
{
	tc_register_callback(&tc_instance,tc_callback_to_change_duty_cycle,TC_CALLBACK_CC_CHANNEL0);
	tc_register_callback(&tc_instance,tc_callback_to_change_duty_cycle,TC_CALLBACK_OVERFLOW);
	tc_enable_callback(&tc_instance, TC_CALLBACK_CC_CHANNEL0);
	tc_enable_callback(&tc_instance, TC_CALLBACK_OVERFLOW);
}
	
int main(void)
{
	configure_tc();
	configure_tc_callbacks();
	system_interrupt_enable_global();
	while (true) {

	}
}

Please suggest something to control each PWM pulse upto 1MHz

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well the first step is to not use ASF callbacks, i.e., you need to replace the TC interrupt handler in the ASF code with your own that only performs this update.

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lars,

 

Thanks for the suggestion...

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have been trying to increase the frequency at which I can control the duty cycle of each pulse upto 1Mhz. I have optimized the ASF function as in the below code 

 

include "samd20.h"    // board specific header file
#include "asf.h"   // asf header 
/* pin definitions for PWM pins defined in the SRC->ASF->SAM0->utils->cmsis->samd20->include->pio->samd20j18.h for TC channel 6 and w(0) */
#define PWM_MODULE  TC3   
#define PWM_OUT_PIN  PIN_PA18F_TC3_WO0  
#define PWM_OUT_MUX  MUX_PA18F_TC3_WO0   
struct tc_module tc_instance;			// Create a module software instance structure for the TC module to store the TC driver state while it is in use

void configure_tc(void)
{
	struct tc_config config_tc;
	tc_get_config_defaults(&config_tc);
	config_tc.counter_size = TC_COUNTER_SIZE_8BIT;
	system_clock_init();
	config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_PWM;
	  config_tc.counter_8_bit.compare_capture_channel[0] = 65;
		config_tc.pwm_channel[0].enabled = true;
		config_tc.pwm_channel[0].pin_out = PWM_OUT_PIN;
		config_tc.pwm_channel[0].pin_mux = PWM_OUT_MUX;
		tc_init(&tc_instance, PWM_MODULE, &config_tc);
		tc_set_top_value(&tc_instance,130);
		tc_enable(&tc_instance);			
}	


void tc_set_comp_value(const struct tc_module *const module_inst,const enum tc_compare_capture_channel channel_index,const uint32_t compare)
{
	Tc *const tc_module = module_inst->hw;
	tc_module->COUNT8.CC[channel_index].reg =(uint8_t)compare;
}


uint32_t tc_get_comp_value(const struct tc_module *const module_inst)
{
	Tc *const tc_module = module_inst->hw;
	return tc_module->COUNT8.CC[TC_COMPARE_CAPTURE_CHANNEL_0].reg;
}


int main(void)
{
	configure_tc();
	while (true) {
		if (TC_CALLBACK_CC_CHANNEL0 || TC_CALLBACK_OVERFLOW){
			if(tc_get_comp_value(&tc_instance)==50)
				tc_set_comp_value(&tc_instance, TC_COMPARE_CAPTURE_CHANNEL_0, 25);
			else
				tc_set_comp_value(&tc_instance, TC_COMPARE_CAPTURE_CHANNEL_0, 50);
		}
	    }

}

But I am not able to control the duty cycle of each pulse above 450KHz, please suggest any better method..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

is it a good idea to use one-shot mode PWM to solve the problem??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tired working on TCC_QUICK_START example for samd21 & things in there were pretty much straight forward.
one short mode PWM will be a simple solution.

Silly beans are always silly
:)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

anand123456 wrote:

Hello everyone,

I want to control the duty cycle of each PWM pulse (PWM frequency 1-4 MHz) generated by SAM D20. Currently the SAM D20 clocked-up to 48Mhz via DFLL. 

Please help...

Leave TC and use TCC. For single-slope PWM generation, the period time is controlled by PER, while CCx control the duty cycle of the generated waveform output. Not sure if I understand exactly what you want , but your code (callbacks) looks scary. Just configure TCC and than set PER and CCx - 10 lines code. Nothing more is needed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

but unfortunately I am unable to get the TCC module for my board. I try downloading the module using ASF wizard, but could not find it

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not sure what you mean "..I am unable to get the TCC module for my board.." SAMD20 has TCC, ASF is not needed. Just read datasheet and go step by step through configuration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, I thought you were suggesting to use the TCC library in ASF. 
But the problem I am facing with one-shot pwm is that, there is always a big delay between two pulses as I  need to restart the counter after every pulse.

 

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

You are mixing different ttc/tcc modes. One shot is designated for counter mode, not for pwm waveform generation. 

READ!!!datasheet first an than you can ask relevant  questions.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While I have no personal experience with SAMD20 there is no evidence supporting it has the TCC from looking at the datasheet.

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I could not find it too.
But my real problem is controlling each pulse of a hardware pwm above 1-2 Mhz.
I have tried many methods already but the maximum frequency I could achieve till now is around 800 Khz.
when I increase the frequency above that I loose the control over each pulse.
I have tried call backs, toggling pin using counter, one shot mode and restarting the counter after each mode, etc.
Is there any other way??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

hello,

 

I could improve the frequency upto 1.6MHz by writing directly to the registers 

 

#include "samd20.h"    // board specific header file
#include "asf.h"   // asf header
/* pin definitions for PWM pins defined in the SRC->ASF->SAM0->utils->cmsis->samd20->include->pio->samd20j18.h for TC channel 6 and w(0) */
#define PWM_MODULE  TC3
#define PWM_OUT_PIN  PIN_PA18F_TC3_WO0
#define PWM_OUT_MUX  MUX_PA18F_TC3_WO0
struct tc_module tc_instance;			// Create a module software instance structure for the TC module to store the TC driver state while it is in use

void configure_tc(void)
{
	struct tc_config config_tc;
	tc_get_config_defaults(&config_tc);
	config_tc.counter_size = TC_COUNTER_SIZE_8BIT;
	system_clock_init();
	config_tc.wave_generation = TC_WAVE_GENERATION_NORMAL_PWM;
	config_tc.oneshot = true;
	config_tc.counter_8_bit.compare_capture_channel[0] = 0;
	config_tc.pwm_channel[0].enabled = true;
	config_tc.pwm_channel[0].pin_out = PWM_OUT_PIN;
	config_tc.pwm_channel[0].pin_mux = PWM_OUT_MUX;
	tc_init(&tc_instance, PWM_MODULE, &config_tc);
	tc_set_top_value(&tc_instance,24);
	tc_enable(&tc_instance);
}	

void set_duty(uint32_t duty)
{     
     TcCount8 *const tc_module = (&tc_instance.hw->COUNT8);
     tc_module->CC[TC_COMPARE_CAPTURE_CHANNEL_0].reg =duty;  // setting the duty cycle    
     tc_module->CTRLBSET.reg = TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_RETRIGGER_Val);  // resetting the counter
}

int main(void)
{
	configure_tc();
	system_interrupt_enable_global();
	while (true) {
		set_duty(6); // set duty of 25%
		set_duty(12); // set duty of 50%
		set_duty(18); // set duty of 75%
	}
}

Still there is a delay of a few cycles to update the compare register and to restart the tc module. This delay occurs only when I do both (updating the compare value and restarting the counter).  But if I performonly one of them there is not much delay.

And when I reduce the top value this delay increases, so that wont help me to increase the frequency.
Is there any to minimize this delay?? or better way to implement the set duty function. 

Last Edited: Fri. Nov 10, 2017 - 01:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lajon wrote:

While I have no personal experience with SAMD20 there is no evidence supporting it has the TCC from looking at the datasheet.

/Lars

 

 

On Atmels web page for the SAM D family:

  • SAM D20 – Offers a rich set of peripherals, flexibility, and ease-of-use with low power consumption
  • SAM D21 – Upwards compatible from the SAM D20 and adds features such as Full Speed USB, DMA, high-end timers/counters

If "high-end timers/counters" means TCC then that page also implies that the D20 has no TCCs (only TCs).

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]