Files
lcdproc/server/drivers/i2500vfd.c
T
mmdolze 66023939a1 Cleanup and documentation updates:
- Remove shared/str.h where not used.
- Remove shared/debug.h from all files as it has been completely replaced
  by shared/report.h (it will be removed later).
- Add more doxygen comments to shared/* and clients/lcdproc/*.
- Include static functions in doxygen output.
2010-02-02 23:20:29 +00:00

468 lines
13 KiB
C

/** \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. //
//////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "i2500vfdfm.h"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "lcd.h"
#include "i2500vfd.h"
#include "report.h"
#include <ftdi.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 ((i2500vfd_fontmap[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, col;
if (n < 0 || n > 255)
return;
if (!dat)
return;
for (row = 0; row < CELLHEIGHT; row++) {
int i = 0;
for (col = 0; col < CELLWIDTH; col++)
i = (i << 1) | (dat[(row * CELLWIDTH) + col] > 0);
i2500vfd_fontmap[n][row] = i;
}
}
/////////////////////////////////////////////////////////////////
// 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*INTRA2NET_VFD_XSIZE*CELLHEIGHT;
// 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;
}
/////////////////////////////////////////////////////////////////
// Reprogrammes character dest to contain an icon given by
// which. Calls set_char() to do this.
//
MODULE_EXPORT int
i2500vfd_icon (Driver *drvthis, int x, int y, int icon)
{
static char heart_open[] = {
1, 1, 1, 1, 1,
1, 0, 1, 0, 1,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 0, 0, 0, 1,
1, 1, 0, 1, 1,
1, 1, 1, 1, 1 };
static char heart_filled[] = {
1, 1, 1, 1, 1,
1, 0, 1, 0, 1,
0, 1, 0, 1, 0,
0, 1, 1, 1, 0,
0, 1, 1, 1, 0,
1, 0, 1, 0, 1,
1, 1, 0, 1, 1,
1, 1, 1, 1, 1 };
switch (icon) {
case ICON_BLOCK_FILLED:
i2500vfd_chr(drvthis, x, y, 255);
break;
case ICON_HEART_FILLED:
i2500vfd_set_char(drvthis, 0, heart_filled);
i2500vfd_chr(drvthis, x, y, 0);
break;
case ICON_HEART_OPEN:
i2500vfd_set_char(drvthis, 0, heart_open);
i2500vfd_chr(drvthis, x, y, 0);
break;
default:
return -1;
}
return 0;
}