File Coverage

blib/lib/Device/BCM2835/LCD.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Device::BCM2835::LCD;
2              
3 1     1   14646 use 5.006;
  1         4  
  1         145  
4 1     1   5 use strict qw(vars);
  1         1  
  1         25  
5 1     1   4 use warnings;
  1         5  
  1         31  
6 1     1   3 use Carp;
  1         1  
  1         82  
7 1     1   161 use Device::BCM2835;
  0            
  0            
8             use Time::HiRes qw(usleep nanosleep);
9              
10             =head1 NAME
11              
12             Device::BCM2835::LCD - Perl extension for driving an HD44780 LCD from a Raspberry Pi's GPIO port
13              
14             =head1 VERSION
15              
16             Version 0.02
17              
18             =cut
19              
20             our $VERSION = '0.02';
21              
22             =head1 SYNOPSIS
23              
24             use Device::BCM2835::LCD;
25              
26             # Init display, specifying the GPIO pin connections
27             my $foo = Device::BCM2835::LCD->new();
28             $foo->init(
29             Display => 2004,
30             RPI_PIN => V2,
31             pin_rs => RPI_GPIO_P1_24,
32             pin_e => RPI_GPIO_P1_23,
33             pin_d4 => RPI_GPIO_P1_07,
34             pin_d5 => RPI_GPIO_P1_11,
35             pin_d6 => RPI_GPIO_P1_13,
36             pin_d7 => RPI_GPIO_P1_15
37             );
38            
39             # print text to the screen
40             $foo->PutMsg("Hello");
41             # move cursor to line 2, col 0
42             $foo->SetPos(2,0);
43             # print text on second line
44             $foo->PutMsg("world!");
45              
46             # Clear the LCD screen
47             $foo->ClearDisplay;
48              
49             # bignums - position, number
50             # display "123"
51             $foo->BigNum(0,1);
52             $foo->BigNum(1,2);
53             $foo->BigNum(2,3);
54              
55             =head1 SUBROUTINES/METHODS
56              
57             =head2 new
58              
59             =cut
60              
61             =head2 init([pin_rs => $pin], [pin_e => $pin], [pin_d4 => $pin] .. [pin_d7 => $pin], [RPI_PIN => V1] )
62              
63             Initialises the LCD display, using either the default wiring arrangement or the pins specified with init().
64             RPI_PIN refers to the P1 header GPIO mapping scheme.
65             Early boards use 'V1', B+ and revision 2 boards use RPI_PIN V2. The default is V2 pinout.
66              
67             Default wiring is:
68             pin_rs => RPI_GPIO_P1_24
69             pin_e => RPI_GPIO_P1_23
70             pin_d4 => RPI_GPIO_P1_07
71             pin_d5 => RPI_GPIO_P1_11
72             pin_d6 => RPI_GPIO_P1_13
73             pin_d7 => RPI_GPIO_P1_15
74             RPI_PIN => V2
75             =cut
76              
77             =head2 SetPos(line,column)
78              
79             Moves the cursor to the specified position.
80             The top/left position of a 20x4 LCD is (1,0),
81             with the bottom/right being (4,19)
82             =cut
83              
84             =head2 ClearDisplay
85              
86             Clears all characters from the display
87             =cut
88              
89             =head2 PutMsg($msg)
90              
91             writes the string $msg to the display starting
92             at the current cursor position.
93             Note that a 4 line display will wrap line 1 to 3, and 2 to 4.
94             =cut
95              
96             =head2 Delay($milliseconds)
97              
98             Delay for $milliseconds ms.
99             =cut
100              
101             =head2 BigNum($position,$digit)
102            
103             Displays $digit in 4x4 large font at position $position.
104             A 20x4 display has 5 positions (0-4), a 16x4 display has 4.
105             This will only work with 4 line displays.
106             =cut
107              
108             =head2 cmd($instruction)
109              
110             Writes command $instruction to the display.
111             Useful instructions are:
112             cmd(1) - clear display
113             cmd(8) - switch off display
114             cmd(12) - switch on display
115             =cut
116              
117             =head1 AUTHOR
118              
119             Joshua Small, C<< >>
120              
121             =head1 BUGS
122              
123             Please report any bugs or feature requests to C, or through
124             the web interface at L. I will be notified, and then you'll
125             automatically be notified of progress on your bug as I make changes.
126              
127              
128              
129              
130             =head1 SUPPORT
131              
132             You can find documentation for this module with the perldoc command.
133              
134             perldoc Device::BCM2835::LCD
135              
136              
137             You can also look for information at:
138              
139             =head2 * RT: CPAN's request tracker (report bugs here)
140              
141             L
142              
143             =head2 * AnnoCPAN: Annotated CPAN documentation
144              
145             L
146              
147             =head2 * CPAN Ratings
148              
149             L
150              
151             =head2 * Search CPAN
152              
153             L
154              
155              
156              
157             =head1 ACKNOWLEDGEMENTS
158              
159              
160             =head1 LICENSE AND COPYRIGHT
161              
162             Copyright 2015 Joshua Small.
163              
164             This program is free software; you can redistribute it and/or modify it
165             under the terms of either: the GNU General Public License as published
166             by the Free Software Foundation; or the Artistic License.
167              
168             See http://dev.perl.org/licenses/ for more information.
169              
170              
171             =cut
172              
173             # Display dimensions - display names are in common 4 digit COLROW format
174             # i.e. a 16x2 display is called 1602, 20x4 is 2004 etc.
175             # Not used for anything at the moment...
176             my %DisplayCols =
177             qw(0801 8 0802 8 1601 16 1602 16 2001 20 2002 20 2004 20 4002 40);
178             my %DisplayRows = qw(0801 1 0802 2 1601 1 1602 2 2001 1 2002 2 2004 4 4002 2);
179              
180             # Map RPI_GPIO_P1_xx to BCM GPIO number
181             # Device::BCM2835 is then passed the BCM GPIO numbers and not RPI_GPIO_*
182              
183             # V1 board GPIO mapping:
184             my %RPI_PIN_V1 = qw(
185             RPI_GPIO_P1_03 0
186             RPI_GPIO_P1_05 1
187             RPI_GPIO_P1_07 4
188             RPI_GPIO_P1_08 14
189             RPI_GPIO_P1_10 15
190             RPI_GPIO_P1_11 17
191             RPI_GPIO_P1_12 18
192             RPI_GPIO_P1_13 21
193             RPI_GPIO_P1_15 22
194             RPI_GPIO_P1_16 23
195             RPI_GPIO_P1_18 24
196             RPI_GPIO_P1_19 10
197             RPI_GPIO_P1_21 9
198             RPI_GPIO_P1_22 25
199             RPI_GPIO_P1_23 11
200             RPI_GPIO_P1_24 8
201             RPI_GPIO_P1_26 7
202             );
203              
204             # V2 board GPIO mapping (3 pins changed from V1)
205             my %RPI_PIN_V2 = qw(
206             RPI_GPIO_P1_03 2
207             RPI_GPIO_P1_05 3
208             RPI_GPIO_P1_07 4
209             RPI_GPIO_P1_08 14
210             RPI_GPIO_P1_10 15
211             RPI_GPIO_P1_11 17
212             RPI_GPIO_P1_12 18
213             RPI_GPIO_P1_13 21
214             RPI_GPIO_P1_15 27
215             RPI_GPIO_P1_16 23
216             RPI_GPIO_P1_18 24
217             RPI_GPIO_P1_19 10
218             RPI_GPIO_P1_21 9
219             RPI_GPIO_P1_22 25
220             RPI_GPIO_P1_23 11
221             RPI_GPIO_P1_24 8
222             RPI_GPIO_P1_26 7
223             );
224              
225             my $debug = 0;
226              
227             my $RPI_PIN; # ref to pin map (V1 or V2)
228             my $rs; # R/S line
229             my $e; # EN
230             my $d4; # Data bits 7-4
231             my $d5; # (4 bit mode)
232             my $d6;
233             my $d7;
234              
235             # Flag to indicate that BigNum CGRAM chars
236             # have already been loaded.
237             # Load on first call to BigNum, in case
238             # we don't want BigNums but have custom chars
239             my $CGRAM_loaded = 0;
240              
241             # Column 0 address for each line of the display
242             # (*04 displays really use 2 lines, so order is 1-3-2-4)
243             my %LinePos = qw(1 128 2 192 3 148 4 212);
244              
245             sub new {
246             my $self = shift;
247             my $class = ref($self) || $self;
248             return bless {}, $class;
249             }
250              
251             # set debug(1) to see a bunch
252             # or uninteresting stuff about
253             # line strobing and bit shifting...
254             sub debug {
255             my $self = shift;
256             $debug = shift;
257             }
258              
259             # init: set up LCD lines.
260             # Defaults are the "common" GPIO pins, and 20x4 screen
261             sub init {
262             my $self = shift;
263             my %defaults = qw(
264             Display 2004
265             pin_rs RPI_GPIO_P1_24
266             pin_e RPI_GPIO_P1_23
267             pin_d4 RPI_GPIO_P1_07
268             pin_d5 RPI_GPIO_P1_11
269             pin_d6 RPI_GPIO_P1_13
270             pin_d7 RPI_GPIO_P1_15
271             RPI_PIN V2
272             );
273             my %args = ( %defaults, @_ );
274              
275             # map the specified pins to actual BCM GPIO numbers
276              
277            
278             if ($args{RPI_PIN} eq 'V1') { $RPI_PIN = \%RPI_PIN_V1;}
279             else { $RPI_PIN = \%RPI_PIN_V2;}
280              
281             $rs = $$RPI_PIN{ $args{pin_rs} };
282             $e = $$RPI_PIN{ $args{pin_e} };
283             $d4 = $$RPI_PIN{ $args{pin_d4} };
284             $d5 = $$RPI_PIN{ $args{pin_d5} };
285             $d6 = $$RPI_PIN{ $args{pin_d6} };
286             $d7 = $$RPI_PIN{ $args{pin_d7} };
287            
288             # debug info to show assigned pins and display size
289             $debug && print "Display mode: $args{Display}\n";
290             $debug && print "RS: $args{pin_rs} ($rs)\n E: $args{pin_e} ($e)\n";
291             $debug && print "D4: $args{pin_d4} ($d4)\nD5: $args{pin_d5} ($d5)\n";
292             $debug && print "D6: $args{pin_d6} ($d6)\nD7: $args{pin_d7} ($d7)\n";
293             $debug
294             && print
295             "Display dimensions: $DisplayCols{$args{Display}}x$DisplayRows{$args{Display}}\n";
296              
297             # Initialise the Device::BCM2835 module.
298             # This is used for the underlying direct GPIO
299             # access, and the short delays
300             # Returns 1 on success
301             Device::BCM2835::init()
302             || croak "Could not init Device::BCM2835 library\n";
303              
304             # set assigned pins to output mode
305             Device::BCM2835::gpio_fsel( $rs, &Device::BCM2835::BCM2835_GPIO_FSEL_OUTP );
306             Device::BCM2835::gpio_fsel( $e, &Device::BCM2835::BCM2835_GPIO_FSEL_OUTP );
307             Device::BCM2835::gpio_fsel( $d4, &Device::BCM2835::BCM2835_GPIO_FSEL_OUTP );
308             Device::BCM2835::gpio_fsel( $d5, &Device::BCM2835::BCM2835_GPIO_FSEL_OUTP );
309             Device::BCM2835::gpio_fsel( $d6, &Device::BCM2835::BCM2835_GPIO_FSEL_OUTP );
310             Device::BCM2835::gpio_fsel( $d7, &Device::BCM2835::BCM2835_GPIO_FSEL_OUTP );
311              
312             # quick sanity test -
313             # if we can't change the state of the RS line,
314             # we're not going to get very far...
315              
316             my $rslevel = Device::BCM2835::gpio_lev($rs);
317             if ( $rslevel == 0 ) {
318             Device::BCM2835::gpio_set($rs);
319             }
320             else {
321             Device::BCM2835::gpio_clr($rs);
322             }
323             my $newrslevel = Device::BCM2835::gpio_lev($rs);
324             if ( $rslevel == $newrslevel ) {
325             croak("GPIO error: pin access test failed.\n");
326             }
327              
328             # end of sanity test
329              
330             # initialise the device in 4 bit mode
331             # During this phase of display init there's
332             # some specific timing requirements,
333             # so can't use generic cmd()s...
334             $self->delay(40); # wait 40ms for screen to power-up
335             # I really don't think this is necessary
336             # considering how long it takes to boot
337             # the R-Pi...
338             Device::BCM2835::gpio_write( $rs, 0 ); # cmd mode
339             Device::BCM2835::gpio_write( $e, 0 ); # start with EN low
340             nibbleToLines(3); # high nibble 0x03
341             &strobe_E; # strobe EN
342             usleep(4100);
343             nibbleToLines(3); # low nibble 0x03
344             &strobe_E; # strobe EN
345             usleep(200);
346             nibbleToLines(3); # high nibble 0x03
347             &strobe_E; # strobe EN
348             usleep(200);
349             nibbleToLines(2); # low nibble 0x02, 4 bit mode
350             &strobe_E; # strobe EN
351             $self->delay(8);
352              
353             # screen is initialised, all timings should be
354             # uniform so now switch to cmd() for rest of setup
355              
356             # set interface to 4 bit, 2 line
357             $self->cmd(0x28);
358              
359             # set cursor style
360             $self->cmd(0x08);
361              
362             # set cursor pos to home
363             $self->cmd(0x01);
364              
365             # set cursor direction
366             $self->cmd(0x06);
367              
368             # finally, turn on display
369             $self->cmd(0x0c);
370              
371             }
372              
373             # strobe_E - pulses the EN pin to tell the
374             # display to load data on GPIO pins
375             sub strobe_E {
376             Device::BCM2835::gpio_write( $e, 1 );
377             Device::BCM2835::gpio_write( $e, 0 );
378             }
379              
380             sub delay {
381             my $self = shift;
382             my $delaymS = $_[0];
383             usleep( $delaymS * 1000 );
384             }
385              
386             # Takes 4 bits and writes them
387             # to the display's data lines 7-4
388             sub nibbleToLines {
389             my $nibble = shift;
390             Device::BCM2835::gpio_write( $d7, ( $nibble & 8 ) );
391             Device::BCM2835::gpio_write( $d6, ( $nibble & 4 ) );
392             Device::BCM2835::gpio_write( $d5, ( $nibble & 2 ) );
393             Device::BCM2835::gpio_write( $d4, ( $nibble & 1 ) );
394             $debug && print "Nibble: $nibble\n";
395             }
396              
397             # instruction byte (rs = low)
398             # cmd(instruction) sends a single HD44780 instruction
399             # to the display's controller
400             sub cmd {
401             my $self = shift;
402             my $byte = $_[0];
403             $debug && print "instruction cmd was $byte\n";
404             my $hi = ( $byte & 0xF0 ) >> 4;
405             my $lo = ( $byte & 0x0F );
406             Device::BCM2835::gpio_write( $rs, 0 );
407             nibbleToLines($hi);
408             strobe_E;
409             nibbleToLines($lo);
410             strobe_E;
411             if ( $byte < 3 ) { usleep(1500); }
412             }
413              
414             # PutChar - write a single character to the
415             # display at the current cursor position
416             sub PutChar {
417             my $self = shift;
418             my $byte = $_[0];
419             $debug && print "Char cmd was $byte\n";
420             my $hi = ( $byte & 0xF0 ) >> 4;
421             my $lo = ( $byte & 0x0F );
422             Device::BCM2835::gpio_write( $rs, 1 );
423             nibbleToLines($hi);
424             strobe_E;
425             nibbleToLines($lo);
426             strobe_E;
427             }
428              
429             # PutMsg - writes a string of characters to the
430             # display starting at the current position
431             sub PutMsg {
432             my $self = shift;
433             my $msg = $_[0];
434             my @chars = split //, $msg;
435             foreach my $char (@chars) {
436             $self->PutChar( ord($char) );
437             }
438             }
439              
440             # SetPos(line,column) - moves the cursor to
441             # the requested position
442             sub SetPos {
443             my $self = shift;
444             my $pos_line = $_[0];
445             my $pos_col = $_[1];
446             my $PosCmd = $LinePos{$pos_line} + $pos_col;
447             $debug && print "Moving cursor to $pos_line:$pos_col ($PosCmd)\n";
448             $self->cmd($PosCmd);
449             }
450              
451             sub ClearDisplay {
452             my $self = shift;
453             $self->cmd(0x01);
454             }
455              
456             sub LoadCGRAM {
457             my $self = shift;
458              
459             # small block bottom left
460             # Small Block on bottom right
461             # small block bottom full
462             # small block top left
463             # small block top right
464             # small block top full
465             # decimal dot
466              
467             my @cgdata = (
468             [ 0, 0, 0, 0, 3, 15, 15, 31 ],
469             [ 0, 0, 0, 0, 31, 31, 31, 31 ],
470             [ 0, 0, 0, 0, 24, 30, 30, 31 ],
471             [ 31, 15, 15, 3, 0, 0, 0, 0 ],
472             [ 31, 30, 30, 24, 0, 0, 0, 0 ],
473             [ 31, 31, 31, 31, 0, 0, 0, 0 ],
474             [ 0, 0, 0, 14, 14, 14, 12, 8 ]
475              
476             #[14,14,14,14,12,8,0,0]
477             );
478              
479             for ( my $cgchar = 0 ; $cgchar < 7 ; $cgchar++ ) {
480             my $shiftchar = ( $cgchar + 1 ) << 3;
481             for ( my $cgline = 0 ; $cgline < 8 ; $cgline++ ) {
482             $self->cmd( 0x40 | $shiftchar | $cgline );
483             $self->PutChar( $cgdata[$cgchar][$cgline] );
484             }
485             }
486             $CGRAM_loaded = 1;
487             }
488              
489             sub BigNum {
490             my $self = shift;
491             my $numpos = $_[0] * 4;
492             unless ($CGRAM_loaded) {
493             $self->LoadCGRAM;
494             }
495              
496             my $numToPrint = $_[1];
497             unless ( $numToPrint =~ m/[0-9.]/ ) { $numToPrint = 0; }
498             if ( $numToPrint eq '.' ) {
499             $self->SetPos( 4, ( $numpos - 1 ) );
500             $self->PutChar(7);
501             return;
502             }
503             $self->SetPos( 4, ( $numpos - 1 ) );
504             $self->PutChar(254);
505              
506             my @big4_1 = (
507             1, 2, 3, 0, 2, 3, 254, 0, 1, 2, 3, 0, 1, 2,
508             3, 0, 2, 254, 254, 0, 2, 2, 2, 0, 1, 2, 3, 0,
509             2, 2, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0
510             );
511             my @big4_2 = (
512             255, 254, 255, 0, 254, 255, 254, 0, 1, 2, 255, 0, 254, 2,
513             255, 0, 255, 2, 2, 0, 255, 2, 2, 0, 255, 2, 3, 0,
514             254, 2, 255, 0, 255, 2, 255, 0, 255, 254, 255, 0
515             );
516             my @big4_3 = (
517             255, 254, 255, 0, 254, 255, 254, 0, 255, 254, 254, 0, 254, 254,
518             255, 0, 254, 255, 254, 0, 254, 254, 255, 0, 255, 254, 255, 0,
519             254, 255, 254, 0, 255, 254, 255, 0, 4, 6, 255, 0
520             );
521             my @big4_4 = (
522             4, 6, 5, 0, 6, 6, 6, 0, 4, 6, 6, 0, 4, 6,
523             5, 0, 254, 6, 254, 0, 6, 6, 5, 0, 4, 6, 5, 0,
524             254, 6, 254, 0, 4, 6, 5, 0, 254, 254, 6, 0
525             );
526              
527             $self->cmd( 0x80 + $numpos );
528             $self->PutChar( $big4_1[ ( $numToPrint * 4 ) + 0 ] );
529             $self->PutChar( $big4_1[ ( $numToPrint * 4 ) + 1 ] );
530             $self->PutChar( $big4_1[ ( $numToPrint * 4 ) + 2 ] );
531              
532             $self->cmd( 0xc0 + $numpos );
533             $self->PutChar( $big4_2[ ( $numToPrint * 4 ) + 0 ] );
534             $self->PutChar( $big4_2[ ( $numToPrint * 4 ) + 1 ] );
535             $self->PutChar( $big4_2[ ( $numToPrint * 4 ) + 2 ] );
536              
537             $self->cmd( 0x94 + $numpos );
538             $self->PutChar( $big4_3[ ( $numToPrint * 4 ) + 0 ] );
539             $self->PutChar( $big4_3[ ( $numToPrint * 4 ) + 1 ] );
540             $self->PutChar( $big4_3[ ( $numToPrint * 4 ) + 2 ] );
541              
542             $self->cmd( 0xD4 + $numpos );
543             $self->PutChar( $big4_4[ ( $numToPrint * 4 ) + 0 ] );
544             $self->PutChar( $big4_4[ ( $numToPrint * 4 ) + 1 ] );
545             $self->PutChar( $big4_4[ ( $numToPrint * 4 ) + 2 ] );
546              
547             }
548              
549             1; # End of Device::BCM2835::LCD