View previous topic :: View next topic |
Author |
Message |
Ranfo
Joined: 08 Sep 2008 Posts: 38
|
DSPIC33 PWM Setup |
Posted: Thu May 04, 2017 11:27 am |
|
|
I have a 33EP512MC806. Want to use PWM1. Need to be able to change duty. Works great with P18F4525, but now everything is completely different. Documentation is clear as mud and cannot find example for PIC33 device.
Do I use "#use pwm(...) ?
Is there a pin assignment I need to make?
HELP!!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Thu May 04, 2017 11:48 am |
|
|
The examples are your friend. First look at ex_pwm.c.
This shows the setting up of a standard PWM, and includes 16bit versions. (using the 'compare' function).
There is also ex_pwm_pcd.c that shows this "on it's own".
This doesn't actually use the separate PWM hardware.
Then there is the high speed PWM. This is another ability on some chips (yours included). ex_hspwm.c.
The hspwm, is more complex, but more powerful (larger divisors, and faster maximum speeds, multiple complementary pins etc.). It's more like the power pwm on the PIC18's.
Examples have also been posted here on using both of these. |
|
|
Ranfo
Joined: 08 Sep 2008 Posts: 38
|
|
Posted: Thu May 04, 2017 12:53 pm |
|
|
Thank you for the reply, Ttelmah. The examples do not seem to be useful. Here is the setup I derived from ex_hspwm.c. It does not work. I have no idea how to fix this.
Code: |
void setup_pwm1() {
setup_adc_ports(sAN0|sAN1);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel(0);
setup_hspwm_unit(1, HSPWM_ENABLE | HSPWM_REDUNDANT | HSPWM_TIME_BASE_FROM_PHASE_REGS | HSPWM_FAULT_MODE_DISABLED);
setup_hspwm(HSPWM_ENABLED | HSPWM_CLOCK_DIV_BY_2, 0xFFFF);
set_hspwm_phase(1, 800);
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Thu May 04, 2017 1:22 pm |
|
|
If you don't need the extra features of the HSPWM, then it is pointless using it. Less work to just use the output_compare function.
You are not setting a duty.
It is set to drive both PWM1H, and PWM1L. |
|
|
Ranfo
Joined: 08 Sep 2008 Posts: 38
|
|
Posted: Thu May 04, 2017 1:41 pm |
|
|
I hate to have to do it this way, but it seems my only option. :(
Code: |
#INT_TIMER1 // Service Timer 1
void isr_timer1() {
static int8 Count = 0;
if (Count == 0) output_low(DSPEN);
if (Count == gPwmDuty) output_high(DSPEN);
Count++;
} |
|
|
|
Ranfo
Joined: 08 Sep 2008 Posts: 38
|
|
Posted: Thu May 04, 2017 1:43 pm |
|
|
Ttelmah wrote: | If you don't need the extra features of the HSPWM, then it is pointless using it. Less work to just use the output_compare function.
You are not setting a duty.
It is set to drive both PWM1H, and PWM1L. |
The other examples do not even compile. Can you give a setup for DSPIC33 PWM or find problem with my setup? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Thu May 04, 2017 2:09 pm |
|
|
What pin do you actually want to use?.
What frequency do you want?.
What is your clock?. |
|
|
Ranfo
Joined: 08 Sep 2008 Posts: 38
|
|
Posted: Thu May 04, 2017 2:25 pm |
|
|
I am using the PWM1 pin as output.
Frequency approx 200 Hz.
#use DELAY(clock=80M, crystal=16M)
Must be able to vary duty from 0% to 100% |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Fri May 05, 2017 2:07 am |
|
|
There is _not_ a 'PWM1' pin on your chip...
There is a PWM1L, and a PWM1H. Which one do you want to use?.....
Assuming PWM1L, just setting up a standard PWM:
Code: |
#include <33EP512MC806.h>
#device ICSP=1
#use delay(clock=80000000,crystal=16000000)
#FUSES NOWDT //No Watch Dog Timer
#FUSES CKSFSM //Clock Switching is enabled, fail Safe clock monitor is enabled
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOJTAG //JTAG disabled
//Map the first output compare peripheral to the required pin
#PIN_SELECT OC1=PIN_E0
void main()
{
//First a basic PWM on PIN_E0
//To give 200Hz, need to divide by 200000. Maximum divisor is for a single
//timer is 65536, so use /8 prescaler (Fp = Fosc/2), and 25000 counts
setup_timer2(TMR_INTERNAL | TMR_DIV_BY_8, 0x61A7);
setup_compare(1, COMPARE_PWM_EDGE | COMPARE_TIMER2); //setup the compare peripheral
set_pwm_duty(1,12500); //setup for 50:50 output to start
while(TRUE)
{
//User Code
}
}
|
There are 16 output compare peripherals available, that can be mapped to any RP pin. Much the simpler peripheral to use.
Alternatively to use the HS PWM peripheral:
Code: |
void main()
{
setup_hspwm(HSPWM_ENABLED | HSPWM_CLOCK_DIV_BY_16, 0x61A7); //HSPWM enabled, PWM Clock = Fosc/16 (not Fp)
setup_hspwm_unit(1, HSPWM_ENABLE_L | HSPWM_REDUNDANT | HSPWM_TIME_BASE_FROM_PHASE_REGS |
HSPWM_FAULT_MODE_DISABLED, 0, 0); //Frequency set with phase register no dead times
//low output enabled, redundant mode
set_hspwm_phase(1, 0x61A7); //ensure phase register is loaded
set_hspwm_duty(1, 12500); //and setup a 50:50 duty
while(TRUE)
{
//User Code
}
}
|
I hope this is right, but haven't got one of those chips to test.
As I've already said multiple times it is only worth using the HS PWM, if you want it's extra abilities. Programmable dead times, complementary outputs, faults control etc..
It's a pig to get working right, with every chip I've met having differences in the setups that actually work. The standard setup is much easier.
The DSPIC's, don't have a normal 'PWM' as such, they have the output compare function, that can be programmed to give a PWM, and is normally available on many of the pins. |
|
|
Ranfo
Joined: 08 Sep 2008 Posts: 38
|
|
Posted: Fri May 05, 2017 11:00 am |
|
|
Thanks for the help Ttelmah. I have it working, sort of...
Setting up as you have I cannot get the full range of duty cycle. It appears that the second parameter of setup_timer2 has no effect. No matter the number it is like 0xFFFF.
I am using this which gives an output of 610 Hz which will work.
Code: |
setup_timer2(TMR_INTERNAL | TMR_DIV_BY_1);
setup_compare(1, COMPARE_PWM_EDGE | COMPARE_TIMER2);
set_pwm_duty(1, 32000);
|
I did not know of #PIN_SELECT OC1=PIN_E0 because I was looking for PWM not OC1. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Fri May 05, 2017 11:16 am |
|
|
Try with
setup_timer_2(TMR_INTERNAL | TMR_DIV_BY_1, xxxx);
Note the extra underbar.
It should be the same, but worth trying with the alternative syntax.
It should work. If it doesn't, report it to CCS. It works with all the older chips I've tried, and suggests there is a fault in the support for this chip (it is very new...). |
|
|
Ranfo
Joined: 08 Sep 2008 Posts: 38
|
|
Posted: Fri May 05, 2017 12:35 pm |
|
|
setup_timer_2 does not exist in PCWHD.
Maybe it is a compiler bug. I tried to find the actual registers this is supposed to set, but could not. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Fri May 05, 2017 1:29 pm |
|
|
It's the PR2 register.
So something like:
Code: |
#word PR2=getenv("SFR:PR2")
PR2 = 24999;
|
If that doesn't work, then it may be a database error for the actual period register. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Fri May 05, 2017 2:10 pm |
|
|
What compiler version are you on?.
I've just checked with 5.070, and 5.071, and the setup function is correctly loading the PR2 register, and the register is in the correct location.
Code: |
//symbolic
.................... setup_timer2(TMR_INTERNAL | TMR_DIV_BY_8, 0x61A7);
00258: CLR T2CON
0025A: MOV #61A7,W4
0025C: MOV W4,PR2
0025E: MOV #8010,W4
00260: MOV W4,T2CON
//Numeric
.................... setup_timer2(TMR_INTERNAL | TMR_DIV_BY_8, 0x61A7);
00258: CLR 110
0025A: MOV #61A7,W4
0025C: MOV W4,10C
0025E: MOV #8010,W4
00260: MOV W4,110
|
|
|
|
Ranfo
Joined: 08 Sep 2008 Posts: 38
|
|
Posted: Fri May 05, 2017 2:48 pm |
|
|
I am using PCWHD 5.071
Have tried a couple more things, but am moving on.
Using
Code: |
#word SFR_PR1 = 0x0102
#word SFR_PR2 = 0x010C |
Then
Code: |
setup_timer1(TMR_INTERNAL|TMR_DIV_BY_64);
SFR_PR1 = 0x8000; |
I can change the timer1 interrupt frequency.
But this is not doing the same for timer2.
At least as seen in the PWM output.
Code: |
setup_timer2(TMR_INTERNAL | TMR_DIV_BY_1);
SFR_PR2 = 0x8000; // Same as 0xFFFF |
|
|
|
|