|
|
View previous topic :: View next topic |
Author |
Message |
J.H.Sun
Joined: 27 Jun 2011 Posts: 4
|
3 interruptions' conflicts |
Posted: Sun Jul 10, 2011 1:35 am |
|
|
Dear all
I use a pic18f458 to decode PWM signals from flight remote controller and tachometer. Also taking it to Receive the data from force gauge. That is I use " 3 " interruption simultaneously to decode the PWM, RPM and force. Now the execution of code is OK, but the accuracy is not well. When I just use CCP1 and CCP2 without using RDA,the RPM and PWM's accuracy is acceptable. While, if taking CCP1, CCP2 and RDA simultaneously to decode the signal, the error here will enlarge. The error here is likely that the true value is 2500, although the PIC can calculate 2500, but it will "jump" to 1500 very quick and back to 2500 repeatly. The frequency is not low. Therefore, I try to ask for help how to solve these kind of problems. I just think of tip if I could know the start time and end time of each interruption, maybe avoiding the conflict between 3 interruption is possible. But, how I could get the time ?? If different input signal, is it the same ??
Code: |
#include <18f458.h>
#include <stdlib.h>
#fuses HS,WDT1
#use delay(clock = 20000000)
#use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7)
#priority CCP1,CCP2,RDA
#define bsize 128
int8 CCP1_flg = 0,CCP1A_flg=0,CCP2_flg =0,CCP2A_flg=0,n,out[13],i,buffer[bsize],len=0,RDA_flg=0,ii ;
int16 rate_ctrl=0;
int32 pwm_ttick = 0 ,rpm=0,rpm_ttick =0,force=0 ;
int32 pwm=0,period=0,rpm_ttick1=0;
char data[4];
void rcv()
{
int8 buf[20], ii, jj ;
for(ii = 0 ; ii < len ; ++ii)
{
for(jj = 0 ; jj < 15 ; jj++)
buf[jj] = buf[jj+1] ;
buf[15] = buffer[ii] ;
if(buf[1] == '4' && buf[2] == '1' &&
buf[3] == '5' && buf[4] == '5' &&
buf[6] == '2' && buf[7] == '0' &&
buf[8] == '0' && buf[9] == '0' &&
buf[10] == '0')
{
data[0] = buf[11] ;
data[1] = buf[12] ;
data[2] = buf[13] ;
data[3] = buf[14] ;
if(buf[5] == '0')
force = atof(data) ;
else if(buf[5] == '1')
force = -atof(data) ;
}
}
for(ii = 0 ; ii < bsize ; ++ii )
buffer[ii] = 0;
len = 0;
}
#INT_CCP1
void CCP1_isr()
{
if(CCP1_flg == 0)
{
set_timer1(0) ;
CCP1_flg = 1 ;
setup_ccp1(CCP_CAPTURE_FE) ;
}
else if(CCP1_flg == 1)
{
pwm_ttick = get_timer1();
CCP1_flg = 0 ;
CCP1A_flg =1 ;
setup_ccp1(CCP_CAPTURE_RE) ;
}
}
#INT_CCP2
void CCP2_isr()
{
if(CCP2_flg ==0)
{
set_timer3(0) ;
CCP2_flg =1 ;
}
else if(CCP2_flg ==1)
{
rpm_ttick = get_timer3();
CCP2_flg =0;
CCP2A_flg =1;
}
}
#INT_RDA
void RDA_isr(void)
{
buffer[len] = getc() ;
++len ;
if(len >= 16)
rcv() ;
}
void main()
{
for(n=0;n<13;n++)
{
out[n]=0;
}
setup_wdt(WDT_ON) ;
setup_ccp1(CCP_CAPTURE_RE) ;
setup_ccp2(CCP_CAPTURE_RE) ;
setup_timer_1(T1_INTERNAL|T1_DIV_BY_4) ;
setup_timer_3(T1_INTERNAL|T1_DIV_BY_4) ;
enable_interrupts(INT_CCP1) ;
enable_interrupts(INT_CCP2) ;
enable_interrupts(INT_RDA) ;
enable_interrupts(GLOBAL) ;
while(TRUE)
{
restart_wdt();
if(CCP1A_flg ==1)
{
pwm = pwm_ttick *8;//0.1us
CCP1A_flg =0;
}
else if(CCP2A_flg ==1)
{
rpm_ttick1=rpm_ttick*8;
rpm = 600000000/rpm_ttick1 ;
CCP2A_flg =0 ;
}
out[0]=252;
out[1]=253;
out[2]=0;
out[3]= pwm/65536;
out[4]=(pwm%65536)/256;
out[5]=(pwm%65536)%256;
out[6]=rpm/256 ;
out[7]=rpm%256 ;
out[8]=force/256;
out[9]=force%256;
out[10]=0;
out[11]=254;
out[12]=255;
//printf("%ld,%ld,%ld\r\n",rpm_ttick,rpm,rpm_ttick1);
if(rate_ctrl ==5200)//20Hz(10750)
{
for(i=0;i<13;i++)
{
printf("%c",out[i]);
}
rate_ctrl = 0 ;
}
rate_ctrl++ ;
}
}
|
Last edited by J.H.Sun on Sun Jul 10, 2011 5:17 pm; edited 2 times in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Sun Jul 10, 2011 2:07 am |
|
|
Critical thing is always 'keep interrupt handlers quick'.
Second thing is to understand that 'priority' does basically nothing. It sets the order in which interrupts are _polled_, but once you are inside an interrupt, unless you use hardware priority, the handler you are in, has to complete, _before_ another can occur. This is why it is always vital to get out of the handlers ASAP.
Now, your CCP1, and CCP2 handlers are quick. Your RDA handler is quick for 15 characters, but the 16'th, is appalling. You have loops inside loops (always slow), and at the centre, atof. I hate to think how long this routine will be, but, given that atof, tries to convert a value to 'float', and therefore can involve float maths. Uurgh!...
Repeat, and repeat again. Do as little as possible in interrupt handlers. If you want to do complex things, set a flag, and do these _outside_.
Rewrite your serial receive routine, in a manner like the EX_SISR. _Just_ buffer the data. Set a flag when you have a complete 'packet', and do the handling in the main code, when this flag is set.
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sun Jul 10, 2011 7:27 am |
|
|
Just a few more remarks to save you future trouble:
1) Add the NOLVP fuse. Without this setting a glitch on the LVP pin (A6) will reset your processor.
2) Add the 'errors' directive to the #use rs232 line. This will have the compiler to add extra code to restart a stalled UART.
3) The atof function requires a zero terminated string. I doubt this is the case in your program.
In general, when working with embedded software it is better to never use floating point. Simple processors like the PIC have no floating point hardware and therefor it takes an incredible lot of processor instructions to do the math. Much better is to use a method called 'Fixed point integer' where you do calculations with normal integers and pretend there is a decimal point. For example, when you want to use the value 1.23 you do the math with the integer 123, and only in the final step when showing the calculation to the user you will insert the graphical '.'. The CCS compiler has the convenient %w parameter in the printf function to insert the dot sign for you. Fixed point integers are much faster than floating point, require less memory and are sometimes even more accurate (and sometimes less accurate).
Code: | for(ii = 0 ; ii < bsize ; ++ii )
buffer[ii] = 0; | Easier to read, and faster executing, is:
Code: | memset(buffer, bsize, 0); |
Code: | out[3]= pwm/65536;
out[4]=(pwm%65536)/256;
out[5]=(pwm%65536)%256; | The compiler is probably smart enough to optimize this code, but why take the chance if you can write easier to read code that is also lightning fast? Code: | out[3] = make8(pwm, 2); // Extract byte 2
out[4] = make8(pwm, 1); // Extract byte 1
out[5] = make8(pwm, 0); // Extract byte 0 | Same can be done for the rpm and force variables. |
|
|
|
|
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
|