Offline
Newbie
Karma: 1
Posts: 35
Good Vibes, Bad Vibes, Random Vibe
|
 |
« on: February 04, 2013, 03:20:08 pm » |
My project involves a motor driver requiring a PWM signal at 1khz and an infrared emitter running at 38khz. I'd like the infrared device to run continuously at 38khz.
Right now I set the PWM frequency in the variants.h file. Although this works, it affects all PWM outputs. Is it possible to set a unique frequency for each PWM output, from within a C++ program?
|
|
|
|
|
Logged
|
|
|
|
|
Montpellier, Fr./ Ottawa,Canada
Offline
Newbie
Karma: 0
Posts: 48
|
 |
« Reply #1 on: February 05, 2013, 02:48:14 am » |
you have two choices :
A) you can use PWMC_ConfigureClocks to create two seperate freq clocks B) you can make 1 clock 38 times slower(faster) than the other ... using PWMC_SetPeriod or C) use timers instead of PWM ...
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 1
Posts: 35
Good Vibes, Bad Vibes, Random Vibe
|
 |
« Reply #2 on: February 05, 2013, 04:29:13 am » |
you have two choices :
A) you can use PWMC_ConfigureClocks to create two seperate freq clocks B)...
Do you have a working example with PWMC_ConfigureClocks for the Due? Is this implemented in the variants.h file or directly in a sketch?
|
|
|
|
|
Logged
|
|
|
|
|
Montpellier, Fr./ Ottawa,Canada
Offline
Newbie
Karma: 0
Posts: 48
|
 |
« Reply #3 on: February 05, 2013, 05:13:30 am » |
its just a function:
uint32_t pwm_clk = VARIANT_MCK/2; //42MHz uint16_t pwm_period = 105; //400kHz==2.5us
PWMC_ConfigureClocks (pwm_clk, 0, VARIANT_MCK); PWMC_SetPeriod (PWM, PWM_CHANNEL_4, pwm_period);
is what i currently use .. if you are trying to get as slow as 1khz you will likely need to do a VARIANT_MCK/1024 or so & adjust your period ..
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 1
Posts: 35
Good Vibes, Bad Vibes, Random Vibe
|
 |
« Reply #4 on: February 09, 2013, 05:09:55 am » |
The SetPeriod function for PWM is very peculiar. void PWMC_SetPeriod( Pwm* pPwm, uint32_t ul_channel, uint16_t period)
Although period can be as large as 65535, it seems to work best at 255. At lower values, it wrongly increases the PWM duty cycle even though duty is set with PWMC_SetDutyCycle. At period values much higher than 255, it wrongly decreases duty cycle. What is the effective range for period? What is the effective range for clka in the ConfigureClocks function? Thank you. void PWMC_ConfigureClocks(uint32_t clka, uint32_t clkb, uint32_t mck)
|
|
|
|
|
Logged
|
|
|
|
|
Montpellier, Fr./ Ottawa,Canada
Offline
Newbie
Karma: 0
Posts: 48
|
 |
