One of the drawbacks, in my opinion, of the raspberry pi and the pine64 A+ is the lack of any analog inputs. To read an analog signal requires an external analog to digital converter chip, and will tie up several of your GPIO lines. My new module has routines to read values on an MCP3004 or MCP3008 10-bit analog to digital converter.
Here is some sample code ->
usePINE64::MCP300x;my$adc= PINE64::MCP300x->new(10,12,11,13); #5 bits because the first is the start bitmy@ch0= (1,1,0,0,0);for(my$s=0;$s<200;$s++){my($reading, $binval, $voltage) = $adc->read300x(\@ch0, 50, 5.01);$voltage= sprintf("%.3f", $voltage);print"binval: $binval\tvoltage: $voltage vdc\n";usleep(500000);}#end for
I have had another cpan module published! This one is a driver for a MAX7219 8-digit LED display on the PINE64 single board computer. The video above is a demo of some of the module’s capabilities. It is implemented as bit-banged SPI using the perl programming language.
A very easy to use display, it only uses 3 GPIO pins, and could be used to output the value of sensors, or simple menus for projects.
One of my personal goals this year was to publish some of my work that uses perl to experiment with single board computers on CPAN (the Comprehensive Perl Archive Network). I have used perl to make windows applications, database applications, web applications, text processing utilities, inventions….. you name it. However, it is not widely used for single board computers like the raspberry pi. There are a lot of reasons for this. Perl is no longer the cool programming language say, like Python is. Programming in perl is almost an anachronism and dates you. It is relatively slow. Even so, perl is incredibly versatile and powerful and has a devoted following of some of the most brilliant people. Perl isn’t going anywhere.
I have been a consumer of CPAN for nearly 10 years. CPAN is a code repository where programmers share their code so you don’t have to re-invent the wheel in your programs if someone else has already figured out how to implement what you are trying to do.
Steve Bertrand and others have published a ton of useful modules for the raspberry pi family of SBC’s on CPAN. Dude even published a book on indiegogo on pi projects using perl. I am not near as smart as Mr. Bertrand, so I wanted to stay away from publishing my pi modules on CPAN. The idea is to do something that hasn’t been done before. No one like in the whole world seems to be using perl on the Pine64 board (if you google, I am virtually the only person doing it), so I decided to post my modules for the PineA64+.
To publish on CPAN, you have to have a PAUSE account (Perl Authors Upload Server). You have to be ‘in the club’. You basically have to apply for membership stating what you intend to offer. I also gave them the address of this site so they could see some of my work. I didn’t hear anything back for nearly six weeks. I assumed they looked at my projects and declined to grant me access. I finally did get an account and proceeded to package my modules to their requirements and uploaded my first module: a script that controls the GPIO pins on the PineA64 board’s Pi-2 bus. I consider it a tremendous honor to have been accepted.
I have a technology degree with some graduate work in computer science. I work in telecom, however, not as a programmer. Programming, though, is my secret weapon. Paired with the knowledge of electronics hardware and fabrication, I can create just about anything I can think up. It is a thrill to finally get published on CPAN. I will have many more modules for the PineA64 to follow for various sensors, analog to digital converters, displays, etc.
pine64 and perl guinea pig habitat environment automation
We’ve moved our guinea pig Monster out to the garage. He’s in no danger of overheating (trust me, he is spoiled rotten!) but it does get a little warm out there. I used my pine64 to turn on a 20W fan when it rises above a chosen temperature. Temperature samples are taken with an analog TMP36 sensor which is read by an MCP3004 analog to digital converter. It also gets pitch dark with the lights out at night, so there is also a photo resistor that is read by the MCP3004 ADC. When it drops below a certain threshold, it turns on an LED night light for Monster. I think he likes it. He has been laying in front of the fan.
Once the ADC reading is taken of the TMP36 sensor, this is how it is converted into degrees Fahrenheit.
my $temperature = ((($voltage_t * 1000) - 500) / 10) * 1.8 + 32;
I am running armbian on my pine64 and have written my own gpio control script in perl. Here is the perl script that controls Monster’s fan and night light.
#!/usr/bin/perl
use strict;
require "./MCP3004.pl";
gpio_enable(17, 'out');
gpio_enable(13, 'out');
#get thermostat arg
my $thermostat = $ARGV[0];
if($thermostat eq ''){
$thermostat = 85;
}#end thermostat
#channels: first bit is set to single, not differential
#5 bits because the first is the start bit
my @ch0 = (1,1,0,0,0);
my @ch1 = (1,1,0,0,1);
my @ch2 = (1,1,0,1,0);
my @ch3 = (1,1,0,1,1);
#differential
my @ch01 = (1,0,0,0,0);
init3004(18,19,24,25);
#loop global variables
my $tmp10 = 0;
my $tavg = 0;
my $bin10 = 0;
my $binavg = 0;
my $fan_f = 0;
my $lt_f = 0;
for(;;){
$tmp10 = 0;
$tavg = 0;
$bin10 = 0;
$binavg = 0;
for(my $n=0;$n<10;$n++){ #photoresisitor reading------------------------------------------- my ($reading, $binval, $voltage ) = read3004(\@ch0, 50, 5.01); print "binval: $binval\t$voltage vdc"; $bin10 += $binval; #print RD "$s\n$voltage\n"; #photocell end---------------------------------------------------- #tmp36 temperature reading---------------------------------------- print "\n---------------------------------------\n"; my ($reading_t, $binval_t, $voltage_t ) = read3004(\@ch1, 50, 5.01); #print "binval_tmp: $binval_t\tvoltage_tmp: $voltage_t vdc\n"; my $temperature = ((($voltage_t * 1000) - 500) / 10) * 1.8 + 32; $temperature = sprintf("%5.2f", $temperature); print "temperature: $temperature deg F\n"; #accumulate temp $tmp10 +=$temperature; print "---------------------------------------\n"; #tmp36 end-------------------------------------------------------- #0.5 pause between samples usleep(500000); }#end for 10 #calculate temperature averages $tavg = $tmp10 / 10; if($tavg > $thermostat){
#turns fan on
gpio_write(17, 1);
$fan_f = 1;
}#end if
else{
gpio_write(17, 0);
$fan_f = 0;
}#end else
#calculate light intensity averages
$binavg = $bin10 / 10;
if($binavg > 610){
#turns light on
gpio_write(13, 0);
$lt_f = 1;
}#end if
else{
gpio_write(13, 1);
$lt_f = 0;
}#end else
print "\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n";
print "avg temp: $tavg stat:$fan_f\tavg light: $binavg stat:$lt_f";
print "\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n";
#wait 1 minute
sleep(60);
}#end outer for
That’s a lot of processing power just to control a light and a fan. I plan to migrate the function to an arduino nano soon.
pine64 and perl guinea pig habitat environment controls
When I first got my 512MB pine64 A64 sbc, I initially loaded a great community based version of debian with an LXDE desktop. I was really happy with it. Recently, I was trying to install i2c-tools and was getting errors. I then tried to update my system, and could not connect to the repository. Same for perl packages using cpan. After searching for an answer in the pin64 forums, I learned that dude that made the awesome debian img for the pine64 stopped supporting it, and there was a suggestion to migrate to an armbian distro that was cli only. I really wanted to use my i2c bus, so I burned a not-so-fresh sd card with armbian.
armbian for pine64 initial login via UART0 on the euler bus
Initially, I was happy. I really don’t need a desktop environment. I quickly noticed that my ethernet interface did not work at all. Once again, I searched the forums, and found a solution. Using martinayotte posted a solution that worked great for me.
Not very promising that my NIC did not work out of the box, but I am going to give it a fair try.
I’ve used gpio on various SBCs to drive small signal relays on a few projects, but never any high-current, high-voltage stuff. The principle is exactly the same, I just hadn’t used it before. The elegoo 37-sensor kit I just got had a handy break out board for a 10A 250VAC relay. I used a gpio line on a pine64 to drive the relay coil using a PN2222A transistor circuit. The script is no different than blinking an LED.
pine64 gpio driving a 10A 250VAC relay circuit using a PN2222A NPN transistor to provide ground
pine64 turning on and off light bulb
perl script for pine64 relay control
Next, I controlled the relay by reading the voltage drop across a photo resistor voltage divider using an MCP3004 10-bit ADC. When the OP code from the ADC rose above 400, the light comes on (because it is dark).
Controlling a relay by reading the voltage drop across a photo resistor using an MCP3004 ADC
Still working on the maker space and expanding my networking lab. I added a cisco 1721 router with a 10-base T wan interface card and a serial 1T WIC in addition to the built-in fast ethernet interface. I also added an old cisco 2514 with two built-in AUI (ethernet) interfaces and two serial interfaces.
Cisco 1721 and 2514 routers
The computer at my work bench doesn’t have a serial interface that I can connect to the routers’ console interfaces, and I was too lazy to go get my usb to serial converter out of my van; especially when I had my pine64 nearby with an rs-232 to TTL converter. I fired up my pine64 and connected the rs-232 to TTL converter to the UART pins on the pi-2-bus. The converter and the console port of the routers are both DTE devices, so a null-modem (serial crossover) cable / adapter are necessary. I hooked it all up like so:
Pine64 as a simple terminal server
Here you can see I have my pine64 connected to a cisco console cable via a null modem adapter and a gender changer. First I had to ssh into the pine. Now to install screen on my pine64.
The pine64, like many single board computers, has a file system interface for its gpio pins. This means that reading and writing logic levels on a particular gpio pin is a matter of reading and writing to the particular file for that pin. Interacting with files in this manner is something that perl excels at. That is one reason why I mostly forego using ready made tools like wiringpi and make my own gpio interface functions. This also help me to understand the system more completely, and customize according to the way that I think, instead of using another’s canned tools.
I have written a number of perl scripts for the raspberry pi that control the gpio system interface and bit-banged drivers for several popular integrated circuits such as the MCP3004, MCP3008, MCP3208 (all analog to digital converters) , MAX7219, 74HC595, and others. The pine64 has a (mostly) compatible 40-pin gpio header. I only had to change one line of my gpio driver to make it work with the pine64. Here is the perl gpio function file in it’s entirety:
#!/usr/bin/perl -w
use strict;
=pod
gpio utilities for pine64
enable, disable, read, write
=cut
#global vars
#array for gpio pin numbers
my @line_nums = (227,226,362,71,233,76,64,65,66,229,230,69,73,80,32,33,72,77,78,79,67,231,68,70,74,75);
my ($iox, $status);
sub gpio_enable{
my $ind = $_[0];
$iox = $line_nums[$ind];
#print "IO line: $iox\n";
my $gpiosz = @line_nums;
#print "gpiosz: $gpiosz\n";
my $direction = $_[1];
#print "direction: $direction\n";
#err chk
if($ind < 0 || $ind > $gpiosz){
print "INVALID GPIO RANGE\n";
exit;
}#end if invalid index
if($direction ne 'in' && $direction ne 'out'){
print "INVALID DIRECTION\n";
exit;
}#end if invalid direction
#write gpio pin val to export file
open(EF, ">", "/sys/class/gpio/export") or die $!;
print EF $iox;
close(EF);
#set direction of gpio pin
open(GF, ">", "/sys/class/gpio/gpio$iox/direction") or die $!;
print GF $direction;
close(GF);
}#end gpio_enable
sub gpio_read{
#gpio number as arg, returns direction in/out
my $ind = $_[0];
$iox = $line_nums[$ind];
my $value = '';
#reads state of gpio pin
open(GS, "/sys/class/gpio/gpio$iox/value") or die $!;
while(){ $value = $_; };
#print "$iox val: $value";
close(GS);
if($value == 0 || $value == 1){
return $value;
}#end unless
else{
print "ERROR: Undefined value on GPIO $iox\n";
exit;
}#end else
}#end gpio_read
sub gpio_write{
my $ind = $_[0];
$iox = $line_nums[$ind];
my $value = $_[1];
#write value to gpio pin
open(GV, ">", "/sys/class/gpio/gpio$iox/value") or die $!;
print GV $value;
close(GV);
}#end gpio_write
1;
Very simple (under 80 lines) and very effective. This has been tested ad infinitum to work as expected. This gpio function file is also the main building block for my bit-banged drivers. To demonstrate, here is an example using a MAX7219 64-LED display driver. I wrote a custom bit-banged driver using this gpio script.
pine64 MAX7219 perl based driver
When I write a driver like this, I get the data sheet from the manufacturer, and write functions to set all of the configuration registers and control the IO functions. I use these 8-position, 7-segment displays frequently in my home brew projects, so I started there with my new pine64. The driver I wrote for this particular display has some visual effects, sets the intensity, and has some easy to use functions that can read text files word by word. I can also handle cascaded displays and a few more novel features that I have not seen anywhere else. Here is the display driver in its current entirety:
#!/usr/bin/perl
use strict;
use Time::HiRes qw (usleep);
require "/home/debian/gpio/gpio_pine64.pl";
=pod
routines to control a MAX7219 8-digit LED
display driver.
for now:
CLK: gpio 0
DS : gpio 1
LOAD:gpio 2
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.
=cut
#global vars for gpio init function
#init should be called from script
#init7219 (0,1,2);
my ($clk, $ds, $load);
#SUBROUTINE PROTOTYPES
=pod
=cut
############### 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);
my @_h = (0,0,1,1,0,1,1,1);
my @_i = (0,0,0,0,0,1,0,0);
my @_j = (0,0,1,1,1,1,0,0);
my @_k = (0,0,1,1,0,1,1,0);
my @_l = (0,0,0,0,1,1,1,0);
my @_m = (1,0,0,1,0,1,0,0);
my @_n = (0,0,0,1,0,1,0,1);
my @_o = (0,0,0,1,1,1,0,1);
my @_p = (0,1,1,0,0,1,1,1);
my @_q = (0,1,1,1,0,0,1,1);
my @_r = (0,0,0,0,0,1,0,1);
my @_s = (0,1,0,1,1,0,1,1);
my @_t = (0,0,0,0,1,1,1,1);
my @_u = (0,0,0,1,1,1,0,0);
my @_v = (0,0,0,1,1,1,0,0);
my @_w = (0,0,0,1,0,1,0,0);
my @_x = (0,0,0,1,1,1,0,1);
my @_y = (0,0,1,1,1,0,1,1);
my @_z = (0,1,1,0,1,1,0,1);
#special chars
my @_dp = (1,0,0,0,0,0,0,0);
my @_sp = (0,0,0,0,0,0,0,0);
my @_dash = (0,0,0,0,0,0,0,1);
my @_comma = (1,0,0,0,0,0,0,0);
my @_period = (1,0,0,0,0,0,0,0);
my @_question = (1,1,1,0,0,0,1,0);
my @_exclaimation = (1,0,1,1,0,0,0,0);
#individual segments
my @_seg_a = (0,1,0,0,0,0,0,0);
my @_seg_b = (0,0,1,0,0,0,0,0);
my @_seg_c = (0,0,0,1,0,0,0,0);
my @_seg_d = (0,0,0,0,1,0,0,0);
my @_seg_e = (0,0,0,0,0,1,0,0);
my @_seg_f = (0,0,0,0,0,0,1,0);
my @_seg_g = (0,0,0,0,0,0,0,1);
#@all_digits is an arra of array references
#representing each digit used to operate
#on each digit
my @all_digits = (\@_d0,\@_d1,\@_d2,\@_d3,\@_d4,\@_d5,\@_d6,\@_d7);
#@all_segs is an array of array references
#representing each segment of a 7-seg LED array
my @all_segs = (\@_seg_a,\@_seg_b,\@_seg_c,\@_seg_d,\@_seg_e,\@_seg_f,\@_seg_g);
#Hash that maps alphanumeric chars to array ref
my %alphanums = (
'A' => \@_a,
'B' => \@_b,
'C' => \@_c,
'D' => \@_d,
'E' => \@_e,
'F' => \@_f,
'G' => \@_g,
'H' => \@_h,
'I' => \@_i,
'J' => \@_j,
'K' => \@_k,
'L' => \@_l,
'M' => \@_m,
'N' => \@_n,
'O' => \@_o,
'P' => \@_p,
'Q' => \@_q,
'R' => \@_r,
'S' => \@_s,
'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
############### SUBROUTINES #####################
sub shift_in{
=pod
data is shifted in with 16 bit packets: 8-bit segment,
4-bit digit address, 4-bit dont care bits
=cut
#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] eq 1 && $i<=14){ gpio_write($ds,1); $hf = 1;#set high flag }#end addr high bit #7-seg data, D0-D7 if($leds->[($i-16)/2] eq 1 && $i > 14){#array ref, light this led up
#test; set q1 high
##print "inside data loop, i: $i\tindex: " .(($i-16)/2) . "\n";
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
gpio_write($clk, $state);
usleep($delay);#sleep .001 sec
#lower data if high flag set
if($hf eq 1){
gpio_write($ds,0);
$hf = 0;#reset high flag
}#end if
$i++;
}#end while
#latch output lines
if($lf eq 1){
load();
}#end latch flag
gpio_write($clk,0);#set clock pulse low
}#end shift_in
sub load{
=pod
toggles LOAD pin on 7219 to send to output pins. When the pin goes from low to high, sets output.
=cut
#enable XIO-p2
gpio_enable($load, 'out');
#ensure it is low
gpio_write($load, 0);#low
#go from low to high
gpio_write($load, 1);#high
#print "LATCH HIGH\n";
usleep(500);#pause 0.0005 second
#reset to low
gpio_write($load, 0);#low
#print "LATCH LOW\n";
}#end load
sub init7219{
=pod
initializes gpio lines to low
=cut
$clk = $_[0];
$ds = $_[1];
$load = $_[2];
gpio_enable($clk, 'out');
gpio_write($clk, 0);
gpio_enable($ds, 'out');
gpio_write($ds, 0);
gpio_enable($load, 'out');
gpio_write($load, 0);
}#end init
sub print_sentence{
#currently for a single 7219 array
my $sentence = $_[0];
#set to all uppercase letters
$sentence = uc $sentence;
#delay between words in microseconds
my $delay = $_[1];
#all off flag, set if reading, unset if
#you want the text to remain on the array
#if empty, clears the text
my $cleartxt_flag = $_[2];
my @words = split / /, $sentence;
my $numwords = @words;
=pod
foreach my $word (@words){
print $word."\n";
}#end foreach
=cut
#print "sentence: $sentence\nNumwords: $numwords\n";
#loop through words
for(my $i=0;$i<$numwords;$i++){
#split word into an array of chars
my @letters = split //, $words[$i];
#number of chars in word
my $numalphanums = @letters;
#print "num letters: $numalphanums\n";
#first letter
my $ln = 0;
#reverse @all_digits to display words
#left to right
my @rev_segs = reverse @all_digits;
foreach my $digit (@rev_segs){
#limited to 8 digits for now
shift_in($alphanums{$letters[$ln]}, $digit, 1, 250, 1 );
#print "letter: $letters[$ln]\n";
$ln++;#go to next letter
}#end letters inner for loop
usleep($delay);
unless($cleartxt_flag == 1){
all_off();
}#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 = $_[0];
my $str2 = $_[1];
#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{
my $ncas = $_[0];
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{
my $ncas = $_[0];
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{
my $ncas = $_[0];
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 = $_[0];
#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 = $_[0];
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
#@all_digits is an array of array references
#representing each digit
foreach my $digit (@all_digits){
shift_in(\@turn_off, $digit, 1, 100, 1);
}#end foreach
}#end else
}#end all_off
sub disp_teston{
#turn on display test,
#all digits and dp
shift_in(\@turn_on, \@disptstreg, 1, 200, 1);
}#end disp_test
sub disp_testoff{
#turn off display test,
#all digits and dp
shift_in(\@turn_off, \@disptstreg, 1, 200, 1);
}#end disp_test
#################### EFFECTS######################
sub clockwise_circles{
#number of iterations
my $i = $_[0];
my $k = 0;
while($k<$i){
for(my $n=0;$n<6;$n++){
#outer for, each segment
for(my $x=0; $x<8; $x++){
#inner for, each digit
shift_in(@all_segs[$n], @all_digits[$x], 1, 100, 1);
}#end inner for
}#end outer for
$k++;
}#end while
all_off();
}#end clockwise_circles
sub countercw_circles{
#number of iterations
my $i = $_[0];
my $k = 0;
my @revall_segs = reverse @all_segs;
while($k<$i){
for(my $n=1;$n<7;$n++){
#outer for, each segment
for(my $x=0; $x<8; $x++){
#inner for, each digit
shift_in(@revall_segs[$n], @all_digits[$x], 1, 100, 1);
}#end inner for
}#end outer for
$k++;
}#end while
all_off();
}#end countercw_circles
sub bullets_lrtop {
#number of iterations
my $ni = $_[0];
for(my $n=0;$n<$ni;$n++){ for(my $i=8;$i>=0;$i--){
shift_in(\@_seg_a, @all_digits[$i], 1, 100, 1);
all_off();
}#end inner for
}#end outer for
all_off();
}#end bullets_lrtop
sub bullets_rltop {
#number of iterations
my $ni = $_[0];
for(my $n=0;$n<$ni;$n++){
for(my $i=0;$i<=8;$i++){
shift_in(\@_seg_a, @all_digits[$i], 1, 100, 1);
all_off();
}#end inner for
}#end outer for
all_off();
}#end bullets_lrtop
sub bullets_lrmid {
#number of iterations
my $ni = $_[0];
for(my $n=0;$n<$ni;$n++){ for(my $i=8;$i>=0;$i--){
shift_in(\@_seg_g, @all_digits[$i], 1, 100, 1);
all_off();
}#end inner for
}#end outer for
all_off();
}#end bullets_lrtop
sub bullets_rlmid {
#number of iterations
my $ni = $_[0];
for(my $n=0;$n<$ni;$n++){
for(my $i=0;$i<=8;$i++){
shift_in(\@_seg_g, @all_digits[$i], 1, 100, 1);
all_off();
}#end inner for
}#end outer for
all_off();
}#end bullets_lrtop
sub bullets_lrbot {
#number of iterations
my $ni = $_[0];
for(my $n=0;$n<$ni;$n++){ for(my $i=8;$i>=0;$i--){
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 = $_[0];
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;
Here is an example of how simple it is to use one of these LED displays using this driver.
#!/usr/bin/perl
use strict;
require 'MAX7219.pl'; #required driver for LED array
init7219(0,1,2); #set the GPIO lines on the pine64 for clock, data in, and latch
turn_on(1); #turn on only 1 MAX7219 chip
set_scanlimit(1); #tell it to use all 8 digits for 1 LED array
set_intensity('max'); #maximum intensity
print_sentence("pine64 rules the world", 500000);
I love tinkering with single board computers. I was an early adopter of the CHIP (until they tanked) and later the raspberry pi family of SBC’s.
the now defunct CHIP
I was looking for a cheaper alternative to the pi model 3 ($35) yet more powerful than the pi zero w. There were several alternatives such as the orangePi, but after reading the forums, it looked like there were some serious overheating problems and poor support from the community. I came across the pine64 and just had to get one.
I haven’t done much with it yet; just burned debian + mate desktop that a member of the community made available, but so far I am impressed. I got the base Allwinner quad-core 64-bit ,512MB memory model for just $15!
pine64 processor info
Somewhat larger than the pi 3, it has 10/100 ethernet, HDMI, 2x USB ports, and lots of GPIO pins. I can’t wait to get started.