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