Mogs
New Member
As a hobby only, I'm developing a sous vide (hot water bath) controller with an ATMega328p (yep, Arduino) micro-controller, that uses a rotary encoder for user input. Debouncing an encoder is very finicky to say the least, since it needs to be solid, but quick enough to avoid being over-run by a quick turn of the knob. Since, in my case, the 328p already has its hands full with many other time critical tasks, it would be very nice to off-load this particular chore to hardware. My idea is that a PIC 16F628A @ < $1, would be easier and even cheaper than using the typical hex Schmitt trigger and 18 resistors and capacitors that would only provide six channels of hardware debounce.
I can envision using port A of the 16F628A for up to 8 bits of Schmitt triggered input, and port B for up to 8 bits of debounced output and, because of its internal oscillator, wouldn't require any other discrete components. The debounced outputs on port B would then be available to the 328p for either polled, or interrupt input at its discretion.
Here's what I would like to implement-
The initialization process of the 16F628 would setup a one byte memory register for port B output bits. It would be initialized by reading port A into the accumulator, and writing that to the port B memory register, as well as writing that value to port B, itself. Eight more two-byte memory registers would also be setup, each acting as a 16-bit shift register for each of the eight bits corresponding to the input pins of port A.
The main loop would then start with another read of port A into the accumulator.
Bit 0 of the accumulator would be bit rotated right, into the carry bit. That carry bit would be right bit rotated into the Most Significant bit of the first byte of the 'Bit 0 serial register'. Since the carry bit now contains the LSbit of the first byte, it is shifted into the second byte with a right bit rotation of that second byte. The entire register is then checked for identical bits (all ones or all zeros), and if so, the corresponding Port B output register bit 0 is updated.
Next, the accumulator would be bit rotated right, into the carry bit again [corresponding to the second bit of port A this time]. Then that carry bit would be bit rotated into the MSbit of the first byte of the 'Bit 1 serial register'. Since the carry bit now contains the LSbit of the first byte, it is shifted into the second byte with a right bit rotation of that second byte. The entire register is then checked for identical bits (all ones or all zeros), and if so, the corresponding Port B output register bit 1 is updated.
… repeated for the corresponding next six bits ...
And finally, port B itself is updated with the port B output register byte, and the program then jumps back to the start of the main loop.
This effectively shuffles all bounce spikes along through their 'shift' registers, but does not change the actual port B output bit until that bit's entire two-byte memory register agrees that the input bit is currently solid, hasn't changed for awhile, and that it is opposite to its previous state.
Since the data sheet says that all 35 of the 14-bit instructions execute in a single-cycle of 200 ns @ 20 MHz, except for branches, then I figure that at 4 MHz, this approximately 40 instruction main loop should only take roughly 50 uSecs per iteration, or just barely a millisecond to complete a full 16 bit shift register cycle. A third byte could be added to each shift register to stretch this routine into a handful of milliseconds. Masking some of the shift register bits would, of course, allow any specific length of register to be used, and a sprinkling of NOPs could be used to fine tune the routine timing.
If I am off the beaten track, here, would appreciate a nudge, and any input on design or implementation considerations. I'm just here to learn new stuff, not optimize a production line.
I can envision using port A of the 16F628A for up to 8 bits of Schmitt triggered input, and port B for up to 8 bits of debounced output and, because of its internal oscillator, wouldn't require any other discrete components. The debounced outputs on port B would then be available to the 328p for either polled, or interrupt input at its discretion.
Here's what I would like to implement-
The initialization process of the 16F628 would setup a one byte memory register for port B output bits. It would be initialized by reading port A into the accumulator, and writing that to the port B memory register, as well as writing that value to port B, itself. Eight more two-byte memory registers would also be setup, each acting as a 16-bit shift register for each of the eight bits corresponding to the input pins of port A.
The main loop would then start with another read of port A into the accumulator.
Bit 0 of the accumulator would be bit rotated right, into the carry bit. That carry bit would be right bit rotated into the Most Significant bit of the first byte of the 'Bit 0 serial register'. Since the carry bit now contains the LSbit of the first byte, it is shifted into the second byte with a right bit rotation of that second byte. The entire register is then checked for identical bits (all ones or all zeros), and if so, the corresponding Port B output register bit 0 is updated.
Next, the accumulator would be bit rotated right, into the carry bit again [corresponding to the second bit of port A this time]. Then that carry bit would be bit rotated into the MSbit of the first byte of the 'Bit 1 serial register'. Since the carry bit now contains the LSbit of the first byte, it is shifted into the second byte with a right bit rotation of that second byte. The entire register is then checked for identical bits (all ones or all zeros), and if so, the corresponding Port B output register bit 1 is updated.
… repeated for the corresponding next six bits ...
And finally, port B itself is updated with the port B output register byte, and the program then jumps back to the start of the main loop.
This effectively shuffles all bounce spikes along through their 'shift' registers, but does not change the actual port B output bit until that bit's entire two-byte memory register agrees that the input bit is currently solid, hasn't changed for awhile, and that it is opposite to its previous state.
Since the data sheet says that all 35 of the 14-bit instructions execute in a single-cycle of 200 ns @ 20 MHz, except for branches, then I figure that at 4 MHz, this approximately 40 instruction main loop should only take roughly 50 uSecs per iteration, or just barely a millisecond to complete a full 16 bit shift register cycle. A third byte could be added to each shift register to stretch this routine into a handful of milliseconds. Masking some of the shift register bits would, of course, allow any specific length of register to be used, and a sprinkling of NOPs could be used to fine tune the routine timing.
If I am off the beaten track, here, would appreciate a nudge, and any input on design or implementation considerations. I'm just here to learn new stuff, not optimize a production line.