« Reply #5 on: February 10, 2013, 08:10:55 am » |
manual says: mclk/1024*1/255 is the slowest clock == 84000000/1024/255==321.69....HZ theoreticaly the longest period should be 321.7/65535 =.00491.... hz or 203sec/ pulse... i dislike the clock setting function / you may have better luck seting it explitistly from the associated values (Find your own dividers & send them to the function ) hook a led(& resistor) to pin 9 & try the code below /* This example code is in the public domain. */
// Pin 13 has an LED connected on most Arduino boards. // give it a name: int led = 13; #define PWM_CHANNEL_0 0 #define PWM_CHANNEL_4 4 #define PWM_CHANNEL_5 5 #define PWM_CHANNEL_6 6 int period =2; void startTimer (Tc * tc, uint32_t channel, IRQn_Type irq, uint32_t frequency) { pmc_set_writeprotect (false); pmc_enable_periph_clk ((uint32_t) irq); TC_Configure (tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4); uint32_t rc = (VARIANT_MCK / 128 / frequency) > 2 ? (VARIANT_MCK / 128 / frequency) : 2; //128 because we selected TIMER_CLOCK4 above TC_SetRA (tc, channel, rc / 2); //50% high, 50% low TC_SetRC (tc, channel, rc); TC_Start (tc, channel); tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPCS; tc->TC_CHANNEL[channel].TC_IDR = ~TC_IER_CPCS; NVIC_EnableIRQ (irq); } //-------------------------------------------------------------------------- // the setup routine runs once when you press reset: void setup() { // initialize the digital pin as an output. pinMode(led, OUTPUT); Serial.begin(9600); startTimer(TC1, 0, TC3_IRQn, 1); //PWM config pmc_enable_periph_clk(ID_PWM); PWMC_DisableChannel(PWM, PWM_CHANNEL_4); PWMC_ConfigureClocks(VARIANT_MCK/1024 /255, 0, VARIANT_MCK);
int ulPin = 9; // Setup PWM for this pin PIO_Configure(g_APinDescription[ulPin].pPort, g_APinDescription[ulPin].ulPinType, g_APinDescription[ulPin].ulPin, g_APinDescription[ulPin].ulPinConfiguration); //***************Channel 4 PWMC_ConfigureChannel( (Pwm*) PWM, // pPwm, (uint32_t) PWM_CHANNEL_4 , // ul_channel, (uint32_t) PWM_CMR_CPRE_CLKA , // prescaler, (uint32_t) 0 , // alignment, 0=left alighned (uint32_t) 0 ); // polarity ) PWMC_SetPeriod( (Pwm*) PWM, //pPwm, (uint32_t) PWM_CHANNEL_4,//ul_channel, (uint16_t) period ); PWMC_SetDutyCycle( (Pwm*) PWM ,//pPwm, (uint32_t) PWM_CHANNEL_4,// ul_channel, (uint16_t) period/2 //duty ); PWMC_EnableChannel( (Pwm*) PWM, //pPwm, (uint32_t) PWM_CHANNEL_4// ul_channel ) ; }
//*******************************************************8 volatile boolean l;
// This function is called every 1 sec. void TC3_Handler() { // You must do TC_GetStatus to "accept" interrupt // As parameters use the first two parameters used in startTimer (TC1, 0 in this case) TC_GetStatus(TC1, 0);
digitalWrite(led, l = !l); period < 4 ? period = 4:period =period << 1; period >65536 ? period = 4 :period; PWMC_SetPeriod( (Pwm*) PWM, //pPwm, (uint32_t) PWM_CHANNEL_4,//ul_channel, (uint16_t) period-1 ); PWMC_SetDutyCycle( (Pwm*) PWM ,//pPwm, (uint32_t) PWM_CHANNEL_4,// ul_channel, (uint16_t) PWM->PWM_CH_NUM[4].PWM_CPRD/2 //duty ); }
// the loop routine runs over and over again forever: void loop() { Serial.println( PWM->PWM_CH_NUM[4].PWM_CPRD); }
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 1
Posts: 35
Good Vibes, Bad Vibes, Random Vibe
|
 |
« Reply #6 on: February 12, 2013, 03:55:31 am » |
Okay, I finally came up with a clean alternate approach for setting up PWM frequencies from within a sketch, for up to two unique frequencies. The trick is to utilize the two PWM clocks (CLKA & CLKB) provided by the SAM3X8E chip. I wrote and enclosed a library (pwm01.h). It includes 4 user functions to: 1) setup PWM resolution, 2) setup PWM pin, frequency & pick clock, 3) write duty cycle, and 4) stop PWM. See example code for usage: #include "C:\Programs\arduino-1.5.1r2\hardware\arduino\sam\libraries\Pwm01\pwm01.h"
void setup() { uint32_t pwm_duty = 32767; uint32_t pwm_freq1 = 2; uint32_t pwm_freq2 = 5000;
// Set PWM Resolution pwm_set_resolution(16);
// Setup PWM Once (Up to two unique frequencies allowed //----------------------------------------------------- pwm_setup( 6, pwm_freq1, 1); // Pin 6 freq set to "pwm_freq1" on clock A pwm_setup( 7, pwm_freq2, 2); // Pin 7 freq set to "pwm_freq2" on clock B pwm_setup( 8, pwm_freq2, 2); // Pin 8 freq set to "pwm_freq2" on clock B pwm_setup( 9, pwm_freq2, 2); // Pin 9 freq set to "pwm_freq2" on clock B // Write PWM Duty Cycle Anytime After PWM Setup //----------------------------------------------------- pwm_write_duty( 6, pwm_duty ); // 50% duty cycle on Pin 6 pwm_write_duty( 7, pwm_duty ); // 50% duty cycle on Pin 7 pwm_write_duty( 8, pwm_duty ); // 50% duty cycle on Pin 8 pwm_write_duty( 9, pwm_duty ); // 50% duty cycle on Pin 9
delay(30000); // 30sec Delay; PWM signal will still stream // Force PWM Stop On All Pins //----------------------------- pwm_stop( 6 ); pwm_stop( 7 ); pwm_stop( 8 ); pwm_stop( 9 ); }
void loop() { }
The pwm01.h library and example code were tested in IDE 1.5.1r2. Additional notes on this library: - Applies to Arduino-Due board, PWM pins 6, 7, 8 & 9. - Libary Does not operate on the TIO pins. - Unique frequencies set via PWM Clock-A ("CLKA") and Clock-B ("CLKB") Therefore, up to two unique frequencies allowed. - Set max duty cycle counts (pwm_max_duty_Ncount) equal to 255 per Arduino approach. This value is best SUITED for low frequency applications (2hz to 40,000hz) such as PWM motor drivers, 38khz infrared transmitters, etc. - Future library versions will address high frequency applications. - Arduino's "wiring_analog.c" function was very helpful in this effort.
|
|
|
|
« Last Edit: February 12, 2013, 04:02:22 am by randomvibe »
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 15
|
 |
« Reply #7 on: March 18, 2013, 02:27:12 pm » |
Okay, I finally came up with a clean alternate approach for setting up PWM frequencies from within a sketch, for up to two unique frequencies. The trick is to utilize the two PWM clocks (CLKA & CLKB) provided by the SAM3X8E chip.
I wrote and enclosed a library (pwm01.h). It includes 4 user functions to: 1) setup PWM resolution, 2) setup PWM pin, frequency & pick clock, 3) write duty cycle, and 4) stop PWM. See example code for usage:
The pwm01.h library and example code were tested in IDE 1.5.1r2. Additional notes on this library: - Applies to Arduino-Due board, PWM pins 6, 7, 8 & 9.
Do you know how use that to get higher frequencies like 25MHz?
|
|
|
|
|
Logged
|
|
|
|
|
Montpellier, Fr./ Ottawa,Canada
Offline
Newbie
Karma: 0
Posts: 48
|
 |
