Page 1 of 1

Compact low-power 8bit Binary Display (pwm, signals, more)

Posted: Thu Jun 02, 2016 3:46 am
by MarkHaysHarris777
bin_8bit_dsp.jpg
8 bit binary display, compact low-power, inexpensive
bin_8bit_dsp.jpg (36.17 KiB) Viewed 766 times
I have been trying to achieve an 8bit compact low power display for GPIO code
experimentation; I think I achieved it with this!

This display is an 8 LED common anode mini-board comprised of 8 3mm LEDs each
with a forward current of 2ma, and having a forward voltate of 1.9v. All of the
ballast resistors are 330 ohm, 1/4 watt (the smaller ones).

The anodes of the LEDs are tied together (orange wires) and pinned to GPIO18
pin(12); the cathodes are individually pinned to four low order pins for green
board pins[37, 35, 33, 32), and to four high order pins for red [18, 15, 13, 11].

(The PWM & signal code, that drives this display demo, can be found below.)

Again, even though these diodes have very similar characteristics, they are
quite different, so that for a given set of ballast resistors the brightness
of the green LEDs is less than that for the red LEDs. We solve this problem
with PWM as the +voltage source for this common anode display.

Also, similar to the three-color RGB LED demo, we communicate with this display code
via interrupt signals. I have provided four signal handlers for this binary
display counter:

SIGINT interrupt will end the program (ctl-c)
SIGHUP will shift the counter to the other colored display register
SIGUSR1 will swap the high-low counter order of the display (either color)
SIGUSR2 will flash all LEDs rapidly in sequence in a fast Z pattern

The codes below are free to study, distribute, or to paper your bird cage. The
driver PWM_counter.sh requies the newer version of binary.py.

edit: run the code with:

Code: Select all

./PWM_counter.sh  38  &
PWM_counter.sh

Code: Select all

#!/usr/bin/python
#
#  PWM_counter.sh
#
#  Mark H. Harris
#  06-03-2016
#  v.01e
#  Rochester, MN  55906
#
#
from binary import *

## obtain red LED pin definitions from binary.py rom dictionary
redHIGH = []
for n in range(4):
    redHIGH.append(binH_rom_STD[n][0])

## obtain green LED pin definitions from binary.py rom dictionary
greenLOW = []
for n in range(4):
    greenLOW.append(binL_rom_STD[n][0])

## Base duty_cycle <26 - 40>
#     red is more intense, need to be dimmed
#     green needs a little more intensity
base_duty_cycle = 38
red_duty_offset = -25  
green_duty_offset = 40
duty_cycle = base_duty_cycle

## Color LED flag  True:Green  False:Red
color_led_flag = False

## XOR flag   True:use XOR display
xor_flag = True

## STD REV  high-low order for display
std_rev = "REV"

## Default pin definitions (colorORDER)
pins = redHIGH

## string value of counter for HUP signal display
hup_value=str(0)

## CMD Flag : gets set if counter>=8 see bin_counter()
#      affects USR1 & USR2 signal processing
#  cmd_flag: True   USR1: inverts high-low order  
#  cmd_flag: False  USR1: inverts xor display
#  cmd_flag: True   USR2: flasher bars
#  cmd_flag: False  USR2: highZ pattern
cmd_flag=False

## Single pin ON
def pI_on(pin,duty):
    p.ChangeDutyCycle(duty)
    led_off(pin)

## Single pin OFF
def pI_off(pin,duty):
    p.ChangeDutyCycle(duty)
    led_on(pin)

## Set all pins ON
def pI_all(pins, duty):
    p.ChangeDutyCycle(duty)
    for pin in pins:
        pI_on(pin, duty)

## pin on no pwm control
def p_on(pin):
    led_off(pin)

## pin off no pwm control
def p_off(pin):
    led_on(pin)

def p_all(pins):
    for pin in pins:
        p_on(pin)

## Set all pins OFF
def p_none():
    for pin in redHIGH:
        p_off(pin)
    for pin in greenLOW:
        p_off(pin)

p_none()

## Main Routine
def bin_counter(duty):
    global hup_value
    global cmd_flag
    p.start(duty+red_duty_offset)
    while(1):
        for n in range(16):
            if (n>=8):
                cmd_flag=True
            else:
                cmd_flag=False
            hup_value = bin(n)+' '+hex(n)
            if (color_led_flag):
                p.ChangeDutyCycle(duty+green_duty_offset)
                if (xor_flag):
                    nl_xor(n,std_rev)
                else:
                    nl(n,std_rev)
            else:
                p.ChangeDutyCycle(duty+red_duty_offset)
                if (xor_flag):
                    nh_xor(n,std_rev)
                else:
                    nh(n,std_rev)
            sleep(0.77)

