Software Overview  |  Sitemap  |  Downloads  |  Developers  |  Forums

Creating State Machines

State Machines are general purpose control devices in the Virtual Wiring system. Have a bunch of devices and sensors which need to be coordinated in a certain way? State Machines have user definable inputs and outputs and programmable functionality. They can function as stateless devices (like the basic combinational And, Or, Not devices), and they can also create and act upon their own state.

A Design Problem - A Debouncer

We are going to create a State Machine which takes the noise of out a digital input signal. The idea is that if a signal sometimes jumps around before settling to its correct value, we want to filter out that jumping around. We only want the system to see the signal's value when it is stable. Sometimes, people call this kind of behavior, "debouncing".

Debouncers are useful for all kinds of things. Perhaps you have a sensor which is sensitive to noise, and you want to ignore false signal values. Or maybe you have a power sensor in your home which turns on when you loose power. If you have a backup generator, it may take a few seconds for it to come on line, and then your power sensor will turn off. You might not want to know about the power failure unless it persists. Any time you have an on/off type signal which is only of interest when it is stable for a prescribed length of time, a debouncer can come in handy.

The word "debouncing" comes from a solution to problem seen when switching digital signals through mechanical switches. When mechanical switches switch, they can bounce their contacts, and the bouncing contacts can create digital signals which bounce around as well. Since some digital devices are sensitive (in a bad way) to the bouncing around, people have created "debouncing" circuits for removing switch bounce. We are going to create a general purpose debouncer which can debounce most any kind of signal. The debouncer uses a State Machine.

An Undebounced and a Debounced Digital Signal
Debouncer Signal

Fleshing Out the Design

A debouncer has an input for a raw signal and an output for the debounced signal. Debouncing requires knowing how long a signal is stable, so the debouncer needs a timer to measure the length of time the raw signal is stable. State Machines can create any function of their inputs and outputs, and they can create and react to state. However, they cannot measure time. State Machines have no notion of time. That doesn't mean that a State Machine is a poor choice for solving this problem - it's just going to need some help.

Imagine we have an external timer, which every time it sees a signal transition from off to on, it generates an on pulse at its output. The pulse has a programmable width. In addition, if the timer sees an off to on transition when it's pulse is already on, it restarts the timing of the pulse width. (So as an example, imagine a timer being stimulated by an oscillating input signal. If the oscillation period is less than the timer's output pulse width, the timer's output will remain high indefinitely.) This timer is exactly what our OneShot Device is.

OneShot Behavior
OneShot Behavior

If we connect the "clk" input of a OneShot to our signal, we can tell when the signal is stable. When the signal transitions from off to on, the OneShot will be on, and when the signal stops transitioning, the OneShot will be off. If we set the pulse width of the OneShot to the time we wish the input signal to be stable, the OneShot will be off when there have been no off to on transitions within our stability time. If we add another OneShot which has an inverted version of our input signal, it will be low when there have been no on to off transitions within our stability time. If both OneShots are low, there will have been no transitions at all within our stability time.

Now that we've decided what Devices we will use for timing, we'll connect them all together. We connect our two OneShot outputs to our State Machine. We will call them "off2ons" and "on2offs". The signals are on when the OneShots are seeing off to on or on to off transitions. We'll hook our input signal, we'll call it "raw_signal", directly to the "off2ons" OneShot. We'll hook the inverted version of "raw_signal" to our "on2offs" OneShot.

Block Diagram for a Debouncer
Debouncer Block Diagram

Designing Our State Machine

Our state machine's job is to look at our two OneShot outputs and generate a "debounced" output. If either or both OneShot outputs are on, the State Machine will not change its output - "raw_signal" is not stable. If one OneShot signal is off and the other transitions from on to off (becomes stable), the State Machine will set its output to the value of "raw_signal". Even though the State Machine does not have direct access to "raw_signal", it can determine the value of "raw_signal" by knowing which OneShot signal last transitioned off.

Let's see how our State Machine looks in a state diagram:

Diagram for a Debouncer State Machine

