As predicted the fill sensor performed miserably on clear bottles using the default settings and one of the operators noticed a mysterious lock up, where the sensor seemed to hang every now and then but became responsive again after a while. Another problem, lesser so, was the system was coming out of the gap detection loop when there clearly were no bottles, this last one while annoying did not trigger anything as the next part looking for the bottle end timed out. Time to investigate!
The first problem was predictably the default exposure for dark bottles would not be the same for light bottles, sort of knew this but ignored it as clear bottles were so rare I hadn’t encountered one yet. Armed with a good sample of clear bottles and various dilutions of blackcurrant juice I set up the test rig and began incorporating some sort of calibration unit into the system to auto-detect the bottle and set the exposure.
The second problem was really tricky. The system would be operating fine, then for no reason would completely hang, no leds, no backlight, no serial, no nothing! By sending various debug codes out through the serial port at each stage I was able to localise the problem to this very common PIC32 Delay routine..
void DelayMicroseconds(uint32 uS) // Blocking wait of 1uS { uint32 ExitCount = ReadCoreTimer()+ (DELAY_FACTOR_50MHZ*uS); while(ReadCoreTimer()<ExitCount); // wait for time to come around }
eg DelayMicroseconds(3); // Wait 3uS
The PIC32 has a core 32 bit timer built into the MIPS core which ticks at half the clock rate. We basically add our delay to this timer then wait until it comes around. What could possibly be wrong with this?
Consider the line:
DelayMicroseconds(SampleRate - 1500 - exposure);
If I set exposure too high, the Delay Microseconds() will be called with a negative number (say -10), however it is an unsigned integer so that negative number will be translated into a very BIG positive number! -10 = 4294967279! – So we are actually calling a delay of about an hour! (whoops!), the simple fix is to ensure DelayMicroseconds() is not called if the number is negative. So that solves the program hanging.
This Delay procedure also has a second, more subtle, bug that is nothing to do with how its called but with the procedure itself, again it is to do with the integer overflowing.
If we read the coretimer just before it overflows then add our delay to it, it will overflow to a tiny number, the next line will then exit immediately as the coretimer will be greater than the exitcount, thus..
DelayMicroseconds(ReallyBigNumber) becomes DelayMicroseconds(0) !
There are lots of timing critical points in the program such as the AquireImage() function which must wait a specific exposure period, if this exposure period becomes 0 then it will detect a much darker image, if this occurs during the time the system is waiting for the bottle gap to end then it will detect a dark image, think the bottle has passed and trigger, hence the above problem and again a fairly simple fix now we know.
The re-calibration I am still working on!