SPI bus access, analog in/out on the Raspberry Pi powered by Perl

Well, all of the learning and testing I've done with C, XS, managing bits, reading and understanding hardware datatsheets etc in the last few months is really starting to pay off, with a lot of kudos going out to many Perlers for providing guidance and help with my questions, particularly with XS and C.

We now have reliable, working Perl code to output and receive input analog signals on the Raspberry Pi. This example uses an MCP41010 digital potentiometer for the analog out, and an ADC1015 analog to digital converter for analog in. I still have two different ADCs to write code for, two more models of digital pots, and later this week I should be receiving my DACs (digital to analog converter), my GPS receiver chip, and my MCP3004/8 ADCs.

As a bonus, we also now have direct access to communicate on the SPI bus (as the potentiometer does), with RPi::SPI. I even learned (with help) how to pass a Perl array reference into a C function which gets converted into a C unsigned char *, and how to return a Perl array back from C (I'll write another blog post about these two actions in the coming days).

This setup doesn't really do much, but it's the base of what will eventually allow me to have a Pi in the corner that all it does is pull from github and continuously (and automatically!) run unit tests for the Pi software. However, with true analog output/inputs, there's a lot more a Pi can do.

The schematic and the breadboard layout for the setup.

Code:

use warnings;
use strict;

use RPi::WiringPi;

my $pi = RPi::WiringPi->new;

my $adc = $pi->adc;

my $cs = $pi->pin(18);
my $dpot = $pi->dpot($cs->num, 0);

$dpot->set(0);

print "\nValue, Output %\n\n";

for (0..255){

    if (($_ % 10) != 0 && $_ != 255){
        next;
    }

    $dpot->set($_);

    my $p = $adc->percent(0);

    print "$_/255: $p %\n";

    select(undef, undef, undef, 0.3);
}

print "\n\nOutput % at 127/255\n\n";

$dpot->set(127);

for (0..10){
    print $adc->percent(0) . " %\n";
    select(undef, undef, undef, 0.2);
}

$pi->cleanup;

All it does is switch to different taps (resistor level) on the digital pot which increases/decreases output voltage. The ADC's input pin (A0) is connected directly to the output of the pot, as is the LED, just so I can see visually the changes as well as receive them digitally via the software.

Output:

Value, Output %

0/255: 0.36 %
10/255: 4.24 %
20/255: 8.12 %
30/255: 12.00 %
40/255: 15.88 %
50/255: 19.76 %
60/255: 23.70 %
70/255: 27.58 %
80/255: 31.45 %
90/255: 35.33 %
100/255: 39.21 %
110/255: 43.09 %
120/255: 46.97 %
130/255: 50.85 %
140/255: 54.79 %
150/255: 58.61 %
160/255: 62.48 %
170/255: 66.42 %
180/255: 70.24 %
190/255: 74.12 %
200/255: 77.70 %
210/255: 81.21 %
220/255: 84.91 %
230/255: 88.67 %
240/255: 92.67 %
250/255: 96.97 %
255/255: 99.21 %


Output % at 127/255

49.70 %
49.70 %
49.70 %
49.70 %
49.70 %
49.70 %
49.70 %
49.70 %
49.76 %
49.76 %
49.70 %

Leave a comment

About Steve Bertrand

user-pic Just Another Perl Hacker