Talking to the outside world
Last week, we introduced a rotary encoder as a sort of digital analog to a potentiometer. However, you may have noticed that it did not cleanly transition between states, there could be quite a bit of noise in the transition.
Instead of rebuilding the circuit, we've created a breakout module that will plug directly into the FPGA board, shown below:
| {{phylabs:lab_courses:phys-226-wiki-home:newencoder.png}} |
| A breakout board connected to one of the 40-pin sockets of the FPGA. The boxes denote connections to the FPGA pins or power. The cables (on the far right) have sockets on the end that connect to the pins on your rotary encoder.|
With that done, we can now access the two rotary encoder values as pins ''V7'' and ''AB9''. Let's try this by going back to our FPGA program and re-configuring the button from pin ''F1'' to ''V7''. After that, compile and test your program.
> What do you observe?
----
You probably noticed the output sporadically changing by more than one increment at a time. To address the issue, we'll build something called a ''debouncer''. This sort of signal processing is often needed when working with mechanical inputs, as the contacts can literally vibrate between open and closed as they move. This in turn causes a quick burst of noise, that would be invisible in most analog circuits but wreaks havoc with digital devices.
Our debouncer will consist of 6 basic elements:
- A pair of flip-flops and a XOR gate will detect any change in state
- Changes in state will start a counter
- When the counter reaches its maximum, it will send a signal to another flip-flop to update the button state that we see on the digital side
- A NOT gate will make it so that we can feed a signal back from the ''maximum output'' of the counter to disable it until the input changes again.
In order to keep things clean, we'll make a sub-circuit here by creating a new block diagram file in our project.
Create a new block diagram file in your project. Start out by adding a pair of inputs and one output, named ''input'', ''clock'' and ''output'' respectively.
| {{:phylabs:lab_courses:phys-226-wiki-home:debounce0.png|}} |
Save your file as ''debounce.bdf''. Make sure you navigate to the main directory of your project; it defaults to the ''output_files'' subdirectory. Failure to do this will make it so that Quartus won't properly recognize that the block is part of your project
| {{:phylabs:lab_courses:phys-226-wiki-home:debounce1.png|}} |
To start off, add in a pair of D Flip-Flops (''dff'') and a XOR gate (''xor''). Connect them together to make a change detection circuit, which will cause an output on the XOR for one clock cycle every time the input changes.
| {{:phylabs:lab_courses:phys-226-wiki-home:debounce2.png|}} |
Next, we'll create a new counter with some features we haven't used yet. Go to the symbol tool and add a ''lpm_counter'' module. On the first screen, change the name to something else such as ''DebounceCounter''
| {{:phylabs:lab_courses:phys-226-wiki-home:countername.png|}} |
If you don't do this, you may get some very strange errors later due to Quartus finding multiple counters with the same name.
Next, click through to the configuration screen. We'll want to change the following settings:
* Screen 1
* set ''q'' to 12 16 bits
* Screen 2
* Select the ''Count enable'' and ''Carry-out'' checkboxes
* Screen 3
* Under the ''Asynchronous inputs'' section check the ''clear'' checkbox
Once your counter is finished, it should look something like the following:
| {{phylabs:lab_courses:phys-226-wiki-home:lpm_counter_img_update.png}} |
Now, place another ''dff'' in your circuit off to the side, and make the following connections:
* Connect the ''clock'' pin to the ''clock'' input of the counter
* Connect the ''cout'' counter output to a ''not'' gate
* Connect that to the ''cnt_en'' input on the counter
* Connect the XOR output to the ''aclr'' input of the counter
* Connect the ''Q'' output on your second original flip-flop to the ''D'' input of the new flip-flop
* Connect the ''cout'' output on the counter to the clock ''>'' pin of the new flip-flop
* Connect the output of the new flip-flop to the output pin you created earlier
After all of this, you should have something like the following:
| {{:phylabs:lab_courses:phys-226-wiki-home:fixeddebounce.png|}} |
Now, why did we do all of this? Well, the CountEnable (''cnt_en'') pin only lets the counter tick upwards when it is held high. The ''cout'' pin on the counter stands for CarryOUT, which goes high after the counter reaches its maximum value. Thus, connecting these makes the counter stop after it goes counts to $2^{16}-1 = 65535$. Since each clock cycle takes 20ns, this makes it so that our circuit takes ~1.3ms of the output being steady before it changes state.
If we were to use 12 bits, this would instead be around 80 $\mu$s, which is too quick for some of these encoders to stop being noisy. If you have a particularly angry encoder, you might need to increase the counter to 17,18, or even 19 bits. Each additional bit doubles the time that the output has to be stable for it to change in the FPGA.
The asynchronous clear ''aclr'' will reset the counter to zero. By connecting it to the XOR gate, we'll reset the counter any time the button's state changes.
The last flip-flip will only change state to match the button after the timer has reached its maximum value because the only time its clock is enabled is when ''cout'' is high.
All of this ensures that the output the code sees will only change after the input has settled down over a little bit of time.
----
Now to actually use our sub-circuit! Navigate the menus to ''File -> Create/Update -> Create Symbol Files for Current File''
| {{:phylabs:lab_courses:phys-226-wiki-home:debounce6.png|}} |
It will try to save the result in the ''output_files'' directory again (ARGH), so navigate up to the main project directory before you save the symbol file.
| {{:phylabs:lab_courses:phys-226-wiki-home:debounce7.png|}} |
With that done, switch back to your main project block diagram. Click on the Symbol Tool and scroll all the way to the top of the options, where you should find your new debouncer circuit waiting for you.
| {{:phylabs:lab_courses:phys-226-wiki-home:debounce8.png|}} |
With that done, its time to add it into our circuit by doing the following:
* Disconnecting the input to our original counter (while still keeping the pin)
* Connecting the rotary encoder pin to the input of the debouncer circuit
* Connecting the debouncer output to the original counter
* Adding a new pin, named something like ''sysclk'' or ''clk''.
* Whatever helps you recognize it as a clock.
The end result should look like the following:
| {{:phylabs:lab_courses:phys-226-wiki-home:debounce9.png|}} |
After Analyzing the project, go to the pin planner and assign your ''sysclk'' input to pin ''G21''. This connects to one of the internal 50 MHz clocks in the FPGA.
Compile and test your project.
You should now have fewer issues with your counter jumping by multiple values at once.
You should be in good shape if you finish here on day 1