File Coverage

blib/lib/Device/BusPirate/Mode/BB.pm
Criterion Covered Total %
statement 89 98 90.8
branch 15 22 68.1
condition 5 9 55.5
subroutine 18 20 90.0
pod 6 7 85.7
total 133 156 85.2


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::BB 0.22;
7              
8 7     7   21043 use v5.14;
  7         29  
9 7     7   41 use warnings;
  7         18  
  7         268  
10 7     7   46 use base qw( Device::BusPirate::Mode );
  7         14  
  7         857  
11              
12 7     7   44 use Carp;
  7         15  
  7         434  
13              
14 7     7   43 use Future;
  7         14  
  7         140  
15 7     7   36 use Future::AsyncAwait;
  7         20  
  7         44  
16              
17 7     7   326 use constant MODE => "BB";
  7         15  
  7         715  
18              
19             use constant {
20 7         9459 MASK_CS => 0x01,
21             MASK_MISO => 0x02,
22             MASK_CLK => 0x04,
23             MASK_MOSI => 0x08,
24             MASK_AUX => 0x10,
25              
26             CONF_PULLUP => 0x20,
27             CONF_POWER => 0x40,
28 7     7   49 };
  7         14  
29              
30             # Convenience hash
31             my %PIN_MASK = map { $_ => __PACKAGE__->${\"MASK_\U$_"} } qw( cs miso clk mosi aux );
32              
33             =head1 NAME
34              
35             C - use C in bit-banging mode
36              
37             =head1 SYNOPSIS
38              
39             use Device::BusPirate;
40              
41             my $pirate = Device::BusPirate->new;
42             my $bb = $pirate->enter_mode( "BB" )->get;
43              
44             my $count = 0;
45             while(1) {
46             $bb->write(
47             miso => $count == 0,
48             cs => $count == 1,
49             mosi => $count == 2,
50             clk => $count == 3,
51             aux => $count == 4,
52             )->then( sub { $pirate->sleep( 0.5 ) })
53             ->get;
54              
55             $count++;
56             $count = 0 if $count >= 5;
57             }
58              
59             =head1 DESCRIPTION
60              
61             This object is returned by a L instance when switching it
62             into C mode. It provides methods to configure the hardware, and interact
63             with the five basic IO lines in bit-banging mode.
64              
65             =cut
66              
67             =head1 METHODS
68              
69             =cut
70              
71             sub start
72             {
73 1     1 0 4 my $self = shift;
74              
75 1         3 $self->{dir_mask} = 0x1f; # all inputs
76              
77 1         4 $self->{out_mask} = 0; # all off
78              
79 1         6 Future->done( $self );
80             }
81              
82             =head2 configure
83              
84             $bb->configure( %args )->get
85              
86             Change configuration options. The following options exist; all of which are
87             simple true/false booleans.
88              
89             =over 4
90              
91             =item open_drain
92              
93             If enabled, a "high" output pin will be set as an input; i.e. hi-Z. When
94             disabled (default), a "high" output pin will be driven by 3.3V. A "low" output
95             will be driven to GND in either case.
96              
97             =back
98              
99             =cut
100              
101             sub configure
102             {
103 0     0 1 0 my $self = shift;
104 0         0 my %args = @_;
105              
106             defined $args{$_} and $self->{$_} = $args{$_}
107 0   0     0 for (qw( open_drain ));
108              
109 0         0 return Future->done
110             }
111              
112             =head2 write
113              
114             $bb->write( %pins )->get
115              
116             Sets the state of multiple output pins at the same time.
117              
118             =cut
119              
120             async sub _writeread
121 4     4   10 {
122 4         9 my $self = shift;
123 4         11 my ( $want_read, $pins_write, $pins_read ) = @_;
124              
125 4         7 my $out = $self->{out_mask};
126 4         8 my $dir = $self->{dir_mask};
127              
128 4         13 foreach my $pin ( keys %$pins_write ) {
129 3 50       10 my $mask = $PIN_MASK{$pin} or
130             croak "Unrecognised BB pin name $pin";
131 3         6 my $val = $pins_write->{$pin};
132              
133 3 100 66     18 if( $val and !$self->{open_drain} ) {
    50          
134 2         5 $dir &= ~$mask;
135 2         5 $out |= $mask;
136             }
137             elsif( $val ) {
138 0         0 $dir |= $mask;
139             }
140             else {
141 1         3 $dir &= ~$mask;
142 1         4 $out &= ~$mask;
143             }
144             }
145              
146 4         10 foreach my $pin ( @$pins_read ) {
147 0 0       0 my $mask = $PIN_MASK{$pin} or
148             croak "Unrecognised BB pin name $pin";
149              
150 0         0 $dir |= $mask;
151             }
152              
153 4         8 my $len = 0;
154 4 100       14 if( $dir != $self->{dir_mask} ) {
155 2         9 $self->pirate->write( chr( 0x40 | $dir ) );
156 2         12 $len++;
157              
158 2         5 $self->{dir_mask} = $dir;
159             }
160              
161 4 100 100     18 if( $want_read or $out != $self->{out_mask} ) {
162 3         15 $self->pirate->write( chr( 0x80 | $out ) );
163 3         17 $len++;
164              
165 3         7 $self->{out_mask} = $out;
166             }
167              
168 4 50       12 return unless $len;
169              
170 4         12 my $buf = await $self->pirate->read( $len );
171              
172 4 100       904 return if !$want_read;
173              
174 2         5 $buf = ord $buf;
175              
176 2         3 my $pins;
177 2         10 foreach my $pin ( keys %PIN_MASK ) {
178 10         15 my $mask = $PIN_MASK{$pin};
179 10 100       24 next unless $self->{dir_mask} & $mask;
180 8         20 $pins->{$pin} = !!( $buf & $mask );
181             }
182              
183 2         9 return $pins;
184             }
185              
186             sub write
187             {
188 1     1 1 1077 my $self = shift;
189 1         7 $self->_writeread( 0, { @_ }, [] );
190             }
191              
192             async sub _input1
193 1     1   3 {
194 1         3 my $self = shift;
195 1         3 my ( $mask ) = @_;
196              
197 1         3 $self->{dir_mask} |= $mask;
198              
199 1         4 $self->pirate->write( chr( 0x40 | $self->{dir_mask} ) );
200 1         8 return ord( await $self->pirate->read( 1 ) ) & $mask;
201             }
202              
203             =head2 read
204              
205             $pins = $bbio->read( @pins )->get
206              
207             Sets given list of pins (which may be empty) to be inputs, and returns a HASH
208             containing the current state of all the pins currently configured as inputs.
209             More efficient than calling multiple C methods when more than one pin
210             is being read at the same time.
211              
212             =cut
213              
214             sub read
215             {
216 2     2 1 1413 my $self = shift;
217 2         10 $self->_writeread( 1, {}, [ @_ ] );
218             }
219              
220             =head2 writeread
221              
222             $in_pins = $bbio->writeread( %out_pins )->get
223              
224             Combines the effects of C and C in a single operation; sets the
225             output state of any pins in C<%out_pins> then returns the input state of the
226             pins currently set as inputs.
227              
228             =cut
229              
230             sub writeread
231             {
232 0     0 1 0 my $self = shift;
233 0         0 $self->_writeread( 1, { @_ }, [] );
234             }
235              
236             =head2 power
237              
238             $bb->power( $power )->get
239              
240             Enable or disable the C 5V and 3.3V power outputs.
241              
242             =cut
243              
244             async sub power
245 1     1 1 926 {
246 1         3 my $self = shift;
247 1         3 my ( $state ) = @_;
248              
249             $state ? ( $self->{out_mask} |= CONF_POWER )
250 1 50       5 : ( $self->{out_mask} &= ~CONF_POWER );
251 1         5 $self->pirate->write( chr( 0x80 | $self->{out_mask} ) );
252 1         23 await $self->pirate->read( 1 );
253 1         199 return;
254             }
255              
256             =head2 pullup
257              
258             $bb->pullup( $pullup )->get
259              
260             Enable or disable the IO pin pullup resistors from C. These are connected
261             to the C, C, C and C pins.
262              
263             =cut
264              
265             async sub pullup
266 1     1 1 66 {
267 1         3 my $self = shift;
268 1         3 my ( $state ) = @_;
269              
270             $state ? ( $self->{out_mask} |= CONF_PULLUP )
271 1 50       6 : ( $self->{out_mask} &= ~CONF_PULLUP );
272 1         5 $self->pirate->write( chr( 0x80 | $self->{out_mask} ) );
273 1         9 await $self->pirate->read( 1 );
274 1         191 return;
275             }
276              
277             =head1 PER-PIN METHODS
278              
279             For each named pin, the following methods are defined. The pin names are
280              
281             cs miso sck mosi aux
282              
283             =head2 I
284              
285             $bbio->PIN( $state )->get
286              
287             Sets the output state of the given pin.
288              
289             =head2 read_I
290              
291             $state = $bbio->read_PIN->get
292              
293             Sets the pin to input direction and reads its current state.
294              
295             =cut
296              
297             foreach my $pin ( keys %PIN_MASK ) {
298 7     7   79 no strict 'refs';
  7         16  
  7         1250  
299 1     1   1007 *$pin = sub { shift->_writeread( 0, { $pin => $_[0] }, [] ) };
300              
301             my $mask = __PACKAGE__->${\"MASK_\U$pin"};
302             my $read_pin = "read_$pin";
303 1     1   77 *$read_pin = sub { shift->_input1( $mask ) };
304             }
305              
306             =head1 TODO
307              
308             =over 4
309              
310             =item *
311              
312             Some method of setting multiple pins into read mode at once, so that a single
313             C method hits them all.
314              
315             =back
316              
317             =head1 AUTHOR
318              
319             Paul Evans
320              
321             =cut
322              
323             0x55AA;