File: i2500vfd.c

package info (click to toggle)
lcdproc 0.5.9-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 5,064 kB
  • sloc: ansic: 59,645; sh: 1,740; perl: 681; makefile: 417
file content (427 lines) | stat: -rw-r--r-- 12,313 bytes parent folder | download
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
/** \file server/drivers/i2500vfd.c
 * LCDd \c i2500vfd driver for Intra2net's Intranator 2500 VFD displays.
 */

//////////////////////////////////////////////////////////////////////////
// This is a driver for the Intra2net Intranator 2500 VFD display       //
//                                                                      //
// The display features:                                                //
// - B/W and two additional grayscale colors                            //
// - USB data transfer                                                  //
// - Animated boot logo                                                 //
// - Hardware double buffering, limit is 27 FPS                         //
// - Adjustable brightness / brightness of single colors                //
//                                                                      //
// (C) 2003,2007 Intra2net AG                                           //
//                                                                      //
// The HD44780 font in i2500vfdfm.c was taken from                      //
// Michael Reinelt / lcd4linux and is (C) 2000 by him.                  //
//                                                                      //
// Code here is basend on sed1520.c:                                    //
// (C) 2001,2002 Robin Adams ( robin@adams-online.de )                  //
//                                                                      //
// This driver is released under the GPL. See file COPYING in this      //
// package for further details.                                         //
//////////////////////////////////////////////////////////////////////////

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ftdi.h>

#include "lcd.h"
#include "i2500vfd.h"
#include "glcd_font5x8.h"
#include "shared/report.h"

// The display itself stores three pixels in one byte
// We waste a little memory as we store one pixel per byte
// as we want to keep the drawing code simple.
// Take a look at i2500vfd_flush for the conversion

#define INTRA2NET_VFD_XSIZE 140
#define INTRA2NET_VFD_YSIZE 32
#define INTRA2NET_VFD_SCREENSIZE INTRA2NET_VFD_XSIZE*INTRA2NET_VFD_YSIZE
#define INTRA2NET_VFD_PACKEDSIZE 47*32
#define INTRA2NET_VFD_XSHIFT 0

#define WIDTH           23
#define HEIGHT          4
#define CELLWIDTH	6
#define CELLHEIGHT	8

/** private data for the \c i2500vfd driver */
typedef struct i2500vfd_private_data {
    struct ftdi_context ftdi;
    unsigned char *framebuf;
    int changed;
} PrivateData;

// Vars for the server core
MODULE_EXPORT char *api_version = API_VERSION;
MODULE_EXPORT int stay_in_foreground = 0;
MODULE_EXPORT int supports_multiple = 0;
MODULE_EXPORT char *symbol_prefix = "i2500vfd_";

/////////////////////////////////////////////////////////////////
// draws char z from fontmap to the framebuffer at position
// x,y. These are zero-based textmode positions.
// The Fontmap is stored in rows while the framebuffer is stored
// in columns, so we need a little conversion.
//
void
drawchar2fb (Driver *drvthis, int x, int y, unsigned char z)
{
    PrivateData *p = drvthis->private_data;

    if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT)
        return;

    x++;

    int font_x, font_y;
    for (font_y = 0; font_y < 8; font_y++) {
        for (font_x = 5; font_x > -1; font_x--) {
            if ((glcd_iso8859_1[z][font_y] & 1<<font_x) == 1<<font_x)
                p->framebuf[INTRA2NET_VFD_XSHIFT+x*6-font_x + (y*8+font_y)*140] = 1;
            else
                p->framebuf[INTRA2NET_VFD_XSHIFT+x*6-font_x + (y*8+font_y)*140] = 0;
        }
    }

    p->changed = 1;
}