We have 2 output states, on and off, and lines representing signal transitions from our 2 OneShot signals. If the raw_signal input relaxes to our current output state, we don't change our output (these are the lines which circle back on their start state). If the raw_signal input relaxes to a state different from our output state, we change our output (these are the lines which start at one state and go to the other).

There's a small problem with the state diagram, however. Though the state machine will faithfully debounce when it's in an on or off state, it is not in either state initially. When the state machine starts up, it will have no output state - it will be in a state not shown our state diagram. If the state machine does not find a way to get into an on or off output state, the state machine will be stuck in its uninitialized state forever.

We'll add a new state to our diagram called "uninitialized". When the state machine is in the "uninitialized" state, it will try to find a state for its output. When in this state, it will take the first value it sees on the "raw_signal" input and apply the value to its output. In this way, the output gets initialized. To support this initialization, we'll add an additional "raw_signal" input to our State Machine.

Diagram for a Debouncer State Machine with Output Initialization
Debouncer with output initialization

Creating the State Machine Table

To build our State Machine, we will turn it into a table. The table will describe what is in our state machine diagram. State Machine tables are described here, so we won't go into their details. However, even if you haven't read about tables, tables are pretty easy to understand. We'll write our table in a description language called YAML (a human friendly language useful for describing many things, including our State Machine).

Our State Machine will have 3 inputs and an output. One input is our "raw_signal" input. The other two inputs are "off2ons" and "on2offs". They are from our 2 OneShots, and they pulse high when our "raw_signal" input has off to on and on to off transitions. Our single output is the debounced output. It is is named "debounced".

Here is the table for our State Machine:


 # normal operation - state transitions
   When: {off2ons: "off"}
   State: {debounced: "off", on2offs: "off"}
   Then: {debounced: "on"}
   When: {on2offs: "off"}
   State: {debounced: "on", off2ons: "off"}
   Then: {debounced: "off"}

 # normal operation - state holds 
   When: {off2ons: "off"}
   State: {debounced: "on", on2offs: "off"}
   Then: {debounced: "on"}
   When: {on2offs: "off"}
   State: {debounced: "off", off2ons: "off"}
   Then: {debounced: "off"}

 # uninitialized operation - grab first raw_signal value we see
   When: {raw_signal: "on"}
   State: {debounced: ""}
   Then: {debounced: "on"}
   When: {raw_signal: "off"}
   State: {debounced: ""}
   Then: {debounced: "off"}

With State Machines, the "" value, means the uninitialized value, or the value all State Machine inputs and outputs have at start up time. So State: {debounced: ""} means when the State Machine is in the uninitialized state.

  • Note: support of the "" value is included in Virtual Wiring Software releases 1.4.x and later. Earlier software releases will reject "" values.

Though this table is a good reflection of the state diagrams, we can simplify it quite a bit. Since State Machine outputs hold their output state by default, there is no need to have statements which set outputs to the same values. We can get rid of all the state holding table entries. In addition, we don't need to include the state of the "debounced" output in our State entries. The next value for "debounced" is purely a function of which OneShot relaxes last. So we can get rid of the "debounced" state in our State statement.

Here's a simpler version of the table.


# normal operation - look for OneShot 'on' to 'off' transitions 
  When: {off2ons: "off"}
  State: {on2offs: ["off", ""]}
  Then: {debounced: "on"}
  When: {on2offs: "off"}
  State: {off2ons: ["off", ""]}
  Then: {debounced: "off"}

# if the debounced output is not initialized, grab first raw_signal we see
  When: {raw_signal: "on"}
  State: {debounced: ""}
  Then: {debounced: "on"}
  When: {raw_signal: "off"}
  State: {debounced: ""}
  Then: {debounced: "off"}

Note that this table has two additional small modifications. See the "State" statements used during normal operation? Instead of the simple "off" values shown in the state diagrams, the "offs" are bracketed with an additional "" value. Brackets in a State statement mean any of the values contained in the brackets. So we are telling our State Machine it can transition when its on2offs or off2ons state is either off or uninitialized.

