1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
|
<chapter id="driver-api">
<title>The LCDproc driver API</title>
<para>
This chapter describes the driver API of v0.5 of LCDproc.
</para>
<sect1 id="api-overview">
<title>Overview of Operation</title>
<para>
The API defines functions which drivers may implement to provide certain
services. If a driver implements a function, the function will be detected by
the server. For some optional functions the server core provides default
implementations.
</para>
<para>
The API consists of functions to:
</para>
<itemizedlist>
<listitem>
<para>print data to the display,</para>
</listitem>
<listitem>
<para>set display properties (backlight, brightness, contrast, extra LEDs),</para>
</listitem>
<listitem>
<para>read input from an input device (may be the display), and</para>
</listitem>
<listitem>
<para>inform the server of the driver/display capabilities.</para>
</listitem>
</itemizedlist>
<para>
The API is best described by starting with the struct lcd_logical_driver
which is defined in server/drivers/lcd.h.
</para>
<para>
Below is a commented version of lcd_logical_driver. Each function is
described more detailed in <xref linkend="function-details"/>.
</para>
<screen>
typedef struct lcd_logical_driver {
//////// Variables to be provided by the driver module
// The driver loader will look for symbols with these names !
// pointer to a string describing the API version
char *api_version;
// Does this driver require to be in foreground ?
int *stay_in_foreground;
/ Does this driver support multiple instances ?
int *supports_multiple;
// What should alternatively be prepended to the function names ?
char **symbol_prefix;
//////// Functions to be provided by the driver module
//// Mandatory functions (necessary for all drivers)
// initialize driver: returns >= 0 on success
int (*init) (Driver *drvthis);
// close driver
void (*close) (Driver *drvthis);
//// Essential output functions (necessary for output drivers)
// get display width / height (in characters; 1-based)
int (*width) (Driver *drvthis);
int (*height) (Driver *drvthis);
// clear screen
void (*clear) (Driver *drvthis);
// flush screen contents to LCD
void (*flush) (Driver *drvthis);
// write string s at position (x,y)
void (*string) (Driver *drvthis, int x, int y, const char *str);
// write char c at position (x,y)
void (*chr) (Driver *drvthis, int x, int y, char c);
//// essential input functions (necessary for input drivers)
// get key from driver: returns a string denoting the key pressed
const char *(*get_key) (Driver *drvthis);
//// Extended output functions (optional; core provides alternatives)
// draw a bar from pos (x,y) upward / to the right filling promille of len chars
void (*vbar) (Driver *drvthis, int x, int y, int len, int promille, int options);
void (*hbar) (Driver *drvthis, int x, int y, int len, int promille, int options);
// display (big) number num at horizontal position x
void (*num) (Driver *drvthis, int x, int num);
// set heartbeat state; animate heartbeat
void (*heartbeat) (Driver *drvthis, int state);
// draw named icon at position (x,y)
int (*icon) (Driver *drvthis, int x, int y, int icon);
// set cursor type and move it to position (x,y)
void (*cursor) (Driver *drvthis, int x, int y, int type);
//// User-defined character functions
// set special character / get free characters
// - It is currently unclear how this system should work exactly
// - The set_char function expects a simple block of data with 1 byte for each pixel-line.
// (So that is 8 bytes for a 5x8 char)
void (*set_char) (Driver *drvthis, int n, unsigned char *dat);
int (*get_free_chars) (Driver *drvthis);
// get width / height of a character cell (in pixels)
// - necessary to provide info about cell size to clients
// - if not defined, the core will provide alternatives returning default values
int (*cellwidth) (Driver *drvthis);
int (*cellheight) (Driver *drvthis);
//// Hardware functions
// get / set the display's contrast
int (*get_contrast) (Driver *drvthis);
void (*set_contrast) (Driver *drvthis, int promille);
// get / set brightness for given backlight state
int (*get_brightness) (Driver *drvthis, int state);
void (*set_brightness) (Driver *drvthis, int state, int promille);
// set backlight state
void (*backlight) (Driver *drvthis, int state);
// set output
void (*output) (Driver *drvthis, int state);
//// Informational functions
// get a string describing the driver and it's features
const char * (*get_info) (Driver *drvthis);
//////// Variables in server core, available for drivers
// name of the driver instance (name of the config file section)
// - do not change from the driver; consider it read-only
// - to be used to access the driver's own section in the config file
char * name;
// pointer to the driver instance's private data
// - filled by the server by calling store_private_ptr()
// - the driver should cast this to it's own private structure pointer
void * private_data;
//////// Functions in server core, available for drivers
// store a pointer to the driver instance's private data
int (*store_private_ptr) (struct lcd_logical_driver * driver, void * private_data);
// Config file functions, cwprovided by the server
// - see configfile.h on how to use these functions
// - as sectionname, always use the driver name: drvthis->name
short (*config_get_bool) (char * sectionname, char * keyname,
int skip, short default_value);
long int (*config_get_int) (char * sectionname, char * keyname,
int skip, long int default_value);
double (*config_get_float) (char * sectionname, char * keyname,
int skip, double default_value);
const char *(*config_get_string) (char * sectionname, char * keyname,
int skip, const char * default_value);
// Returns a string in server memory space.
// Copy this string.
int config_has_section (const char *sectionname);
int config_has_key (const char *sectionname, const char *keyname);
// Display properties functions (for drivers that adapt to other loaded drivers)
// - the return the size of another already loaded driver
// - if no driver is loaded yet, the return values will be 0
int (*get_display_width) ();
int (*get_display_height) ();
} Driver;
</screen>
</sect1>
<sect1 id="private-data">
<title>Private Data</title>
<para>
To support multiple instances it is necessary to stop using global
variables to store a driver's data in. Instead, you should store it in a
structure, that you allocate and store on driver's init.
</para>
<para>
In the driver's private structure will probably at least be something like:
</para>
<screen>
typedef struct MyDriver_private_data {
int fd; // file descriptor for the LCD device
int width, height; // dimension of the LCD (in characters, 1-based
int cellwidth, cellheight; // Size of each LCD cell, in pixels
unsigned char *framebuf; // Frame buffer...
} PrivateData;
</screen>
<para>
You allocate and store this structure like this:
</para>
<screen>
PrivateData *p;
// Allocate and store private data
p = (PrivateData *) malloc(sizeof(PrivateData));
if (p == NULL)
return -1;
if (drvthis->store_private_ptr( drvthis, p ) < 0)
return -1;
// initialize private data
p->fd = -1;
p->cellheight = 8;
p->cellwidth = 6;
(... continue with the rest of your init routine)
</screen>
<para>
You retrieve this private data pointer by adding the following code to the
beginning of your functions:
</para>
<screen>
PrivateData *p = (PrivateData *) drvthis->private_data;
</screen>
<para>
Then you can access your data like:
</para>
<screen>
p->framebuf
</screen>
</sect1>
<sect1 id="function-details">
<title>Functions in Detail</title>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*init)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
The init() function.
It starts up the LCD, initializes all variables, allocates private data space
and stores the pointer by calling store_private_ptr();
Returns <0 on error.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*close)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Shut down the connection with the LCD.
Called just before unloading the driver.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*width)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Get the screen width in characters.
The result is 1-based.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*height)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Get the screen height in character lines.
The result is 1-based.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*clear)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Clear the framebuffer.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*flush)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Flush the framebuffer to the LCD.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*string)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>x</parameter></paramdef>
<paramdef>int <parameter>y</parameter></paramdef>
<paramdef>const char *<parameter>str</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Place string <replaceable>str</replaceable> into position
(<replaceable>x</replaceable>,<replaceable>y</replaceable>) in the framebuffer.
All coordinates are 1-based, i.e. (1,1) is top left.
The driver should check for overflows, i.e. that the positional parameters
are within the screen's boundaries and cut off the part of the string
that is out of bounds.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*chr)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>x</parameter></paramdef>
<paramdef>int <parameter>y</parameter></paramdef>
<paramdef>char <parameter>c</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Place a single character <replaceable>c</replaceable> into position
(<replaceable>x</replaceable>,<replaceable>y</replaceable>) in the framebuffer.
The driver should check for overflows, i.e. that the positional parameters
are within the screen's boundaries and ignore the request if
the character is out of bounds.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*vbar)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>x</parameter></paramdef>
<paramdef>int <parameter>y</parameter></paramdef>
<paramdef>int <parameter>len</parameter></paramdef>
<paramdef>int <parameter>promille</parameter></paramdef>
<paramdef>int <parameter>options</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Draw a vertical bar at position (<replaceable>x</replaceable>,<replaceable>y</replaceable>)
that has maximal length <replaceable>len</replaceable>, where a fraction of
(<replaceable>promille</replaceable> / 1000) is filled.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*hbar)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>x</parameter></paramdef>
<paramdef>int <parameter>y</parameter></paramdef>
<paramdef>int <parameter>len</parameter></paramdef>
<paramdef>int <parameter>promille</parameter></paramdef>
<paramdef>int <parameter>options</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Draw a horizontal bar at position (<replaceable>x</replaceable>,<replaceable>y</replaceable>)
that has maximal length <replaceable>len</replaceable>, where a fraction of
(<replaceable>promille</replaceable> / 1000) is filled.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*num)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>x</parameter></paramdef>
<paramdef>int <parameter>num</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Display big number <replaceable>num</replaceable> at horizontal position <replaceable>x</replaceable>.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*heartbeat)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>state</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Sets the heartbeat to the indicated state: 0=off, 1=on.
Use HEARTBEAT_ON to say that we want to display/refresh the heartbeat.
The driver choose how to do it.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*icon)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>x</parameter></paramdef>
<paramdef>int <parameter>y</parameter></paramdef>
<paramdef>int <parameter>icon</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Draw named icon <replaceable>icon</replaceable> at position
(<replaceable>x</replaceable>,<replaceable>y</replaceable>).
If the driver returns -1 the server core will draw an appropriate replacement
character.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*cursor)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>x</parameter></paramdef>
<paramdef>int <parameter>y</parameter></paramdef>
<paramdef>int <parameter>type</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Move cursor to position (<replaceable>x</replaceable>,<replaceable>y</replaceable>),
setting its type to <replaceable>type</replaceable>.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*set_char)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>char <parameter>ch</parameter></paramdef>
<paramdef>unsigned char *<parameter>dat</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
The set_char function expects a simple block of data with 1 byte for each pixel-line.
(So that is 8 bytes for a 5x8 char)
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*get_free_chars)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Get total number of custom characters available.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*cellwidth)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Return the width of a character cell in pixels.
The result is 1-based.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*cellheight)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Return the height of a character cell in pixels.
The result is 1-based.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*get_contrast)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Get the contrast value from the driver.
The return value is an integer in the range from 0 to 1000.
Many displays do not support getting or setting contrast using software.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*set_contrast)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>promille</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Sets the contrast to the given value, which is an integer in the range from 0 to 1000.
It is up to the driver to map the logical interval [0, 1000] into the
interval that the hardware supports.
Many displays do not support software setting of contrast.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*get_brightness)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>state</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Get the brightness value from the driver for the given backlight state.
The parameter <parameter>state</parameter> determines which one
is returned.
The return value is an integer in the range from 0 to 1000.
Many displays do not support getting or setting brightness using software.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>(*set_brightness)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>state</parameter></paramdef>
<paramdef>int <parameter>promille</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Set the brightness for the given backlight state to the value given.
Value must be an integer in the range from 0 to 1000.
It is up to the driver to map the logical interval [0, 1000] into the
interval that the hardware supports.
Many displays do not support software setting of brightness.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*backlight)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>state</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Sets the backlight to the given brightness state.
Often hardware can only support two values for the backlight:
on and off.
In that case any value of state > 0 will switch the backlight on.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>void <function>(*output)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
<paramdef>int <parameter>state</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Sets the output value. Some displays/wirings have a general purpose
output, which can be controlled by calling this function. See the
'output' command in the 'widget language'.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>const char *<function>(*get_key)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Checks if a key has been pressed on the device.
Returns NULL for "no key pressed", or a string describing the pressed key.
These characters should match the keypad-layout.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>const char *<function>(*get_info)</function></funcdef>
<paramdef>Driver *<parameter>drvthis</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Returns a string describing the driver and its features.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>short <function>(*config_get_bool)</function></funcdef>
<paramdef>char *<parameter>sectionname</parameter></paramdef>
<paramdef>char *<parameter>keyname</parameter></paramdef>
<paramdef>int <parameter>skip</parameter></paramdef>
<paramdef>short <parameter>default_value</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Call to server. Retrieve a bool from the config file.
Sectionname should be the name of the driver (as in the struct).
If the key cannot be found, the default value will be returned.
skip should be 0 usually, but if you want to retrieve multiple
identical keys, then increase skip to get every next value.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>long int <function>(*config_get_int)</function></funcdef>
<paramdef>char *<parameter>sectionname</parameter></paramdef>
<paramdef>char *<parameter>keyname</parameter></paramdef>
<paramdef>int <parameter>skip</parameter></paramdef>
<paramdef>long int <parameter>default_value</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Call to server. Retrieve an integer from the config file.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>double <function>(*config_get_float)</function></funcdef>
<paramdef>char *<parameter>sectionname</parameter></paramdef>
<paramdef>char *<parameter>keyname</parameter></paramdef>
<paramdef>int <parameter>skip</parameter></paramdef>
<paramdef>double <parameter>default_value</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Call to server. Retrieve a float from the config file.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>const char *<function>(*config_get_string)</function></funcdef>
<paramdef>char *<parameter>sectionname</parameter></paramdef>
<paramdef>char *<parameter>keyname</parameter></paramdef>
<paramdef>int <parameter>skip</parameter></paramdef>
<paramdef>const char *<parameter>default</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Call to server. Retrieve a string from the config file.
Fill result with a pointer to some available space. You can fill it
with a default value. If the key is found, it will be overwritten
with the value from the key.
Note that you should always first copy the the returned string.
It is in the address space of the server, and will be freed at the
next call.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>config_has_section</function></funcdef>
<paramdef>const char *<parameter>sectionname</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Returns whether a section exists. Does not need to be called prior
to a call to a config_get_* function.
</para>
<funcsynopsis>
<funcprototype>
<funcdef>int <function>config_has_key</function></funcdef>
<paramdef>const char *<parameter>sectionname</parameter></paramdef>
<paramdef>const char *<parameter>keyname</parameter></paramdef>
</funcprototype>
</funcsynopsis>
<para>
Returns the number of times a key exists. Does not need to be called
prior to a call to a config_get_* function.
</para>
<screen>
First version, Joris Robijn, 20011016
Corrected and expanded, Peter Marschall 20060411
Sync'd with lcd.h, Markus Dolze, 20090322
</screen>
</sect1>
</chapter>
|