SAM4S Xplained Pro PWM Demo

Posted on Sep 12, 2016 | 0 comments


SAM4S Xplained Pro PWM Demo

ASF version

Tested for ASF version 3.31.0

ASF Modules required 

  • PWM – Pulse Width Modulation (driver)

Basic Demo

This program drives a Hitec HS-645MG servomotor. It goes from 0 to 90 to 180 degrees. Then starts again. To drive the servo the PWM has to be to 20ms period, and a duty cycle between .6ms and 2.4ms. Duty cycle is given in percentage with respect to the period, so between 3 and 12%.

This example drives the lines PWM+ and PWM- in the PROTO1 daughter board. Connect the servo signal cable to PWM -. PWM+ and PWM- are complements, meaning whenever PMW+ is high PMW- is low and vice versa.

See PWM Servo Demo.

Code
#include <asf.h>

#define PMWA IOPORT_CREATE_PIN(PIOA, 23)
#define PMWB IOPORT_CREATE_PIN(PIOA, 19)

pwm_channel_t pwm_opts;

int main(void){
	
	sysclk_init();
	board_init();
	
	//PWM Clock options
	pwm_clock_t pwm_clock_opts = {
		.ul_clka = 10000, //10Khz frequency = .1 ms steps
		.ul_clkb = 0,
		.ul_mck =  sysclk_get_main_hz() //main clock speed is 240Mhz!, not 120mhz
	};
	
	//PWM options
	pwm_opts.ul_prescaler = PWM_CMR_CPRE_CLKA;
	pwm_opts.ul_period = 200; //20ms period
	pwm_opts.ul_duty = 0;
	pwm_opts.channel = PWM_CHANNEL_0;
	
	//configure IO pins
	pio_configure_pin( PMWA, PIO_TYPE_PIO_PERIPH_B );
	pio_configure_pin( PMWB, PIO_TYPE_PIO_PERIPH_B );
	
	//Enable PWM clock
	pmc_enable_periph_clk(ID_PWM);
	
	//disable temporarily
	pwm_channel_disable(PWM, PWM_CHANNEL_0);
	
	//Start PWM
	pwm_init( PWM, &pwm_clock_opts );
	pwm_channel_init( PWM, &pwm_opts );
	pwm_channel_enable( PWM, PWM_CHANNEL_0 );
	
	uint32_t duty;
	
	while(true){
		pwm_channel_update_duty( PWM, &pwm_opts, duty = 3 ); // 0 degrees = .6ms high = 3% duty cycle
		delay_ms(3000);
		
		pwm_channel_update_duty( PWM, &pwm_opts, duty = 8 ); // 90 degrees = 1.5ms high = ~8% duty cycle
		delay_ms(3000);
		
		pwm_channel_update_duty( PWM, &pwm_opts, duty = 12 ); // 180 degrees = 2.4ms high = 12% duty cycle
		delay_ms(3000);
	}
}

 

Advanced Demo

The SAM4SD32C PWM controller allows to configure 4 independent PWM channels. Each channel is composed of two complementary outputs, meaning whenever one output is high, the complementary is low. This means, in total, there are 8 outputs: PWML0, PWMH0, PWML1, PWMH1, PWML2, PWMH2, PWML3, and PWMH3, each of which is associated with a pin in the MCU. The SAM4S board only exposes one PWM channel: PWM- and PWM+ in the PROTO1 Board. They correspond to PWML0 and PWMH0 in the MCU, respectively.

The MCU has more peripheral outputs than it has pins, so pins in the MCU are multiplexed. That is, one pin can be configured as different outputs (from different peripherals). The board happens to expose only one PWM channel: PWML0 and PWMH0. Still it is very likely we have access to the rest of the PWM channels. Then one must look at documentation and figure out which outputs, from all the available ones in the MCU, are mapped to pins that are available in the board. This is where to look in the documentation:

  • Section Peripheral Signal Multiplexing on I/O Lines (page 50) SAM4S Datasheet, and
  • Section I/O Extension Headers (page 11) SAM4S Xplained Pro User guide

Trying to minimize soldering, these are the outputs that seem reasonable (we only need PWM-):

  • PWML0 channel -> pin PA19 (peripheral B) -> Accessible through Pin 8 (PWM-) in EXT1
  • PWML1 channel -> pin PA20 (peripheral B) -> Accessible through Pin 8 in EXT2
  • PWML2 channel -> pin PA30 (peripheral A) -> Accessible through PIODC6 in the PIODC Interface
  • PWML3 channel -> pin PC22 (peripheral B) -> Accessible through Pin 6 in EXT3

Only soldering in PIODC6 is required.
The demo consists of moving a servomotor Hitec HS-75BB from 0 to 180 degrees, back and forth.

See PWM 4 Channels Demo.

 

Code
#include <asf.h>

#define PMWL0		IOPORT_CREATE_PIN(PIOA, 19)
#define PMWL0_PERIPHERAL	PIO_TYPE_PIO_PERIPH_B
#define PWML0_CHANNEL		PWM_CHANNEL_0

