Wednesday, 8 March 2017

MOSFET Switches - Experiments

Introduction

MOSFET = Metal Oxide Semiconductor Field Effect Transistor

I have been using a level converter circuit based on a MOSFET BS170 to connect Raspberry Pi and Arduino processors running at 3.3v to external 5v logic circuits.

I have a good understanding about bipolar transistors but I realised I needed a bit of a refresher on how MOSFETs work and how I might be able to use these in the future.

A good explanation of MOSFETs can be found at:

https://en.wikipedia.org/wiki/MOSFET

In essence the voltage applied to the Gate terminal of a MOSFET determines the conductivity of the device.



I put together 3 circuits below:

MOSFET Switch


This circuit is a demonstration of how to manually switch a MOSFET on and off with a Light Emitting Diode (LED) as an indicator. I used this to take measurements of voltage and current levels and to make observations for any issues that might arise.

Using an oscilloscope I detected a couple of issues which if left unaddressed could cause erratic operation of this circuit and anything else connected to it.

Firstly, when the switch is closed, switch bounce (which I have covered in an earlier article) creates a voltage spike drop of nearly 1 volt at the Gate of the MOSFET which also appears at the Drain. If this circuit was just switching the LED on and off this wouldn't be too serious, however, if this circuit connects to another logic circuit, then any random voltage spikes passing through logic circuitry are unwelcome and can cause unpredictable results.

To overcome this, C1 is placed across the switch and prevents the voltage spikes appearing.

Secondly, when power is applied to this circuit a voltage spike of nearly 2 volts was measured at the Gate of the MOSFET, which also appeared at the Drain. It was sufficient to briefly illuminate the LED. The combination of C1 and C2 absorbs this voltage spike.

Circuit operation:
With power applied and the switch open, the Gate of the MOSFET is taken to 0 volts via R1. The MOSFET is off and the LED is also off.
When the switch is closed +V is applied to the Gate and the MOSFET is turned on, the LED is also on. C2 is charged via the switch.
When the switch is opened C2 discharges via R1 and the voltage on the gate drops and the MOSFET is switched off. There is a small delay before the MOSFET is switched off due to the time taken for C2 to discharge. In this example the delay is 4mS.

Various measurements were taken as I was interested in the amount of power used.
With +V at 5V, with switch closed, current draw via R1 is 14.8uA, the MOSFET (BS170) draws 4.9uA, so total is 19.7uA. Current draw via R2, LED and BS170 is 3.06mA.
With +V at 3.3V, with switch closed, current draw via R1 is 10uA, the MOSFET draws 3.3uA, total 13.3uA. It can be seen that the amount of current used is very small which is a key improvement over using bipolar transistors.

Dual MOSFET Logic Interface



Running at 5v, the input can be either 0/+3.3v or 0/+5v.
Running at 3.3v, the input can be 0/+3.3v.

Circuit operation:
With the input at 0v, the Gate of Q1 is held at 0v, the MOSFET is off and the Drain is high at +V. This switches on Q2, and the Drain is effectively at 0v. Therefore, the ouput of this interface follows the input level.
With the input at +3.3 or +5v, the Gate of Q1 is high, and the MOSFET is on. The Drain is effectively at 0v therefore Q2 is turned off. The Drain of Q2 is at +V.

This circuit can provide an isolation function between two different logic circuits, or act as a +3.3v to +5v logic level converter.

With 0v at the input the total current draw is 50uA. With the input at either +3.3v or +5v total current draw is 0.5mA.


LED String Driver


I have a small string of LEDs, used as a small Christmas decoration. I gave some thought as to how I might provide control of these LEDs as normally they connect to a 3v power source and are either on or off. Testing this string of LEDs, 20 in total, current draw from a battery source was 59mA.

The circuit above has the string of LEDs wired to the Drain of Q2.

The BS170 can switch and provide a continuous operating current of 500mA, so it is operating well within its parameters.

Circuit Operation:
With 0v applied to the input, Q1 is off and Q2 is switched on and the LEDs are illuminated.
When +3.3v is applied to the input, Q1 is switched on which switches off Q2 and the LEDs are off.

I experimented using both a Raspberry Pi and Arduino to operate the circuit,
and using a GPIO port on either microprocessor the maximum GPIO current draw was 10uA. However, just switching the GPIO high or low to turn the LEDs off / on wasn't that interesting.

