#################################################
## Pico I/O Shift Registers Example #############
## Version 5.0.03 #############################
## By: Corey Beck ###############################
import machine
import utime
from utilities import ModeType, Utilities
from calculator import CalculatorKeyPad
from stopwatch import StopWatchKeyPad
#########################################
# DON'T LIKE THE VERBOSITY SET TO FALSE #
#########################################
VERBOSE = False
###########################
# PIN DECLARATIONS
###########################
# Setup OUTPUT Shift register Pins
# Pi Pico
OSR_SDI = machine.Pin(19, machine.Pin.OUT) # DS
OSR_RCLK = machine.Pin(20, machine.Pin.OUT) # STCP
OSR_SRCLK = machine.Pin(21, machine.Pin.OUT) # SHCP
# Setup INPUT Shift register Pins
# Pi Pico
ISR_SDI = machine.Pin(18, machine.Pin.IN) # Q7
ISR_RCLK = machine.Pin(17, machine.Pin.OUT, value=1) # PL
ISR_SRCLK = machine.Pin(16, machine.Pin.OUT, value=0) # CP
# Initialize digit select pins (common cathodes)
# Pi Pico
OSR_DIGIT_PINS = [
machine.Pin(0, machine.Pin.OUT), # Digit 1
machine.Pin(1, machine.Pin.OUT), # Digit 2
machine.Pin(2, machine.Pin.OUT), # Digit 3
machine.Pin(3, machine.Pin.OUT) # Digit 4
]
# Define Pins A-B on 4 Digit 7 Segment Display
# Shift Register
OSR_DISPLAY_PINS = [0, 1, 2, 3, 4, 5, 6, 7] # 74HC595 A(Q0-Q7) Pin# = Q#
# Set to None if ignore colon
# OSR_COLON = None
# Pi Pico
OSR_COLON = machine.Pin(4, machine.Pin.OUT) # CLN
# Define the GPIO pins connected to the rows and columns
# Shift Register
OSR_KEYPAD_ROW_PINS = [8, 9, 10, 11] # 74HC595 B(Q0-Q3) Pin# = [(Q# + len(OSR_DISPLAY_PINS))]
# Shift Register
ISR_KEYPAD_COL_PINS = [7, 6, 5, 4] # 74HC165 A(Q7-Q4) Pin# = Q#
# Define Button Pins
# Shift Register
ISR_BUTTON_PINS = [0, 1, 2, 3] # 74HC165 A(Q0-Q3) Pin# = Q#
# define LEDS Pins
# Output Shift Register
OSR_LED_PINS = [12, 13, 14, 15] # 74HC595 B(Q4-Q7) Pin# = [(Q# + len(OSR_DISPLAY_PINS))]
############################
# Declarations
############################
# Define the characters on the keypad
ISR_KEYS = [
['A', '3', '2', '1'],
['B', '6', '5', '4'],
['C', '9', '8', '7'],
['D', '#', '0', '*']
]
# Define the binary codes for each digit (0-9)
SEGMENT_CODES = [
0x3F, # 0
0x06, # 1
0x5B, # 2
0x4F, # 3
0x66, # 4
0x6D, # 5
0x7D, # 6
0x07, # 7
0x7F, # 8
0x6F # 9
]
utime.sleep(0.1) # Wait for USB to become ready
########################
# ADJUSTMENTS
########################
# sets the button debounce time
button_debounce = 1 # ~second(by 0.02(20ms) cause thats what the clock runs on)
# sets the led change time
led_change_time = 0.4 # ~seconds(by 0.02(20ms) cause thats what the clock runs on)
##############################
# PRIVATE DECLARATIONS
##############################
# used for storing the current mode of this operation using imported from utilities type ModeType
current_mode = ModeType.DEFAULT
# used for button debounce
__current_ticks__ = utime.ticks_ms()
__last_button_ticks__ = utime.ticks_ms()
# used for storage of current display
__current_display__ = ""
# last_key pressed
last_key = None
# used for led counter for keeping track of current led lit
__led_counter__ = 0
__led_selected__ = 0
__last_led_change__ = utime.ticks_ms()
# global last scan buttons return value
__last_scan_buttons = None
# global last read shift return value
__last_read_shift__ = None
# global last display digit return value
__last_display_digit = None
# global last display number return value
__last_display_number__ = None
# used for Calculator Mode
__calculator__ = CalculatorKeyPad() # simple Calculator Base Class with KeyPad capabilities extended
# used for StopWatch Mode
__stopwatch__ = StopWatchKeyPad() # simple StopWatch Base class with KeyPad capabilities extended
###################################
# METHODS
###################################
# Function scans buttons for being pressed and return array of values in an array
# matching button value to button zero-index
def scan_buttons(): return Utilities.scan_buttons(pl=ISR_RCLK, cp=ISR_SRCLK, q7=ISR_SDI, btns=ISR_BUTTON_PINS)
# Function that checks keypad for new inputs to handle and returns the key if detected or None
# -> returns None if no key pressed, or the key pressed as a string representation
def scan_keypad(): return Utilities.scan_keypad(rows=OSR_KEYPAD_ROW_PINS, cols=ISR_KEYPAD_COL_PINS, keys=ISR_KEYS, pl=ISR_RCLK, cp=ISR_SRCLK, q7=ISR_SDI, stcp=OSR_RCLK, shcp=OSR_SRCLK, ds=OSR_SDI)
# Function to display a number on the 4-digit display
# number -> multi digit number or none to just update usecolon explicitly
# usecolon -> True colon will be shown, False colon will be ignored and shut off
def display_number(number=None, usecolon=False, useempty=False): return Utilities.display_number(number=number, usecolon=usecolon, useempty=useempty, digitspins=OSR_DIGIT_PINS, colon=OSR_COLON, segcodes=SEGMENT_CODES, stcp=OSR_RCLK, shcp=OSR_SRCLK, ds=OSR_SDI)
# Function to send membrane key to stopwatch to handle
# key -> key pressed
# verbose -> verbose mode set to true will print out operations
def update_stopwatch_keypress(key, verbose=False): __stopwatch__.update_via_keypad(key, verbose=verbose)
# Function grabs the StopWatch display() and updates it to the current display
def update_stopwatch_display():
# Display Stop Watch
__current_display__ = __stopwatch__.display()
if __current_display__ == None: __current_display__ = "0"
sw_usecolon = (":" in str(__current_display__))
# remove the colon if its present
if sw_usecolon: __current_display__ = Utilities.remove_char(__current_display__, __current_display__.find(":"))
display_number(number=int(__current_display__), usecolon=sw_usecolon, useempty=False)
# Function to send membrane key to calculator to handle
# key -> key pressed
# verbose -> verbose mode set to true will print out operations
def update_calculator_keypress(key, verbose=False): __calculator__.update_via_keypad(key, verbose=verbose)
# Function grabs the Calculator display() and updates it to the current display
def update_calculator_display():
# grab calculator current display from Calculator
__current_display__ = __calculator__.display()
# update display
display_number(number=int(__current_display__), usecolon=False, useempty=True)
return __calculator__.display()
# Function updates LEDS based on ticks and mode parameters
def update_leds(cntr, mode, ledblinktime, lastledticks, ticks): return Utilities.update_leds(cntr=cntr, ledblinktime=ledblinktime, ledpins=OSR_LED_PINS, mode=mode, ticks=ticks, lastledticks=lastledticks, stcp=OSR_RCLK, shcp=OSR_SRCLK, ds=OSR_SDI)
# gets or sets the current program mode
def mode(value=None):
global current_mode
if value == None: return current_mode
else:
if value == ModeType.DEFAULT:
current_mode = value
print("----- Default Mode ---------")
print("[White Button] - Counter")
print("[Green Button] - Calculator")
print("[Yellow Button] - Stop Watch")
print("[Blue Button] - Mode 4")
print("----------------------------")
elif value == ModeType.COUNTER:
current_mode = value
print("----- Counter Mode -----------------")
print("Just counts the seconds that has")
print("elapsed since microprocessor powered")
print("on")
print("Keypad [*] = Go To Default Mode")
print("------------------------------------")
elif value == ModeType.CALCULATOR:
current_mode = value
# Clear Display
__calculator__.clear(verbose=VERBOSE)
# Clear Mode
# -1 == Calculator.CalculationTypes.NONE
__calculator__.mode(-1, verbose=VERBOSE)
# Clear actual display to 0
__current_display__ = "0"
print("----- Calculator Mode -------------")
print("Keypad [A] = [+]")
print("Keypad [B] = [-]")
print("Keypad [C] = [Clear]")
print("Keypad [D] = [=]")
print("Keypad [*] = [x]")
print("Keypad [#] = [/]")
print("Keypad [0-9] = [0-9]")
print("-----------------------------------")
elif value == ModeType.STOPWATCH:
current_mode = value
# Make sure to reset StopWatch
__stopwatch__.reset()
print("----- StopWatch Mode -------------")
print("Keypad [A] = [Start]")
print("Keypad [B] = [Stop]")
print("Keypad [C] = [Un/Pause]")
print("Keypad [D] = [Reset]")
print("Keypad [*] = Go To Default Mode")
print("Keypad [#] = disabled")
print("Keypad [0-9] = disabled")
print("-----------------------------------")
elif value == ModeType.MODE4:
current_mode = value
print("----- Mode 4 Mode -------------")
print("Keypad [*] = Go To Default Mode")
print("-------------------------------")
# Set Mode To Default
mode(ModeType.DEFAULT)
##################
# System Loop
##################
while True:
# Update current ticks for evaluation purposes
__current_ticks__ = utime.ticks_ms()
# update leds
__led_counter__ = update_leds(cntr=__led_counter__, mode=mode(), ledblinktime=led_change_time, ticks=__current_ticks__, lastledticks=__last_led_change__)
if utime.ticks_diff(__current_ticks__, __last_led_change__) > led_change_time * 1000: __last_led_change__ = utime.ticks_ms()
__led_selected__ = __led_counter__
# scan keypad
key = scan_keypad()
# handle key pressed if any
if key != last_key:
if key is not None:
if mode() == ModeType.COUNTER:
# CounterKey Handling
if key == "*":
if VERBOSE: print("Counter: '{}' Key pressed: Set Mode to: ModeType.DEFAULT".format(key))
mode(ModeType.DEFAULT)
else:
if VERBOSE: print("Counter: Key pressed:", key)
pass
elif mode() == ModeType.CALCULATOR:
# Calculator Key Handling
update_calculator_keypress(key=key, verbose=VERBOSE)
elif mode() == ModeType.STOPWATCH:
if key == "*":
if VERBOSE: print("Stop Watch: '{}' Key pressed: Set Mode to: ModeType.DEFAULT".format(key))
mode(ModeType.DEFAULT)
else:
# Stop Watch Key Handling
update_stopwatch_keypress(key=key, verbose=VERBOSE)
elif mode() == ModeType.MODE4:
# Mode 4 Key Handling
if key == "*":
if VERBOSE: print("Mode 4: '{}' Key pressed: Set Mode to: ModeType.DEFAULT".format(key))
mode(ModeType.DEFAULT)
else:
if VERBOSE: print("Mode 4: Key pressed:", key)
pass
else:
# key was pressed
if VERBOSE: print("Default: Key pressed:", key)
pass
last_key = key
# Update Display
# current display default ticks since started in seconds format
# only numbers
if mode() == ModeType.DEFAULT or mode() == ModeType.COUNTER:
# Display Counter and Default
__current_display__ = str(int((__current_ticks__ / 1000)))
display_number(number=int(__current_display__), usecolon=False, useempty=True)
elif mode() == ModeType.CALCULATOR:
# Display Calculator
# update calculator mode display
update_calculator_display()
elif mode() == ModeType.STOPWATCH:
# Display Stop Watch
# update stopwatch mode display
update_stopwatch_display()
elif mode() == ModeType.MODE4:
# Display mode 4
__current_display__ = "0"
display_number(number=int(__current_display__), usecolon=False, useempty=True)
# display 0 if none
if __current_display__ == None: display_number(number=0, usecolon=False)
# scan buttons
# returns an array of buttons is pressed value in the same order the pins are defined
buttons = scan_buttons()
# determine if buttons was pressed and is outside debounce time frame
debounced = utime.ticks_diff(__current_ticks__, __last_button_ticks__) > button_debounce * 1000
if buttons[0] and debounced: button1 = buttons[0]
else: button1 = False
if buttons[1] and debounced: button2 = buttons[1]
else: button2 = False
if buttons[2] and debounced: button3 = buttons[2]
else: button3 = False
if buttons[3] and debounced: button4 = buttons[3]
else: button4 = False
# Evaluate buttons to see if they are pressed
if button1:
# White button Pressed
if mode() != ModeType.COUNTER:
if VERBOSE: print("White button pressed! -> Set Mode to: ModeType.COUNTER")
mode(ModeType.COUNTER)
__last_button_ticks__ = utime.ticks_ms()
if button2:
# Green button Pressed
if mode() != ModeType.CALCULATOR:
if VERBOSE: print("Green button pressed! -> Set Mode to: ModeType.CALCULATOR")
mode(ModeType.CALCULATOR)
__last_button_ticks__ = utime.ticks_ms()
if button3:
# Yellow button Pressed
if mode() != ModeType.STOPWATCH:
if VERBOSE: print("Yellow button pressed! -> Set Mode to: ModeType.STOPWATCH")
mode(ModeType.STOPWATCH)
__last_button_ticks__ = utime.ticks_ms()
if button4:
# Blue button Pressed
if mode() != ModeType.MODE4:
if VERBOSE: print("Blue button pressed! -> Set Mode to: ModeType.MODE4")
mode(ModeType.MODE4)
__last_button_ticks__ = utime.ticks_ms()
# update debounce settings
if button4 or button3 or button2 or button1: __last_button_ticks__ = utime.ticks_ms()
# slow it down for eyes to view
utime.sleep(0.02) # 20 ms
White Button - Counter Mode
Green Button - Calculator Mode
Yellow Button - Stop Watch Mode
Blue Button - Mode 4