PINE64-MAX7219
view release on metacpan or search on metacpan
lib/PINE64/MAX7219.pm view on Meta::CPAN
#!/usr/bin/perl -w
use strict;
use PINE64::GPIO;
use Time::HiRes qw(usleep);
package PINE64::MAX7219;
our $VERSION = '0.9101';
#routines to control a MAX7219 8-digit LED
#display driver.
#This is implemented as bit-banged SPI.
#data is shifted into regs on rising edge of CLK,
#output displayed on rising edge of LOAD
#NOTES:
#This version supports cascading several 7219's. I have
#noticed that even with programs that assume only one
#7219, that if there is another cascaded, the exact same
#output is mirrored on subsequent displays.
#I assume that turn_on, set_intenisty, set_scanlimit, etc
#all get shifted into the next chip anyway, however,
#these subroutines take the number of cascaded 7219s as
#an argument and shift in and latch the necessary
#controls to use the display.
#global vars for gpio init function
#$clk SPI clock
#$ds SPI data
#$load SPI chip select
my ($clk, $ds, $load);
############### GLOBAL VARS #####################
#shutdown register
my @sdreg = (0,0,0,0,1,1,0,0);
my @turn_on = (0,0,0,0,0,0,0,1);
my @turn_off = (0,0,0,0,0,0,0,0);
#set scan limit register
my @slimreg = (0,0,0,0,1,0,1,1);
#last 3 LSBs scan all 8 digits
my @slregall = (0,0,0,0,0,1,1,1);
#display test register
my @disptstreg = (0,0,0,0,1,1,1,1);
#intensity register
my @intreg = (0,0,0,0,1,0,1,0);
#digit addrs
#first 4 bits D15-D12
#don't care bits, then addr
my @_d0 = (0,1,0,1,0,0,0,1);
my @_d1 = (0,1,0,1,0,0,1,0);
my @_d2 = (0,1,0,1,0,0,1,1);
my @_d3 = (0,1,0,1,0,1,0,0);
my @_d4 = (0,1,0,1,0,1,0,1);
my @_d5 = (0,1,0,1,0,1,1,0);
my @_d6 = (0,1,0,1,0,1,1,1);
my @_d7 = (0,1,0,1,1,0,0,0);
#0-9
my @_1 = (0,0,1,1,0,0,0,0);
my @_2 = (0,1,1,0,1,1,0,1);
my @_3 = (0,1,1,1,1,0,0,1);
my @_4 = (0,0,1,1,0,0,1,1);
my @_5 = (0,1,0,1,1,0,1,1);
my @_6 = (0,1,0,1,1,1,1,1);
my @_7 = (0,1,1,1,0,0,0,0);
my @_8 = (0,1,1,1,1,1,1,1);
my @_9 = (0,1,1,1,0,0,1,1);
my @_0 = (0,1,1,1,1,1,1,0);
my @_all = (1,1,1,1,1,1,1,1);
#alpha chars
my @_a = (0,1,1,1,0,1,1,1);
my @_b = (0,0,0,1,1,1,1,1);
my @_c = (0,1,0,0,1,1,1,0);
my @_d = (0,0,1,1,1,1,0,1);
my @_e = (0,1,0,0,1,1,1,1);
my @_f = (0,1,0,0,0,1,1,1);
my @_g = (0,1,1,1,1,0,1,1);
lib/PINE64/MAX7219.pm view on Meta::CPAN
'T' => \@_t,
'U' => \@_u,
'V' => \@_v,
'W' => \@_w,
'X' => \@_x,
'Y' => \@_y,
'Z' => \@_z,
'0', => \@_0,
'1', => \@_1,
'2', => \@_2,
'3', => \@_3,
'4', => \@_4,
'5', => \@_5,
'6', => \@_6,
'7', => \@_7,
'8', => \@_8,
'9', => \@_9,
'.' => \@_period,
'-' => \@_dash,
',' => \@_comma,
'?' => \@_question,
'!' => \@_exclaimation
);#end %alphanums declaration
#instantiate PINE64 gpio device
my $p64 = PINE64::GPIO->new();
############### SUBROUTINES #####################
sub new{
my $class = shift;
my $self = bless {}, $class;
#args are the GPIO pin numbers from PINE64::GPIO
#that will be used for bit-bang SPI
#initializes gpio lines and set to low
$clk = $_[0];
$ds = $_[1];
$load = $_[2];
$p64->gpio_enable($clk, 'out');
$p64->gpio_write($clk, 0);
$p64->gpio_enable($ds, 'out');
$p64->gpio_write($ds, 0);
$p64->gpio_enable($load, 'out');
$p64->gpio_write($load, 0);
return $self;
}#end new
sub shift_in{
#data is shifted in with 16 bit packets: 8-bit segment,
#4-bit digit address, 4-bit dont care bits
#used by internal methods only
#array ref seg data
my $leds = $_[0];
#array ref seg addr
my $addr = $_[1];
#number cascaded 7219s
my $ncas = $_[2];
#delay in milliseconds
my $delay = $_[3];
#latch flag
my $lf = $_[4];
#high flag for data gpio line
my $hf = 0;
#my $ncp = $ncas * 32; #min number 32 for 16 clock pulses
my $ncp = 32;
##print "ncp: $ncp\n";
#main clock loop
my $i=0;
#$state toggles from 1 to 0 needed for clock pulse
my $state = 0;
#ensures first pulse goes from low to high
my $seed = 3;
while($i<$ncp){#correct num clock pulses
$state = $seed%2;
$seed++;
#load data in last 8 clock pulses
#make DS high before clock pulse; only on even num
#so index array is whole number
#MSB read first, then addr, last data
if(($i%2) eq 0){
#address D8-D11, and don't care bits D12-D15
if($addr->[$i/2] == 1 && $i<=14){
$p64->gpio_write($ds,1);
$hf = 1;#set high flag
}#end addr high bit
#7-seg data, D0-D7
if($leds->[($i-16)/2] == 1 && $i > 14){#array ref, light this led up
#test; set q1 high
##print "inside data loop, i: $i\tindex: " .(($i-16)/2) . "\n";
$p64->gpio_write($ds, 1);
$hf = 1;#set high flag
}#end if build D0-D7
}#end if even $i
#TEST
#print "i:\t$i\nD:\t$hf\n\n";
#toggle clock pulse
$p64->gpio_write($clk, $state);
Time::HiRes::usleep($delay);#sleep .001 sec
#lower data if high flag set
if($hf eq 1){
$p64->gpio_write($ds,0);
$hf = 0;#reset high flag
}#end if
$i++;
lib/PINE64/MAX7219.pm view on Meta::CPAN
}#end if
}#end for numwords
}#end print_sentence
sub print_interleaved{
#takes separate strings for each
#line of displays. The strings are
#assumed to fit into the 8-digit
#line of an led array for a 7219
my $str1 = $_[1];
my $str2 = $_[2];
#upper case
$str1 = uc $str1;
$str2 = uc $str2;
#convert strings to array of chars
my @s1 = split //, $str1;
my @s2 = split //, $str2;
#pad string arrays with spaces
#if less than 8 chars
my $s1_len = @s1;
my $s2_len = @s2;
#print "s1len: $s1_len\ts2len: $s2_len\n";
if($s1_len < 8){
my $nsp = 8-$s1_len;
for(my $n=$s1_len;$n<8;$n++){
$s1[$n] = " ";
}#end for pad w/ spaces
}#end pad str1 with spaces
if($s2_len < 8){
my $nsp = 8-$s2_len;
for(my $n=$s2_len;$n<8;$n++){
$s2[$n] = " ";
}#end for pad w/ spaces
}#end pad str1 with spaces
$s1_len = @s1;
$s2_len = @s2;
#print "s1len: $s1_len\ts2len: $s2_len\n";
#reverse @all_digits to display words
#left to right
my @rev_segs = reverse @all_digits;
#init letter number to 0
my $ln = 0;
foreach my $digit (@rev_segs){
shift_in($alphanums{$s2[$ln]}, $digit, 1, 250, 0 );
shift_in($alphanums{$s1[$ln]}, $digit, 1, 250, 0 );
load();
$ln++;
}#end for
}#end print_interleaved
sub turn_on{
#ncas is number of cascaded MAX7219 displays
my $ncas = $_[1];
if(defined($ncas)){
#print "tu ncas defined\n";
for(my $ni=0;$ni<$ncas;$ni++){
shift_in(\@turn_on, \@sdreg, $ncas, 250, 0);
if($ni ==($ncas-1)){
#print "tu load\n";
load();
}#end if
}#end for
}#end if multiple 7219's
else{#just one
#set shutdown register to normal operation
shift_in(\@turn_on, \@sdreg, 1, 250, 1);
}#end else
}#end turn_on
sub turn_off{
#ncas is number of cascaded MAX7219 displays
my $ncas = $_[1];
if(defined($ncas)){
for(my $ni=0;$ni<$ncas;$ni++){
shift_in(\@turn_off, \@sdreg, $ncas, 250, 0);
if($ni ==($ncas-1)){
load();
}#end if
}#end for
}#end if multiple 7219's
else{#just one
#set shutdown register to off
shift_in(\@turn_off, \@sdreg, 1, 250, 1);
}#end else
}#end turn_off
sub set_scanlimit{
#ncas is number of cascaded MAX7219 displays
my $ncas = $_[1];
if(defined($ncas)){
#print "sl ncas defined\n";
for(my $ni=0;$ni<$ncas;$ni++){
shift_in(\@slregall, \@slimreg, $ncas, 250, 0);
if($ni ==($ncas-1)){
load();
#print "sl load\n";
}#end if
}#end for
}#end if multiple 7219's
else{#just one
#set scan limit register
shift_in(\@slregall, \@slimreg, 1, 500, 1);
#print "sl 1 chip\n";
}#end else
}#end set_scanlimit
sub set_intensity{
#takes string as arg: min, dim, mid, bright, max
my $intensity = $_[1];
#default to max
my @intregdata = (0,0,0,0,1,1,1,1);
if($intensity eq 'min'){
@intregdata = (0,0,0,0,0,0,0,0);
}#end if
if($intensity eq 'dim'){
@intregdata = (0,0,0,0,0,0,1,1);
}#end if
if($intensity eq 'mid'){
@intregdata = (0,0,0,0,0,1,1,1);
}#end if
if($intensity eq 'bright'){
@intregdata = (0,0,0,0,1,0,1,1);
}#end if
if($intensity eq 'max'){
@intregdata = (0,0,0,0,1,1,1,1);
}#end if
#print "Intensity: $intensity\n";
shift_in(\@intregdata, \@intreg, 1, 250, 1);
}#end set_intensity
sub all_off{
#clear display
#call after turned on, and
#scan reg set to all digits
my $ncas = $_[1];
if(defined($ncas)){
foreach my $digit(@all_digits){
for(my $ni=0;$ni<$ncas;$ni++){
shift_in(\@turn_off, $digit, $ncas, 100, 0);
if($ni ==($ncas-1)){
load();
}#end if
}#end for
}#end outer for
}#end if multiple 7219's
else{#just one
lib/PINE64/MAX7219.pm view on Meta::CPAN
shift_in(\@_seg_d, @all_digits[$i], 1, 100, 1);
all_off();
}#end inner for
}#end outer for
all_off();
}#end bullets_lrtop
sub bullets_rlbot {
#number of iterations
my $ni = $_[1];
for(my $n=0;$n<$ni;$n++){
for(my $i=0;$i<=8;$i++){
shift_in(\@_seg_d, @all_digits[$i], 1, 100, 1);
all_off();
}#end inner for
}#end outer for
all_off();
}#end bullets_lrtop
1;
__END__
=head1 NAME
PINE64::MAX7219 driver for 8-digit 7-seg MAX7219 displays
=head1 SYNOPSIS
use PINE64::MAX7219;
my $max = PINE64::MAX7219->new(0,1,2);
$max->turn_on(1);
$max->set_scanlimit(1);
$max->set_intensity('max');
#display test (all on / all off))
$max->disp_teston();
sleep(2);
$max->disp_testoff();
#clockwise circles
$max->clockwise_circles(10);
#counter clockwise circles
$max->countercw_circles(10);
#print a sentence, 0.5sec / word
$max->print_sentence("perl rules on pine64", 500000);
#endless KnightRider effect!
for(;;){
$max->bullets_lrmid(1);
$max->bullets_rlmid(1);
}#end for
=head1 DESCRIPTION
This module is a driver for 8-digit seven-segment MAX7219 displays. It
is implemented as bit-banged SPI. Using the object's methods, you can
set the intensity of the display, print words, and cascade multiple
displays. It also comes with several built-in effects.
Only three GPIO pins are required: SPI clk, SPI data, and SPI chip
select. This modules uses the PINE64::GPIO numbering scheme.
=head1 METHODS
=head2 new($clock,$data,$chip_select)
Takes the GPIO pin numbers that will be used to inplement the bit-bang
SPI interface to the MAX7219 as arguments. Returns an object to
control an 8-digit display.
=head2 shift_in($leds, $digit, $n_cascaded, $delay, $latch_flag)
This method is only used internally. It takes an array of a single
seven-segment's LEDs, the digit position, the number of cascaded
MAX7219 displays, a delay in usec (between SPI clock pulses), and a
latch flag. Each individual letter of a word is shifted in one at
a time. Once all the letters are shifted in, the latch_flag is set
high, and displays the word.
=head2 load()
This method is only used internally. It manipulates the chip select
line, aka latch pin. When called, it will render what has been
shifted into the display.
=head2 print_sentence($sentence, $delay, $clear_flag)
Perhaps the most useful method. Takes a string, however long, and
displays each word for $delay micro seconds. $sentence could be the
text of an entire book. $clear_flag is not required.
=head2 print_interleaved($string1, $string2)
This method is for use with two 8-digit displays cascaded. $string1
will be displayed in the first display, $string2 in the cascaded
display.
=head2 turn_on($num_cascaded)
Turns on the MAX7219 chip by writing to the turn on register. Takes
the number of cascaded displays as an argument. Enter 1 if only
using one display.
=head2 set_scanlimit($num_cascaded)
Writes to the scan-limit register. Sets it up to use all 8-digits
of the display. Takes number of cascaded displays an arg.
=head2 set_intensity($intensity);
Adjusts the brightness of the display. Takes a string as an arg.
Valid vlaues are: min, dim, mid, bright, max.
=head2 all_off($num_cascaded)
Turns off all digits. Takes number of cascaded displays as an arg.
=head2 disp_teston()
Turns on all segments on all digits.
=head2 disp_testoff()
Turns off all segments on all digits.
=head2 clockwise_circles($number_iterations)
Clockwise circles effect.
=head2 countercq_circles($number_iterations)
Counter clockwise circles effects
=head2 bullets_lrtop($number_iterations)
Knight-rider like bullets effect. Top row of horizontal LEDs of each
digit move from right to left.
=head2 bullets_rltop($number_iterations)
Knight-rider like bullets effect. Top row of horizontal LEDs of each
digit move from left to right.
=head2 bullets_lrmid($number_iterations)
Knight-rider like bullets effect. Mid row of horizontal LEDs of each
digit move from left to right.
=head2 bullets_rlmid($number_iterations)
Knight-rider like bullets effect. Mid row of horizontal LEDs of each
digit move from right to left.
=head2 bullets_lrbot($number_iterations)
Knight-rider like bullets effect. Bottom row of horizontal LEDs of
each digit move from left to right.
=head2 bullets_rlbot($number_iterations)
Knight-rider like bullets effect. Bottom row of horizontal LEDs of
each digit move from right to left.
( run in 0.849 second using v1.01-cache-2.11-cpan-13bb782fe5a )