« Reply #8 on: March 20, 2013, 03:13:23 am » |
Do you know how use that to get higher frequencies like 25MHz?
not possible must beable to divide 84MHz by an integer so the limits are 42MHz , 28, 21, 16.8, 14, 12, 10.5 , ....
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 15
|
 |
« Reply #9 on: March 20, 2013, 11:07:12 am » |
I have been using the pmc but i cannot get a clean square wave on higher frequencies, how can i use the PMW to generate 21MHz?
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 9
|
 |
« Reply #10 on: March 29, 2013, 04:56:04 am » |
8-bit resolution gives you a maximum PWM frequency of 84000000 Hz (MCK) / 255 = 329411 Hz 6-bit resolution a max of 84Mhz/63 = 1,3Mhz etc..
|
|
|
|
« Last Edit: March 29, 2013, 07:56:04 am by vorkiej »
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 1
|
 |
« Reply #11 on: April 18, 2013, 03:40:34 am » |
Hiya all, I have been working on something along the same lines. I was hoping to build a function to set 1 or more of the three timers to produce a clock pulse of 100Hz to 2MHz. Built a library and a function using the AT91 and sam3x8w datasheets provided by Atmel. Register_Deff.h //Software
//Define control registers locations.
/////////////////////////////////////////////////////////////////// // Timers ///////////////////////////////////////////////////////////////////
//For Timer 1
#define TC1_CCR ((volatile unsigned int *) 0x40080000) #define TC1_CMR ((volatile unsigned int *) 0x40080004) #define TC1_RA ((volatile unsigned int *) 0x40080014) #define TC1_RB ((volatile unsigned int *) 0x40080018) #define TC1_RC ((volatile unsigned int *) 0xFFFE005C)
//For Timer 2
#define TC2_CCR ((volatile unsigned int *) 0x40080040) #define TC2_CMR ((volatile unsigned int *) 0x40080044) #define TC2_RA ((volatile unsigned int *) 0x40080054) #define TC2_RB ((volatile unsigned int *) 0x40080058) #define TC2_RC ((volatile unsigned int *) 0x4008005C)
//For Timer 3
#define TC3_CCR ((volatile unsigned int *) 0x40080080) #define TC3_CMR ((volatile unsigned int *) 0x40080084) #define TC3_RA ((volatile unsigned int *) 0x40080094) #define TC3_RB ((volatile unsigned int *) 0x40080098) #define TC3_RC ((volatile unsigned int *) 0x4008009C)
//////////////////////////////////////////////////////////////////// // General Ports ////////////////////////////////////////////////////////////////////
#define PortA_DIR ((volatile unsigned int *) 0x400E0E04) #define PORTB_DIR ((volatile unsigned int *) 0x400E1004) #define PORTC_DIR ((volatile unsigned int *) 0x400E1204) #define PORTD_DIR ((volatile unsigned int *) 0x400E1404) #define PORTE_DIR ((volatile unsigned int *) 0x400E1604) #define PORTF_DIR ((volatile unsigned int *) 0x400E1804)
Function int Pulse(Timer_ID, Frequency, Pulse_Width) { /* This function will put out two PWM signals out of TIOAx (Desired pulse width),TIOBx (Always set to 50%). This is a hardware function, meaning once set it will have no effect on the external program. Unless the external program trys to use the timer that has been initialised here */
//* TC_CMR: Timer Counter Channel Mode Register Bits Definition
unsigned int TC_CLKS_MCK2 = 0x0, TC_EEVT_XC0 = 0x400, TC_CPCTRG = 0x4000, TC_WAVE = 0x8000, TC_ACPA_TOGGLE_OUTPUT = 0x30000, TC_ACPC_TOGGLE_OUTPUT = 0xC0000, TC_ASWTRG_SET_OUTPUT = 0x400000, TC_BCPB_TOGGLE_OUTPUT = 0x3000000, TC_BCPC_TOGGLE_OUTPUT = 0xC000000, TC_BSWTRG_SET_OUTPUT = 0x40000000;
//* TC_CCR: Timer Counter Control Register Bits Definition unsigned int TC_CLKEN = 0x1, TC_CLKDIS = 0x2, TC_SWTRG = 0x4;
// Porting int PIOTIOA1 = 4, // Timer 1 Signal A PIOTIOB1 = 5; // Timer 1 Signal B
*TC1_CCR = TC_CLKDIS ; // Disable the Clock Counter
*PortA_DIR = (1<<PIOTIOA1) | (1<<PIOTIOB1) ; // Define TIOA1 and TIOB1 as peripheral
//* Compare registers initialization RC_Value = (42000000)/Frequency ; /* MCK/2/Frequency =>PWM generation Frequency */ RB_Value= RC_Value/2; /* 50% duty cycle on TIOB1 */ RA_Value = RC_Value*Pulse_Width; /* Desired Duty Cycle on TIOA1 */
//Timer Select
switch (Timer_ID) {
case 1: *TC1_CMR = TC_BSWTRG_SET_OUTPUT | /* BSWTRG : software trigger set TIOB */ TC_BCPC_TOGGLE_OUTPUT | /* BCPC : Register C compare toggle TIOB */ TC_BCPB_TOGGLE_OUTPUT | /* BCPB : Register B compare toggle TIOB */ TC_ASWTRG_SET_OUTPUT | /* ASWTRG : software trigger set TIOA */ TC_ACPC_TOGGLE_OUTPUT | /* ACPC : Register C compare toggle TIOA */ TC_ACPA_TOGGLE_OUTPUT | /* ACPA : Register A compare toggle TIOA */ TC_WAVE | /* WAVE : Waveform mode */ TC_CPCTRG | /* CPCTRG : Register C compare trigger enable */ TC_EEVT_XC0 | /* EEVT : XC0 as external event (TIOB=output) */ TC_CLKS_MCK2 ; /* TCCLKS : MCK / 2 */
*TC1_RC = RC_Value ; /* PWM generation */ *TC1_RB = RB_Value ; /* duty cycle on TIOB1 */ *TC1_RA = RA_Value ; /* duty cycle on TIOA1 */
break;
case 2: *TC2_CMR= TC_BSWTRG_SET_OUTPUT | /* BSWTRG : software trigger set TIOB */ TC_BCPC_TOGGLE_OUTPUT | /* BCPC : Register C compare toggle TIOB */ TC_BCPB_TOGGLE_OUTPUT | /* BCPB : Register B compare toggle TIOB */ TC_ASWTRG_SET_OUTPUT | /* ASWTRG : software trigger set TIOA */ TC_ACPC_TOGGLE_OUTPUT | /* ACPC : Register C compare toggle TIOA */ TC_ACPA_TOGGLE_OUTPUT | /* ACPA : Register A compare toggle TIOA */ TC_WAVE | /* WAVE : Waveform mode */ TC_CPCTRG | /* CPCTRG : Register C compare trigger enable */ TC_EEVT_XC0 | /* EEVT : XC0 as external event (TIOB=output) */ TC_CLKS_MCK2 ; /* TCCLKS : MCK / 2 */ *TC2_RC = RC_Value ; *TC2_RB = RB_Value ; *TC2_RA = RA_Value ;
break;
case 3: *TC3_CMR= TC_BSWTRG_SET_OUTPUT | /* BSWTRG : software trigger set TIOB */ TC_BCPC_TOGGLE_OUTPUT | /* BCPC : Register C compare toggle TIOB */ TC_BCPB_TOGGLE_OUTPUT | /* BCPB : Register B compare toggle TIOB */ TC_ASWTRG_SET_OUTPUT | /* ASWTRG : software trigger set TIOA */ TC_ACPC_TOGGLE_OUTPUT | /* ACPC : Register C compare toggle TIOA */ TC_ACPA_TOGGLE_OUTPUT | /* ACPA : Register A compare toggle TIOA */ TC_WAVE | /* WAVE : Waveform mode */ TC_CPCTRG | /* CPCTRG : Register C compare trigger enable */ TC_EEVT_XC0 | /* EEVT : XC0 as external event (TIOB=output) */ TC_CLKS_MCK2 ; /* TCCLKS : MCK / 2 */
*TC3_RC = RC_Value ; *TC3_RB = RB_Value ; *TC3_RA = RA_Value ;
break; default:
break; }
//Turn it all on *TC1_CCR = TC_CLKEN ; /* Enable the Clock counter */ *TC1_CCR = TC_SWTRG ; /* Trig the timer */ }
Though I ran into a couple of problems: -Everyone's code I have seen so far has been using 16 bit timers for the AT91 (including Atmel's), though by the looks of the sam3x8e data sheet it has 32 bit registers (timer and compare registers). This would explain the constant high output I am getting. The Due should be able to produce hardware driven PWM frequencies into the MHZ (havn't seen any posts on it yet), even if they are not perfectly clean. Can someone please point me in the right direction, is the coding or my text book reading wrong.
|
|
|
|
« Last Edit: April 29, 2013, 01:53:39 am by MickD »
|
Logged
|
|
|
|
|
|