/////////////////////////////////////////////////////////////////
// This initialises the stuff.
//
MODULE_EXPORT int
i2500vfd_init (Driver *drvthis)
{
    PrivateData *p;
    int i;
    unsigned char c;

    /* Allocate and store private data */
    p = (PrivateData *) calloc(1, sizeof(PrivateData));
    if (p == NULL)
        return -1;
    if (drvthis->store_private_ptr(drvthis, p))
        return -1;

    if (ftdi_init (&p->ftdi) < 0) {
        report (RPT_ERR, "ftdi_init failed. Out of memory?");
        return -1;
    }

    i = ftdi_usb_open (&p->ftdi, 0x0403, 0xF8A8);
    if (i != 0 && i != -5) {
        report (RPT_ERR, "Unable to find i2500 VFD display on USB bus. Aborting");
        return -1;
    }

    // Allocate our framebuffer
    p->framebuf = (unsigned char *) malloc(INTRA2NET_VFD_SCREENSIZE * 2 + INTRA2NET_VFD_PACKEDSIZE + 1);
    if (p->framebuf == NULL) {
        report(RPT_ERR, "%s: unable to allocate framebuffer", drvthis->name);
        i2500vfd_close (drvthis);
        return -1;
    }

    // Fade out (set brightness to zero)
    c = 4|64;
    ftdi_write_data (&p->ftdi, &c, 1);
    c = 0|64;
    ftdi_write_data (&p->ftdi, &c, 1);
    sleep (1);

    // Blank display
    c = 2|64;
    ftdi_write_data (&p->ftdi, &c, 1);

    // Bring voltage up again
    c = 4|64;
    ftdi_write_data (&p->ftdi, &c, 1);
    c = 63|64;
    ftdi_write_data (&p->ftdi, &c, 1);

    // Flip to blank page
    c = 64;
    ftdi_write_data (&p->ftdi, &c, 1);
    sleep (1);

    // Clear internal screen
    i2500vfd_clear(drvthis);

    // Unblank display
    c = 3|64;
    ftdi_write_data (&p->ftdi, &c, 1);

    report(RPT_DEBUG, "%s: init() done", drvthis->name);
    return 0;
}

/////////////////////////////////////////////////////////////////
// Frees the frambuffer and exits the driver.
//
MODULE_EXPORT void
i2500vfd_close (Driver *drvthis)
{
    PrivateData *p = drvthis->private_data;

    if (p) {
        ftdi_usb_close (&p->ftdi);
        ftdi_deinit(&p->ftdi);

        if (p->framebuf)
            free(p->framebuf);

        free(p);
    }
    drvthis->store_private_ptr(drvthis, NULL);
}

/////////////////////////////////////////////////////////////////
// Returns the display width
//
MODULE_EXPORT int
i2500vfd_width (Driver *drvthis)
{
    return WIDTH;
}

/////////////////////////////////////////////////////////////////
// Returns the display height
//
MODULE_EXPORT int
i2500vfd_height (Driver *drvthis)
{
    return HEIGHT;
}

/////////////////////////////////////////////////////////////////
// Returns the display width
//
MODULE_EXPORT int
i2500vfd_cellwidth (Driver *drvthis)
{
    return CELLWIDTH;
}

/////////////////////////////////////////////////////////////////
// Returns the display height
//
MODULE_EXPORT int
i2500vfd_cellheight (Driver *drvthis)
{
    return CELLHEIGHT;
}

/////////////////////////////////////////////////////////////////
// Clears the LCD screen
//
MODULE_EXPORT void
i2500vfd_clear (Driver *drvthis)
{
    PrivateData *p = drvthis->private_data;

    memset(p->framebuf, 0, INTRA2NET_VFD_SCREENSIZE);
    p->changed = 1;
}

/////////////////////////////////////////////////////////////////
//
// Flushes all output to the VFD...
//
MODULE_EXPORT void
i2500vfd_flush (Driver *drvthis)
{
    PrivateData *p = drvthis->private_data;

    if (!p->changed)
        return;

    // Grayscales are currently discarded.
    // Could be useful for antialised fonts
    int packed_begin = INTRA2NET_VFD_SCREENSIZE*2;
    int packed_offset = packed_begin, offset = 0, pixpos = 0, xpos = 0;
    memset (p->framebuf+packed_begin, 0, INTRA2NET_VFD_PACKEDSIZE);
    while (offset != INTRA2NET_VFD_SCREENSIZE) {
        if (p->framebuf[offset] != 0) {
            switch(pixpos) {
                case 0:
                    p->framebuf[packed_offset] = 3;
                    break;
                case 1:
                    p->framebuf[packed_offset] |= 3<<2;
                    break;
                case 2:
                    p->framebuf[packed_offset] |= 3<<4;
                    break;
            }
        }

        xpos++;
        offset++;
        pixpos++;
        if (pixpos == 3) {
            pixpos = 0;
            packed_offset++;
        }
        // Special: The display is organized in 3-pixel columns, but the last column got only 2 pixels
        if (xpos == 140) {
            packed_offset++;
            pixpos = 0;
            xpos = 0;
        }
    }

    // Page flip command
    p->framebuf[INTRA2NET_VFD_SCREENSIZE * 2 + INTRA2NET_VFD_PACKEDSIZE] = 64;

    // Write data to display
    ftdi_write_data (&p->ftdi, p->framebuf+packed_begin, INTRA2NET_VFD_PACKEDSIZE+1);

    p->changed = 0;
}