## Signal Handlers  for  HUP, USR1, USR1
#  USR2
def ssigusr2(signum, frame):
    if (cmd_flag):
        print("USR2:"+str(signum)+" caught: high-Z pattern")
        print(" ")
        for n in range(7):
            for pin in greenLOW:
                p_none()
                pI_on(pin,duty_cycle+green_duty_offset)
                sleep(.047)
            for pin in redHIGH:
                p_none()
                pI_on(pin,duty_cycle+red_duty_offset)
                sleep(.047)
                p_none()
    else:
        print("USR2:"+str(signum)+" caught: flasher bars ON")
        print(" ")
        for n in range(7):
            pI_all(greenLOW, duty_cycle+green_duty_offset)
            sleep(.128)            
            p_none()
            pI_all(redHIGH, duty_cycle+red_duty_offset)
            sleep(.128)
            p_none()

# USR1
def ssigusr1(signum, frame):
    global std_rev
    global xor_flag
    if (cmd_flag):
        if (std_rev=="REV"):
            std_rev="STD"
        else:
            std_rev="REV"
        print("USR1:"+str(signum)+" caught: inverting high-low order")
        print(" ")
    else:
        if (xor_flag):
            xor_flag=False
        else:
            xor_flag=True
        print("USR1:"+str(signum)+" caught: inverting xor display")
        print(" ")

# HUP
def ssighup(signum, frame):
    global color_led_flag
    global pins
    p_none()
    if (color_led_flag):
        pins=redHIGH 
        color_led_flag=False
        offset=red_duty_offset
        regColor="RED"
    else:
        pins=greenLOW
        color_led_flag=True
        offset=green_duty_offset
        regColor="GREEN"
    p_all(pins)
    print("HUP:"+str(signum)+" caught: shifting to "+regColor+" display: "+hup_value)
    print(" ")
    for n in range(3):
        if (color_led_flag):
            p.ChangeDutyCycle(duty_cycle+offset+20)
        else:
            p.ChangeDutyCycle(duty_cycle+offset+5)
        sleep(.4)
        if (color_led_flag):
            p.ChangeDutyCycle(14)
        else:
            p.ChangeDutyCycle(1)
        sleep(.4)
    p_none()

signal.signal(signal.SIGHUP, ssighup)
signal.signal(signal.SIGUSR1, ssigusr1)
signal.signal(signal.SIGUSR2, ssigusr2)

## Main Try Block calls Main Routine
#
try:
    duty_cycle = base_duty_cycle
    bin_counter(duty_cycle)
except KeyboardInterrupt:
    print("blinker ended by interrupt, bye!")
    print(" ")
except:
    print(" ")
    print("unmonitored exception in bin_counter()")
finally:
    p.stop()
    gpio.cleanup()

binary.py

Code: Select all

#################################################################
## binary.py header for BreadBoard (8) bit LED binary display
#  v0.02e
#
#  Mark H. Harris
#  6-01-2016
#  Rochester, MN
#
#################################################################
## 
# Import the GPIO library and set the mode to "board numbering"
#
from time import sleep
from sys import argv
import signal, os
import RPi.GPIO as gpio
gpio.setmode(gpio.BOARD)

# Define the binary pin dictionarys REV {reverse} STD {standard} 
#      note 1: GPIO #s in these dictionarys are text labels only.
#                    {index:[pin#,"GPIO#"]}
bin_rom_STD = {0:[37,"GPIO26"], 1:[35,"GPIO19"], 2:[33,"GPIO13"], 3:[32,"GPIO12"], 4:[18,"GPIO24"], 5:[15,"GPIO22"], 6:[13,"GPIO27"], 7:[11,"GPIO17"]}

bin_rom_REV = {7:[37,"GPIO26"], 6:[35,"GPIO19"], 5:[33,"GPIO13"], 4:[32,"GPIO12"], 3:[18,"GPIO24"], 2:[15,"GPIO22"], 1:[13,"GPIO27"], 0:[11,"GPIO17"]}

binH_rom_STD = {0:[18,"GPIO24"], 1:[15,"GPIO22"], 2:[13,"GPIO27"], 3:[11,"GPIO17"]}

binL_rom_STD = {0:[37,"GPIO26"], 1:[35,"GPIO19"], 2:[33,"GPIO13"], 3:[32,"GPIO12"]}

