File Coverage

blib/lib/VIC/PIC/Functions/GPIO.pm
Criterion Covered Total %
statement 211 252 83.7
branch 112 160 70.0
condition 18 33 54.5
subroutine 17 17 100.0
pod 0 8 0.0
total 358 470 76.1


line stmt bran cond sub pod time code
1             package VIC::PIC::Functions::GPIO;
2 31     31   17187 use strict;
  31         57  
  31         790  
3 31     31   128 use warnings;
  31         55  
  31         627  
4 31     31   131 use bigint;
  31         49  
  31         155  
5             our $VERSION = '0.32';
6             $VERSION = eval $VERSION;
7 31     31   18339 use Carp;
  31         56  
  31         1667  
8 31     31   158 use POSIX ();
  31         48  
  31         626  
9 31     31   132 use Scalar::Util qw(looks_like_number);
  31         48  
  31         1337  
10 31     31   157 use Moo::Role;
  31         71  
  31         226  
11              
12             sub get_output_pin {
13 37     37 0 420 my ($self, $ipin) = @_;
14 37 100       161 return $ipin if exists $self->output_pins->{$ipin};
15             # find the correct GPIO pin then matching this pin
16 4         7 my $pin_no = $self->pins->{$ipin};
17 4         7 my $allpins = $self->pins->{$pin_no};
18 4 50       23 unless (ref $allpins eq 'ARRAY') {
19 0         0 carp "Invalid data for pin $pin_no";
20 0         0 return;
21             }
22 4         5 my $opin;
23 4         6 foreach my $iopin (@$allpins) {
24 4 50       12 next unless exists $self->output_pins->{$iopin};
25             # we have now found the correct iopin for the analog_pin
26 4         4 $opin = $iopin;
27 4         5 last;
28             }
29 4         8 return $opin;
30             }
31              
32             sub get_input_pin {
33 121     121 0 647 my ($self, $ipin) = @_;
34 121 100       389 return $ipin if exists $self->input_pins->{$ipin};
35             # find the correct GPIO pin then matching this pin
36 37         61 my $pin_no = $self->pins->{$ipin};
37 37         54 my $allpins = $self->pins->{$pin_no};
38 37 50       100 unless (ref $allpins eq 'ARRAY') {
39 0         0 carp "Invalid data for pin $pin_no";
40 0         0 return;
41             }
42 37         40 my $opin;
43 37         64 foreach my $iopin (@$allpins) {
44 37 50       85 next unless exists $self->input_pins->{$iopin};
45             # we have now found the correct iopin for the analog_pin
46 37         53 $opin = $iopin;
47 37         38 last;
48             }
49 37         78 return $opin;
50             }
51              
52             sub _gpio_select {
53 37     37   73 my $self = shift;
54 37         152 my ($io, $ad, $outp) = @_;
55 37 50       333 return unless $self->doesroles(qw(Chip GPIO));
56 37 50       190 return unless defined $outp;
57 37 100       185 $io = 0 if $io =~ /output/i;
58 37 100       175 $io = 1 if $io =~ /input/i;
59 37 100       877 $ad = 0 if $ad =~ /digital/i;
60 37 100       194 $ad = 1 if $ad =~ /analog/i;
61 37 50 66     630 return unless (($io == 0 or $io == 1) and ($ad == 0 or $ad == 1));
      66        
      66        
62             #TODO: check if banksel works for all chips
63             #if not then allow for a way to map instruction codes
64             #to something else
65              
66             # is this a register
67 37         2475 my ($trisp_code, $port_code, $an_code) = ('', '', '');
68 37 100 66     389 if (exists $self->io_ports->{$outp} and
    50          
69             exists $self->registers->{$outp}) {
70 13         63 my $trisp = $self->io_ports->{$outp};
71 13 50       40 my $flags = ($ad == 0) ? 0xFF : 0;
72 13 50       368 my $flagsH = ($ad == 0) ? 0xFF : 0;
73 13 50       376 if (exists $self->registers->{ANSEL}) {
74             # get the pins that belong to the register
75 13         41 my @portpins = ();
76 13 100       29 if ($io == 0) {
77 12         316 foreach (keys %{$self->output_pins}) {
  12         235  
78 204 100       3627 push @portpins, $_ if $self->output_pins->{$_}->[0] eq $outp;
79             }
80             } else {
81 1         40 foreach (keys %{$self->input_pins}) {
  1         23  
82 18 100       329 push @portpins, $_ if $self->input_pins->{$_}->[0] eq $outp;
83             }
84             }
85 13         256 foreach (@portpins) {
86 97         219 my $pin_no = $self->pins->{$_};
87 97 50       169 next unless defined $pin_no;
88 97         248 my $allpins = $self->pins->{$pin_no};
89 97 50       173 next unless ref $allpins eq 'ARRAY';
90 97         150 foreach my $anpin (@$allpins) {
91 324 100       13154 next unless exists $self->analog_pins->{$anpin};
92 72         96 my ($pno, $pbit) = @{$self->analog_pins->{$anpin}};
  72         281  
93 72 100       188 $flags ^= 1 << $pbit if $pbit < 8;
94 72 100       19707 $flagsH ^= 1 << ($pbit - 8) if $pbit >= 8;
95             }
96             }
97 13 50       40 my $iorandwf = ($ad == 0) ? 'andwf' : 'iorwf';
98 13 50       387 if ($flags != 0) {
99 13         465 $flags = sprintf "0x%02X", $flags;
100 13         329 $an_code .= "\tbanksel ANSEL\n";
101 13         38 $an_code .= "\tmovlw $flags\n";
102 13         45 $an_code .= "\t$iorandwf ANSEL, F\n";
103             }
104 13 50       60 if (exists $self->registers->{ANSELH}) {
105 13 50       45 if ($flagsH != 0) {
106 13         365 $flagsH = sprintf "0x%02X", $flagsH;
107 13         268 $an_code .= "\tbanksel ANSELH\n";
108 13         43 $an_code .= "\tmovlw $flagsH\n";
109 13         59 $an_code .= "\t$iorandwf ANSELH, F\n";
110             }
111             }
112             }
113 13 100       32 if ($io == 0) { # output
114 12         329 $trisp_code = "\tbanksel $trisp\n\tclrf $trisp";
115 12         64 $port_code = "\tbanksel $outp\n\tclrf $outp";
116             } else { # input
117 1         27 $trisp_code = "\tbanksel $trisp\n\tmovlw 0xFF\n\tmovwf $trisp";
118 1         3 $port_code = "\tbanksel $outp";
119             }
120             } elsif (exists $self->pins->{$outp}) {
121 24 100       59 my $iopin = ($io == 0) ? $self->get_output_pin($outp) :
122             $self->get_input_pin($outp);
123 24 50       115 unless (defined $iopin) {
124 0 0       0 my $iostr = ($io == 0) ? 'output' : 'input';
125 0         0 carp "Cannot find $outp in the list of registers or $iostr pins supporting GPIO for the chip " . $self->type;
126 0         0 return;
127             }
128             my ($port, $trisp, $pinbit) = ($io == 0) ?
129 14         411 @{$self->output_pins->{$iopin}} :
130 24 100       99 @{$self->input_pins->{$iopin}};
  10         303  
131              
132 24 50       225 if (exists $self->registers->{ANSEL}) {
133 24         76 my $pin_no = $self->pins->{$iopin};
134 24         57 my $allpins = $self->pins->{$pin_no};
135 24 50       72 unless (ref $allpins eq 'ARRAY') {
136 0         0 carp "Invalid data for pin $pin_no";
137 0         0 return;
138             }
139 24         145 foreach my $anpin (@$allpins) {
140 52 100       222 next unless exists $self->analog_pins->{$anpin};
141 20         34 my ($pno, $pbit) = @{$self->analog_pins->{$anpin}};
  20         65  
142 20         37 my $ansel = 'ANSEL';
143 20 50       127 if (exists $self->registers->{ANSELH}) {
144 20 50       65 $ansel = ($pbit >= 8) ? 'ANSELH' : 'ANSEL';
145             }
146             ##TODO: make sure that ANS$pbit exists for all header files
147 20 100       1725 my $bcfbsf = ($ad == 0) ? 'bcf' : 'bsf';
148 20         534 $an_code = "\tbanksel $ansel\n\t$bcfbsf $ansel, ANS$pbit";
149 20         58 last;
150             }
151             }
152 24 100       81 if ($io == 0) { # output
153 14         403 $trisp_code = "\tbanksel $trisp\n\tbcf $trisp, $trisp$pinbit";
154 14         55 $port_code = "\tbanksel $port\n\tbcf $port, $pinbit";
155             } else { # input
156 10         282 $trisp_code = "\tbanksel $trisp\n\tbsf $trisp, $trisp$pinbit";
157 10         20 $port_code = "\tbanksel $port";
158             }
159             } else {
160 0         0 carp "Cannot find $outp in the list of registers or pins supporting GPIO";
161 0         0 return;
162             }
163 37         228 return << "...";
164             $trisp_code
165             $an_code
166             $port_code
167             ...
168             }
169              
170             sub digital_output {
171 26     26 0 190 my $self = shift;
172 26         127 return $self->_gpio_select(output => 'digital', @_);
173             }
174              
175             sub digital_input {
176 8     8 0 61 my $self = shift;
177 8         41 return $self->_gpio_select(input => 'digital', @_);
178             }
179              
180             sub analog_input {
181 3     3 0 14 my $self = shift;
182 3         19 return $self->_gpio_select(input => 'analog', @_);
183             }
184              
185             sub setup {
186 3     3 0 24 my $self = shift;
187 3         6 my ($outp) = @_;
188 3 50       20 if ($outp =~ /US?ART/) {
189 3 50 33     20 if ($self->doesrole('USART') and exists $self->usart_pins->{$outp}) {
190 3         30 return $self->usart_setup(@_);
191             }
192             }
193 0         0 carp "The 'setup' function is not valid for $outp. Use something else.";
194 0         0 return;
195             }
196              
197             sub write {
198 46     46 0 261 my $self = shift;
199 46         104 my ($outp, $val) = @_;
200 46 50       148 return unless $self->doesroles(qw(CodeGen Operations Chip GPIO));
201 46 50       141 return unless defined $outp;
202 46 100 66     435 if (exists $self->io_ports->{$outp} and
    100          
    50          
203             exists $self->registers->{$outp}) {
204 20         60 my $port = $self->io_ports->{$outp};
205 20 50       84 unless (defined $val) {
206 0         0 return << "...";
207             \tclrf $outp
208             \tcomf $outp, 1
209             ...
210             }
211 20 50       97 if ($self->validate($val)) {
212             # ok we want to write the value of a pin to a port
213             # that doesn't seem right so let's provide a warning
214 0 0       0 if ($self->pins->{$val}) {
215 0         0 carp "$val is a pin and you're trying to write a pin to a port" .
216             " $outp. You can write a pin to a pin or a port to a port only.\n";
217 0         0 return;
218             }
219             }
220             # this handles the variable to port assigning
221 20         84 return $self->op_assign($outp, $val);
222             } elsif (exists $self->pins->{$outp}) {
223 19         69 my $iopin = $self->get_output_pin($outp);
224 19 50       51 unless (defined $iopin) {
225 0         0 carp "Cannot find $outp in the list of VALID ports, register or pins to write to";
226 0         0 return;
227             }
228 19         37 my ($port, $trisp, $pinbit) = @{$self->output_pins->{$iopin}};
  19         77  
229 19 100       140 if ($val =~ /^\d+$/) {
    100          
    100          
230 10 100       64 return "\tbanksel $port\n\tbcf $port, $pinbit\n" if "$val" eq '0';
231 7 50       54 return "\tbanksel $port\n\tbsf $port, $pinbit\n" if "$val" eq '1';
232 0         0 carp "$val cannot be applied to a pin $outp\n";
233 0         0 return;
234             } elsif (exists $self->pins->{$val}) {
235             # ok we want to short two pins, and this is not bit-banging
236             # although seems like it
237 4         9 my $vpin = $self->get_output_pin($val);
238 4 50       8 if ($vpin) {
239 4         25 my ($vport, $vtris, $vpinbit) = @{$self->output_pins->{$vpin}};
  4         14  
240 4         22 return << "...";
241             \tbtfss $vport, $vpin
242             \tbcf $port, $outp
243             \tbtfsc $vport, $vpin
244             \tbsf $port, $outp
245             ...
246             } else {
247 0         0 carp "$val is a port or unknown pin and cannot be written to a pin $outp. ".
248             "Only a pin can be written to a pin.\n";
249 0         0 return;
250             }
251             } elsif ($self->is_variable($val)) {
252 4         13 $val = uc $val;
253 4         29 return << "...";
254             ;;;; assigning $val to a pin => using the last bit
255             \tbtfss $val, 0
256             \tbcf $port, $outp
257             \tbtfsc $val, 0
258             \tbsf $port, $outp
259             ...
260             } else {
261 1         188 carp "$val is a port or unknown pin and cannot be written to a pin $outp. ".
262             "Only a pin can be written to a pin.\n";
263 1         203 return;
264             }
265 0         0 return $self->op_assign($port, $val);
266             } elsif (exists $self->registers->{$outp}) { # write a value to a register
267 0         0 my $code = "\tbanksel $outp\n";
268 0         0 $code .= $self->op_assign($outp, $val);
269 0         0 return $code;
270             } else {
271 7 50 33     18 if ($self->doesrole('USART') and exists $self->usart_pins->{$outp}) {
272 7         29 return $self->usart_write(@_);
273             }
274 0         0 carp "Cannot find $outp in the list of ports, register or pins to write to";
275 0         0 return;
276             }
277             }
278              
279             sub _macro_read_var {
280 6     6   18 my $v = $_[1];
281 6         11 $v = uc $v;
282 6         56 return << "...";
283             ;;;;;;; $v VARIABLES ;;;;;;
284             $v\_UDATA udata
285             $v res 1
286             ...
287             }
288              
289             sub read {
290 6     6 0 41 my $self = shift;
291 6         11 my $inp = shift;
292 6         11 my $var = undef;
293 6         11 my %action = ();
294 6 100       26 if (scalar(@_) == 1) {
295 1         94 $var = shift;
296             } else {
297 5         544 %action = @_;
298             }
299 6 50       39 return unless $self->doesroles(qw(CodeGen Chip GPIO));
300 6         43 my ($code, $funcs, $macros, $tables) = ('', {}, {}, []);
301              
302 6 100       26 if (defined $var) {
303 1 50 33     16 if (looks_like_number($var) or ref $var eq 'HASH') {
304 0         0 carp "Cannot read from $inp into a constant $var";
305 0         0 return;
306             }
307 1         3 $var = uc $var;
308             } else {
309             ## we need only 1 variable here
310 5 50       31 if (defined $action{PARAM}) {
311 5         15 $var = $action{PARAM} . '0';
312             } else {
313 0         0 carp "Implementation errors implementing the Action block";
314 0         0 return undef;
315             }
316 5         12 $var = uc $var;
317 5         20 $macros->{lc("m_read_$var")} = $self->_macro_read_var($var);
318 5 50 66     43 return unless (defined $action{ACTION} or defined $action{ISR});
319 5 50       16 return unless defined $action{END};
320             }
321 6         34 my $bits = $self->address_bits($var);
322 6         18 my ($port, $portbit);
323 6 100 66     61 if (exists $self->io_ports->{$inp} and
    100          
324             exists $self->registers->{$inp}) {
325             # this is a port like PORT[A-Z]
326             # we may end up reading from all pins on a port
327 1         3 $port = $inp;
328 1         20 $code = <<"...";
329             ;;; instant reading from $port into $var
330             \tbanksel $port
331             \tmovf $port, W
332             \tbanksel $var
333             \tmovwf $var
334             ...
335             } elsif (exists $self->pins->{$inp}) {
336 4         12 my $ipin = $self->get_input_pin($inp);
337 4 50       10 unless (defined $ipin) {
338 0         0 carp "Cannot find $inp in the list of GPIO ports or pins";
339 0         0 return;
340             } else {
341 4         13 my $tris;
342 4         7 ($port, $tris, $portbit) = @{$self->input_pins->{$inp}};
  4         31  
343 4         36 $code = <<"....";
344             ;;; instant reading from $inp into $var
345             \tclrw
346             \tbanksel $port
347             \tbtfsc $port, $portbit
348             \taddlw 0x01
349             \tbanksel $var
350             \tmovwf $var
351             ....
352             }
353             } else {
354 1 50 33     4 if ($self->doesrole('USART') and exists $self->usart_pins->{$inp}) {
355 1         10 return $self->usart_read($inp, @_);
356             }
357 0         0 carp "Cannot find $inp in the list of ports or pins to read from";
358 0         0 return;
359             }
360 5 100       16 if (%action) {
361 4 100       34 if (exists $action{ACTION}) {
    50          
362 1         2 my $action_label = $action{ACTION};
363 1         2 my $end_label = $action{END};
364 1         12 $code .= <<"...";
365             ;;; invoking $action_label
366             \tgoto $action_label
367             $end_label:\n
368             ...
369             } elsif (exists $action{ISR}) {
370             ## ok we can read from a port too, so let's do that as well
371 3 100       19 if (defined $portbit) {
372             # if we are a pin, then find the right pin
373 2         6 $inp = $self->get_input_pin($inp);
374             }
375             ## reset the code here since we have to check IOC pins
376 3         7 my ($ioc_bit, $ioc_reg, $ioc_flag, $ioc_enable);
377 3 100       21 if (exists $self->ioc_pins->{$inp}) {
    50          
378 2         3 my $apin;
379 2         4 ($apin, $ioc_bit, $ioc_reg) = @{$self->ioc_pins->{$inp}};
  2         8  
380             } elsif (exists $self->ioc_ports->{$inp}) {
381 1         4 $ioc_reg = $self->ioc_ports->{$inp};
382             } else {
383 0         0 carp "Reading using interrupt-on-change has to be for a pin ".
384             "that supports it, $inp does not support it or is not a pin.";
385 0         0 return;
386             }
387 3         12 $ioc_flag = $self->ioc_ports->{FLAG};
388 3         9 $ioc_enable = $self->ioc_ports->{ENABLE};
389 3         21 my $ioch = { bit => $ioc_bit, reg => $ioc_reg, flag =>
390             $ioc_flag, enable => $ioc_enable };
391 3         43 $code = $self->isr_ioc($ioch, $inp);
392 3 50       15 my $isr_label = 'isr_' . ((defined $ioc_bit) ? lc($ioc_bit) :
    100          
393             ((defined $ioc_reg) ? lc($ioc_reg) :
394             lc($inp)));
395 3         22 $funcs->{$isr_label} = $self->isr_ioc($ioch, $inp, $var, $port, $portbit, %action);
396             } else {
397 0         0 carp "Unknown action requested. Probably a bug in implementation";
398 0         0 return;
399             }
400             }
401 5 50       35 return wantarray ? ($code, $funcs, $macros, $tables) : $code;
402             }
403              
404             1;
405             __END__