File Coverage

blib/lib/Device/BusPirate/Mode/SPI.pm
Criterion Covered Total %
statement 93 97 95.8
branch 23 28 82.1
condition 4 8 50.0
subroutine 18 20 90.0
pod 8 9 88.8
total 146 162 90.1


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 7     7   18520 use v5.14;
  7         27  
7 7     7   48 use Object::Pad 0.45;
  7         90  
  7         126  
8              
9             package Device::BusPirate::Mode::SPI 0.23;
10             class Device::BusPirate::Mode::SPI isa Device::BusPirate::Mode;
11              
12 7     7   1926 use Carp;
  7         16  
  7         498  
13              
14 7     7   49 use Future::AsyncAwait;
  7         14  
  7         40  
15 7     7   407 use List::Util 1.33 qw( any );
  7         111  
  7         546  
16              
17 7     7   50 use constant MODE => "SPI";
  7         15  
  7         592  
18              
19 7   50 7   49 use constant PIRATE_DEBUG => $ENV{PIRATE_DEBUG} // 0;
  7         15  
  7         15324  
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 0     0 1 0 has $_open_drain :mutator;
  0         0  
65 4     4 1 6 has $_cke :mutator;
  4         15  
66 6     6 1 12 has $_ckp :mutator;
  6         24  
67 0     0 1 0 has $_sample :mutator;
  0         0  
68             has $_cs_high;
69             has $_speed;
70             has $_version;
71              
72             async method start
73 1         4 {
74             # Bus Pirate defaults
75 1         2 $_open_drain = 1;
76 1         2 $_cke = 0;
77 1         3 $_ckp = 1;
78 1         2 $_sample = 0;
79              
80 1         2 $_cs_high = 0;
81 1         2 $_speed = 0;
82              
83 1         7 await $self->_start_mode_and_await( "\x01", "SPI" );
84 1         71 ( $_version ) = await $self->pirate->read( 1, "SPI start" );
85              
86 1         566 print STDERR "PIRATE SPI STARTED\n" if PIRATE_DEBUG;
87 1         4 return $self;
88 1     1 0 3 }
89              
90             =head2 configure
91              
92             $spi->configure( %args )->get
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 4         8 method configure ( %args )
  4         11  
  4         6  
167 4     4 1 1337 {
168             # Convert other forms of specifying SPI modes
169              
170 4 100       13 if( defined $args{mode} ) {
171 1         2 my $mode = delete $args{mode};
172 1         4 $args{ckp} = $mode & 2;
173 1         3 $args{cke} = !( $mode & 1 );
174             }
175              
176 4 100       12 defined $args{cpol} and $args{ckp} = delete $args{cpol};
177 4 100       11 defined $args{cpha} and $args{cke} = !delete $args{cpha};
178              
179 4 50       9 defined $args{cs_high} and $_cs_high = !!$args{cs_high};
180              
181 4         5 my @f;
182              
183 4 100   11   23 if( any { defined $args{$_} and !!$args{$_} != $self->$_ } qw( open_drain ckp cke sample ) ) {
  11 100       35  
184 3   66     17 defined $args{$_} and $self->$_ = !!$args{$_} for qw( open_drain ckp cke sample );
185              
186 3 50       11 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 4 100       935 if( defined $args{speed} ) {{
195 1         3 my $speed = $SPEEDS{$args{speed}} //
196 1   33     7 croak "Unrecognised speed '$args{speed}'";
197              
198 1 50       4 last if $speed == $_speed;
199              
200 1         2 $_speed = $speed;
201 1         4 push @f, $self->pirate->write_expect_ack(
202             chr( 0x60 | $_speed ), "SPI set speed" );
203             }}
204              
205 4         296 return Future->needs_all( @f );
206             }
207              
208             =head2 chip_select
209              
210             $spi->chip_select( $cs )->get
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 7     7 1 843 {
220 7         32 $self->_set_cs( my $_cs = !!shift );
221              
222 7         11 print STDERR "PIRATE SPI CHIP-SELECT(", $_cs || "0", ")\n" if PIRATE_DEBUG;
223              
224 7 100       18 $self->pirate->write_expect_ack( $_cs ? "\x03" : "\x02", "SPI chip_select" );
225             }
226              
227             =head2 writeread
228              
229             $miso_bytes = $spi->writeread( $mosi_bytes )->get
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 6         9 async method _writeread ( $bytes )
  6         12  
  6         7  
242 6         17 {
243 6         9 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 6 50       15 my $maxchunk = $_speed == 0 ? 6 : 16;
250              
251 6         59 my @chunks = $bytes =~ m/(.{1,$maxchunk})/gs;
252 6         14 my $ret = "";
253              
254 6         22 foreach my $bytes ( @chunks ) {
255 6         12 my $len_1 = length( $bytes ) - 1;
256              
257 6         20 $ret .= await $self->pirate->write_expect_acked_data(
258             chr( 0x10 | $len_1 ) . $bytes, length $bytes, "SPI bulk transfer"
259             );
260             }
261              
262 6         417 printf STDERR "PIRATE SPI READ %v02X\n", $ret if PIRATE_DEBUG;
263 6         24 return $ret;
264 6     6   12 }
265              
266 3         7 method writeread ( $bytes )
  3         6  
  3         4  
267 3     3 1 1750 {
268             $self->pirate->enter_mutex( sub {
269 3     3   320 $self->_writeread( $bytes )
270 3         11 });
271             }
272              
273             =head2 writeread_cs
274              
275             $miso_bytes = $spi->writeread_cs( $mosi_bytes )->get
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         4 method writeread_cs ( $bytes )
  3         6  
  3         6  
286 3     3 1 2645 {
287 3     3   266 $self->pirate->enter_mutex( async sub {
288 3         8 await $self->chip_select( $_cs_high );
289 3         219 my $buf = await $self->_writeread( $bytes );
290 3         207 await $self->chip_select( !$_cs_high );
291 3         199 return $buf;
292 3         9 });
293             }
294              
295             =head1 AUTHOR
296              
297             Paul Evans
298              
299             =cut
300              
301             0x55AA;