I’m going through the interupt routine in gyro.c to figure it out, and I believe I found a major issue with it. Kevin: Please check!
In the interupt routine, it does the following things (in order):
Initialize i and accum if calculating bias
Set old_calc_gyro_bias to calc_gyro_bias
Reads ADC value
Adds it to the accumulator
Begins another ADC conversion
Adds to accumulator.
Does biasing code
The problem with this is if you try to read an analog channel (and clear the ADC) outside of the interupt. For this to work correctly, *the gyro
must
be the
only
sensor connected to your analog inputs*.
The Details:
The ADC converter works asyncronously from the controller. To use it, you have to:
Set the channel or open it and wait for it to settle
Tell it to begin a conversion
Wait until it finishes
Read the value
Close it (?)
If anything is changed during a conversion, you have to start over from #2. The interrupt assumes that the conversion finnished successfully, and that the value in the registers is the one from the gyro. If you read a value, it will likely read the value from the last sensor you used.
Is there any fix to this? This seems to be a large blocker, since the gyro code is dependent on it.
Did you read the readme.txt file. No? Well, here’s a paragraph that might interest you:
This software makes the assumption that when it’s running, it
solely controls the analog to digital conversion hardware and
that only the gyro, on analog input one, is being sampled.
Calling Disable_Gyro() will disable the gyro software and allow
you to use the ADC for other purposes.
One way around this is to double the timer 2 interrupt rate and then interleave your ADC measurements within the timer 2 ISR. If there’s interest, I can modify the code to include accelerometer measurements too.
I would like to see an example of how you would interleave the measurements. Not necessarily for the accelerometer but for a potentiometer mounted to our arm.
We have a similar situation, where we would like to juggle inputs from a pot and the gyro.
Once you call Disable_Gyro(), can you turn it back on again? In autonomous mode we’d be perfectly content to read only from the gyro while turning, then read from our pot on the straightaways, and then go back to reading from the gyro for a bit.
Is doing that more complicated than writting a complimentary function to Disable_Gyro() that just flips those two interupt bits? Or would we need a call to Initialize_Gyro()? Or do neither of those options work?
For those that need heading too, you need to integrate the gyro’s output, which means that you must also know the amount of time between ADC samples. The way I look at it, reading and restarting the ADC during a timer interrupt service routine is the simplest and most elegant way to solve the problem short of using external hardware.
Doesn’t get_analog() obtain a reading when requested since it is off the FRC, or is it only updated once per master loop?
In which case how about I use the timer only to gain an accurate measure of the master loop interval?
I want to run several other analog inputs along with the Gyro, and heading is important as I am using your robot code. Slightly modified to handle a steered front wheel and counting every trigger from the encoders, since they are only 16 pulse.
I really appreciate the ability to start with your code and tweak from there!!
After reading some more out of the PIC18C manual, and doing some testing, here’s what I did:
Using the EDU, I made meassurements of how long it takes to read the analog input, from start to finnish. (Using timer 1 with 1:8 prescaler.) It averaged around 138.8586 clock cycles to go through, or about 0.111087 milliseconds (which is a bit long with all the extra instructions and the overhead to get the timer value).
Based on this value, and the relative rarity that the gyro timer goes off (about 400Hz if the comments are correct), we decided it was ok to switch channels and wait and everything inside the interupt. If anyone encounters problems doing this, please say so.
Me and my team are planning on hooking up some switches and arm feedback as analog inputs, but when we run the program the gyro “hogs up all the space” for lack of better words. Any way around this or to fix this???
Everyone serious about autonomous and capping this year needs a gyro and some kind of arm feedback (ie, potentiometer) yet I couldn’t find any real code on reading two analog inputs. So, assuming the sample rate is doubled (800 Hz), will this code work?
Our RC is a few thousand miles away in some spooky Caribbean harbor and our EDU is locked up somewhere over at the school, so I can’t really test it. :yikes:
void Timer_2_Int_Handler(void)
{
unsigned int adc_result;
unsigned int adc_result_2;
if (last_conversion == POTENCIOMETRO)
{
ADCON0bits.CHS0 = 0;
ADCON0bits.CHS1 = 0;
ADCON0bits.CHS2 = 0;
ADCON0bits.CHS3 = 0;
// get the latest ADC conversion
adc_result = ADRESH;
adc_result <<= 8;
adc_result += ADRESL;
// add the ADC data to the accumulator
accum += adc_result;
// increment the sample counter
samples++;
// start another analog to digital conversion
ADCON0bits.GO = 1;
// check to see if we've got a full sample set
if(samples >= GYRO_SAMPLES_PER_UPDATE)
{
// should the completed sample set be used to calculate the gyro bias?
if(calc_gyro_bias == TRUE)
{
// convert the accumulator to an integer and update gyro_bias
avg_accum += accum;
avg_samples++;
}
else
{
// update the gyro rate
gyro_rate = (int)accum - gyro_bias;
// integrate the gyro rate to derive the heading
gyro_angle += (long)gyro_rate;
}
// reset the accumulator to zero
accum = 0;
// start a fresh sample set
samples = 0;
}
last_conversion == GYRO;
}
else
{
ADCON0bits.CHS0 = 1;
ADCON0bits.CHS1 = 0;
ADCON0bits.CHS2 = 0;
ADCON0bits.CHS3 = 0;
// get the latest ADC conversion
adc_result_2 = ADRESH;
adc_result_2 <<= 8;
adc_result_2 += ADRESL;
ADCON0bits.GO = 1;
potenciometro = adc_result_2;
last_conversion = POTENCIOMETRO;
}
}
To be safe, you should change the channel after getting the value. Also, be sure to double check the reference manual to make sure you have the bits right for the channel selection, we had some trouble with that when we to alternate between gyro and accelerometer.
In the end, we didn’t use it though; our sensors were giving bad readings, presumably broken. We did come up with two rather ingenuitive ways of solving the direction and arm height problems, though I’m not too sure I’m supposed to share them just yet. What’s it matter though if we can’t get the robot to drive straight when it’s going for the tetra
You can definetely get arm feedback without using two analog inputs. If nothing else you can use an external adc and put it into 8 digital inputs, or however many are practical.
I realize I’m a little late coming to this thread… but I wanted to put in a comment about sampling multiple analog sensors, either to add value to the discussion or to get feedback.
We needed to sample several analog inputs and wanted to use the same sample rate for them all (250 Hz), and ran into the problem with using Get_Analog_Value() more than once in an ISR. The “fix” we used was to have the ISR increment a counter of 4ms tics but not do any analog sampling. Code running in the “fast” routine would then read all the inputs when it saw the non-zero 4ms counter, and decrement the counter. This does cause sample jitter but no lost samples, so the average sample rate remains correct. Our “main loop” code used about 6ms, so about 1 sample every 4 or 5 would be delayed.
The problem with this method is if the main loop code is big compared to the sample rate, many samples will be delayed and the jitter will become a significant problem. For us this seemed to work. Then again, there are lots of reasons why integrated sensor values get “wrong” after a while, and this technique probably contributed to the causes.