/////////////////////////////////////////////////////////////////
// Prints a string on the lc display, at position (x,y).  The
// upper-left is (1,1), and the lower right should be (20,4).
//
MODULE_EXPORT void
i2500vfd_string (Driver *drvthis, int x, int y, const char string[])
{
    int i;
    x--;            // Convert 1-based coords to 0-based
    y--;

    for (i = 0; string[i]; i++)
        drawchar2fb (drvthis, x + i, y, string[i]);
}

/////////////////////////////////////////////////////////////////
// Writes  char c at position x,y into the framebuffer.
// x and y are 1-based textmode coordinates.
//
MODULE_EXPORT void
i2500vfd_chr (Driver *drvthis, int x, int y, char c)
{
    y--;
    x--;
    drawchar2fb(drvthis, x, y, c);
}

/////////////////////////////////////////////////////////////////
// Changes the font of character n to a pattern given by *dat.
// HD44780 Controllers only posses 8 programmable chars. But
// we store the fontmap completely in RAM, so every character
// can be altered. !Important: Characters have to be redrawn
// by drawchar2fb() to show their new shape. Because we use
// a non-standard 6x8 font a *dat not calculated from
// width and height will fail.
//
MODULE_EXPORT void
i2500vfd_set_char (Driver *drvthis, int n, char *dat)
{
    int row;
    unsigned char mask = (1 << CELLWIDTH) - 1;

    if (n < 0 || n > 255)
        return;
    if (!dat)
        return;

    for (row = 0; row < CELLHEIGHT; row++) {
        glcd_iso8859_1[n][row] = dat[row] & mask;
    }
}

/////////////////////////////////////////////////////////////////
// Draws a vertical from the bottom up to the last 3 rows of the
// framebuffer at 1-based position x. len is given in pixels.
//
MODULE_EXPORT void
i2500vfd_vbar(Driver *drvthis, int x, int y, int len, int promille, int pattern)
{
    PrivateData *p = drvthis->private_data;
    unsigned int offset;
    int i, j, pixels;

    x--;
    // don't do y-- as we draw bottom up

    if (x < 0 || y < 1 || x >= WIDTH || y > HEIGHT || len > HEIGHT) {
        report(RPT_DEBUG, "%s: [vbar ERROR] x: %d, y: %d, len: %d", drvthis->name, x, y, len);
        return;
    }

    offset = INTRA2NET_VFD_XSHIFT + x*CELLWIDTH + y*INTRA2NET_VFD_XSIZE*CELLHEIGHT;
    pixels = len*CELLHEIGHT*promille/1000;

    // printf("[vbar] x: %d, y: %d, len: %d, offset: %d, pixels: %d\n", x, y, len, offset, pixels);
    for (i = 0; i < pixels; i++) {
        for (j = 0; j < CELLWIDTH; j++) {
            p->framebuf[offset+j] = 1;
        }
        // go to next y-line
        offset -= INTRA2NET_VFD_XSIZE;
    }

    p->changed = 1;
}

/////////////////////////////////////////////////////////////////
// Draws a horizontal bar from left to right at 1-based position
// x,y into the framebuffer. len is given in characters;
//
MODULE_EXPORT void
i2500vfd_hbar(Driver *drvthis, int x, int y, int len, int promille, int pattern)
{
    PrivateData *p = drvthis->private_data;
    unsigned int offset;
    int i, j, pixels;

    x--;
    y--;

    if (y < 0 || y >= HEIGHT || x < 0 || len < 0 || x + len > WIDTH) {
        return;
    }

    offset = INTRA2NET_VFD_XSHIFT + 2 + x*CELLWIDTH + (y*CELLHEIGHT+1)*INTRA2NET_VFD_XSIZE;

    // calculate length of bar
    pixels = len*CELLWIDTH*promille/1000;

    for (i = 0; i < CELLHEIGHT-1; i++) {
        for (j = 0; j < pixels; j++) {
            p->framebuf[offset+j] = 1;
        }
        // go to next y-line
        offset += INTRA2NET_VFD_XSIZE;
    }

    p->changed = 1;
}

/////////////////////////////////////////////////////////////////
// Gets an icon character from the font definition and calls
// chr() to draw it.
//
MODULE_EXPORT int
i2500vfd_icon (Driver *drvthis, int x, int y, int icon)
{
    int icon_char;

    if ((icon_char = glcd_icon5x8(icon)) != -1) {
        i2500vfd_chr(drvthis, x, y, icon_char);
        return 0;
    }
    return -1;
}