File Coverage

blib/lib/VIC/PIC/P16F690.pm
Criterion Covered Total %
statement 44 53 83.0
branch 13 26 50.0
condition n/a
subroutine 6 6 100.0
pod 0 2 0.0
total 63 87 72.4


line stmt bran cond sub pod time code
1             package VIC::PIC::P16F690;
2 31     31   185 use strict;
  31         51  
  31         883  
3 31     31   172 use warnings;
  31         50  
  31         1414  
4              
5             our $VERSION = '0.32';
6             $VERSION = eval $VERSION;
7              
8 31     31   145 use Carp;
  31         57  
  31         1649  
9 31     31   13833 use Moo;
  31         288424  
  31         139  
10             extends 'VIC::PIC::Base';
11              
12             # role CodeGen
13             has type => (is => 'ro', default => 'p16f690');
14             has include => (is => 'ro', default => 'p16f690.inc');
15              
16             #role Chip
17             has f_osc => (is => 'ro', default => 4e6); # 4MHz internal oscillator
18             has pcl_size => (is => 'ro', default => 13); # program counter (PCL) size
19             has stack_size => (is => 'ro', default => 8); # 8 levels of 13-bit entries
20             has wreg_size => (is => 'ro', default => 8); # 8-bit register WREG
21             # all memory is in bytes
22             has memory => (is => 'ro', default => sub {
23             {
24             flash => 4096, # words
25             SRAM => 256,
26             EEPROM => 256,
27             }
28             });
29             has address => (is => 'ro', default => sub {
30             {
31             isr => [ 0x0004 ],
32             reset => [ 0x0000 ],
33             range => [ 0x0000, 0x0FFF ],
34             }
35             });
36              
37             has pin_counts => (is => 'ro', default => sub { {
38             pdip => 20, ## PDIP or DIP ?
39             soic => 20,
40             ssop => 20,
41             total => 20,
42             io => 18,
43             }});
44              
45             has banks => (is => 'ro', default => sub {
46             {
47             count => 4,
48             size => 0x80,
49             gpr => {
50             0 => [ 0x020, 0x07F],
51             1 => [ 0x0A0, 0x0EF],
52             2 => [ 0x120, 0x16F],
53             },
54             # remapping of these addresses automatically done by chip
55             common => [0x070, 0x07F],
56             remap => [
57             [0x0F0, 0x0FF],
58             [0x170, 0x17F],
59             [0x1F0, 0x1FF],
60             ],
61             }
62             });
63              
64             has registers => (is => 'ro', default => sub {
65             {
66             INDF => [0x000, 0x080, 0x100, 0x180], # indirect addressing
67             TMR0 => [0x001, 0x101],
68             OPTION_REG => [0x081, 0x181],
69             PCL => [0x002, 0x082, 0x102, 0x182],
70             STATUS => [0x003, 0x083, 0x103, 0x183],
71             FSR => [0x004, 0x084, 0x104, 0x184],
72             PORTA => [0x005, 0x105],
73             TRISA => [0x085, 0x185],
74             PORTB => [0x006, 0x106],
75             TRISB => [0x086, 0x186],
76             PORTC => [0x007, 0x107],
77             TRISC => [0x087, 0x187],
78             PCLATH => [0x00A, 0x08A, 0x10A, 0x18A],
79             INTCON => [0x00B, 0x08B, 0x10B, 0x18B],
80             PIR1 => [0x00C],
81             PIE1 => [0x08C],
82             EEDAT => [0x10C],
83             EECON1 => [0x18C],
84             PIR2 => [0x00D],
85             PIE2 => [0x08D],
86             EEADR => [0x10D],
87             EECON2 => [0x18D], # not addressable apparently
88             TMR1L => [0x00E],
89             PCON => [0x08E],
90             EEDATH => [0x10E],
91             TMR1H => [0x00F],
92             OSCCON => [0x08F],
93             EEADRH => [0x10F],
94             T1CON => [0x010],
95             OSCTUNE => [0x090],
96             TMR2 => [0x011],
97             T2CON => [0x012],
98             PR2 => [0x092],
99             SSPBUF => [0x013],
100             SSPADD => [0x093],
101             SSPCON => [0x014],
102             SSPSTAT => [0x094],
103             CCPR1L => [0x015],
104             WPUA => [0x095],
105             WPUB => [0x115],
106             CCPR1H => [0x016],
107             IOCA => [0x096],
108             IOCB => [0x116],
109             CCP1CON => [0x017],
110             WDTCON => [0x097],
111             RCSTA => [0x018],
112             TXSTA => [0x098],
113             VRCON => [0x118],
114             TXREG => [0x019],
115             SPBRG => [0x099],
116             CM1CON0 => [0x119],
117             RCREG => [0x01A],
118             SPBRGH => [0x09A],
119             CM2CON0 => [0x11A],
120             BAUDCTL => [0x09B],
121             CM2CON1 => [0x11B],
122             PWM1CON => [0x01C],
123             ECCPAS => [0x01D],
124             PSTRCON => [0x19D],
125             ADRESH => [0x01E],
126             ADRESL => [0x09E],
127             ANSEL => [0x11E],
128             SRCON => [0x19E],
129             ADCON0 => [0x01F],
130             ADCON1 => [0x09F],
131             ANSELH => [0x11F],
132             }
133             });
134              
135             has pins => (is => 'ro', default => sub {
136             my $h = {
137             # number to pin name and pin name to number
138             1 => [qw(Vdd)],
139             2 => [qw(RA5 T1CKI OSC1 CLKIN)],
140             3 => [qw(RA4 AN3 T1G OSC2 CLKOUT)],
141             4 => [qw(RA3 MCLR Vpp)],
142             5 => [qw(RC5 CCP1 P1A)],
143             6 => [qw(RC4 C2OUT P1B)],
144             7 => [qw(RC3 AN7 C12IN3- P1C)],
145             8 => [qw(RC6 AN8 SS)],
146             9 => [qw(RC7 AN9 SDO)],
147             10 => [qw(RB7 TX CK)],
148             11 => [qw(RB6 SCK SCL)],
149             12 => [qw(RB5 AN11 RX DT)],
150             13 => [qw(RB4 AN10 SDI SDA)],
151             14 => [qw(RC2 AN6 C12IN2- P1D)],
152             15 => [qw(RC1 AN5 C12IN1-)],
153             16 => [qw(RC0 AN4 C2IN+)],
154             17 => [qw(RA2 AN2 T0CKI INT C1OUT)],
155             18 => [qw(RA1 AN1 C12IN0- Vref ICSPCLK)],
156             19 => [qw(RA0 AN0 C1N+ ICSPDAT ULPWU)],
157             20 => [qw(Vss)],
158             };
159             foreach my $k (keys %$h) {
160             my $v = $h->{$k};
161             foreach (@$v) {
162             $h->{$_} = $k;
163             }
164             }
165             return $h;
166             });
167              
168             has clock_pins => (is => 'ro', default => sub {
169             {
170             out => 'CLKOUT',
171             in => 'CLKIN',
172             }
173             });
174              
175             has oscillator_pins => (is => 'ro', default => sub {
176             {
177             1 => 'OSC1',
178             2 => 'OSC2',
179             }
180             });
181              
182             has program_pins => (is => 'ro', default => sub {
183             {
184             clock => 'ICSPCLK',
185             data => 'ICSPDAT',
186             }
187             });
188              
189             has io_ports => (is => 'ro', default => sub {
190             {
191             #port => tristate,
192             PORTA => 'TRISA',
193             PORTB => 'TRISB',
194             PORTC => 'TRISC',
195             }
196             });
197              
198             has input_pins => (is => 'ro', default => sub {
199             {
200             #I/O => [port, tristate, bit]
201             RA0 => ['PORTA', 'TRISA', 0],
202             RA1 => ['PORTA', 'TRISA', 1],
203             RA2 => ['PORTA', 'TRISA', 2],
204             RA3 => ['PORTA', 'TRISA', 3], # input only
205             RA4 => ['PORTA', 'TRISA', 4],
206             RA5 => ['PORTA', 'TRISA', 5],
207             RB4 => ['PORTB', 'TRISB', 4],
208             RB5 => ['PORTB', 'TRISB', 5],
209             RB6 => ['PORTB', 'TRISB', 6],
210             RB7 => ['PORTB', 'TRISB', 7],
211             RC0 => ['PORTC', 'TRISC', 0],
212             RC1 => ['PORTC', 'TRISC', 1],
213             RC2 => ['PORTC', 'TRISC', 2],
214             RC3 => ['PORTC', 'TRISC', 3],
215             RC4 => ['PORTC', 'TRISC', 4],
216             RC5 => ['PORTC', 'TRISC', 5],
217             RC6 => ['PORTC', 'TRISC', 6],
218             RC7 => ['PORTC', 'TRISC', 7],
219             }
220             });
221              
222             has output_pins => (is => 'ro', default => sub {
223             {
224             #I/O => [port, tristate, bit]
225             RA0 => ['PORTA', 'TRISA', 0],
226             RA1 => ['PORTA', 'TRISA', 1],
227             RA2 => ['PORTA', 'TRISA', 2],
228             RA4 => ['PORTA', 'TRISA', 4],
229             RA5 => ['PORTA', 'TRISA', 5],
230             RB4 => ['PORTB', 'TRISB', 4],
231             RB5 => ['PORTB', 'TRISB', 5],
232             RB6 => ['PORTB', 'TRISB', 6],
233             RB7 => ['PORTB', 'TRISB', 7],
234             RC0 => ['PORTC', 'TRISC', 0],
235             RC1 => ['PORTC', 'TRISC', 1],
236             RC2 => ['PORTC', 'TRISC', 2],
237             RC3 => ['PORTC', 'TRISC', 3],
238             RC4 => ['PORTC', 'TRISC', 4],
239             RC5 => ['PORTC', 'TRISC', 5],
240             RC6 => ['PORTC', 'TRISC', 6],
241             RC7 => ['PORTC', 'TRISC', 7],
242             }
243             });
244              
245             has analog_pins => (is => 'ro', default => sub {
246             {
247             # use ANSEL for pins AN0-AN7 and ANSELH for AN8-AN11
248             #pin => number, bit
249             AN0 => [19, 0],
250             AN1 => [18, 1],
251             AN2 => [17, 2],
252             AN3 => [3, 3],
253             AN4 => [16, 4],
254             AN5 => [15, 5],
255             AN6 => [14, 6],
256             AN7 => [ 7, 7],
257             AN8 => [ 8, 8],
258             AN9 => [ 9, 9],
259             AN10 => [13, 10],
260             AN11 => [12, 11],
261             }
262             });
263              
264             has adc_channels => (is => 'ro', default => 12);
265             has adcs_bits => (is => 'ro', default => sub {
266             {
267             2 => '000',
268             4 => '100',
269             8 => '001',
270             16 => '101',
271             32 => '010',
272             64 => '110',
273             internal => '111',
274             }
275             });
276             has adc_chs_bits => (is => 'ro', default => sub {
277             {
278             #pin => chsbits
279             AN0 => '0000',
280             AN1 => '0001',
281             AN2 => '0010',
282             AN3 => '0011',
283             AN4 => '0100',
284             AN5 => '0101',
285             AN6 => '0110',
286             AN7 => '0111',
287             AN8 => '1000',
288             AN9 => '1001',
289             AN10 => '1010',
290             AN11 => '1011',
291             CVref => '1100',
292             '0.6V' => '1101',
293             }
294             });
295              
296             has timer_prescaler => (is => 'ro', default => sub {
297             {
298             2 => '000',
299             4 => '001',
300             8 => '010',
301             16 => '011',
302             32 => '100',
303             64 => '101',
304             128 => '110',
305             256 => '111',
306             }
307             });
308              
309             has wdt_prescaler => (is => 'ro', default => sub {
310             {
311             TMR0 => {
312             1 => '000',
313             2 => '001',
314             4 => '010',
315             8 => '011',
316             16 => '100',
317             32 => '101',
318             64 => '110',
319             128 => '111',
320             },
321             WDT => {
322             32 => '0000',
323             64 => '0001',
324             128 => '0010',
325             256 => '0011',
326             512 => '0100',
327             1024 => '0101',
328             2048 => '0110',
329             4096 => '0111',
330             8192 => '1000',
331             16384 => '1001',
332             32768 => '1010',
333             65536 => '1011',
334             ## others are reserved
335             },
336             LFINTOSC => 31000, #31kHZ
337             }
338             });
339              
340             has timer_pins => (is => 'ro', default => sub {
341             {
342             #reg #reg #ireg #flag #enable
343             TMR0 => { reg => 'TMR0', freg => 'INTCON', flag => 'T0IF', enable => 'T0IE', ereg => 'INTCON' },
344             TMR1 => { reg => ['TMR1H', 'TMR1L'], freg => 'PIR1', ereg => 'PIE1', flag => 'TMR1IF', enable => 'TMR1E' },
345             TMR2 => { reg => 'TMR2', freg => 'PIR1', flag => 'TMR2IF', enable => 'TMR2IE', ereg => 'PIE1' },
346             T0CKI => 17,
347             T1CKI => 2,
348             T1G => 3,
349             }
350             });
351              
352             has eccp_pins => (is => 'ro', default => sub {
353             { # pin => pin_no, tris, bit
354             P1D => [14, 'TRISC', 2],
355             P1C => [7, 'TRISC', 3],
356             P1B => [6, 'TRISC', 4],
357             P1A => [5, 'TRISC', 5],
358             CCP1 => [5, 'TRISC', 5],
359             }
360             });
361              
362             #external interrupt
363             has eint_pins => (is => 'ro', default => sub {
364             {
365             INT => 17,
366             }
367             });
368              
369             has ioc_pins => (is => 'ro', default => sub {
370             {
371             #pin #ioc-bit #ioc-reg
372             RA0 => [19, 'IOCA0', 'IOCA'],
373             RA1 => [18, 'IOCA1', 'IOCA'],
374             RA2 => [17, 'IOCA2', 'IOCA'],
375             RA3 => [4, 'IOCA3', 'IOCA'],
376             RA4 => [3, 'IOCA4', 'IOCA'],
377             RA5 => [2, 'IOCA5', 'IOCA'],
378             RB4 => [13, 'IOCB4', 'IOCB'],
379             RB5 => [12, 'IOCB5', 'IOCB'],
380             RB6 => [11, 'IOCB6', 'IOCB'],
381             RB7 => [10, 'IOCB7', 'IOCB'],
382             }
383             });
384              
385             has ioc_ports => (is => 'ro', default => sub {
386             {
387             # for ports to be used as well
388             PORTA => 'IOCA',
389             PORTB => 'IOCB',
390             FLAG => 'RABIF',
391             ENABLE => 'RABIE',
392             }
393             });
394              
395             has usart_pins => (is => 'ro', default => sub {
396             {
397             async_in => 'RX',
398             async_out => 'TX',
399             sync_clock => 'CK',
400             sync_data => 'DT',
401             rx_int => { reg => 'PIE1', flag => 'RCIF', enable => 'RCIE', preg =>
402             'INTCON', penable => 'PEIE' },
403             tx_int => { reg => 'PIR1', flag => 'TXIF', enable => 'TXIE', preg =>
404             'INTCON', penable => 'PEIE' },
405             # this defines the port names that the user can use
406             # validly. The port names define whether the user wants to use them in
407             # synchronous or asynchronous mode
408             UART => 'async',
409             USART => 'sync',
410             }
411             });
412              
413             sub usart_baudrates {
414 3     3 0 10 my ($self, $baud, $f_osc, $sync) = @_;
415 3 50       6 $baud = 9600 unless defined $baud;
416 3         8 my %acceptable = map { $_ => 1 } (300, 1200, 2400, 9600, 19200, 57600, 115200);
  21         49  
417 3 50       12 unless (exists $acceptable{$baud}) {
418 0         0 my $str = sprintf "Baud rate %d is unacceptable. Allowed rates are: %s",
419             $baud, join(',', keys %acceptable);
420 0         0 carp $str;
421 0         0 return;
422             }
423 3 50       8 $f_osc = defined $f_osc ? $f_osc : $self->f_osc;
424 3 50       7 $sync = defined $sync ? $sync : 0;
425             ## we check with the expected error rates and pick the appropriate baud-rate
426             ## generation parameters such as the values of BRG16 and BRGH
427 3 50       7 if ($sync) {
428             # BRGH = x, BRG16 = 1
429 0         0 my $spbrg = int(($f_osc / ($baud * 4)) - 1);
430 0         0 my $cbaud = int($f_osc / (($spbrg + 1) * 4));
431 0         0 my $error = (($cbaud - $baud) * 100) / $baud;
432 0         0 return { SPBRG => $spbrg, BRGH => 0, BRG16 => 1,
433             error => $error, actual => $cbaud, baud => $baud };
434             } else {
435             # BRG16 = 0, BRGH = 0, BRG is 8-bit
436 3         19 my $spbrg_00 = int(($f_osc / ($baud * 64)) - 1);
437 3         9 my $cbaud_00 = $f_osc / (($spbrg_00 + 1) * 64);
438 3         13 my $error_00 = (($cbaud_00 - $baud) * 100) / $baud;
439 3 50       19 my $hh_00 = { SPBRG => $spbrg_00, error => $error_00, actual => $cbaud_00,
440             BRG16 => 0, BRGH => 0, baud => $baud } if $spbrg_00 < 0x100;
441             # BRG16 = 0, BRGH = 1, BRG is 8-bit
442 3         9 my $spbrg_01 = int(($f_osc / ($baud * 16)) - 1);
443 3         5 my $cbaud_01 = $f_osc / (($spbrg_01 + 1) * 16);
444 3         5 my $error_01 = (($cbaud_01 - $baud) * 100) / $baud;
445 3 50       11 my $hh_01 = { SPBRG => $spbrg_01, error => $error_01, actual => $cbaud_01,
446             BRG16 => 0, BRGH => 1, baud => $baud } if $spbrg_01 < 0x100;
447             # BRG16 = 1, BRGH = 0, BRG is 16-bit
448 3         13 my $spbrg_10 = int(($f_osc / ($baud * 16)) - 1);
449 3         7 my $cbaud_10 = $f_osc / (($spbrg_10 + 1) * 16);
450 3         5 my $error_10 = (($cbaud_10 - $baud) * 100) / $baud;
451 3 50       11 my $hh_10 = { SPBRG => $spbrg_10, error => $error_10, actual => $cbaud_10,
452             BRG16 => 1, BRGH => 0, baud => $baud } if $spbrg_10 < 0x10000;
453             # BRG16 = 1, BRGH = 1, BRG is 16-bit
454 3         13 my $spbrg_11 = int(($f_osc / ($baud * 4)) - 1);
455 3         7 my $cbaud_11 = $f_osc / (($spbrg_11 + 1) * 4);
456 3         7 my $error_11 = (($cbaud_11 - $baud) * 100) / $baud;
457 3 50       18 my $hh_11 = { SPBRG => $spbrg_11, error => $error_11, actual => $cbaud_11,
458             BRG16 => 1, BRGH => 1, baud => $baud } if $spbrg_11 < 0x10000;
459             ## sort based on error in ascending order and remove NaN
460 15         94 my @sorted = sort { $a->{error} <=> $b->{error} }
461 3 50       9 grep { defined $_ and $_->{error} == $_->{error} } ($hh_00, $hh_01, $hh_10, $hh_11);
  12         45  
462 3 50       14 unless (@sorted) {
463 0         0 carp "Cannot seem to find appropriate baud generator values for $baud";
464 0         0 return;
465             }
466 3 50       20 return wantarray ? @sorted : $sorted[0];
467             }
468             }
469              
470             has selector_pins => (is => 'ro', default => sub {
471             {
472             'spi_or_i2c' => 'SS',
473             }
474             });
475              
476             has spi_pins => (is => 'ro', default => sub {
477             {
478             data_out => 'SDO',
479             data_in => 'SDI',
480             clock => 'SCK',
481             }
482             });
483              
484             has i2c_pins => (is => 'ro', default => sub {
485             {
486             data => 'SDA',
487             clock => 'SCL',
488             }
489             });
490              
491             has cmp_input_pins => (is => 'ro', default => sub {
492             {
493             'C1IN+' => 'C1IN+',
494             'C12IN0-' => 'C12IN0-',
495             'C2IN+' => 'C2IN+',
496             'C12IN1-' => 'C12IN1-',
497             'C12IN2-' => 'C12IN2-',
498             'C12IN3-' => 'C12IN3-',
499             }
500             });
501              
502             has cmp_output_pins => (is => 'ro', default => sub {
503             {
504             C1OUT => 'C1OUT',
505             C2OUT => 'C2OUT',
506             }
507             });
508              
509             has chip_config => (is => 'ro', default => sub {
510             {
511             on_off => {
512             MCLRE => 0,
513             WDT => 0,
514             PWRTE => 0,
515             CP => 0,
516             BOR => 0,
517             IESO => 0,
518             FCMEN => 0,
519             },
520             f_osc => {
521             INTRC_OSC => 0,
522             },
523             }
524             });
525              
526             my @rolenames = qw(CodeGen Operators Chip GPIO ADC ISR Timer Operations ECCP
527             USART SPI I2C Comparator Power);
528             my @roles = map (("VIC::PIC::Roles::$_", "VIC::PIC::Functions::$_"), @rolenames);
529             with @roles;
530              
531             sub list_roles {
532 1     1 0 1442 my @arr = grep {!/CodeGen|Oper|Chip|ISR/} @rolenames;
  14         31  
533 1 50       6 return wantarray ? @arr : [@arr];
534             }
535              
536             1;
537             __END__