|
|
View previous topic :: View next topic |
Author |
Message |
notbad
Joined: 10 Jan 2013 Posts: 68
|
problem in changing spwm amplitude |
Posted: Wed Apr 10, 2013 7:30 am |
|
|
i’m trying to generate a sinusoidal PWM from frequency 1hz to 100hz.
sinusoidal points are updated in int_ccp4 working with timer1
and its overflow rate is determined by pre-calculated ccp4vals selected by adc.
and for frequencies less than 50 hz, amplitude must be weakened wich function vf_calc does that correctly.
the problem is that if freq is less than 50 and vf_calf comes to work, the output frequency is corrupted.
why do you think?
Code: |
#include <18F26K22.h> // ccs v4.130
#device adc=10
#FUSES NOWDT, WDT128, HSH, NOPLLEN, NOFCMEN, NOIESO, PUT, NOBROWNOUT, WDT_NOSLEEP, NOLVP, NOXINST
#use delay(clock=16000000)
//////////////////////////////////////////////////////////////
int8 index=0,duty;
int16 speed=0;
int8 CONST sine[256]={
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c};
int16 CONST ccp4vals[100]={15625,7813,5208,3906,3125,2604,2232,1953,1736,1563,1420,
1302,1202,1116,1042,977,919,868,822,781,744,710,679,651,625,601,579,558,539,521,504,
488,473,460,446,434,422,411,401,391,381,372,363,355,347,340,332,326,319,313,306,300,
295,289,284,279,274,269,265,260,256,252,248,244,240,237,233,230,226,223,220,217,214,
211,208,206,203,200,198,195,193,191,188,186,184,182,180,178,176,174,172,170,168,166,
164,163,161,159,158,156};
int8 vf_calc(int8 sinea, int8 Ain)
{
int16 sine_temp;
int8 sine1;
sine1=((sinea<128)?~sinea:sinea);
sine_temp=sine1<<1;
sine_temp-=255;
sine_temp*=Ain;
sine_temp>>=8;
sine_temp+=255;
sine_temp>>=1;
sine_temp++;
sine1=sine_temp;
if (sinea<128) sine1=~sine1;
return sine1;
}
#int_ccp4
void ccp4_isr(){
++index;
if(speed>=49) //freq >= 50 Hz
duty=sine[index];
else
{
int32 A=0;
A=(((int32)255*(speed))/50);
duty=vf_calc(sine[index],A);
}
set_pwm1_duty(duty);
}
void main()
{
enable_interrupts(INT_CCP4);
enable_interrupts(GLOBAL);
setup_ccp1(CCP_PWM_PLUS_3|CCP_PWM_HALF_BRIDGE|CCP_USE_TIMER1_AND_TIMER2);
setup_ccp4(CCP_COMPARE_RESET_TIMER|CCP_USE_TIMER1_AND_TIMER2);
setup_timer_2(T2_DIV_BY_1,255,1);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_adc_ports(sAN0);
setup_adc(ADC_CLOCK_DIV_32);
set_adc_channel(0);
while(1)
{
delay_ms(10);
speed=read_adc()/10;
if (speed>99)speed=99;
CCP_4=(ccp4vals[speed]);
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Wed Apr 10, 2013 7:41 am |
|
|
/50 is the killer. At 16MHz, this takes nearly 300uSec to execute. The * takes 132uSec. Both unacceptable times. Total over 400uSec....
Now, this is effectively multiplication by 5.1. Speed is always under 100. So if you stay with int16, instead of using int32, and multiply speed by 326, then >>6 (/64), you get effectively *5.09375, taking about 60uSec instead. May be low enough to let it work.
Best Wishes |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Wed Apr 10, 2013 10:02 am |
|
|
TTelmah is pointing you in the direction of much reduced #int time
and very small error
B U T
if you can stand a 2% error -
really cut to the chase and use :
Code: |
// A=(((int32)255*(speed))/50);
A=speed*5;
|
and my gut says says vf_calc could perhaps use a further simplification/tweaking too,
as my head reels just looking at it.
|
|
|
notbad
Joined: 10 Jan 2013 Posts: 68
|
|
Posted: Thu Apr 11, 2013 1:32 am |
|
|
wow thanks
i didn't think those instructions will take that long to execute.
another question is what happens in the processor? we are in the isr routine and another interrupt occurs. does it get serviced? if yes, shouldn't it cause stack overflow and hence device reset? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Thu Apr 11, 2013 1:34 am |
|
|
No.
You are only using one hardware interrupt level.
Second interrupt will be serviced when the first handler finishes.
The manual has a section 'How much time do maths operations take'.
Now there are extensions and caveats. For instance /64, can be done using simple rotations, so this only takes perhaps 5uSec at your clock rate. The compiler is actually 'smart enough', that if you ask for a fixed division by a binary value (2,4,8 etc.), it will substitute a rotation.
Then other things can be much slower than you think, vara=varb, takes just a couple of instructions for an int8 (depends on whether bank switching is involved). However vara=varc[varb];, will typically take nearly twenty instructions (has to do a table lookup. This is where playing with very small programs, and the stopwatch in MPLAB SIM, can be really informative.
Best Wishes |
|
|
notbad
Joined: 10 Jan 2013 Posts: 68
|
|
Posted: Thu Apr 11, 2013 1:01 pm |
|
|
Ttelmah wrote: | No.
You are only using one hardware interrupt level.
Second interrupt will be serviced when the first handler finishes.
|
I'm not talking about a second interrupt. the same interrupt.
ccp4 interrupt occurs > we go to its ISR > ccp4 interrupt occurs again > do we go to the start of the ISR or wait until the current routine is finished and then we go to the start of the ISR?
or is it just missed?
Quote: |
This is where playing with very small programs, and the stopwatch in MPLAB SIM, can be really informative.
|
After you pointed about execution times, I tested some statements as follows and it was very informative:
set timer to 0
do something
value=get timer
printf timer value |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Thu Apr 11, 2013 2:59 pm |
|
|
On the interrupt, neither.
If an interrupt triggers a second time inside itself, the second trigger is lost. For each interrupt, there is only one interrupt flag. This is cleared when the interrupt exits. It doesn't matter if it has triggered ten times, or one....
This is why interrupt handlers should always be as short as possible.
One other way of doing what you want, is to just have a second array. Have the code switch which array it uses based on the number. No maths at all.
Best Wishes |
|
|
notbad
Joined: 10 Jan 2013 Posts: 68
|
|
Posted: Fri Apr 12, 2013 12:15 am |
|
|
thank you Ttelmah
and you asmboy.
it's good to have you guys here. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Fri Apr 12, 2013 2:47 am |
|
|
Besides the good things that have been said about effective arithmetic: It seems completely useless to calculate the magnitude factor A in the ISR... |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|