I found and modified a Python script to run on the Raspberry Pi to produce a Pulse Width Modulated (PWM) pulse stream to apply to the circuit. Various effects can be produced, such as a flickering candle effect which is quite realistic
with all 20 LEDs operating at once. The python script is provided below for the Raspberry Pi.

# Python3 script to run on RaspberryPi 2
# Use pwm to control switching of mosfets & a string of christmas LEDS.

import RPi.GPIO as GPIO
import time
import random

LED = 18 # gpio port

GPIO.setwarnings(False)
GPIO.cleanup()

def setup():
    global pwm
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(LED, GPIO.OUT)
    pwm = GPIO.PWM(LED, 200)
    pwm.start(100)

def set_brightness(new_brightness):
    pwm.ChangeDutyCycle(new_brightness)

def flicker():
    set_brightness(random.randrange(0,70))
    time.sleep(random.randrange(1, 10) * 0.01)

def loop():
    try:
        while True:
            flicker()
    except KeyboardInterrupt:
        pass
    finally:
        GPIO.cleanup()
setup()

loop()


Additionally, I used an ESP8266 Feather Huzzah running a MicroPython script to control the circuit. The script is below, with some slight variations in
random number generation to that used in the Python script.


# MicroPython script to run on ESP8266
# Use pwm to control switching of mosfets & string of christmas LEDS.

import time
import machine
import urandom

def setup():
    global pwm
    pwm = machine.PWM(machine.Pin(15))
    pwm.freq(60)

def set_brightness(new_brightness):
    pwm.duty(new_brightness)

def flicker():
    set_brightness(urandom.getrandbits(10))
    time.sleep(urandom.getrandbits(5) * 0.01)

def loop():
    try:
        while True:
            flicker()
    except KeyboardInterrupt:
        pass

setup()

loop()


Given the physical size of the ESP8266 Feather Huzzah combined with a small battery pack, a small self contained and discrete operating unit could be produced.


Thursday, 19 January 2017

Raspberry Pi - Serial interfacing with DACIO300 Input/Output Module


