I'm developing code for a motor controller that we've constructed from scratch. In order for the motor controller to operate correctly, I need to calculate the realtime velocity of the motor. This is done by recording the time between ticks of the encoders.
During operation, the velocity seems to be all over the place. I narrowed down the issue to the times that were being returned by the micros() function in the interrupt being triggered by the encoders.
In an effort to debug, I created the following code to output all of the times that were being recorded when the interrupt fired.
Below is a snippet of the resulting output. You'll notice that, though the times are trending upwards, it's not a straight linear progression. I'm using the latest version of the Arduino IDE (1.0.5) on OSX 10.8.4. Any idea what's going on here, or, more importantly, how to fix it? Suffice it to say, this is extremely frustrating.
I narrowed down the issue to the times that were being returned by the micros() function in the interrupt being triggered by the encoders.
That may or may not be true. What is true is that your debug code fails to prove that micros is a problem. There are at least three problems with your debug code...
You are printing values off the tail of the array and adding values to the tail of the array. If an interrupt occurs during printing, the values will be out of order.
The elements of time are multi-byte which means they have to be protected from access (interrupts disabled when reading an element in loop). Technincally, i has to also be protected but, because the values are kept below 256, there will not be a symptom.
Modifying i in loop has to be protected (interrupts disabled).
I suggest you find and use ring-buffer code that is known to work correctly.
The problem is that loop() is counting 'i' down to zero and as soon as 'i' goes below MAX_RECORDS your ISR will start trying to count it forward again, filling the array with newer data. Try detaching the interrupt before you start counting 'i' down.
Edit: That's how you managed to get 342 samples from a 200 element array.
If you sort the samples in ascending order you get deltas all between 1628 and 1660.
Coding Badly nailed it. I'm really not used to coding embedded systems and don't fully understand the intricacies of reading/writing registers and interrupts.
As for the point about new data in the array, you're right, I'm technically going back in time every 200 entries, but each of those 200 entries should be sequential. If you look at the data set, it displays completely erratic behavior for all chunks, due in part to my oversite with regard to disabling interrupts before reading or manipulating my counter or the array itself.
Serial.print/write etc are not instant and they are blocking.
In other words if serials output buffer is full then your program hangs until there is enough space to complete the write. But of course the isr keeps on modifying the data and no matter what baud rate you use serial is very slow compared with a 16MHz processor.
Just wrote this snippet of code and wanted to bounce it off you guys. It works under the assumption that reading a byte (uint8_t) is done instantly and can not be interrupted which Coding Badly alluded to.
I'm grabbing the current number of ticks, immediately reading the timer value, then checking to see if the ticks I read match the current ticks that were recorded. If not, I know an interrupt occurred and that my data was corrupted, so I repeat the process. The reason I'm writing it this way is because I can not afford to disable the interrupts, as I want to keep extremely accurate tracks of how many encoder ticks I've received. Again, I'm assuming that a uint8_t can be read in a single pass and will remain uncorrupted by the interrupt.
THEtheChad:
Just wrote this snippet of code and wanted to bounce it off you guys. It works under the assumption that reading a byte (uint8_t) is done instantly and can not be interrupted which Coding Badly alluded to.
The short reason... the compiler is free to reorder the assignments to tick_count and recorded in the do/while.
The reason I'm writing it this way is because I can not afford to disable the interrupts...
The timer 0 overflow handler disables interrupts. You haven't mentioned it being a problem.
...as I want to keep extremely accurate tracks of how many encoder ticks I've received.
What's the correlation between disabling interrupts for five clock cycles (313 nanoseconds) and missing an encoder tick? Is the size of your encoder measured in Ångströms?
THEtheChad:
The reason I'm writing it this way is because I can not afford to disable the interrupts, as I want to keep extremely accurate tracks of how many encoder ticks I've received. Again, I'm assuming that a uint8_t can be read in a single pass and will remain uncorrupted by the interrupt.
However an interrupt is "remembered" if interrupts are disabled, so you won't miss it unless you wait too long. And as Coding Badly said, the time it spends servicing a timer interrupt means it won't be totally accurate anyway (the timer amount anyway).
If you want "tick_count" and "recorded" to match the same event, you have to disable interrupts while obtaining those two variables. The access to them is not atomic.
In particular, "recorded" may be corrupted as well as out of sync with tick_count.
Imagine for a moment that the processor is copying "time" into "recorded" and after copying two of those four bytes, an interrupt occurs, and also imagine that a byte has overflowed.
So for example:
Old value: 0x0000FFFF
New value: 0x00010000
Depending on the order in which bytes are copied (and it's an 8-bit machine after all) the copy might end up being:
0x0001FFFF
Or even:
0x00000000
This sort of stuff is covered in my page about interrupts, and in particular "critical sections":