#define PMWL1		IOPORT_CREATE_PIN(PIOA, 20)
#define PMWL1_PERIPHERAL	PIO_TYPE_PIO_PERIPH_B
#define PWML1_CHANNEL		PWM_CHANNEL_1

#define PMWL2		IOPORT_CREATE_PIN(PIOA, 30)
#define PMWL2_PERIPHERAL	PIO_TYPE_PIO_PERIPH_A
#define PWML2_CHANNEL		PWM_CHANNEL_2

#define PMWL3		IOPORT_CREATE_PIN(PIOC, 22)
#define PMWL3_PERIPHERAL	PIO_TYPE_PIO_PERIPH_B
#define PWML3_CHANNEL		PWM_CHANNEL_3

int main(void){
	
	sysclk_init();
	board_init();
	
	//PWM Clock options for all channels
	pwm_clock_t pwm_clock_opts = {
		.ul_clka = 10000,		//10Khz frequency = .1 ms steps
		.ul_clkb = 0,
		.ul_mck =  sysclk_get_main_hz() //main clock speed is 240Mhz!, not 120mhz
	};
	
	//PWM Options. Each PWM Channel can have different options
	pwm_channel_t pwm_opts = {
		.ul_prescaler = PWM_CMR_CPRE_CLKA, //These options are common
		.ul_period	= 200,	//20ms period  //for all channels.
		.ul_duty = 0					   //
	};
	
	//configure IO pins
	pio_configure_pin( PMWL0, PMWL0_PERIPHERAL );
	pio_configure_pin( PMWL1, PMWL1_PERIPHERAL );
	pio_configure_pin( PMWL2, PMWL2_PERIPHERAL );
	pio_configure_pin( PMWL3, PMWL3_PERIPHERAL );
	
	//enable PWM clock
	pmc_enable_periph_clk(ID_PWM);
	
	//disable temporarily all channels (until everything has been properly setup)
	pwm_channel_disable(PWM, PWML0_CHANNEL);
	pwm_channel_disable(PWM, PWML1_CHANNEL);
	pwm_channel_disable(PWM, PWML2_CHANNEL);
	pwm_channel_disable(PWM, PWML3_CHANNEL);
	
	//start PWM
	pwm_init( PWM, &pwm_clock_opts );
	
	//start channels
	pwm_opts.channel = PWML0_CHANNEL;
	pwm_channel_init( PWM, &pwm_opts );
	pwm_channel_enable( PWM, PWML0_CHANNEL );

	pwm_opts.channel = PWML1_CHANNEL;
	pwm_channel_init( PWM, &pwm_opts );
	pwm_channel_enable( PWM, PWML1_CHANNEL );
	
	pwm_opts.channel = PWML2_CHANNEL;
	pwm_channel_init( PWM, &pwm_opts );
	pwm_channel_enable( PWM, PWML2_CHANNEL );

	pwm_opts.channel = PWML3_CHANNEL;
	pwm_channel_init( PWM, &pwm_opts );
	pwm_channel_enable( PWM, PWML3_CHANNEL );
	
	uint32_t duty;
	
	while(true){
		// ----  0 degrees = .9ms high = 3% duty cycle ----
		duty = 5; // (.9ms / 20ms ) x 100 = 4.5%
		
		//Channel 0
		pwm_opts.channel = PWML0_CHANNEL;
		pwm_channel_update_duty( PWM, &pwm_opts, duty ); 
		
		//Channel 1
		pwm_opts.channel = PWML1_CHANNEL;
		pwm_channel_update_duty( PWM, &pwm_opts, duty );
		
		//Channel 2
		pwm_opts.channel = PWML2_CHANNEL;
		pwm_channel_update_duty( PWM, &pwm_opts, duty );
		
		//Channel 3
		pwm_opts.channel = PWML3_CHANNEL;
		pwm_channel_update_duty( PWM, &pwm_opts, duty );
		
		delay_ms(3000);
		
		// ---- 180 degrees = 2.1ms high = 12% duty cycle ----
		duty = 10; // (2.1ms / 20ms ) x 100 = 10.5%
		
		//Channel 0
		pwm_opts.channel = PWML0_CHANNEL;
		pwm_channel_update_duty( PWM, &pwm_opts, duty ); 
		
		//Channel 1
		pwm_opts.channel = PWML1_CHANNEL;
		pwm_channel_update_duty( PWM, &pwm_opts, duty ); 
		
		//Channel 2
		pwm_opts.channel = PWML2_CHANNEL;
		pwm_channel_update_duty( PWM, &pwm_opts, duty );
		
		//Channel 3
		pwm_opts.channel = PWML3_CHANNEL;
		pwm_channel_update_duty( PWM, &pwm_opts, duty );
		
		delay_ms(3000);
	}
}

Submit a Comment

Your email address will not be published. Required fields are marked *