Some years ago (back in the early 1990's !) I bought a serial to digital input/output converter module to experiment with connecting things to my computer(s). This module was sold by Maplin, made by R.M.Electronics and was named the RM9011. As with many things I've tried in the past, I can't recall having much success using it with a range of computers I owned, these being the Dragon 32, Atari ST, finally migrating to the PC running Windows 95. So it was put away for a future project...

With the arrival of the Raspberry Pi my interest in interfacing with technology and sensors has been rekindled. So digging out the RM9011 I have spent a happy few hours figuring out how to interface this with the Raspberry Pi using some discreet logic circuitry and using a serial console (minicom) to configure and test different scenarios with sensors and LEDs.  Using Python I've also coded some scripts to run various monitoring and output scenarios. As this module is way out of date, considered legacy and I've not been able to find spares or originals I have decided not to use it for any serious projects. However, for completeness for this article I've included a description of the module, an image of the board together with the circuit diagram (courtesy of Maplin magazine - January 1992).

RM9011 - description
RS232 to 8-bit digital I/O converter module - introduced in 1992 by Maplin Electronics, made by R.M.Electronics.

Features:
Each input/output line individually configurable as input or output.
Bit or byte read and write.
Configuration changes made via RS232 / Serial interface maximum speed 1200bps.
5V DC supply.
I/O Lines TTL / CMOS compatible.
On-board CMOS controller (pre-programmed).

An image of the board and circuit diagram is below:

RM9011 board by R.M.Electronics

RM9011 circuit diagram



While searching on-line for information on serial to digital converters to take the place of the RM9011 I found this device - the DACIO300.



The DACIO300 by Tronisoft in many ways has some similarities to the RM9011, though much more advanced and more importantly still available to purchase. The way it is controlled is also similar to the RM9011, in that ASCII characters are sent to the interface board via RS232/Serial. This method of operating lends itself to being controlled directly from a serial console, such as minicom on the Raspberry Pi, or from the Raspberry Pi using Python serial commands or Arduino using serial print commands.

This board is fully populated but it is possible to buy this unpopulated and exclude many of the components from the board depending on how it might be used. It is also possible to buy just the pre-programmed chip to build your own project(s).


DACIO300 - Specification

The DACIO 300 series modules are powerful, ultra low power consumption, microcontroller (MCU) based PC interfacing RS232 IO boards.
A single RS232 IO board provides two 8-bit digital input/output ports and up to 8 analogue input channels to computers equipped with a spare RS232 COM port or via a USB to serial converter such as item 2455 or 2327.
Each I/O is individually configurable.
The DACIO 300 features 8 10-bit A/D and a high speed 115.2kbps serial interface.
Easy to use communication and control open protocol.
Details about the RS-232 standard are available and provides useful information about remote operating distances.


Raspberry Pi - DACIO300 interface

Connecting the Raspberry Pi to the DACIO300 is relatively straightforward. The serial transmit output from the RPi is connected to the serial receive input on the DACIO300, and the serial transmit from the DACIO300 is connected to the serial receive input on the RPi. There should be no direct connections between the two devices as the RPi operates at 3.3v while the DACIO300 operates at 5v. It is necessary to provide an interfacing circuit between the two as shown below.

This simple interface allows the Raspberry Pi serial transmit port operating at 3.3v to connect via one hex buffer of the HCF4050 IC and connect to the DACIO300 serial receive port operating at 5v. In the reverse direction, the 5v serial transmit from the DACIO300 is stepped down to 3.3v (though not absolutely necessary) via R1 & R2 to drive another hex buffer whose output connects to the Raspberry Pi serial receive port. The HCF4050 is powered at 3.3v with pin 1 +Vdd and pin 8 Ov or Vss. I have also used an Arduino clone (RasPi.TV Duino) in the same way to successfully interface with the DACIO300.

Simple 3.3v to 5v interface (Raspberry Pi to DACIO300)

Example input / outputs

Here are a few simple examples of how the DACIO300 inputs and outputs could be used.
Port A comprises 8 bits, channels 0 - 7 and is by default an analogue input port.
Ports B and C are both 8 bits, channels 0 - 7 digital ports, Port B by default is set as inputs and Port C as outputs. Both Ports B and C are configurable at bit/channel level and can be either inputs or outputs.

Simple input and outputs
The commands to interact with the DACIO300 are straightforward and comprise a string of ascii characters forming a command.

Each command has a start character either ! or #

Characters to issue a command; typically comprising the Port name, an operator (e.g. '=' to write, '?' to read), a character if needed to provide an input parameter and an end character ';'

If a valid command is received, the module returns a '!' followed by any data requested.

Examples:
!C=255;
This command is instructing the module that Port C has a decimal byte value of 255 written to it.

!Bx?;
This command reads bit x (0-7) from Port B, where the reply is !x<0D> where x is 1 or 0 and 0D is a carriage return character.

!B?;
This command reads the whole of Port B and returns a decimal byte value.

!Ax?;
This command reads Port A, bit x and returns a reply !xxxx<0D> where xxxx is 0000-1023
Further details are provided in the DACIO300 manual to translate the numeric reading to a voltage level.

Many other commands can be sent to the module to set Ports as inputs or outputs, to read ports at a bit or byte level, to write out data to the digital ports at bit or byte levels, and to read the analogue port values.

Full details and instructions are detailed in the DACIO300 manual which is a very clear useful guide.

I have experimented with this module running various scenarios of reading digital values at both bit and byte levels and setting digital values again both at bit and byte levels. Using a minicom terminal running at 9600 enables some simple experimentation to confirm the principles of what I've thought is possible. I've then followed this with using Python3 scripts to send and receive serial messages to configure, read and write date to and from the module. I can see this module has many practical uses where a simple and reliable serial connection is preferred over alternative I2C and SPI interfaces.

A Python3 script is listed below that queries the byte level values present on Port B and sets Port C outputs to match them. This script could be used so that Port B is monitoring various input lines and Port C writes out the 'state' of the input lines to light LEDs to provide a visual indication. A download of this script is available: DACIO300-auto-portb-byte-read-portc-byte-set.py

# DACIO300-auto-portb-byte-read-portc-byte-set.py
# Python3 script to run on RaspberryPi to continuously send serial messages to the DACIO 300
# only when GPIO port 17 is set high.
# RS232 interface board to query the logic levels on the digital Port B at 'byte' level
# and set Port C to the same value at byte level in order to mirror the values at Port B
# These levels can be used to drive LEDs, relays etc to reflect Port B status.
# Hardware interfacing with RPi:
# GPIO serial o/p connected to hex i/p of IC4050 running at 3.3v with
# hex output directly connected to TTL interface pin 4 (tx i/p) on the DACIO 300
# the DACIO300 TTL interface pin 5 (rx o/p) is connected to a potential divider
# to drop the TTL 5v to 3.3v, which is then connected to hex input on IC4050.
# the corresponding hex o/p at 3.3v is connected to RPi GPIO serial input.
# GPIO port 17 connected to a switch or set/reset latch circuit to control
# when the script sends/receives serial commands/data

import serial
import time
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN)

