File Coverage

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