binH_rom_REV = {3:[18,"GPIO24"], 2:[15,"GPIO22"], 1:[13,"GPIO27"], 0:[11,"GPIO17"]}

binL_rom_REV = {3:[37,"GPIO26"], 2:[35,"GPIO19"], 1:[33,"GPIO13"], 0:[32,"GPIO12"]}

# Set the pinblock for PWM on GPIO18
#
gpio.setup(12, gpio.OUT)
p = gpio.PWM(12, 440)
p.stop()

# Set the pinblock for input on GPIO16
#
button1=36
gpio.setup(button1, gpio.IN, pull_up_down=gpio.PUD_UP)

# Set the pinblock for output on all eight dictionary pins
#
for n in range(8):
    gpio.setup(bin_rom_STD[n][0],gpio.OUT)
    
## FUNCTION DEFINTIONS ##########################################
#
def push_button():
    if gpio.input(button1):
        return False
    else:
        return True

def led_on(pin):
    gpio.output(pin, True)

def led_off(pin):
    gpio.output(pin, False)

def test_leds(orient):
    if (orient=="REV"):
        pins=bin_rom_REV
    else:
        pins=bin_rom_STD
    for n in range(8):
        led_on(pins[n][0])
        sleep(.68)
    for n in range(7,-1,-1):
        led_off(pins[n][0])
        sleep(.68)

def bin_nibble_display(nval,high_low,orient):
    if (orient=="REV"):
       if (high_low=="H"):
           pins=binH_rom_REV
       else:
           pins=binL_rom_REV
    else:
       if (high_low=="H"):
           pins=binH_rom_STD
       else:
           pins=binL_rom_STD
    for n in range(3,-1,-1):
        if (nval & 2**n != 0):
            led_on(pins[n][0])
        else:
            led_off(pins[n][0])

def nl_xor(val,std_rev):
    xor_val = val^0xf
    bin_nibble_display(xor_val,"L",std_rev)

def nh_xor(val,std_rev):
    xor_val = val^0xf
    bin_nibble_display(xor_val,"H",std_rev)

def nl(val,std_rev):
    bin_nibble_display(val,"L",std_rev)

def nh(val,std_rev):
    bin_nibble_display(val,"H",std_rev)

def binary_display(nval,orient):
    if (orient=="REV"):
        pins=bin_rom_REV
    else:
        pins=bin_rom_STD
    for n in range(7,-1,-1):
        if (nval & 2**n != 0):
            led_on(pins[n][0])
        else:
            led_off(pins[n][0])

def bd(val):
    binary_display(val,"REV")

def all_off():
    nl(0x0)
    nh(0x0)

def all_on():
    bd(0xff)

def p_count(orient,t_delay):
    button=False
    for n in range(1,256,1):
        binary_display(n,orient)
        sleep(t_delay)
        if push_button():
            button=True
            break
    return button

def t_count(orient,t_delay):
    for n in range(1,256,1):
        binary_display(n,orient)
        sleep(t_delay)
        if push_button():
            sleep(2)
            break

def counter(maxVal,orient,t_delay):
    for n in range(1,maxVal,1):
        binary_display(n,orient)
        sleep(t_delay)

def count(orient,t_delay):
    for n in range(1,256,1):
        binary_display(n,orient)
        sleep(t_delay)

def walk(orient,t_delay):
    for n in range(8):
        binary_display(2**n,orient)
        sleep(t_delay)
    all_off()

def dsp_rom(high_low,orient):
    if (orient=="REV"):
       if (high_low=="H"):
           pins=binH_rom_REV
       else:
           pins=binL_rom_REV
    else:
       if (high_low=="H"):
           pins=binH_rom_STD
       else:
           pins=binL_rom_STD
    for n in range(4):
        print(pins[n][1]," BRD#:",pins[n][0],"  index:",n)

def dsp_bin_rom():
    print("L------------------------------------------")
    dsp_rom("L","REV")
    print("H------------------------------------------")
    dsp_rom("H","REV")

def end():
    gpio.cleanup()
    quit()


marcus

edit: PS When signal SIGHUP is sent the code will shift to the other register... with an intermediate PWM flashing of low-high that somewhat resembles the brake-lights on a modern vehicle. If you scan the area quickly with your eyes, or shake gently the display, you can actually see the PWM working !
I find this extremely annoying on the highway at night; the new Cadillac is particularly annoying... because they have their PWM frequency set to low, and the high brightness set too high. Because red has a VERY intense persistence of vision on the eye's retinas, if PWM is not implemented correctly the effect is somewhat less than desireable !