x=1
latch_state=0

# set serial parameters
ser = serial.Serial("/dev/ttyAMA0",
                    baudrate=9600,
                    bytesize=8,
                    parity='N',
                    stopbits=1,
                    timeout=1,
                    xonxoff=False,
                    rtscts=False,
                    dsrdtr=False)
ser.close()

def queryByte():
    ser.open()
    ser.write(bytes('!'+port+'?;','utf_8'))
    global byteQuery # make variable available outside function
    byteQuery = ser.readline(4).decode("utf_8","ignore").strip()[-3:]
    print ('\tPort ', port, '\tstate = ', byteQuery)
    ser.close()

def setByte(PortC):
    ser.open()
    ser.write(bytes('!C='+PortC+';','utf_8'))
    print ('\tPort  C', '\tstate = ', PortC)
    bitQuery = ser.readline(1).decode("utf_8","ignore").strip()[-1:]
    if bitQuery == '!':
        print ("Command received by DACIO300 ok:")
    else:
       print ("Error:")
    ser.close()

try:
    while x:
        #print reminder of DACIO 300 default conditions
        print ("Waiting to activate..")
        if latch_state == 0:
            if GPIO.input(17):
                print ("Monitoring starts..")
                print ("This script queries DACIO300 Port B logic levels on each input channel")
                print ("The result displayed is in decimal byte value")
                print ("Port C 'bits' are set to the same values as read on Port B")
                port = 'B'
                latch_state = 1
                queryByte()
                setByte(byteQuery) # pass value obtained from queryByte function to setByte function
                time.sleep(5)
        if latch_state == 1:
            if GPIO.input(17):
                port = 'B'
                queryByte()
                setByte(byteQuery)
                time.sleep(5)
        if latch_state == 1:
            if not GPIO.input(17):
                latch_state = 0
                print ("Monitoring ends..")
                time.sleep(5)
        time.sleep(5)
       
except KeyboardInterrupt:
    GPIO.cleanup()

Wednesday, 15 June 2016

RaspberryPi GPIO Pulse Generator - Windows Based

Background

In one of my previous posts - RaspberryPi Pulse Generator Console Based, I put together a simple Python3 script to take user input supplied parameters to have the RaspberryPi generate pulses from a GPIO port.

Whilst this was adequate for simple applications it had the drawback of having to supply the parameters via keyboard every time. This method of use was also prone to errors due to the entry of incorrect port numbers.

I decided to experiment with the idea of creating a Windows interface to capture user supplied input with the use of a mouse click to start the pulse generator.

I re-used some of the Pulse Generator code and Windows code from the GPIO Ports Set & Toggle application. Some new code was required in order to allow the capture of user input into entry boxes.

Some screen shots are provided below that illustrate the 4 user input areas:
1. GPIO port number
2. Pulse length (the 'on' or 'high' time)
3. Pulse inter-space length (the 'off' or 'low' time)
4. Number of pulses

A button to 'Generate' the pulse(s) and a Quit button to exit the application.

The advantage of providing the 'Generate' button is the ability to create the pulse stream by one click of the mouse. Once the pulse stream has been completed, the generate button can be used again. If the pulse parameters need modifying it is a simple case of changing the value in the relevant input box.

One additional piece of code I included was to check the port number supplied was valid to be used as an output port.


Start-up Window


Parameters entered - pulses generated - and previous values shown



Incorrect Port number with error message when 'Generate' selected

It is possible to run more than one instance of this application using the format:

python3 <filename.py> &

