File Coverage

blib/lib/Device/BusPirate/Mode/SPI.pm
Criterion Covered Total %
statement 93 95 97.8
branch 23 26 88.4
condition 4 8 50.0
subroutine 19 20 95.0
pod 7 8 87.5
total 146 157 92.9


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