3 // This is a driver for the ULA-200 from ELV (http://www.elv.de). It's a small
4 // board which can be connected to the PC via USB interface and that allows to
5 // connect a HD44780-compatible display. It also has a connection for up to 6
8 // It uses libftdi (which uses libusb) for communication, so no kernel driver
9 // is needed. You have to take care that ftdi_sio module doesn't claim the ELV
10 // device. If you didn't change the ids in the driver (ftdi_sio.c), you don't
16 // - text display is supported
17 // - standard symbols (heart, checkbox) are supported
18 // - backlight is supported
19 // - input buttons are supported
20 // - bars are NOT supported
23 // Configuration options (the values are the default values):
36 // Sometimes the display hangs (the ACK response is not received) on shutdown.
37 // Reconnect the display in that case. The same if it hangs while starting
38 // up (only happens if it was not the first time lcdproc talked to the
41 // I tried several hours to fix this, I simply find the reason for this problem.
44 // Implementation note:
46 // The ULA-200 talks a text protocol which allows to display text using a
47 // high-level language, i.e. STX 's' <len> <char0> <char1> ... ETX. It also
48 // allows low-level register access to the HD44780. So in theory, it would be
49 // possible to write a hd44780 extension and let the hd44780 core do the rest.
50 // I tried this. It was slow and didn't work with user-specific characters
51 // (the hd44780 frequently changes this characters which seems to confuse the
52 // microcontroller, at least I cannot explain why it didn't work, there was
55 // So I wrote a own driver (this here) which uses the high-level language and
56 // should work for displays with all sizes. I only tested 20x4, so maybe for
57 // other sizes the positioning code may be adapted.
59 // As I mentioned, there were problems with frequently changing the
60 // user-definable characters. I also tried to implement bar code in the ula200
61 // driver with similar effects. I gave it up because I don't need it personally
62 // and it can be done later. However, standard icons are implemented. The
63 // user-definable characters are set in startup and are not changed. This
64 // works like a charm. It is not possible to use character 0 with the
65 // high-level language (or at least it isn't documented how to escape it).
66 // It could be done with hd44780 code, but I replaced the character with
67 // a standard character which looks good.
72 // Bernhard Walle <bernhard.walle@gmx.de>
73 // Feel free to contact me if there are problems with this driver.
76 /* Copyright (C) 2006 Bernhard Walle <bernhard.walle@gmx.de>
78 This program is free software; you can redistribute it and/or modify
79 it under the terms of the GNU General Public License as published by
80 the Free Software Foundation; either version 2 of the License, or
83 This program is distributed in the hope that it will be useful,
84 but WITHOUT ANY WARRANTY; without even the implied warranty of
85 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
86 GNU General Public License for more details.
88 You should have received a copy of the GNU General Public License
89 along with this program; if not, write to the Free Software
90 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */
108 #include "hd44780-charmap.h"
116 /* Vars for the server core */
117 MODULE_EXPORT
char *api_version
= API_VERSION
;
118 MODULE_EXPORT
int stay_in_foreground
= 0;
119 MODULE_EXPORT
int supports_multiple
= 1;
120 MODULE_EXPORT
char *symbol_prefix
= "ula200_";
123 ///////////////////////////////////////////////////////////////////////////////
126 #define DISPLAY_VENDOR_ID 0x0403
127 #define DISPLAY_PRODUCT_ID 0xf06d
137 #define MAX_KEY_MAP 6
138 static char *default_key_map
[MAX_KEY_MAP
] = { "Up", "Down", "Left", "Right", "Enter", "Escape" };
143 /* repeat conts for responses */
144 #define MAX_REPEATS 20
147 ///////////////////////////////////////////////////////////////////////////////
148 // Key ring implementation from CFontz633io
151 /* KeyRing management */
152 #define KEYRINGSIZE 16
155 unsigned char contents
[KEYRINGSIZE
];
163 * KeyRing handling functions.
164 * This separates the producer from the consumer.
165 * It is just a small fifo of unsigned char.
168 /** initialize/empty key ring by resetting its read & write pointers */
169 void EmptyKeyRing(KeyRing
*kr
)
171 kr
->head
= kr
->tail
= 0;
175 /** add byte to key ring; return success (byte added) / failure (key ring is full) */
176 int AddKeyToKeyRing(KeyRing
*kr
, unsigned char key
)
178 if (((kr
->head
+ 1) % KEYRINGSIZE
) != (kr
->tail
% KEYRINGSIZE
)) {
179 /* fprintf(stderr, "We add key: %d\n", key); */
181 kr
->contents
[kr
->head
% KEYRINGSIZE
] = key
;
182 kr
->head
= (kr
->head
+ 1) % KEYRINGSIZE
;
186 /* KeyRing overflow: do not accept extra key */
191 /** get byte from key ring (or '\\0' if key ring is empty) */
192 unsigned char GetKeyFromKeyRing(KeyRing
*kr
)
194 unsigned char retval
= '\0';
196 kr
->tail
%= KEYRINGSIZE
;
198 if ((kr
->head
% KEYRINGSIZE
) != kr
->tail
) {
199 retval
= kr
->contents
[kr
->tail
];
200 kr
->tail
= (kr
->tail
+ 1) % KEYRINGSIZE
;
203 /* if (retval) fprintf(stderr, "We remove key: %d\n", retval); */
207 ///////////////////////////////////////////////////////////////////////////////
208 // for HD 44780 compatibility
211 // commands for senddata
213 #define RS_INSTR 0x01
215 #define SETCHAR 0x40 /* Only reachable with EXTREG clear */
218 ///////////////////////////////////////////////////////////////////////////////
219 // private data types
223 // the handle for the USB FTDI library
224 struct ftdi_context ftdic
;
226 // the width and the height (in number of characters) of the library
229 // The framebuffer and the framebuffer for the last contents (incr. update)
230 unsigned char *framebuf
, *lcd_contents
;
232 // first time => all is dirty
233 unsigned char all_dirty
;
235 // backlight (-1 = unset, 0 = off, 1 = on)
242 char *key_map
[MAX_KEY_MAP
];
247 ///////////////////////////////////////////////////////////////////////////////
248 // Reads a USB characters
250 ula200_ftdi_usb_read(PrivateData
*p
)
252 unsigned char buffer
[1];
255 while ((err
= ftdi_read_data(&p
->ftdic
, buffer
, 1)) == 0);
266 ///////////////////////////////////////////////////////////////////////////////
267 // Reads the response
269 // @return true if ACK was responded, false if NACk was responded
272 ula200_ftdi_read_response(Driver
*drvthis
)
274 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
276 bool answer_read
= false;
283 while (((ret
= ula200_ftdi_usb_read(p
)) != CH_STX
) && (ret
> 0));
289 ch
= ula200_ftdi_usb_read(p
);
293 ch
= ula200_ftdi_usb_read(p
);
294 AddKeyToKeyRing(&p
->keyring
, (unsigned char)(ch
- 0x40));
308 report(RPT_INFO
, "%s: read invalid answer (0x%02X)", drvthis
->name
, ch
);
312 while (((ret
= ula200_ftdi_usb_read(p
)) != CH_ETX
) && (ret
> 0));
321 ///////////////////////////////////////////////////////////////////////////////
322 // Write a command to the display. Adds the STX and ETX header/trailer.
324 // @param p the private data
325 // @param data the data bytes
326 // @param length the number of bytes in @p data which are valid
327 // @param escape if the data should be escaped (see the User's Guide of the
329 // @return 0 on success, negative value on error
332 ula200_ftdi_write_command(Driver
*drvthis
, unsigned char *data
, int length
, bool escape
)
334 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
336 int repeat_count
= 0;
338 unsigned char buffer
[1024];
345 buffer
[pos
++] = CH_STX
;
346 for (i
= 0; i
< length
; i
++) {
348 if (data
[i
] == CH_STX
) {
349 buffer
[pos
++] = CH_ENQ
;
350 buffer
[pos
++] = CH_DC2
;
352 else if (data
[i
] == CH_ETX
) {
353 buffer
[pos
++] = CH_ENQ
;
354 buffer
[pos
++] = CH_DC3
;
356 else if (data
[i
] == CH_ENQ
) {
357 buffer
[pos
++] = CH_ENQ
;
358 buffer
[pos
++] = CH_NAK
;
361 buffer
[pos
++] = data
[i
];
365 buffer
[pos
++] = data
[i
];
368 buffer
[pos
++] = CH_ETX
;
372 err
= ftdi_write_data(&p
->ftdic
, buffer
, pos
);
374 report(RPT_WARNING
, "%s: ftdi_write_data failed", drvthis
->name
);
378 while (!ula200_ftdi_read_response(drvthis
) && (repeat_count
++ < MAX_REPEATS
));
383 ///////////////////////////////////////////////////////////////////////////////
387 ula200_ftdi_clear(Driver
*drvthis
)
389 //PrivateData *p = (PrivateData *) drvthis->private_data;
390 unsigned char command
[1];
394 err
= ula200_ftdi_write_command(drvthis
, command
, 1, true);
396 report(RPT_WARNING
, "%s: ula200_ftdi_clear: "
397 "ula200_ftdi_write_command failed", drvthis
->name
);
404 ///////////////////////////////////////////////////////////////////////////////
408 ula200_ftdi_position(Driver
*drvthis
, int x
, int y
)
410 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
411 unsigned char command
[5];
422 err
= ula200_ftdi_write_command(drvthis
, command
, 3, true);
424 report(RPT_WARNING
, "%s: ula200_ftdi_position(%d,%d): "
425 "ula200_ftdi_write_command failed",
426 drvthis
->name
, x
, y
);
433 ///////////////////////////////////////////////////////////////////////////////
437 ula200_ftdi_rawdata(Driver
*drvthis
, unsigned char flags
, unsigned char ch
)
439 //PrivateData *p = (PrivateData *) drvthis->private_data;
440 unsigned char command
[3];
444 command
[1] = flags
== (RS_DATA
) ? '2' : '0';
446 err
= ula200_ftdi_write_command(drvthis
, command
, 3, false);
448 report(RPT_ERR
, "%s: ftdi_write_command() failed", drvthis
->name
);
455 ///////////////////////////////////////////////////////////////////////////////
459 ula200_ftdi_string(Driver
*drvthis
, const unsigned char *string
, int len
)
461 //PrivateData *p = (PrivateData *) drvthis->private_data;
462 unsigned char buffer
[128];
472 for (i
= 0; i
< len
; i
++) {
473 buffer
[i
+2] = HD44780_charmap
[(unsigned char)string
[i
]];
476 err
= ula200_ftdi_write_command(drvthis
, buffer
, len
+2, true);
478 report(RPT_WARNING
, "%s: ula200_ftdi_string: "
479 "ula200_ftdi_write_command() failed", drvthis
->name
);
486 ///////////////////////////////////////////////////////////////////////////////
487 // Enables the raw register access mode.
490 ula200_ftdi_enable_raw_mode(Driver
*drvthis
)
492 unsigned char command
[3];
494 report(RPT_DEBUG
, "%s: enable raw mode", drvthis
->name
);
499 return ula200_ftdi_write_command(drvthis
, command
, 3, false);
502 ///////////////////////////////////////////////////////////////////////////////
503 // Loads custom characters in the display
506 ula200_load_curstom_chars(Driver
*drvthis
)
510 char custom_chars
[8][CELLHEIGHT
*CELLHEIGHT
] = {
576 for (i
= 0; i
< 8 && err
== 0; i
++)
578 /* Tell the HD44780 we will redefine char number i */
579 ula200_ftdi_rawdata(drvthis
, RS_INSTR
, SETCHAR
| i
* 8);
581 report(RPT_WARNING
, "%s: ula200_ftdi_rawdata failed", drvthis
->name
);
585 /* Send the subsequent rows */
586 for (row
= 0; row
< CELLHEIGHT
; row
++) {
589 for (col
= 0; col
< CELLWIDTH
; col
++) {
591 value
|= (custom_chars
[i
][(row
* CELLWIDTH
) + col
] > 0) ? 1 : 0;
593 err
= ula200_ftdi_rawdata(drvthis
, RS_DATA
, value
);
595 report(RPT_WARNING
, "%s: ula200_ftdi_rawdata failed", drvthis
->name
);
605 ///////////////////////////////////////////////////////////////////////////////
606 // Init the driver and display
609 ula200_init(Driver
*drvthis
)
615 // Alocate and store private data
616 p
= (PrivateData
*) malloc( sizeof( PrivateData
) );
620 if (drvthis
->store_private_ptr(drvthis
, p
)) {
626 EmptyKeyRing(&p
->keyring
);
628 // Get and parse size
629 s
= drvthis
->config_get_string(drvthis
->name
, "size", 0, "20x4");
630 if ((sscanf(s
, "%dx%d", &(p
->width
), &(p
->height
)) != 2)
631 || (p
->width
<= 0) || (p
->width
> LCD_MAX_WIDTH
)
632 || (p
->height
<= 0) || (p
->height
> LCD_MAX_HEIGHT
)) {
633 report(RPT_ERR
, "%s: cannot read Size %s", drvthis
->name
, s
);
638 for (i
= 0; i
< MAX_KEY_MAP
; i
++) {
641 // First fill with default value
642 p
->key_map
[i
] = default_key_map
[i
];
645 sprintf(buf
, "KeyMap_%c", i
+'A');
646 s
= drvthis
->config_get_string(drvthis
->name
, buf
, 0, NULL
);
648 // Was a key specified in the config file ?
650 p
->key_map
[i
] = strdup(s
);
651 report(RPT_INFO
, "%s: Key '%c' mapped to \"%s\"",
652 drvthis
->name
, i
+'A', s
);
656 /* End of config file parsing */
659 // Allocate framebuffer
660 p
->framebuf
= (unsigned char *) malloc(p
->width
* p
->height
);
661 if (p
->framebuf
== NULL
) {
662 report(RPT_ERR
, "%s: unable to allocate framebuffer", drvthis
->name
);
666 // Allocate and clear the buffer for incremental updates
667 p
->lcd_contents
= (unsigned char *) malloc(p
->width
* p
->height
);
668 if (p
->lcd_contents
== NULL
) {
669 report(RPT_ERR
, "%s: unable to allocate framebuffer backing store", drvthis
->name
);
672 memset(p
->lcd_contents
, 0, p
->width
* p
->height
);
674 // open the FTDI library
675 ftdi_init(&p
->ftdic
);
676 (&p
->ftdic
)->usb_write_timeout
= 20;
677 (&p
->ftdic
)->usb_read_timeout
= 20;
680 err
= ftdi_usb_open(&p
->ftdic
, DISPLAY_VENDOR_ID
, DISPLAY_PRODUCT_ID
);
682 report(RPT_ERR
, "%s: cannot open USB device", drvthis
->name
);
687 err
= ftdi_set_baudrate(&p
->ftdic
, 19200);
689 report(RPT_ERR
, "%s: cannot set baudrate", drvthis
->name
);
693 // set communication parameters
694 err
= ftdi_set_line_property(&p
->ftdic
, BITS_8
, STOP_BIT_1
, EVEN
);
696 report(RPT_ERR
, "%s: cannot set line properties", drvthis
->name
);
700 // user is able to write commands
701 err
= ula200_ftdi_enable_raw_mode(drvthis
);
703 report(RPT_ERR
, "%s: unable to enable the raw mode", drvthis
->name
);
708 err
= ula200_load_curstom_chars(drvthis
);
710 report(RPT_ERR
, "%s: unable to write the custom characters", drvthis
->name
);
714 report(RPT_DEBUG
, "%s: init() done", drvthis
->name
);
719 ftdi_usb_close(&p
->ftdic
);
720 ftdi_deinit(&p
->ftdic
);
724 free(p
->lcd_contents
);
730 ///////////////////////////////////////////////////////////////////////////////
734 ula200_close(Driver
*drvthis
)
736 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
739 ftdi_usb_purge_buffers(&p
->ftdic
);
740 ftdi_usb_close(&p
->ftdic
);
741 ftdi_deinit(&p
->ftdic
);
743 if (p
->framebuf
!= NULL
)
746 if (p
->lcd_contents
!= NULL
)
747 free(p
->lcd_contents
);
751 drvthis
->store_private_ptr(drvthis
, NULL
);
754 ///////////////////////////////////////////////////////////////////////////////
755 // Returns the display width
758 ula200_width (Driver
*drvthis
)
760 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
764 ///////////////////////////////////////////////////////////////////////////////
765 // Returns the display height
768 ula200_height (Driver
*drvthis
)
770 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
774 ///////////////////////////////////////////////////////////////////////////////
775 // Clear the framebuffer
778 ula200_clear (Driver
*drvthis
)
780 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
781 memset(p
->framebuf
, ' ', p
->width
* p
->height
);
784 ///////////////////////////////////////////////////////////////////////////////
785 // Place a character in the framebuffer
788 ula200_chr (Driver
*drvthis
, int x
, int y
, char ch
)
790 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
794 p
->framebuf
[ (y
* p
->width
) + x
] = ch
;
797 ///////////////////////////////////////////////////////////////////////////////
798 // Place a string in the framebuffer
801 ula200_string (Driver
*drvthis
, int x
, int y
, const char string
[])
803 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
806 x
--; // Convert 1-based coords to 0-based
809 for (i
= 0; string
[i
] != '\0'; i
++) {
810 // Check for buffer overflows...
811 if ((y
* p
->width
) + x
+ i
> (p
->width
* p
->height
))
813 p
->framebuf
[(y
*p
->width
) + x
+ i
] = string
[i
];
817 ///////////////////////////////////////////////////////////////////////////////
818 // Sets the backlight on or off
821 ula200_backlight (Driver
*drvthis
, int on
)
823 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
824 unsigned char command
[2];
827 if (p
->backlight
!= on
) {
831 command
[1] = (on
) ? '1' : '0';
832 err
= ula200_ftdi_write_command(drvthis
, command
, 2, false);
834 report(RPT_WARNING
, "%s: error in ula200_ftdi_write_command",
837 report(RPT_INFO
, "%s: turn backlight %s",
838 drvthis
->name
, (on
) ? "on" : "off");
842 ///////////////////////////////////////////////////////////////////////////////
843 // Flush the framebuffer to the display
846 ula200_flush(Driver
*drvthis
)
848 PrivateData
*p
= (PrivateData
*) drvthis
->private_data
;
858 ula200_ftdi_clear(drvthis
);
862 // Update LCD incrementally by comparing with last contents
864 for (y
= 0; y
< p
->height
; y
++) {
868 for (x
= 0 ; x
< wid
; x
++) {
869 ch
= p
->framebuf
[(y
* wid
) + x
];
870 if (ch
!= p
->lcd_contents
[(y
*wid
)+x
]) {
871 p
->lcd_contents
[(y
*wid
)+x
] = ch
;
872 if (firstdiff
== -1) {
879 if (firstdiff
>= 0) {
880 ula200_ftdi_position(drvthis
, firstdiff
, y
);
881 ula200_ftdi_string(drvthis
, p
->framebuf
+ (y
*wid
) + firstdiff
,
882 lastdiff
- firstdiff
+ 1);
888 ///////////////////////////////////////////////////////////////////////////////
889 // Set default icon into a userdef char
892 ula200_icon (Driver
*drvthis
, int x
, int y
, int icon
)
894 /* Yes I know, this is a VERY BAD implementation */
896 case ICON_BLOCK_FILLED
:
897 ula200_chr(drvthis
, x
, y
, 0xff);
899 case ICON_HEART_FILLED
:
900 ula200_chr(drvthis
, x
, y
, 2);
902 case ICON_HEART_OPEN
:
903 ula200_chr(drvthis
, x
, y
, 1);
906 ula200_chr(drvthis
, x
, y
, 3);
908 case ICON_ARROW_DOWN
:
909 ula200_chr(drvthis
, x
, y
, 4);
911 case ICON_ARROW_LEFT
:
912 ula200_chr(drvthis
, x
, y
, 0x7F);
914 case ICON_ARROW_RIGHT
:
915 ula200_chr(drvthis
, x
, y
, 0x7E);
917 case ICON_CHECKBOX_OFF
:
918 ula200_chr(drvthis
, x
, y
, 5);
920 case ICON_CHECKBOX_ON
:
921 ula200_chr(drvthis
, x
, y
, 6);
923 case ICON_CHECKBOX_GRAY
:
924 ula200_chr(drvthis
, x
, y
, 7);
927 return -1; /* Let the core do other icons */
933 ///////////////////////////////////////////////////////////////////////////////
934 // Set default icon into a userdef char
936 MODULE_EXPORT
const char *
937 ula200_get_key (Driver
*drvthis
)
939 PrivateData
*p
= drvthis
->private_data
;
943 // The libftdi has no non-blocking read (`select' system call), so we force
944 // a read that could not block by updating one character on the display.
945 // As long as lcdproc is single-threaded, we can write to the display because
946 // we're not inside a read here.
947 ula200_ftdi_position(drvthis
, 0, 0);
948 ula200_ftdi_string(drvthis
, p
->lcd_contents
, 1);
950 key
= GetKeyFromKeyRing(&p
->keyring
);
952 // search the bit that was set by the hardware
953 for (i
= 0; i
< MAX_KEY_MAP
; i
++) {
955 return p
->key_map
[i
];
959 report(RPT_INFO
, "%s: Untreated key 0x%02X", drvthis
->name
, key
);