![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
using mulitple interrupts.... |
Posted: Mon Dec 12, 2005 8:54 am |
|
|
I'm really new to using interrupts so any answer is welcome and no response would be too simple or basic.
I'm writing code to operate a balancing robot and want to have at least 3 and possibly 4 interrupt driven functions. Right now I've got 2, each of which works well by itself but the timing in one goes way out if both are in the program at the same time. I guess that makes sense and an obvious solution would be to add another timing chip. However, I'd like to try to solve the problem with s/w...there are still a lot of resources avaiable on my 16F877.
One isr calculates the period and duty cycle of an accelerometer. It gives me readings from -1g to +1g based on the fwd/backwd tilt of the robot. Code: | #int_ccp1 // Calculate duty cycle and period of Accelerometer output on X-axis
void accelerometer_isr(void) {
static long adxl_last_rise;
if(adxl_waiting_for_fall) {
adxl_t1=CCP_1-adxl_last_rise;
setup_ccp1(CCP_CAPTURE_RE);
adxl_waiting_for_fall=FALSE;
} else {
adxl_t2=CCP_1-adxl_last_rise;
adxl_last_rise=CCP_1;
setup_ccp1(CCP_CAPTURE_FE);
adxl_waiting_for_fall=TRUE; |
It runs accurately by itself and seemingly also when my other isr is running.
The other isr is just creating a standard RC servo centering signal with a period of about 20 mS and duty cycle of 1500 uS to act as input to the RC rate gyro that I'm using. Code: | #int_RTCC
Gyro_input_isr()
{
set_timer0(6);
if (++count==180)
count = 0;
else
{
if(count<15)
output_high(PIN_E2);
else
output_low(PIN_E2);
}
} |
It also works well if it is the only interrupt running but falls apart when both are enabled and running.
My question is whether there are any general rules for dealing with this type of situation. Or is there an obvious solution that I'm just not aware of?
As an aside, once I get this running I have to time the gyro outputs with another isr to get a real time rate of tilt. Then I may have to generate 2 PWM signals to operate the DC motors.
Thanks.....Harry |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah Guest
|
|
Posted: Mon Dec 12, 2005 9:44 am |
|
|
What you don't say, is the clock rate, and what divider is being used with the RTC, also the intervals involved in the accelerometer pulses.
The first code works, because assuming the routines are declared in the order shown, it has priority over the other call.
Now unfortunately, the PIC, is relatively poor in it's interrupt handling. Some part of this is CCS, but the main part is the hardware. The chip has a _lot_ of general purpose registers, and no quick way of saving/restoring these. The global interrupt handler, saves the global registers, then tests what interrupt has triggered, and only then calls the first handler 'found' (triggered and enabled). Then the code restores all the registers, exists, and if another interrupt is present, the whole sequence happens again. The overhead associated with this, is perhaps 50 instruction times (it is _worse_on the 18 family chips....).
It sounds as if the accelerometer, must generate a fairly frequent output. So what is happening, is that there is a high probability that when the second interrupt occurs, the code finds the first interrupt has also triggered. This then gets serviced first, adding this extra latency to the response for the second handler. If the interval involved with the accelerometer is short, it may even generate multiple triggers, effectively locking out the second handler for a while.
There has been a post in the last few days, where I posted an 'overview', for a way to alter the way that the interrupt handler is called, so that one handler checks before exiting, whether other events have occured, massively reducing this latency. So long as all the events are asynchronous, you are always going to have the problem of one event changing the delays involved in reaching the other handler, unless you add hardware to do some part of the handling for you.
It _may_ be possible to make a better solution, by generating your own global handler, and if you post some of the timings involved, and the same for the third event (and which interrupt it is going to use), some ideas about this may be forthcoming. However you need to post about all three events, since otherwise may solutions may exist for the two source problem, which then have problems in the 'three source' situation.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Mon Dec 12, 2005 10:11 am |
|
|
Ttelmah wrote: | What you don't say, is the clock rate, and what divider is being used with the RTC, also the intervals involved in the accelerometer pulses.
|
Sorry, in the interests of clarity and brevity I had thought it best to include just the isr's. Wrong....Here is the whole program as it stands. Code: | #include <16F877.H>
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 20000000)
#use RS232(BAUD=38400,XMIT=PIN_C6,RCV=PIN_C7) //communication link with BotLoader
//raise this value as high as you can without overflowing the math
#define ADXL202_SCALING_FACTOR 100000
//global vars used to calculate G
long adxl_t1, adxl_t2;
short adxl_waiting_for_fall;
//global vars used calibrate
signed int32 adxl_zerog;
long adxl_ppg;
int8 count = 0;
#int_RTCC
Gyro_input_isr()
{
set_timer0(6);
if (++count==180)
count = 0;
else
{
if(count<15)
output_high(PIN_E2);
else
output_low(PIN_E2);
}
}
/*
#int_ccp1 // Calculate duty cycle and period of Accelerometer output on X-axis
void accelerometer_isr(void) {
static long adxl_last_rise;
if(adxl_waiting_for_fall) {
adxl_t1=CCP_1-adxl_last_rise;
setup_ccp1(CCP_CAPTURE_RE);
adxl_waiting_for_fall=FALSE;
} else {
adxl_t2=CCP_1-adxl_last_rise;
adxl_last_rise=CCP_1;
setup_ccp1(CCP_CAPTURE_FE);
adxl_waiting_for_fall=TRUE;
}
}
*/
void initialization(signed int32 zerog, long ppg) {
adxl_zerog=zerog;
adxl_ppg=ppg;
setup_ccp1(CCP_CAPTURE_RE);
adxl_waiting_for_fall=FALSE;
setup_timer_0 (RTCC_INTERNAL | RTCC_DIV_2);
setup_timer_1 (T1_INTERNAL);
enable_interrupts(INT_RTCC);
enable_interrupts(INT_CCP1);
enable_interrupts(GLOBAL);
}
// A(g) = (T1/T2 - 0.456) / 0.118
// 0g = 50% duty cycle
signed int16 accelerometer_read(void) { // Returns hundreths of a g
float value;
value = adxl_t1 * ADXL202_SCALING_FACTOR;
value = value / adxl_t2;
value = value - adxl_zerog;
value = (value / adxl_ppg) * 10000;
return(value);
}
main()
{
initialization(45600,11750); //calibrated values
output_low(PIN_E2);
{
while (1)
{
signed int16 value;
value = accelerometer_read(); // "Value" varies from -10,000 to +10,000
printf("\r\nAcceleration due to gravity = %4ld.%02ld",value/100, value%100);
}
}
}
|
Regards.....Harry |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Mon Dec 12, 2005 10:27 am |
|
|
Ttelmah wrote: | It sounds as if the accelerometer, must generate a fairly frequent output. | The accelerometer is calibrated to run at approximately 1 KHz.
Ttelmah wrote: | There has been a post in the last few days, where I posted an 'overview', for a way to alter the way that the interrupt handler is called, so that one handler checks before exiting, whether other events have occured, massively reducing this latency. |
I'll see if I can locate this post and try to make sense of it. |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Mon Dec 12, 2005 11:35 am |
|
|
Thanks Ttelmah, I think I've digested what you've said.
I don't want to steer this thread into a different direction and my original purpose is still the first priority.
Given what you've said I've made some assumptions. Please correct me if I'm wrong.
The CCP1 interrupt runs every time an edge changes on the accelerometer signal....averaging about every 500 uS.
The Timer_0 interrupt runs every 100 uS.
You've suggested that interrupt overhead is about 50 instruction cycles long or about 10 uS for my clock speed.( BTW, I can't see how an effect this small could completley obliterate my servo centering signal.)
If I can figure out how long it takes to run the code in each interrupt I should be able to manipulate my timing to get an approximate solution. This is not really elegant, I'm just rambling here, trying to figure out how to deal with this.
Regards....Harry |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah Guest
|
|
Posted: Mon Dec 12, 2005 3:27 pm |
|
|
First comment, the set_time0(6), sightly lengthens the interrupt repeat 'time'. At this point, the processor, will have probably executed about 28 instructions from the start of the interrupt event, and the counter will already be at 14. The interrupt will then occur 249*2 instruction times from this point. Your total repeat rate for this interrupt will be about 526 instruction times. About 9500 times/sec. The handler itself, will involve 25 instructions, plus those in the global handler (about 60 instructions), so the processor will be spending about 1/6th it's time in this handler. This makes the probability of a delay when the second handler is called very high...
Now you seem to be using this as effectively a pulse generator, generating a pulse that is high for 14 counts, then low for 166. So about 1.5mSec high, and 17.5mSec low. I'd have to sit down and do an instruction count for the real code, to get the figures accurate, but I doubt if I am far off. Realistically, if this is all this is doing, then consider using the other CCP module, programmed as a PWM, and doing this in hardware. The existing code is using a huge amount of processor time to do very little.
You still have not said what the third interrupt wants to be. This affects whether it will 'mind' a timing error caused by the other interrupt or not.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
Harry Mueller
Joined: 17 Oct 2005 Posts: 116
|
|
Posted: Mon Dec 12, 2005 3:57 pm |
|
|
I'm not really stingy with my information...I just keep forgetting. Sorry. I've spent the last few hours thinking about this and I'm already understanding it much better.
Based on your comments, I've reprogrammed timer_0 to interrupt every 1.5 mS and with this code both interrupts are working well. Now timer_0 is being called every 1.5 mS rather than every 50 uS.
I was trying to save CCP2 to use in compare mode to measure the gyro output duty cycle.
Three interrupts may be enough for now because I may be able to balance the robot just by turning the motor on or off without involving PWM.
If I can get this working I may try to implement some sort of Kalman filter but right now I'm leaning towards just trying the empirical approach.
The last step would be quadrature encoding on both wheels but that may beyond the capabilities of the 16F877. I'll see. |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|