Why the modification? Looking at our state diagrams, we only move from the "on" to "off" state or "off" to "on" state when a OneShot turns off, and the other OneShot is already off. However, at start up, both OneShot outputs will be uninitialized. Rather than waiting for the "raw_signal" to transition and trigger our OneShots and initialize our OneShot inputs, we tell the State Machine that an uninitialized OneShot input is the same as as being off. This way, our debouncer is ready to go when it starts up.

Saving the Table

We need to save this table in a file, so we can use it in our State Machine. We will save it in our "Scripts/examples/virtual/state_space" subdirectory. The Scripts directory is in the "virtualizer" directory, and the "virtualizer" directory is a top level directory in our Virtual Wiring software release. The table file should have the name "debouncer_table.yaml". (There's a good chance you won't need to save this Script, as it is already there.)

Building the Debouncer

In this section we'll build our debouncer. We'll build things using the Scripts page.

Building the OneShot Devices

To start, we'll create our two OneShots. Go to the Scripts page, and at the top of the page, click on the "View Filter" button. In the list of choices that follow, click on "Active". The Active filter shows only those Virtual Logic Device Scripts which are for active Devices (OneShots turn on reactively, but they turn off at a time of their own choosing). You should see a page which looks something like this:

List of Active Virtual Logic Devices
List of Active Devices

Click on the "Run" button of the OneShot Script. You will see a dialog box with "duration" and "id" fields. For the ID, we'll name the first OneShot "oneshot_off2on". In this example, we will ignore raw_signal values which persist for less than 10 seconds, so we'll set the "duration" field to 10. Your dialog box should look something like this:

OneShot Dialog Box
OneShot Dialog Box

Click on the "Submit" button.

Create a second OneShot with a duration of 10 seconds, but name this OneShot "oneshot_on2off".

Building the Inverter Device

Since the second OneShot will need its raw_signal value inverted, we need to add an Inverter Device. At the top of the Scripts page, click on the "View Filter" and select "Combinational". You will see a list of Combinational Logic Device Scripts. Click on the "Run" button of the Inverter Script. You will see a dialog box with "id", "inputs", and "outputs" fields. Type "raw_signal_inverter" into the ID field, "raw_signal" into the "inputs" field, and "raw_signal_" into the "outputs" field. Remember to quote all your values.

Inverter Dialog Box
Inverter Dialog Box

Building the State Machine

Building State Machines is fully described here. We will follow the same procedure.

  • From the Scripts page, click on the "View Filter" and select "StateSpace". You will see the StateSpace Scripts.

  • Create a State Space for holding our table and State Machine. We'll call the State Space "debouncer".

    • Click on the "Run" action of the StateSpace Script.
    • You will see a dialog box with a single "id" parameter. Type in the ID "debouncer" (quoted).
    • Click the submit button.
  • Load our State Machine Table into our State Space.

    • Click on the "Run" action of the loadTablesFile Script.
    • You will see a dialog box with "file_name" and "state_space_id" parameters. Type in the file name "Scripts/examples/virtual/state_space/debouncer_table.yaml", and the State Space ID "debouncer" (quoted).
    • Click on the submit button.
  • Create the State Machine.

    • Click on the "Run" action of the StateMachine Script.
    • You will see a dialog box with with "state_space_id" and "table_name" parameters. Type in the State Space ID "debouncer" and the table name "debouncer" (quoted).
    • Click on the submit button.
    • You will see another dialog box. It is asking you how you want to name your State Machine's inputs and outputs. We are going to give them the same names that the table uses.

State Machine Dialog Box
State Machine Dialog Box

Wiring Things Together

Now that we have created 2 OneShots, an Inverter, and our State Machine, we have all the Devices we need. Next we are going to wire them together.

Go to the Wires page. You will see a "punchblock" view with two columns, each column containing all the Device terminals in the system. We are going to "wire" our Devices together by clicking on terminals in the left column and dragging them to terminals in the right column.

