root/trunk/drv_ula200.c

Revision 897, 18.3 kB (checked in by michael, 7 weeks ago)

properties fixed

  • Property svn:keywords set to Id URL Rev
Line 
1/* $Id$
2 * $URL$
3 *
4 * ULA200 driver for lcd4linux
5 *
6 * Copyright (C) 2008 Bernhard Walle <bernhard.walle@gmx.de>
7 *
8 * This file is part of LCD4Linux.
9 *
10 * LCD4Linux is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * LCD4Linux is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26/*
27 * Driver for the ELV ULA200 USB device. The device can control one
28 * HD44780 display up to 4x20 characters.
29 *
30 * Implemented functions:
31 *   - displaying characters :-)
32 *   - controlling the backlight
33 *
34 * Todo:
35 *   - input buttons
36 *
37 * Configuration:
38 *   - Size (XxY): size of the display (e.g. '20x4')
39 *   - Backlight (0/1): initial state of the backlight
40 *
41 * Author:
42 *   Bernhard Walle <bernhard.walle@gmx.de>
43 *
44 * exported fuctions:
45 *   struct DRIVER drv_ula200
46 *
47 */
48
49#include "config.h"
50
51#include <stdlib.h>
52#include <stdio.h>
53#include <unistd.h>
54#include <string.h>
55#include <errno.h>
56
57#include <ftdi.h>
58
59#include "debug.h"
60#include "cfg.h"
61#include "qprintf.h"
62#include "udelay.h"
63#include "plugin.h"
64#include "widget.h"
65#include "widget_text.h"
66#include "widget_icon.h"
67#include "widget_bar.h"
68#include "drv.h"
69
70/* text mode display? */
71#include "drv_generic_text.h"
72
73/****************************************/
74/***        Global variables          ***/
75/****************************************/
76
77static char Name[] = "ULA200";
78static struct ftdi_context *Ftdi = NULL;
79
80
81/****************************************/
82/***        Constants                 ***/
83/****************************************/
84
85/* USB connection */
86#define ULA200_VENDOR_ID      0x0403
87#define ULA200_PRODUCT_ID     0xf06d
88
89/* connection parameters */
90#define ULA200_BAUDRATE   19200
91#define ULA200_DATABITS   BITS_8
92#define ULA200_STOPBITS   STOP_BIT_1
93#define ULA200_PARITY   EVEN
94
95/* character constants used for the communication */
96#define ULA200_CH_STX     0x02
97#define ULA200_CH_ETX     0x03
98#define ULA200_CH_ENQ     0x05
99#define ULA200_CH_ACK     0x06
100#define ULA200_CH_NAK     0x15
101#define ULA200_CH_DC2     0x12
102#define ULA200_CH_DC3     0x13
103
104/* commands used for the communication (names are German) */
105#define ULA200_CMD_POSITION 'p' /* 'position' */
106#define ULA200_CMD_STRING 's' /* 'string' */
107#define ULA200_CMD_CLEAR  'l' /* 'loeschen' */
108#define ULA200_CMD_BACKLIGHT  'h' /* 'hintergrund' */
109#define ULA200_CMD_CHAR       'c' /* 'character' */
110
111/* raw register access */
112#define ULA200_RS_DATA      0x00  /* data */
113#define ULA200_RS_INSTR     0x01  /* instruction */
114#define ULA200_SETCHAR    0x40  /* set user-defined character */
115
116/* character sizes */
117#define ULA200_CELLWIDTH  5
118#define ULA200_CELLHEIGHT 8
119
120/* internal implementation constants */
121#define ULA200_BUFFER_LENGTH  1024
122#define ULA200_MAXLEN   512
123#define ULA200_MAX_REPEATS  20
124
125/* define TRUE and FALSE for better code readability if not already defined */
126#ifndef TRUE
127#   define TRUE 1
128#endif
129#ifndef FALSE
130#   define FALSE 0
131#endif
132
133
134/****************************************/
135/***        Macros                    ***/
136/****************************************/
137
138#define ULA200_ERROR(msg, ...) \
139  error("%s: In %s():%d: " msg, Name, \
140      __FUNCTION__, __LINE__, ##__VA_ARGS__)
141
142#define ULA200_INFO(msg, ...) \
143  info("%s: " msg, Name, ##__VA_ARGS__)
144
145#define ULA200_DEBUG(msg, ...) \
146  debug("%s: In %s():%d: " msg, Name, \
147      __FUNCTION__, __LINE__, ##__VA_ARGS__)
148
149#define ULA200_TRACE() \
150  debug("%s: Calling %s()", Name, __FUNCTION__)
151
152
153/****************************************/
154/***        Prototypes                ***/
155/****************************************/
156
157static int drv_ula200_ftdi_read_response(void);
158static int drv_ula200_ftdi_usb_read(void);
159static int drv_ula200_ftdi_write_command(const unsigned char *, int);
160static int drv_ula200_backlight(int);
161static int drv_ula200_close(void);
162
163static void plugin_backlight(RESULT *, RESULT *);
164
165/****************************************/
166/***        Internal (helper) funcs   ***/
167/****************************************/
168
169/**
170 * Write a command to the display. Adds the STX and ETX header/trailer.
171 *
172 * @param[in] data the data bytes
173 * @param[in] length the number of bytes in data which are valid
174 * @return 0 on success, negative value on error
175 */
176static int drv_ula200_ftdi_write_command(const unsigned char *data, int length)
177{
178    int i, err;
179    int repeat_count = 0;
180    int pos = 0;
181    unsigned char buffer[ULA200_BUFFER_LENGTH];
182
183    /* check for the maximum length */
184    if (length > ULA200_MAXLEN) {
185  return -EINVAL;
186    }
187
188    /* fill the array */
189    buffer[pos++] = ULA200_CH_STX;
190    for (i = 0; i < length; i++) {
191  if (data[i] == ULA200_CH_STX) {
192      buffer[pos++] = ULA200_CH_ENQ;
193      buffer[pos++] = ULA200_CH_DC2;
194  } else if (data[i] == ULA200_CH_ETX) {
195      buffer[pos++] = ULA200_CH_ENQ;
196      buffer[pos++] = ULA200_CH_DC3;
197  } else if (data[i] == ULA200_CH_ENQ) {
198      buffer[pos++] = ULA200_CH_ENQ;
199      buffer[pos++] = ULA200_CH_NAK;
200  } else {
201      buffer[pos++] = data[i];
202  }
203    }
204    buffer[pos++] = ULA200_CH_ETX;
205
206    do {
207  /* ULA200_DEBUG("ftdi_write_data(%p, %d)", buffer, pos); */
208  err = ftdi_write_data(Ftdi, buffer, pos);
209  if (err < 0) {
210      ULA200_ERROR("ftdi_write_data() failed");
211      return -1;
212  }
213    }
214    while (!drv_ula200_ftdi_read_response() && (repeat_count++ < ULA200_MAX_REPEATS));
215
216    return 0;
217}
218
219/**
220 * Reads a character from USB.
221 *
222 * @return a positive value between 0 and 255 indicates the character that
223 *         has been read successfully, -1 indicates an error
224 */
225static int drv_ula200_ftdi_usb_read(void)
226{
227    unsigned char buffer[1];
228    int err;
229
230    while ((err = ftdi_read_data(Ftdi, buffer, 1)) == 0);
231    return err >= 0 ? buffer[0] : -1;
232}
233
234
235/**
236 * Reads the response of the display. Currently, key input is ignored
237 * and only ACK / NACK is read.
238 *
239 * @return TRUE on success (ACK), FALSE on failure (NACK)
240 */
241static int drv_ula200_ftdi_read_response(void)
242{
243    int result = FALSE;
244    int answer_read = FALSE;
245    int ret;
246    int ch;
247
248    while (!answer_read) {
249  /* wait until STX */
250  do {
251      ret = drv_ula200_ftdi_usb_read();
252      /* ULA200_DEBUG("STX drv_ula200_ftdi_usb_read = %d", ret); */
253  } while ((ret != ULA200_CH_STX) && (ret > 0));
254
255  if (ret < 0) {
256      return FALSE;
257  }
258
259  /* read next char */
260  ch = drv_ula200_ftdi_usb_read();
261  /* ULA200_DEBUG("drv_ula200_ftdi_usb_read = %d", ch); */
262
263  switch (ch) {
264  case 't':
265      ch = drv_ula200_ftdi_usb_read();
266      /* ULA200_DEBUG("drv_ula200_ftdi_usb_read = %d", ch); */
267      /* ignore currently */
268      break;
269
270  case ULA200_CH_ACK:
271      answer_read = TRUE;
272      result = TRUE;
273      break;
274
275  case ULA200_CH_NAK:
276      answer_read = TRUE;
277      result = FALSE;
278      break;
279
280  default:
281      answer_read = TRUE;
282      ULA200_ERROR("Read invalid answer");
283  }
284
285  /* wait until ETX */
286  do {
287      ret = drv_ula200_ftdi_usb_read();
288      /* ULA200_DEBUG("ETX drv_ula200_ftdi_usb_read = %d", ret); */
289  } while ((ret != ULA200_CH_ETX) && (ret > 0));
290
291  if (ret < 0) {
292      return FALSE;
293  }
294    }
295
296    return result;
297}
298
299static int drv_ula200_ftdi_enable_raw_mode(void)
300{
301    unsigned char command[3];
302
303    command[0] = 'R';
304    command[1] = 'E';
305    command[2] = '1';
306    return drv_ula200_ftdi_write_command(command, 3);
307}
308
309
310/**
311 * Writes raw data (access the HD44780 registers directly.
312 *
313 * @param[in] flags ULA200_RS_DATA or ULA200_RS_INSTR
314 * @param[in] ch the real data
315 * @return 0 on success, a negative value on error
316 */
317static int drv_ula200_ftdi_rawdata(unsigned char flags, unsigned char ch)
318{
319    unsigned char command[3];
320    int err;
321
322    command[0] = 'R';
323    command[1] = flags == ULA200_RS_DATA ? '2' : '0';
324    command[2] = ch;
325    err = drv_ula200_ftdi_write_command(command, 3);
326    if (err < 0) {
327  ULA200_ERROR("ula200_ftdi_write_command() failed");
328  return -1;
329    }
330
331    return 0;
332}
333
334/**
335 * Sets the cursor position.
336 *
337 * @param[in] x the x coordinate of the position
338 * @param[in] y the y coordinate of the position
339 * @return 0 on success, a negative value on error
340 */
341static int drv_ula200_set_position(int x, int y)
342{
343    unsigned char command[3];
344    int err;
345
346    if (y >= 2) {
347  y -= 2;
348  x += DCOLS;   /* XXX: multiply by 2? */
349    }
350
351    command[0] = ULA200_CMD_POSITION;
352    command[1] = x;
353    command[2] = y;
354    err = drv_ula200_ftdi_write_command(command, 3);
355    if (err < 0) {
356  ULA200_ERROR("ula200_ftdi_write_command() failed");
357    }
358
359    return err;
360}
361
362/**
363 * Sends the text
364 *
365 * @param[in] data the data bytes
366 * @param[in] len the number of valid bytes in @p data
367 * @return 0 on success, a negative value on error
368 */
369static int drv_ula200_send_text(const unsigned char *data, int len)
370{
371    unsigned char buffer[ULA200_BUFFER_LENGTH];
372    int err;
373
374    if (len > ULA200_MAXLEN) {
375  return -EINVAL;
376    }
377
378    buffer[0] = ULA200_CMD_STRING;
379    buffer[1] = len;
380    memcpy(buffer + 2, data, len);
381    buffer[2 + len] = 0;  /* only necessary for the debug message */
382
383    /* ULA200_DEBUG("Text: =%s= (%d)", buffer+2, len); */
384
385    err = drv_ula200_ftdi_write_command(buffer, len + 2);
386    if (err < 0) {
387  ULA200_ERROR("ula200_ftdi_write_command() failed");
388  return -1;
389    }
390
391    return 0;
392}
393
394/**
395 * Sends one character.
396 *
397 * @param[in] ch the character to send
398 * @return 0 on success, a negative value on error
399 */
400static int drv_ula200_send_char(char ch)
401{
402    unsigned char buffer[2];
403    int err;
404
405    buffer[0] = ULA200_CMD_CHAR;
406    buffer[1] = ch;
407
408    err = drv_ula200_ftdi_write_command(buffer, 2);
409    if (err < 0) {
410  ULA200_ERROR("ula200_ftdi_write_command() failed");
411  return -1;
412    }
413
414    return 0;
415}
416
417/**
418 * Opens the ULA200 display. Uses libftdi to initialise the USB communication to
419 * the display.
420 *
421 @ @return a value less then zero on failure, 0 on success
422 */
423static int drv_ula200_open(void)
424{
425    int err;
426
427    /* check if the device was already open */
428    if (Ftdi != NULL) {
429  ULA200_ERROR("open called although device was already open");
430  drv_ula200_close();
431    }
432
433    /* get memory for the device descriptor */
434    Ftdi = malloc(sizeof(struct ftdi_context));
435    if (Ftdi == NULL) {
436  ULA200_ERROR("Memory allocation failed");
437  return -1;
438    }
439
440    /* open the ftdi library */
441    ftdi_init(Ftdi);
442    Ftdi->usb_write_timeout = 20;
443    Ftdi->usb_read_timeout = 20;
444
445    /* open the device */
446    err = ftdi_usb_open(Ftdi, ULA200_VENDOR_ID, ULA200_PRODUCT_ID);
447    if (err < 0) {
448  ULA200_ERROR("ftdi_usb_open() failed");
449  free(Ftdi);
450  Ftdi = NULL;
451  return -1;
452    }
453
454    /* set the baudrate */
455    err = ftdi_set_baudrate(Ftdi, ULA200_BAUDRATE);
456    if (err < 0) {
457  ULA200_ERROR("ftdi_set_baudrate() failed");
458  ftdi_usb_close(Ftdi);
459  free(Ftdi);
460  Ftdi = NULL;
461  return -1;
462    }
463    /* set communication parameters */
464    err = ftdi_set_line_property(Ftdi, ULA200_DATABITS, ULA200_STOPBITS, ULA200_PARITY);
465    if (err < 0) {
466  ULA200_ERROR("ftdi_set_line_property() failed");
467  ftdi_usb_close(Ftdi);
468  free(Ftdi);
469  Ftdi = NULL;
470  return -1;
471    }
472
473    return 0;
474}
475
476/**
477 * Closes the display.
478 *
479 * @return 0 on success, a negative value on failure
480 */
481static int drv_ula200_close(void)
482{
483    ULA200_TRACE();
484
485    ftdi_usb_purge_buffers(Ftdi);
486    ftdi_usb_close(Ftdi);
487    ftdi_deinit(Ftdi);
488
489    free(Ftdi);
490    Ftdi = NULL;
491
492    return 0;
493}
494
495/**
496 * Clears the contents of the display.
497 *
498 * @return 0 on success, a negative value on error
499 */
500static void drv_ula200_clear(void)
501{
502    unsigned const char command[] = { ULA200_CMD_CLEAR };
503    int err;
504
505    ULA200_TRACE();
506
507    err = drv_ula200_ftdi_write_command(command, 1);
508    if (err < 0) {
509  ULA200_ERROR("ula200_ftdi_write_command() failed");
510    }
511}
512
513/**
514 * Writes data to the display.
515 *
516 * @param[in] row the row where the data should be written to
517 * @param[in] col the column where the data should be written to
518 * @param[in] data the data that should actually be written
519 * @param[in] len the number of valid bytes in @p data
520 */
521static void drv_ula200_write(const int row, const int col, const char *data, int len)
522{
523    int ret;
524
525    /* do the cursor positioning here */
526    ret = drv_ula200_set_position(col, row);
527    if (ret < 0) {
528  ULA200_ERROR("drv_ula200_set_position() failed");
529  return;
530    }
531
532    /* send string to the display */
533    if (len == 1) {
534  ret = drv_ula200_send_char(data[0]);
535    } else {
536  ret = drv_ula200_send_text((unsigned char *) data, len);
537    }
538    if (ret < 0) {
539  ULA200_ERROR("drv_ula200_send_text() failed");
540  return;
541    }
542}
543
544/* text mode displays only */
545static void drv_ula200_defchar(const int ascii, const unsigned char *matrix)
546{
547    int err, i;
548
549    if (ascii >= 8) {
550  ULA200_ERROR("Invalid value in drv_ula200_defchar");
551  return;
552    }
553
554    /* Tell the HD44780 we will redefine char number 'ascii' */
555    err = drv_ula200_ftdi_rawdata(ULA200_RS_INSTR, ULA200_SETCHAR | (ascii * 8));
556    if (err < 0) {
557  ULA200_ERROR("drv_ula200_ftdi_rawdata() failed");
558  return;
559    }
560
561    /* Send the subsequent rows */
562    for (i = 0; i < YRES; i++) {
563  err = drv_ula200_ftdi_rawdata(ULA200_RS_DATA, *matrix++ & 0x1f);
564  if (err < 0) {
565      ULA200_ERROR("ula200_ftdi_rawdata() failed");
566      return;
567  }
568    }
569}
570
571/**
572 * Controls the backlight of the ULA200 display.
573 *
574 * @param[in] backlight a negative value if the backlight should be turned off,
575 *            a positive value if it should be turned on
576 * @return 0 on success, any other value on failure
577 */
578static int drv_ula200_backlight(int backlight)
579{
580    unsigned char cmd[2] = { ULA200_CMD_BACKLIGHT };
581    int ret;
582
583    if (backlight <= 0) {
584  backlight = '0';
585    } else {
586  backlight = '1';
587    }
588
589    cmd[1] = backlight;
590    ret = drv_ula200_ftdi_write_command(cmd, 2);
591    if (ret < 0) {
592  ULA200_ERROR("ula200_ftdi_write_command() failed");
593    }
594
595    return backlight == '1';
596}
597
598/**
599 * Starts the display.
600 *
601 * @param[in] section the section of the configuration file
602 * @return 0 on success, a negative value on failure
603 */
604static int drv_ula200_start(const char *section)
605{
606    int rows = -1, cols = -1;
607    char *s;
608    int backlight = 0;
609    int err;
610
611    s = cfg_get(section, "Size", NULL);
612    if (s == NULL || *s == '\0') {
613  ULA200_ERROR("No '%s.Size' entry from %s", section, cfg_source());
614  return -1;
615    }
616    if (sscanf(s, "%dx%d", &cols, &rows) != 2 || rows < 1 || cols < 1) {
617  ULA200_ERROR("Bad %s.Size '%s' from %s", section, s, cfg_source());
618  free(s);
619  return -1;
620    }
621
622    DROWS = rows;
623    DCOLS = cols;
624
625    /* open communication with the display */
626    err = drv_ula200_open();
627    if (err < 0) {
628  return -1;
629    }
630
631    cfg_number(section, "Backlight", 0, 0, 1, &backlight);
632    err = drv_ula200_backlight(backlight);
633    if (err < 0) {
634  ULA200_ERROR("drv_ula200_backlight() failed");
635  return -1;
636    }
637
638    /* clear display */
639    drv_ula200_clear();
640
641    /* enable raw mode for defining own chars */
642    drv_ula200_ftdi_enable_raw_mode();
643
644    return 0;
645}
646
647/****************************************/
648/***            plugins               ***/
649/****************************************/
650
651/**
652 * Backlight plugin
653 */
654static void plugin_backlight(RESULT * result, RESULT * arg1)
655{
656    double backlight;
657
658    backlight = drv_ula200_backlight(R2N(arg1));
659    SetResult(&result, R_NUMBER, &backlight);
660}
661
662/****************************************/
663/***        exported functions        ***/
664/****************************************/
665
666/**
667 * list models
668 *
669 * @return 0 on success, a negative value on failure
670 */
671int drv_ula200_list(void)
672{
673    printf("generic");
674    return 0;
675}
676
677/**
678 * initialize driver & display
679 *
680 * @param[in] section the name of the section in the configuration file
681 * @param[in] quiet TRUE on quiet mode
682 * @return 0 on success, any negative error value on failure
683 */
684/* use this function for a text display */
685int drv_ula200_init(const char *section, const int quiet)
686{
687    WIDGET_CLASS wc;
688    int ret;
689
690    ULA200_INFO("%s", "$Rev$");
691
692    /* display preferences */
693    XRES = ULA200_CELLWIDTH;  /* pixel width of one char  */
694    YRES = ULA200_CELLHEIGHT; /* pixel height of one char  */
695    CHARS = 7;      /* number of user-defineable characters */
696    CHAR0 = 1;      /* ASCII of first user-defineable char */
697    GOTO_COST = 4;    /* number of bytes a goto command requires */
698
699    /* real worker functions */
700    drv_generic_text_real_write = drv_ula200_write;
701    drv_generic_text_real_defchar = drv_ula200_defchar;
702
703    /* start display */
704    if ((ret = drv_ula200_start(section)) != 0) {
705  return ret;
706    }
707
708    if (!quiet) {
709  char buffer[40];
710  qprintf(buffer, sizeof(buffer), "%s %dx%d", Name, DCOLS, DROWS);
711  if (drv_generic_text_greet(buffer, "ULA 200")) {
712      sleep(3);
713      drv_ula200_clear();
714  }
715    }
716
717    /* initialize generic text driver */
718    if ((ret = drv_generic_text_init(section, Name)) != 0)
719  return ret;
720
721    /* initialize generic icon driver */
722    if ((ret = drv_generic_text_icon_init()) != 0)
723  return ret;
724
725    /* initialize generic bar driver */
726    if ((ret = drv_generic_text_bar_init(0)) != 0)
727  return ret;
728
729    /* add fixed chars to the bar driver */
730    drv_generic_text_bar_add_segment(0, 0, 255, 32);  /* ASCII  32 = blank */
731
732    /* register text widget */
733    wc = Widget_Text;
734    wc.draw = drv_generic_text_draw;
735    widget_register(&wc);
736
737    /* register icon widget */
738    wc = Widget_Icon;
739    wc.draw = drv_generic_text_icon_draw;
740    widget_register(&wc);
741
742    /* register bar widget */
743    wc = Widget_Bar;
744    wc.draw = drv_generic_text_bar_draw;
745    widget_register(&wc);
746
747    /* register plugins */
748    AddFunction("LCD::backlight", -1, plugin_backlight);
749
750    return 0;
751}
752
753/**
754 * close driver & display
755 *
756 * @param[in] quiet TRUE on quiet mode
757 * @return 0 on success, any negative error value on failure
758 */
759/* use this function for a text display */
760int drv_ula200_quit(int quiet)
761{
762    ULA200_INFO("shutting down.");
763
764    drv_generic_text_quit();
765
766    /* turn backlight off */
767    drv_ula200_backlight(0);
768
769    /* clear display */
770    drv_ula200_clear();
771
772    /* say goodbye... */
773    if (!quiet) {
774  drv_generic_text_greet("goodbye!", NULL);
775    }
776
777    debug("closing connection");
778    drv_ula200_close();
779
780    return 0;
781}
782
783/* use this one for a text display */
784DRIVER drv_ula200 = {
785    .name = Name,
786    .list = drv_ula200_list,
787    .init = drv_ula200_init,
788    .quit = drv_ula200_quit,
789};
790
791/* :indentSize=4:tabSize=8:noTabs=false: */
Note: See TracBrowser for help on using the browser.