File Coverage

blib/lib/Device/BusPirate/Mode.pm
Criterion Covered Total %
statement 71 75 94.6
branch 19 24 79.1
condition 4 7 57.1
subroutine 16 17 94.1
pod 7 8 87.5
total 117 131 89.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 0.22;
7              
8 7     7   100 use v5.14;
  7         26  
9 7     7   48 use warnings;
  7         16  
  7         183  
10              
11 7     7   36 use Carp;
  7         16  
  7         514  
12              
13 7     7   44 use Future::AsyncAwait;
  7         14  
  7         59  
14              
15 7   50 7   486 use constant PIRATE_DEBUG => $ENV{PIRATE_DEBUG} // 0;
  7         35  
  7         767  
16              
17             use constant {
18 7         4621 CONF_CS => 0x01,
19             CONF_AUX => 0x02,
20             CONF_PULLUP => 0x04,
21             CONF_POWER => 0x08,
22 7     7   50 };
  7         115  
23              
24             =head1 NAME
25              
26             C - base class for C modes
27              
28             =head1 DESCRIPTION
29              
30             The following methods are implemented by all the various mode subclasses.
31              
32             =cut
33              
34             sub new
35             {
36 5     5 0 16 my $class = shift;
37 5         15 my ( $bp ) = @_;
38              
39 5         30 my $self = bless {
40             bp => $bp,
41             }, $class;
42              
43 5         29 $self->{cs} = 0;
44 5         14 $self->{power} = 0;
45 5         12 $self->{pullup} = 0;
46 5         23 $self->{aux} = 0;
47              
48 5         33 return $self;
49             }
50              
51             =head1 METHODS
52              
53             =cut
54              
55             =head2 pirate
56              
57             $pirate = $mode->pirate
58              
59             Returns the underlying L instance.
60              
61             =cut
62              
63             sub pirate
64             {
65 148     148 1 252 my $self = shift;
66 148         727 return $self->{bp};
67             }
68              
69             async sub _start_mode_and_await
70 4     4   9 {
71 4         61 my $self = shift;
72 4         16 my ( $send, $await ) = @_;
73              
74 4         21 my $pirate = $self->pirate;
75              
76 4         20 $pirate->write( $send );
77 4         36 my $buf = await $pirate->read( length $await, "start mode" );
78              
79 4 50       2787 return $buf if $buf eq $await;
80 0         0 die "Expected '$await' response but got '$buf'";
81             }
82              
83             =head2 power
84              
85             $mode->power( $power )->get
86              
87             Enable or disable the C 5V and 3.3V power outputs.
88              
89             =cut
90              
91             sub power
92             {
93 2     2 1 804 my $self = shift;
94 2         8 $self->{power} = !!shift;
95 2         8 $self->_update_peripherals;
96             }
97              
98             =head2 pullup
99              
100             $mode->pullup( $pullup )->get
101              
102             Enable or disable the IO pin pullup resistors from C. These are connected
103             to the C, C, C and C pins.
104              
105             =cut
106              
107             sub pullup
108             {
109 6     6 1 171 my $self = shift;
110 6         16 $self->{pullup} = !!shift;
111 6         40 $self->_update_peripherals;
112             }
113              
114             =head2 aux
115              
116             $mode->aux( $aux )->get
117              
118             Set the C output pin level.
119              
120             =cut
121              
122             sub aux
123             {
124 2     2 1 2323 my $self = shift;
125 2         8 $self->{aux} = !!shift;
126 2         14 $self->_update_peripherals;
127             }
128              
129             =head2 cs
130              
131             $mode->cs( $cs )->get
132              
133             Set the C output pin level.
134              
135             =cut
136              
137             sub cs
138             {
139 0     0 1 0 my $self = shift;
140 0         0 $self->{cs} = !!shift;
141 0         0 $self->_update_peripherals;
142             }
143              
144             sub _update_peripherals
145             {
146 10     10   22 my $self = shift;
147              
148             $self->pirate->write_expect_ack( chr( 0x40 |
149             ( $self->{power} ? CONF_POWER : 0 ) |
150             ( $self->{pullup} ? CONF_PULLUP : 0 ) |
151             ( $self->{aux} ? CONF_AUX : 0 ) |
152 10 100       27 ( $self->{cs} ? CONF_CS : 0 ) ), "_update_peripherals" );
    100          
    100          
    100          
153             }
154              
155             =head2 set_pwm
156              
157             $mode->set_pwm( freq => $freq, duty => $duty )->get
158              
159             Sets the PWM generator to the given frequency and duty cycle, as a percentage.
160             If unspecified, duty cycle will be 50%. Set frequency to 0 to disable.
161              
162             =cut
163              
164             use constant {
165 7         3663 PRESCALE_1 => 0,
166             PRESCALE_8 => 1,
167             PRESCALE_64 => 2,
168             PRESCALE_256 => 3,
169 7     7   58 };
  7         90  
170              
171             sub set_pwm
172             {
173 5     5 1 709 my $self = shift;
174 5         18 my %args = @_;
175              
176 5 50       19 $self->MODE eq "BB" or
177             croak "Cannot ->set_pwm except in BB mode";
178              
179 5   33     16 my $freq = $args{freq} // croak "Require freq";
180 5   100     24 my $duty = $args{duty} // 50;
181              
182 5 100       15 if( $freq == 0 ) {
183 1         3 print STDERR "PIRATE BB CLEAR-PWM\n" if PIRATE_DEBUG;
184 1         4 return $self->pirate->write_expect_ack( "\x13", "clear PWM" );
185             }
186              
187             # in fCPU counts at 16MHz
188 4         12 my $period = 16E6 / $freq;
189              
190 4         7 my $prescale = PRESCALE_1;
191 4 100       11 $prescale = PRESCALE_8, $period /= 8 if $period >= 2**16;
192 4 100       10 $prescale = PRESCALE_64, $period /= 8 if $period >= 2**16;
193 4 50       11 $prescale = PRESCALE_256, $period /= 4 if $period >= 2**16;
194 4 50       9 croak "PWM frequency too low" if $period >= 2**16;
195              
196 4         8 $duty = $period * $duty / 100;
197              
198 4         8 print STDERR "PIRATE BB SET-PWM\n" if PIRATE_DEBUG;
199 4         9 $self->pirate->write_expect_ack(
200             pack( "C C S> S>", 0x12, $prescale, $duty, $period ), "set PWM"
201             );
202             }
203              
204             =head2 read_adc_voltage
205              
206             $voltage = $mode->read_adc_voltage->get
207              
208             Reads the voltage on the ADC pin and returns it as a numerical value in volts.
209              
210             =cut
211              
212             async sub read_adc_voltage
213 1     1 1 553 {
214 1         3 my $self = shift;
215              
216 1 50       6 $self->MODE eq "BB" or
217             croak "Cannot ->read_adc except in BB mode";
218              
219 1         5 await $self->pirate->write( "\x14" );
220 1         26 my $buf = await $self->pirate->read( 2 );
221              
222 1         237 return unpack( "S>", $buf ) * 6.6 / 1024;
223             }
224              
225             =head1 AUTHOR
226              
227             Paul Evans
228              
229             =cut
230              
231             0x55AA;