File Coverage

blib/lib/Device/BusPirate/Mode/SPI.pm
Criterion Covered Total %
statement 84 84 100.0
branch 22 26 84.6
condition 5 11 45.4
subroutine 17 17 100.0
pod 4 5 80.0
total 132 143 92.3


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2014-2021 -- leonerd@leonerd.org.uk
5              
6             package Device::BusPirate::Mode::SPI 0.21;
7              
8 7     7   18831 use v5.14;
  7         31  
9 7     7   36 use warnings;
  7         13  
  7         218  
10 7     7   38 use base qw( Device::BusPirate::Mode );
  7         12  
  7         751  
11              
12 7     7   49 use Carp;
  7         24  
  7         519  
13              
14 7     7   51 use Future::AsyncAwait;
  7         35  
  7         41  
15 7     7   370 use List::Util 1.33 qw( any );
  7         129  
  7         475  
16              
17 7     7   48 use constant MODE => "SPI";
  7         14  
  7         543  
18              
19 7   50 7   47 use constant PIRATE_DEBUG => $ENV{PIRATE_DEBUG} // 0;
  7         13  
  7         8823  
20              
21             =head1 NAME
22              
23             C - use C in SPI mode
24              
25             =head1 SYNOPSIS
26              
27             Simple output (e.g. driving LEDs on a shift register)
28              
29             use Device::BusPirate;
30              
31             my $pirate = Device::BusPirate->new;
32             my $spi = $pirate->enter_mode( "SPI" )->get;
33              
34             $spi->configure( open_drain => 0 )->get;
35              
36             my $count = 0;
37             while(1) {
38             $spi->writeread_cs( chr $count )->get;
39             $count++; $count %= 255;
40             }
41              
42             Simple input (e.g. reading buttons on a shift register)
43              
44             while(1) {
45             my $in = ord $spi->writeread_cs( "\x00" )->get;
46             printf "Read %02x\n", $in;
47             }
48              
49             =head1 DESCRIPTION
50              
51             This object is returned by a L instance when switching it
52             into C mode. It provides methods to configure the hardware, and interact
53             with an SPI-attached chip.
54              
55             =cut
56              
57             =head1 METHODS
58              
59             The following methods documented with a trailing call to C<< ->get >> return
60             L instances.
61              
62             =cut
63              
64             async sub start
65 1     1 0 3 {
66 1         2 my $self = shift;
67              
68             # Bus Pirate defaults
69 1         3 $self->{open_drain} = 1;
70 1         2 $self->{cke} = 0;
71 1         4 $self->{ckp} = 1;
72 1         2 $self->{sample} = 0;
73              
74 1         2 $self->{cs_high} = 0;
75 1         2 $self->{speed} = 0;
76              
77 1         6 await $self->_start_mode_and_await( "\x01", "SPI" );
78 1         70 ( $self->{version} ) = await $self->pirate->read( 1, "SPI start" );
79              
80 1         574 print STDERR "PIRATE SPI STARTED\n" if PIRATE_DEBUG;
81 1         4 return $self;
82             }
83              
84             =head2 configure
85              
86             $spi->configure( %args )->get
87              
88             Change configuration options. The following options exist; all of which are
89             simple true/false booleans.
90              
91             =over 4
92              
93             =item open_drain
94              
95             If enabled (default), a "high" output pin will be set as an input; i.e. hi-Z.
96             When disabled, a "high" output pin will be driven by 3.3V. A "low" output will
97             be driven to GND in either case.
98              
99             =item sample
100              
101             Whether to sample input in the middle of the clock phase or at the end.
102              
103             =item cs_high
104              
105             Whether "active" Chip Select should be at high level. Defaults false to be
106             active-low. This only affects the C method; not the
107             C method.
108              
109             =back
110              
111             The SPI clock parameters can be specified in any of three forms:
112              
113             =over 4
114              
115             =item ckp
116              
117             =item cke
118              
119             The SPI Clock Polarity and Clock Edge settings, in F style.
120              
121             =item cpol
122              
123             =item cpha
124              
125             The SPI Clock Polarity and Clock Phase settings, in F style.
126              
127             =item mode
128              
129             The SPI mode number, 0 to 3.
130              
131             =back
132              
133             The following non-boolean options exist:
134              
135             =over 4
136              
137             =item speed
138              
139             A string giving the clock speed to use for SPI. Must be one of the values:
140              
141             30k 125k 250k 1M 2M 2.6M 4M 8M
142              
143             By default the speed is C<30kHz>.
144              
145             =back
146              
147             =cut
148              
149             my %SPEEDS = (
150             '30k' => 0,
151             '125k' => 1,
152             '250k' => 2,
153             '1M' => 3,
154             '2M' => 4,
155             '2.6M' => 5,
156             '4M' => 6,
157             '8M' => 7,
158             );
159              
160             sub configure
161             {
162 4     4 1 1258 my $self = shift;
163 4         13 my %args = @_;
164              
165             # Convert other forms of specifying SPI modes
166              
167 4 100       13 if( defined $args{mode} ) {
168 1         3 my $mode = delete $args{mode};
169 1         3 $args{ckp} = $mode & 2;
170 1         2 $args{cke} = !( $mode & 1 );
171             }
172              
173 4 100       10 defined $args{cpol} and $args{ckp} = delete $args{cpol};
174 4 100       10 defined $args{cpha} and $args{cke} = !delete $args{cpha};
175              
176             defined $args{$_} and $self->{$_} = !!$args{$_}
177 4   33     15 for (qw( cs_high ));
178              
179 4         5 my @f;
180              
181 4 100   11   22 if( any { defined $args{$_} and !!$args{$_} != $self->{$_} } qw( open_drain ckp cke sample ) ) {
  11 100       36  
182 3   66     21 defined $args{$_} and $self->{$_} = !!$args{$_} for qw( open_drain ckp cke sample );
183              
184             push @f, $self->pirate->write_expect_ack(
185             chr( 0x80 |
186             ( $self->{open_drain} ? 0 : 0x08 ) | # sense is reversed
187             ( $self->{ckp} ? 0x04 : 0 ) |
188             ( $self->{cke} ? 0x02 : 0 ) |
189 3 50       10 ( $self->{sample} ? 0x01 : 0 ) ), "SPI configure" );
    100          
    100          
    50          
190             }
191              
192 4 100       901 if( defined $args{speed} ) {{
193 1         3 my $speed = $SPEEDS{$args{speed}} //
194 1   33     7 croak "Unrecognised speed '$args{speed}'";
195              
196 1 50       4 last if $speed == $self->{speed};
197              
198 1         2 $self->{speed} = $speed;
199             push @f, $self->pirate->write_expect_ack(
200 1         4 chr( 0x60 | $self->{speed} ), "SPI set speed" );
201             }}
202              
203 4         289 return Future->needs_all( @f );
204             }
205              
206             =head2 chip_select
207              
208             $spi->chip_select( $cs )->get
209              
210             Set the C output pin level. A false value will pull it to ground. A true
211             value will either pull it up to 3.3V or will leave it in a hi-Z state,
212             depending on the setting of the C configuration.
213              
214             =cut
215              
216             sub chip_select
217             {
218 7     7 1 467 my $self = shift;
219 7         14 $self->{cs} = !!shift;
220              
221 7         12 print STDERR "PIRATE SPI CHIP-SELECT(", $self->{cs} || "0", ")\n" if PIRATE_DEBUG;
222              
223 7 100       20 $self->pirate->write_expect_ack( $self->{cs} ? "\x03" : "\x02", "SPI chip_select" );
224             }
225              
226             =head2 writeread
227              
228             $miso_bytes = $spi->writeread( $mosi_bytes )->get
229              
230             Performs an actual SPI data transfer. Writes bytes of data from C<$mosi_bytes>
231             out of the C pin, while capturing bytes of input from the C pin,
232             which will be returned as C<$miso_bytes> when the Future completes. This
233             method does I toggle the C pin, so is safe to call multiple times to
234             effect a larger transaction.
235              
236             This is performed atomically using the C method.
237              
238             =cut
239              
240             async sub _writeread
241 6     6   11 {
242 6         10 my $self = shift;
243 6         12 my ( $bytes ) = @_;
244              
245 6         9 printf STDERR "PIRATE SPI WRITEREAD %v02X\n", $bytes if PIRATE_DEBUG;
246              
247             # "Bulk Transfer" command can only send up to 16 bytes at once.
248              
249             # The Bus Pirate seems to have a bug, where at the lowest (30k) speed, bulk
250             # transfers of more than 6 bytes get stuck and lock up the hardware.
251 6 50       26 my $maxchunk = $self->{speed} == 0 ? 6 : 16;
252              
253 6         58 my @chunks = $bytes =~ m/(.{1,$maxchunk})/gs;
254 6         11 my $ret = "";
255              
256 6         14 foreach my $bytes ( @chunks ) {
257 6         11 my $len_1 = length( $bytes ) - 1;
258              
259 6         18 $ret .= await $self->pirate->write_expect_acked_data(
260             chr( 0x10 | $len_1 ) . $bytes, length $bytes, "SPI bulk transfer"
261             );
262             }
263              
264 6         450 printf STDERR "PIRATE SPI READ %v02X\n", $ret if PIRATE_DEBUG;
265 6         24 return $ret;
266             }
267              
268             sub writeread
269             {
270 3     3 1 1706 my $self = shift;
271 3         8 my ( $bytes ) = @_;
272              
273             $self->pirate->enter_mutex( sub {
274 3     3   319 $self->_writeread( $bytes )
275 3         8 });
276             }
277              
278             =head2 writeread_cs
279              
280             $miso_bytes = $spi->writeread_cs( $mosi_bytes )->get
281              
282             A convenience wrapper around C which toggles the C pin before
283             and afterwards. It uses the C configuration setting to determine the
284             active sense of the chip select pin.
285              
286             This is performed atomically using the C method.
287              
288             =cut
289              
290             sub writeread_cs
291             {
292 3     3 1 2428 my $self = shift;
293 3         7 my ( $bytes ) = @_;
294              
295 3     3   276 $self->pirate->enter_mutex( async sub {
296 3         9 await $self->chip_select( $self->{cs_high} );
297 3         213 my $buf = await $self->_writeread( $bytes );
298 3         212 await $self->chip_select( !$self->{cs_high} );
299 3         199 return $buf;
300 3         11 });
301             }
302              
303             =head1 AUTHOR
304              
305             Paul Evans
306              
307             =cut
308              
309             0x55AA;