Background
With my renewed interest in digital circuits I recently sorted out all my old TTL and CMOS integrated circuits I obtained many years ago. I had no idea whether any or all of these devices had survived the best part of 25 years! What I needed to do before purchasing any more integrated circuits was to check my existing stock.
Giving some thought as to how I might go about all of this testing, the first idea was to use physical push buttons or switches to interface with the digital circuits either to set or reset logic levels, or to physically switch on and off to simulate clock pulses. As you will have read previously, the dreaded switch bounce problem put paid to this approach, as physical switching produces erratic and unreliable results in sensitive logic circuits.
Solution
As I had used the Raspberry Pi to produce pulses [see previous article], I thought about producing a simple application to do the following:
1. Set a GPIO port as an output.
2. Toggle the level of that GPIO output either High or Low.
As I wanted to be able to set more than one GPIO port and potentially toggle each output port High or Low, I decided to try to produce a Graphical User Interface (GUI) or Window interface, using a mouse button to both set and toggle the GPIO ports. Additionally, the benefit of using software to control the GPIO ports would mean clean switching of the logic levels.
Searching the many useful sources of Python code on-line proved very helpful. Having little experience of coding in Python and none in GUI/Window building, I knew this project was going to be challenging.
The resultant code is listed below. Now I confess, it's likely to be considered clumsy and probably poorly coded, but as my first attempt I was pleased as it actually works and it does what I needed it to do. What I particularly enjoyed about this little project was the ability to code and play around with the results; making changes to the various parameters to set window sizes, labels, check buttons and text formatting. Sometimes it left me scratching my head wondering what some of the code was actually doing, but again, the wealth of Python resources on-line provided most of the answers.
The Python3 code
The Python listing is below. A copy of the file is available here.
=================================================
# Python v3 script 'Toggle_Model_B.py' to Run on RaspberryPi B.
# A windows interface to provide 4 checkboxes to set 4 GPIO ports as Outputs
# Uses the checkbox function to set GPIO ports either HIGH or LOW.
# Display the Port state and a count of how many times the checkbox is
# selected/toggled.
# GPIO ports to use 17, 21, 23 & 24 for RPi Model B
from tkinter import *
import RPi.GPIO as GPIO
cc17 = 0 # variables will be global
cc21 = 0
cc23 = 0
cc24 = 0
state17 = 0 # variables will be global
state21 = 0
state23 = 0
state24 = 0
port17 = 17 # GPIO port to use as outputs
port21 = 21
port23 = 23
port24 = 24
GPIO.setmode(GPIO.BCM)
# disable warning messages
GPIO.setwarnings(False)
# set 'ports' as outputs
GPIO.setup(port17,GPIO.OUT)
GPIO.setup(port21,GPIO.OUT)
GPIO.setup(port23,GPIO.OUT)
GPIO.setup(port24,GPIO.OUT)
# set all ports to LOW
GPIO.output(port17,GPIO.LOW)
GPIO.output(port21,GPIO.LOW)
GPIO.output(port23,GPIO.LOW)
GPIO.output(port24,GPIO.LOW)
class Welcome(): # functions menu class
def __init__(self,master):
self.master=master
self.master.geometry('380x150')
self.master.title('RaspberryPi B Toggle GPIO')
self.label1=Label(self.master,text='Select port to toggle output & increment', fg='black').grid(row=2,column=0)
self.label1a=Label(self.master,text='GPIO port 17', fg='black').grid(row=3,column=0)
self.label1b=Label(self.master,text='GPIO port 21', fg='black').grid(row=4,column=0)
self.label1c=Label(self.master,text='GPIO port 23', fg='black').grid(row=5,column=0)
self.label1d=Label(self.master,text='GPIO port 24', fg='black').grid(row=6,column=0)
self.label2=Label(self.master,text='State', fg='black').grid(row=2, column=2)
self.label3=Label(self.master,text='Count', fg='black').grid(row=2, column=4)
self.button2=Button(self.master,text='Quit',fg='red',command=self.finish).grid(row=7,column=0)
self.var0s=IntVar() # Each port can be set high or low once set as an output
self.Checkbutton=Checkbutton(self.master, variable=self.var0s, command=self.p0s).grid(row=3,column=1)
self.var1s=IntVar()
self.Checkbutton=Checkbutton(self.master, variable=self.var1s, command=self.p1s).grid(row=4,column=1)
self.var2s=IntVar()
self.Checkbutton=Checkbutton(self.master, variable=self.var2s, command=self.p2s).grid(row=5,column=1)
self.var3s=IntVar()
self.Checkbutton=Checkbutton(self.master, variable=self.var3s, command=self.p3s).grid(row=6,column=1)
def p0s(self): # toggle port 17 high or low & display state & count
GPIO.setwarnings(False)
global cc17
global state17
p0=self.var0s.get()
if p0 == 1:
state17 = 1
cc17 +=1
self.label4=Label(self.master,text=str(state17), fg='black').grid(row=3,column=2)
self.label5=Label(self.master,text=str(cc17), fg='red').grid(row=3,column=4)
GPIO.output(port17,GPIO.HIGH)
else:
state17 = 0
self.label6=Label(self.master,text=str(state17), fg='black').grid(row=3,column=2)
GPIO.output(port17,GPIO.LOW)
def p1s(self): # toggle port 21 high or low & display state & count
GPIO.setwarnings(False)
global cc21
global state21
p1=self.var1s.get()
if p1 == 1:
state21 = 1
cc21 +=1
self.label7=Label(self.master,text=str(state21), fg='black').grid(row=4,column=2)
self.label8=Label(self.master,text=str(cc21), fg='red').grid(row=4,column=4)
GPIO.output(port21,GPIO.HIGH)
else:
state21 = 0
self.label7=Label(self.master,text=str(state21), fg='black').grid(row=4,column=2)
GPIO.output(port21,GPIO.LOW)
def p2s(self): # toggle port 23 high or low & display state & count
GPIO.setwarnings(False)
global cc23
global state23
p1=self.var2s.get()
if p1 == 1:
state23 = 1
cc23 +=1
self.label9=Label(self.master,text=str(state23), fg='black').grid(row=5,column=2)
self.label10=Label(self.master,text=str(cc23), fg='red').grid(row=5,column=4)
GPIO.output(port23,GPIO.HIGH)
else:
state23 = 0
self.label9=Label(self.master,text=str(state23), fg='black').grid(row=5,column=2)
GPIO.output(port23,GPIO.LOW)
def p3s(self): # toggle port 24 high or low & display state & count
GPIO.setwarnings(False)
global cc24
global state24
p1=self.var3s.get()
if p1 == 1:
state24 = 1
cc24 +=1
self.label11=Label(self.master,text=str(state24), fg='black').grid(row=6,column=2)
self.label12=Label(self.master,text=str(cc24), fg='red').grid(row=6,column=4)
GPIO.output(port24,GPIO.HIGH)
else:
state24 = 0
self.label11=Label(self.master,text=str(state24), fg='black').grid(row=6,column=2)
GPIO.output(port24,GPIO.LOW)
def finish(self): # exit
GPIO.cleanup()
self.master.destroy()
def main(): # main function
root=Tk()
myGUIWelcome=Welcome(root)
root.resizable(0,0)
root.mainloop()
if __name__ == '__main__': # start and run the script
main()
=================================================
Application Output
When you first run the Python script above on the Raspberry Pi, the GUI below appears:
What this shows is that I have coded in Python3 to use 4 GPIO ports;
17, 21, 23 and 24.
These 4 ports can be connected to appropriate circuitry - some suggestions are shown below. Selecting the Checkbutton against each port entry will Set the port as an output and Toggle the output level on that port to High (or 3.3v). Selecting the Checkbutton again will Toggle the port Low (0v) and the Check mark will disappear. What I also coded was a 'State' and 'Count' display. The 'State' column shows what level each port is set to. The 'Count' column shows how many times you have selected the port. Further examples are shown below:
In the example above GPIO port 17 has been selected, and it's output is High (3.3v), the Count is 1 to show the port has been selected and set High once.
In the examples above GPIO 17 has been 'deselected' so it's output is set Low (0v), the Count remains at 1 even though the port has been set Low. It indicates that just one complete 'cycle' of setting High then Low has occurred.
GPIO 21 has been taken through 2 complete cycles of setting and toggling High and Low.
GPIO 23 and 24 have been selected and both set High.
Some examples of how I used this application was to confirm the correct operation of logic circuits, testing of binary counters, flip-flops and bistable latches. I also used the Raspberry Pi and this application to interface with some Arduino circuits too; mostly to simulate that logic changes were correctly detected on Arduino inputs, to help with code debugging.
Benefits
The benefits of using this relatively simple application were:
1. Logic levels being set were reliable - if the 'State' showed 1, then I knew the relevant port was High.
2. Reliable switching - no switch bounce.
3. Status display - when testing it's useful to know how many 'cycles' of switching has occurred.
4. Slow switching - sometimes being able to single step one logic level to another is very helpful, and sometimes it's possible to simulate a slow clock pulse by just using the mouse button.
Circuits
The following circuits are very simple illustrations which could also be used to teach simple logic circuit behaviours:
The example above uses 2 GPIO ports to test that a dual input AND gate works correctly. In each testing scenario when using either TTL or CMOS integrated circuits running at +5v, you must use a 4050 Hex Buffer running at +3.3v to provide a correct interface between the Raspberry Pi GPIO ports and the external circuits. In the example above, both GPIO ports will need to be High to switch the Led on.
The example above tests a dual input NOR gate. The Led is on only when both GPIO ports are Low.
The circuit above was put together to test the Binary Counter (4020).
GPIO 17 was set High to enable the AND gate, whilst GPIO 21 was set alternately High and Low by mouse button clicks, to act as a slow clock pulse stream.
Another method of testing I employed was using my Python clock pulse generator script to generate a number of pulses instead of using GPIO 21. That way, the clock pulse stream input to the 4020 was controlled by GPIO 17 by either enabling or disabling the AND gate.
To reset the counter I connected GPIO 23 via the 4050 to the Reset pin on the 4020 so that after testing, whatever the state of the counter I could reset the counter. In practice I connected each counter output to an Led to provide a visual counter output.
* Note: for clarity, the circuit diagrams do not show power or earthing arrangements - the datasheets for the TTL and CMOS devices are readily available on-line.