LiquidCrystal.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. # LiquidCrystal.pm
  2. #
  3. # Perl-library to drive LCD-displays controlled by Hitachi HD44780 LCD controller
  4. #
  5. # Copyright (C) 2013/2014 Norbert Truchsess (norbert.truchsess@t-online.de)
  6. #
  7. # based on LiquidCrystal.cpp as of:
  8. #
  9. # www.DFRobot.com
  10. # last updated on 21/12/2011
  11. # Tim Starling Fix the reset bug (Thanks Tim)
  12. # wiki doc http://www.dfrobot.com/wiki/index.php?title=I2C/TWI_LCD1602_Module_(SKU:_DFR0063)
  13. # Support Forum: http://www.dfrobot.com/forum/
  14. # Compatible with the Arduino IDE 1.0
  15. # Library version:1.1
  16. #
  17. # When the display powers up, it is configured as follows:
  18. #
  19. # 1. Display clear
  20. # 2. Function set:
  21. # DL = 1; 8-bit interface data
  22. # N = 0; 1-line display
  23. # F = 0; 5x8 dot character font
  24. # 3. Display on/off control:
  25. # D = 0; Display off
  26. # C = 0; Cursor off
  27. # B = 0; Blinking off
  28. # 4. Entry mode set:
  29. # I/D = 1; Increment by 1
  30. # S = 0; No shift
  31. #
  32. # Note, however, that resetting the Arduino doesn't reset the LCD, so we
  33. # can't assume that its in that state when a sketch starts (and the
  34. # LiquidCrystal constructor is called).
  35. package LiquidCrystal;
  36. use warnings;
  37. use strict;
  38. #Basic commands and constants
  39. use constant LCD_CLEARDISPLAY => 0x01;
  40. use constant LCD_RETURNHOME => 0x02;
  41. use constant LCD_ENTRYMODESET => 0x04;
  42. use constant LCD_DISPLAYCONTROL => 0x08;
  43. use constant LCD_CURSORSHIFT => 0x10;
  44. use constant LCD_FUNCTIONSET => 0x20;
  45. use constant LCD_SETCGRAMADDR => 0x40;
  46. use constant LCD_SETDDRAMADDR => 0x80;
  47. # flags for display entry mode
  48. use constant LCD_ENTRYRIGHT => 0x00;
  49. use constant LCD_ENTRYLEFT => 0x02;
  50. use constant LCD_ENTRYSHIFTINCREMENT => 0x01;
  51. use constant LCD_ENTRYSHIFTDECREMENT => 0x00;
  52. # flags for display on/off control
  53. use constant LCD_DISPLAYON => 0x04;
  54. use constant LCD_DISPLAYOFF => 0x00;
  55. use constant LCD_CURSORON => 0x02;
  56. use constant LCD_CURSOROFF => 0x00;
  57. use constant LCD_BLINKON => 0x01;
  58. use constant LCD_BLINKOFF => 0x00;
  59. # flags for display/cursor shift
  60. use constant LCD_DISPLAYMOVE => 0x08;
  61. use constant LCD_CURSORMOVE => 0x00;
  62. use constant LCD_MOVERIGHT => 0x04;
  63. use constant LCD_MOVELEFT => 0x00;
  64. # flags for function set
  65. use constant LCD_8BITMODE => 0x10;
  66. use constant LCD_4BITMODE => 0x00;
  67. use constant LCD_2LINE => 0x08;
  68. use constant LCD_1LINE => 0x00;
  69. use constant LCD_5x10DOTS => 0x04;
  70. use constant LCD_5x8DOTS => 0x00;
  71. # flags for backlight control
  72. use constant LCD_BACKLIGHT => 0x08;
  73. use constant LCD_NOBACKLIGHT => 0x00;
  74. use constant En => 0b00000100; # Enable bit
  75. use constant Rw => 0b00000010; # Read / Write bit
  76. use constant Rs => 0b00000001; # Register select bit
  77. our %mapping_bits = (
  78. RS => 0,
  79. RW => 1,
  80. E => 2,
  81. LED => 3,
  82. D4 => 4,
  83. D5 => 5,
  84. D6 => 6,
  85. D7 => 7,
  86. );
  87. sub print($$) {
  88. my ($self,$c) = @_;
  89. my @buf = unpack "c*",$c;
  90. foreach my $s (@buf) {
  91. $self->write($s);
  92. }
  93. };
  94. sub write($$) {
  95. my ( $self, $value ) = @_;
  96. $self->send( $value, Rs );
  97. return 0;
  98. }
  99. sub new($$$$@) {
  100. my ( $class, $lcd_cols, $lcd_rows ) = @_;
  101. my $lcd = bless {
  102. cols => $lcd_cols,
  103. rows => $lcd_rows,
  104. backlightval => LCD_NOBACKLIGHT,
  105. }, $class;
  106. $lcd->setMapping();
  107. return $lcd;
  108. }
  109. # $lcd_mapping = {
  110. # 'P0' => 'RS',
  111. # 'P1' => 'RW',
  112. # 'P2' => 'E',
  113. # 'P3' => 'LED',
  114. # 'P4' => 'D4',
  115. # 'P5' => 'D5',
  116. # 'P6' => 'D6',
  117. # 'P7' => 'D7',
  118. #};
  119. sub setMapping(@) {
  120. my ( $self, $lcd_mapping ) = @_;
  121. my %mapping;
  122. if ( defined $lcd_mapping ) {
  123. while (my ($key, $value) = each %$lcd_mapping) {
  124. die "illegal value for MAPPING: $value, must be one of RS,RW,E,LED,D4,D5,D6,D7" unless grep {$value eq $_} keys %mapping_bits;
  125. die "illegal value for PIN: $key, must be one of P0..P7" unless $key =~ /^P([\d])$/;
  126. $mapping{0x01 << $mapping_bits{$value}} = 0x01 << $1;
  127. }
  128. } else {
  129. foreach my $i (0..7) {
  130. $mapping{0x01 << $i} = 0x01 << $i;
  131. }
  132. }
  133. $self->{mapping} = \%mapping;
  134. }
  135. sub init($) {
  136. my $self = shift;
  137. $self->init_priv();
  138. }
  139. sub attach($$) {
  140. my ($self,$dev) = @_;
  141. $self->{io} = $dev;
  142. }
  143. sub init_priv($) {
  144. my $self = shift;
  145. $self->{displayfunction} =
  146. LCD_4BITMODE | LCD_1LINE |
  147. LCD_5x8DOTS;
  148. $self->begin( $self->{cols}, $self->{rows} );
  149. }
  150. sub begin($$$$) {
  151. my ( $self, $cols, $lines, $dotsize ) = @_;
  152. if ( $lines > 1 ) {
  153. $self->{displayfunction} |= LCD_2LINE;
  154. }
  155. $self->{numlines} = $lines;
  156. # for some 1 line displays you can select a 10 pixel high font
  157. if ( (defined $dotsize) && ($dotsize != 0) && ( $lines == 1 ) ) {
  158. $self->{displayfunction} |= LCD_5x10DOTS;
  159. }
  160. # SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
  161. # according to datasheet, we need at least 40ms after power rises above 2.7V
  162. # before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
  163. select( undef, undef, undef, 0.050 );
  164. # Now we pull both RS and R/W low to begin commands
  165. $self->expanderWrite( $self->{backlightval} )
  166. ; # reset expanderand turn backlight off (Bit 8 =1)
  167. select( undef, undef, undef, 1 );
  168. #put the LCD into 4 bit mode
  169. # this is according to the hitachi HD44780 datasheet
  170. # figure 24, pg 46
  171. # we start in 8bit mode, try to set 4 bit mode
  172. $self->write4bits( 0x03 << 4 );
  173. select( undef, undef, undef, 0.0045 ); # wait min 4.1ms
  174. # second try
  175. $self->write4bits( 0x03 << 4 );
  176. select( undef, undef, undef, 0.0045 ); # wait min 4.1ms
  177. # third go!
  178. $self->write4bits( 0x03 << 4 );
  179. select( undef, undef, undef, 0.00015 );
  180. # finally, set to 4-bit interface
  181. $self->write4bits( 0x02 << 4 );
  182. # set # lines, font size, etc.
  183. $self->command(
  184. LCD_FUNCTIONSET | $self->{displayfunction} );
  185. # turn the display on with no cursor or blinking default
  186. $self->{displaycontrol} =
  187. LCD_DISPLAYON | LCD_CURSOROFF |
  188. LCD_BLINKOFF;
  189. $self->display();
  190. # clear it off
  191. $self->clear();
  192. # Initialize to default text direction (for roman languages)
  193. $self->{displaymode} =
  194. LCD_ENTRYLEFT |
  195. LCD_ENTRYSHIFTDECREMENT;
  196. # set the entry mode
  197. $self->command(
  198. LCD_ENTRYMODESET | $self->{displaymode} );
  199. $self->home();
  200. }
  201. #********** high level commands, for the user!
  202. sub clear($) {
  203. my $self = shift;
  204. $self->command(LCD_CLEARDISPLAY)
  205. ; # clear display, set cursor position to zero
  206. select( undef, undef, undef, 0.002 ); # this command takes a long time!
  207. }
  208. sub home($) {
  209. my $self = shift;
  210. $self->command(LCD_RETURNHOME)
  211. ; # set cursor position to zero
  212. select( undef, undef, undef, 0.002 ); # this command takes a long time!
  213. }
  214. sub setCursor($$$) {
  215. my ( $self, $col, $row ) = @_;
  216. my @row_offsets = ( 0x00, 0x40, 0x14, 0x54 );
  217. if ( $row > $self->{numlines} ) {
  218. $row = $self->{numlines} - 1; # we count rows starting w/0
  219. }
  220. $self->command(
  221. LCD_SETDDRAMADDR | ( $col + $row_offsets[$row] ) );
  222. }
  223. # Turn the display on/off (quickly)
  224. sub noDisplay($) {
  225. my $self = shift;
  226. $self->{displaycontrol} &=
  227. ~LCD_DISPLAYON; #TODO validate '~'
  228. $self->command(
  229. LCD_DISPLAYCONTROL | $self->{displaycontrol} );
  230. }
  231. sub display($) {
  232. my $self = shift;
  233. $self->{displaycontrol} |= LCD_DISPLAYON;
  234. $self->command(
  235. LCD_DISPLAYCONTROL | $self->{displaycontrol} );
  236. }
  237. # Turns the underline cursor on/off
  238. sub noCursor($) {
  239. my $self = shift;
  240. $self->{displaycontrol} &=
  241. ~LCD_CURSORON; #TODO validate '~'
  242. $self->command(
  243. LCD_DISPLAYCONTROL | $self->{displaycontrol} );
  244. }
  245. sub cursor($) {
  246. my $self = shift;
  247. $self->{displaycontrol} |= LCD_CURSORON;
  248. $self->command(
  249. LCD_DISPLAYCONTROL | $self->{displaycontrol} );
  250. }
  251. # Turn on and off the blinking cursor
  252. sub noBlink($) {
  253. my $self = shift;
  254. $self->{displaycontrol} &=
  255. ~LCD_BLINKON; #TODO validate '~'
  256. $self->command(
  257. LCD_DISPLAYCONTROL | $self->{displaycontrol} );
  258. }
  259. sub blink($) {
  260. my $self = shift;
  261. $self->{displaycontrol} |= LCD_BLINKON;
  262. $self->command(
  263. LCD_DISPLAYCONTROL | $self->{displaycontrol} );
  264. }
  265. # These commands scroll the display without changing the RAM
  266. sub scrollDisplayLeft($) {
  267. my $self = shift;
  268. $self->command( LCD_CURSORSHIFT |
  269. LCD_DISPLAYMOVE |
  270. LCD_MOVELEFT );
  271. }
  272. sub scrollDisplayRight($) {
  273. my $self = shift;
  274. $self->command( LCD_CURSORSHIFT |
  275. LCD_DISPLAYMOVE |
  276. LCD_MOVERIGHT );
  277. }
  278. # This is for text that flows Left to Right
  279. sub leftToRight($) {
  280. my $self = shift;
  281. $self->{displaymode} |= LCD_ENTRYLEFT;
  282. $self->command(
  283. LCD_ENTRYMODESET | $self->{displaymode} );
  284. }
  285. # This is for text that flows Right to Left
  286. sub rightToLeft($) {
  287. my $self = shift;
  288. $self->{displaymode} &=
  289. ~LCD_ENTRYLEFT; #TODO validate '~'
  290. $self->command(
  291. LCD_ENTRYMODESET | $self->{displaymode} );
  292. }
  293. # This will 'right justify' text from the cursor
  294. sub autoscroll($) {
  295. my $self = shift;
  296. $self->{displaymode} |= LCD_ENTRYSHIFTINCREMENT;
  297. $self->command(
  298. LCD_ENTRYMODESET | $self->{displaymode} );
  299. }
  300. # This will 'left justify' text from the cursor
  301. sub noAutoscroll($) {
  302. my $self = shift;
  303. $self->{displaymode} &=
  304. ~LCD_ENTRYSHIFTINCREMENT; #TODO validate '~'
  305. $self->command(
  306. LCD_ENTRYMODESET | $self->{displaymode} );
  307. }
  308. # Allows us to fill the first 8 CGRAM locations
  309. # with custom characters
  310. sub createChar($$$) {
  311. my ( $self, $location, $charmap ) = @_;
  312. $location &= 0x7; # we only have 8 locations 0-7
  313. $self->command( LCD_SETCGRAMADDR | ( $location << 3 ) );
  314. for ( my $i = 0 ; $i < 8 ; $i++ ) {
  315. $self->write( @$charmap[$i] );
  316. }
  317. }
  318. # Turn the (optional) backlight off/on
  319. sub noBacklight($) {
  320. my $self = shift;
  321. $self->{backlightval} = LCD_NOBACKLIGHT;
  322. $self->expanderWrite(0);
  323. }
  324. sub backlight($) {
  325. my $self = shift;
  326. $self->{backlightval} = LCD_BACKLIGHT;
  327. $self->expanderWrite(0);
  328. }
  329. #*********** mid level commands, for sending data/cmds
  330. sub command($$) {
  331. my ( $self, $value ) = @_;
  332. $self->send( $value, 0 );
  333. }
  334. #************ low level data pushing commands **********
  335. # write either command or data
  336. sub send($$$) {
  337. my ( $self, $value, $mode ) = @_;
  338. my $highnib = $value & 0xf0;
  339. my $lownib = ( $value << 4 ) & 0xf0;
  340. $self->write4bits( ($highnib) | $mode );
  341. $self->write4bits( ($lownib) | $mode );
  342. }
  343. sub write4bits($$) {
  344. my ( $self, $value ) = @_;
  345. $self->expanderWrite($value);
  346. $self->pulseEnable($value);
  347. }
  348. sub expanderWrite($$) {
  349. my ( $self, $data ) = @_;
  350. $data |= $self->{backlightval};
  351. my $mapped = 0;
  352. while (my ($orig, $remapped) = each %{$self->{mapping}}) {
  353. $mapped |= $data & $orig ? $remapped : 0;
  354. }
  355. $self->{io}->write($mapped);
  356. }
  357. sub pulseEnable($$) {
  358. my ( $self, $data ) = @_;
  359. $self->expanderWrite( $data | En ); # En high
  360. select( undef, undef, undef, 0.000001 ); # enable pulse must be >450ns
  361. $self->expanderWrite( $data & ~En )
  362. ; # En low TODO: validate '~'
  363. select( undef, undef, undef, 0.000050 ); # commands need > 37us to settle
  364. }
  365. # Alias functions
  366. sub cursor_on($) {
  367. my $self = shift;
  368. $self->cursor();
  369. }
  370. sub cursor_off($) {
  371. my $self = shift;
  372. $self->noCursor();
  373. }
  374. sub blink_on($) {
  375. my $self = shift;
  376. $self->blink();
  377. }
  378. sub blink_off($) {
  379. my $self = shift;
  380. $self->noBlink();
  381. }
  382. sub load_custom_character($$$) {
  383. my ( $self, $char_num, $rows ) = @_;
  384. $self->createChar( $char_num, $rows );
  385. }
  386. sub setBacklight($$) {
  387. my ( $self, $new_val ) = @_;
  388. if ($new_val) {
  389. $self->backlight(); # turn backlight on
  390. }
  391. else {
  392. $self->noBacklight(); # turn backlight off
  393. }
  394. }
  395. # unsupported API functions
  396. sub off($) { }
  397. sub on($) { }
  398. sub setDelay ($$$) { }
  399. sub status($) {
  400. return 0;
  401. }
  402. sub keypad ($) {
  403. return 0;
  404. }
  405. sub init_bargraph($$) {
  406. return 0;
  407. }
  408. sub draw_horizontal_graph($$$$$) { }
  409. sub draw_vertical_graph($$$$$) { }
  410. sub setContrast($$) { }
  411. 1;