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 |