This allows multiple GPIO ports to be used to generate separate pulse streams.

It is important when connecting GPIO ports to external circuits that appropriate interfacing is provided. In the case of connecting +3.3v RaspberryPi GPIO ports to external +5v TTL or CMOS components I prefer to use the CMOS 4050 Hex buffer as detailed in my other blog posts (see the GPIO Set & Toggle example).


Python3 code:

Here is the Python3 code listing for this application. The file can be downloaded from here.

# Python3 script to run on RaspberryPi B to enter pulse parameters within a Window
# Apply the parameters to a RaspberryPi B GPIO port in order to generate a pulse stream
# Port numbers are validated as those available to use as outputs:
# numbers 0,1,4,7,8,9,10,11,14,15,17,18,21,22,23,24,25
# Entered parameters are displayed next to the input areas
# Error message provided if invalid port number entered

from tkinter import *
import RPi.GPIO as GPIO
import time

GPIO.setwarnings(False)
GPIO.cleanup()
GPIO.setmode(GPIO.BCM)

def pulse_gen():
    portlist = ['0','1','4','7','8','9','10','11','14','15','17','18','21','22','23','24','25'] # string list of valid ports available
    ps=(portstr.get()) # get the entered value as a string
    if ps not in portlist: # check if entered value not in list              
        label6=Label(myGUI, text='Invalid port', fg='red').grid(row=1,column=2)
        return # if invalid do not proceed    
    port=int(portstr.get())
    label10=Label(myGUI, text="                  ").grid(row=1,column=2)
    label6=Label(myGUI, text=ps, fg='red').grid(row=1,column=2)
    length=float(lengthstr.get()) # get pulse duration
    label7a=Label(myGUI, text="                  ").grid(row=2,column=2)
    label7=Label(myGUI, text=length, fg='red').grid(row=2,column=2)
    space=float(spacestr.get()) # get intra pulse duration
    label8a=Label(myGUI, text="                  ").grid(row=3,column=2)
    label8=Label(myGUI, text=space, fg='red').grid(row=3,column=2)
    number=int(numberstr.get()) # get number of pulses
    label9a=Label(myGUI, text="                  ").grid(row=4,column=2)
    label9=Label(myGUI, text=number, fg='red').grid(row=4,column=2)

    GPIO.setup(port,GPIO.OUT)
    GPIO.output(port,GPIO.LOW)
    loop = 0
    while loop < number:
        GPIO.output(port,GPIO.HIGH)
        time.sleep(length)
        GPIO.output(port,GPIO.LOW)
        time.sleep(space)
        loop += 1
    return

def finish():
    myGUI.destroy()
    GPIO.cleanup()

myGUI = Tk()
myGUI.geometry('500x170')
myGUI.title('RPi B GPIO Pulse Generator')
myGUI.resizable(0,0)

portstr=StringVar()
lengthstr=StringVar()
spacestr=StringVar()
numberstr=StringVar()

label1=Label(myGUI, text='RaspberryPi Pulse Generator', fg='black').grid(row=0,column=0)
label1a=Label(myGUI, text='Enter values', fg='black').grid(row=0,column=1)
label1b=Label(myGUI, text='Last values', fg='black').grid(row=0,column=2)
label2=Label(myGUI, text='Enter valid GPIO port number').grid(row=1,column=0)
label3=Label(myGUI, text='Enter pulse length (secs)').grid(row=2,column=0)
label4=Label(myGUI, text='Enter pulse inter-space (secs)').grid(row=3,column=0)
label5=Label(myGUI, text='Enter number of pulses').grid(row=4,column=0)

portgpio=Entry(myGUI, textvariable=portstr).grid(row=1,column=1)
lengthgpio=Entry(myGUI, textvariable=lengthstr).grid(row=2,column=1)
spacegpio=Entry(myGUI, textvariable=spacestr).grid(row=3,column=1)
numbergpio=Entry(myGUI, textvariable=numberstr).grid(row=4,column=1)

button1=Button(myGUI,text='Generate', fg='blue', command=pulse_gen).grid(row=5,column=0)
button2=Button(myGUI,text='Quit', fg='red', command=finish).grid(row=6,column=0)

myGUI.mainloop()

Wednesday, 1 June 2016

RaspberryPi GPIO Ports Set & Toggle

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.