The PWM freq is set in this demo to concert A, 440 Hz.

marcus

Re: Compact low-power 8bit Binary Display (pwm, signals, mor

Posted: Fri Jun 03, 2016 6:51 am
by MarkHaysHarris777
... couple things; I changed my basic send signal script so I thought I would share that here, since this project requires that capability. I should also point out that all of the signals can be sent with the htop program (one of that facility's strengths!)

ssig.sh

Code: Select all

#!/bin/sh
exec  kill  -SIG$2  $1
To send a hup signal to process 1192 use:

Code: Select all

ssig.sh  1192  HUP
... be sure to place the ssig.sh send signal script in ~/bin/ and make it executable with :

Code: Select all

chmod  0754  ~/bin/ssig.sh
Now for an interesting situation that arose for me this evening with this project (I was explaining it to someone and in the process I managed to pull out one of my red LEDs and then place it back into the display backwards! :oops:

And it still worked !! :o

:shock:

... what is going on? ... and to make things worse even though it still worked (and LEDs plugged in backwards don't work) it didn't work right...

Well, for duty cycles near the center of the range, pins in PWM are both source and drain ! I was genuinely surprised momentarily by this; until I thought about it. Because the PWM pin oscillates between 3v3 and ground rapidly (duty_cycle) it can be a source (3v3) and a drain (ground) and the GPIO pin will be source or drain (depending on logic)... soooo, when lighting an LED with PWM it can be plugged in either way! However, if plugged in anode to PWM pin the logic will be GPIO-OFF:LED-ON, and if the LED is plugged cathode to PWM pin the logic will be reversed GPIO-ON: LED-ON. :?

what a hoot...

edit: I should also point out that not only will the PWM logic be reversed (if you plug your LED in backwards, but the duty cycle will also be reversed ! :roll:

Re: Compact low-power 8bit Binary Display (pwm, signals, mor

Posted: Fri Jun 03, 2016 5:16 pm
by Burngate
Do you mind if I post a small diagram, as working with words only causes me to lose the thread?
1screen,b60.png
1screen,b60.png (5.97 KiB) Viewed 636 times
Aside, We in the centre of the civilised world have yet to see that new Cadillac, though doubtless it'll arrive soon!

Re: Compact low-power 8bit Binary Display (pwm, signals, mor

Posted: Fri Jun 03, 2016 10:23 pm
by MarkHaysHarris777
Burngate wrote:Do you mind if I post a small diagram, as working with words only causes me to lose the thread?
1screen,b60.png
@Burngate, not at all, thank you ! :)

edit: OP updated the PWM_counter.sh code (first post) {final}.

The signal handler has been expanded to allow the USR1 and USR2 signals to have multiple functions!
(based on the display counter value) So, if the high-order bit is set on USR1 (or USR2) behave differently. Also, I created a new signal sending mechanism (code below) that removes the need to provide the PID in the command line. The pwmsig.sh script determines what the PID is, and also determines whether the input signal is valid.

Code: Select all

pwmsig.sh HUP                swaps display register color
pwmsig.sh INT                  terminates the PWM_counter.sh code
pwmsig.sh USR1              high-order bit set:  inverts the high-low order display
pwmsig.sh USR1              high-order off: inverts the xor display (binary space counter)
pwmsig.sh USR2              high-order bit set:  high-Z rapid light pattern
pwmsig.sh USR2              high-order off:  green-red  flasher bars
pwmsig.sh

Code: Select all

#!/bin/bash
echo
PWMPID=$(ps ax |grep PWM_counter |grep -v grep |awk '{print $1}' )
echo "PID: $PWMPID"
if [ "$PWMPID" != "" ]
  then
    if [[ "$1" != "HUP" && "$1" != "INT" && "$1" != "USR1" && "$1" != "USR2" ]]
      then
        echo "Error: valid signals are HUP INT USR1 USR2"
      else
        exec kill -SIG$1 $PWMPID
    fi
  else
    echo "PWM_counter.sh does not appear to be running"
fi

Notice that the code above uses /bin/bash. Typically, I use /bin/sh, which on most gnu+linux systems defaults to bash (some others too). On the Raspberry PI (Debian Jessie) /bin/sh links to dash. Unfortunately dash does not support [[ ]] double bracket tests ! That's why I explicitly set /bin/bash.
I don't remember if I had to load that from repositories, at this point. ;)

marcus