Here we have clicked down on the "raw_signal" pin of our "debouncer" State Machine and are dragging a connection to the "raw_signal" input of our Inverter.

  • If after clicking down on a terminal, a wire does not follow your mouse, try clicking a little off center of the terminal ball.

Adding a Punchblock Wire
Adding a Punchblock Wire

Looking at our block diagram, there are 4 wires interconnecting our Devices. We added a fifth wire when we modified our State Machine, so that it could initialize the "debounce" output after start up. The fifth wire connects the State Machine to the "raw_input" signal. Using the block diagram as your guide, draw all 5 wires.

Here is the completed wiring for the debouncer with all 5 wires.

Wiring for the Debouncer
Wiring for the Debouncer

Testing Things Out

Our design is complete. All we need to do is test it out. Go the the Device Explorer page, where you will see each of your Devices. If you click on your Device expanders, you can see each Device's terminals and their terminal values. Click on the expander for the "debouncer" Device. The "debouncer" has the "debounced" output we are interested in and the "raw_signal" input.

Click on the "raw_signal" terminal of the "debouncer" Device. You will be presented with a dialog box for inputting on or off values (unquoted). If you start with an off value, note how the "debounced" output goes immediately off (that's due to the initialization portion of the State Machine).

  • To see up to date values, you may need to click on the "Refresh" or "Auto Refresh" buttons at the bottom of the page.

If you started by setting "raw_signal" to an off value, now set it to on. The "debounced" output will change, about 10 seconds later. Try setting "raw_signal" to off, then on, then off less then 10 seconds apart. "debounced" changes to off 10 seconds after you are done.

Stimulating the "raw_signal" input of the Debouncer
Stimulating the raw_signal input

Saving the Design

If you go to the Console page (or the Session Script page) and copy the lines which created our debouncer, you can put them in a Script. Running this Script will recreate the debouncer. We've created a "debouncer" Script in the "examples" area which does just this. Here is the debouncer Script (we've added some comments and spacing to make it easier to read).

# Script that builds a debouncer

# Create the Devices

# 2 OneShots
run_script("Scripts/Device/Virtual/Logic/Active/OneShot", duration:10, id:"oneshot_off2on")
run_script("Scripts/Device/Virtual/Logic/Active/OneShot", duration:10, id:"oneshot_on2off")

# 1 Inverter
run_script("Scripts/Device/Virtual/Logic/Combinational/Inverter", id:"raw_signal_inverter", inputs:"raw_signal", outputs:"raw_signal_")

# 1 State Machine

# create a state space for holding our debouncer table and state machine
run_script("Scripts/Device/Virtual/StateSpace/StateSpace", id:"debouncer")

# load state machine tables into state space from a file
run_script("Scripts/Device/Virtual/StateSpace/loadTablesFile", file_name:"Scripts/examples/virtual/state_space/debouncer_table.yaml", state_space_id:"debouncer")

# create our State Machine
run_script("Scripts/Device/Virtual/StateSpace/StateMachine", debounced:"debounced", off2ons:"off2ons", on2offs:"on2offs", raw_signal:"raw_signal", state_space_id:"debouncer", table_name:"debouncer")

# Wire things together

wire("debouncer:raw_signal", "raw_signal_inverter:raw_signal")
wire("raw_signal_inverter:raw_signal_", "oneshot_on2off:clk")
wire("debouncer:raw_signal", "oneshot_off2on:clk")
wire("oneshot_off2on:out", "debouncer:off2ons")
wire("oneshot_on2off:out", "debouncer:on2offs")

Wrap Up

This is a general purpose debouncing circuit. In your particular application, you may want to have different durations for your OneShots (you can even make your durations asymmetrical). Some debouncers only debounce off to on transitions, others on to off. If you only need to debounce one type of transition, you can make your "debounced" output more responsive by setting a OneShot's duration to 0.

Instead of changing the duration values in your "debouncer" Script and restarting your system, try:

poke "oneshot_on2off", "set_pulse_width 0"

Catalina Computing, LLC.

Copyright © Catalina Computing, LLC. (2013-2018)

Page last updated: Wed May 16 21:28:39 2018 (UTC)