Files
lcdproc/server/drivers/serialVFD.c
T

826 lines
23 KiB
C

/** \file server/drivers/serialVFD.c
* LCDd \c serialVFD driver for various serial VFD dvices.
*/
/*
Copyright (C) 2006 Stefan Herdler
This driver is based on wirz-sli.c, hd44780.c, drv_base.c and NoritakeVFD
driver.
It may contain parts of other drivers of this package too.
2006-05-16 Version 0.3: everything should work (not all hardware tested!)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
The driver should operate most of NEC, Futaba and Noritake 7x5 dot VFDs with
serial(rs232) and/or parallel interface. See /docs/lcdproc-user/serialvfd-howto.html
for further information
List of driver entry point:
init Implemented.
close Implemented.
width Implemented.
height Implemented.
clear Implemented by space filling no custom char info.
flush Implemented.
string Implemented.
chr Implemented.
vbar Implemented.
hbar Implemented.
num Implemented.
heartbeat Implemented.
icon Implemented.
cursor NOT IMPLEMENTED: Is it really used?
set_char Implemented.
get_free_chars Implemented.
cellwidth Implemented.
cellheight Implemented.
get_contrast Not implemented, no software control.
set_contrast Not implemented, no software control.
get_brightness Implemented.
set_brightness Implemented.
backlight Implemented.
output Not implemented.
get_key Not implemented, no keys.
get_info Implemented.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "lcd.h"
#include "serialVFD.h"
#include "report.h"
#include "lcd_lib.h"
#include "adv_bignum.h"
#define DEFAULT_OFF_BRIGHTNESS 300
#define DEFAULT_ON_BRIGHTNESS 1000
#define pos1_cursor 4 //moves cursor to top left character.
#define mv_cursor 5 //moves cursor to position specified by the next byte.
#define reset 6 //reset
#define init_cmds 7 //commands needed to initialize the display.
#define set_user_char 8 //set user character.
#define hor_tab 9 //moves cursor 1 chr right
#define next_line 10 //moves cursor to the first character of the next line (= LF + CR)
#define LPTPORT 0x378
/* 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 = "serialVFD_";
/* Internal functions */
static void serialVFD_init_vbar (Driver *drvthis);
static void serialVFD_init_hbar (Driver *drvthis);
static void serialVFD_put_char (Driver *drvthis, int n);
static void serialVFD_hw_write (Driver *drvthis, int i);
// Opens com port and sets baud correctly...
//
MODULE_EXPORT int
serialVFD_init (Driver *drvthis)
{
int tmp, w, h;
char size[200] = DEFAULT_SIZE;
PrivateData *p;
/* 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;
/* Initialize the PrivateData structure */
p->cellwidth = DEFAULT_CELL_WIDTH;
p->cellheight = DEFAULT_CELL_HEIGHT;
p->ccmode = CCMODE_STANDARD;
p->ISO_8859_1 = 1;
p->refresh_timer = 480;
p->hw_brightness = 0;
p->para_wait = DEFAULT_PARA_WAIT;
p->hw_cmd[next_line][0] = 0;
debug(RPT_INFO, "%s(%p)", __FUNCTION__, drvthis);
/* Read config file */
p->use_parallel = drvthis->config_get_bool(drvthis->name, "use_parallel", 0, 0);
/* Which device should be used */
strncpy(p->device, drvthis->config_get_string(drvthis->name, "Device", 0, DEFAULT_DEVICE), sizeof(p->device));
p->device[sizeof(p->device)-1] = '\0';
report(RPT_INFO, "%s: using Device %s", drvthis->name, p->device);
if (p->use_parallel) {
p->port = drvthis->config_get_int(drvthis->name, "port", 0, LPTPORT);
}
else {
/* Which speed */
tmp = drvthis->config_get_int(drvthis->name, "Speed", 0, DEFAULT_SPEED);
if ((tmp != 1200) && (tmp != 2400) && (tmp != 9600) && (tmp != 19200) && (tmp != 115200)) {
report(RPT_WARNING, "%s: Speed must be 1200, 2400, 9600, 19200 or 115200. Using default %d",
drvthis->name, DEFAULT_SPEED);
tmp = DEFAULT_SPEED;
}
if (tmp == 1200) p->speed = B1200;
else if (tmp == 2400) p->speed = B2400;
else if (tmp == 9600) p->speed = B9600;
else if (tmp == 19200) p->speed = B19200;
else if (tmp == 115200) p->speed = B115200;
}
// report(RPT_ERR, "%s: Port: %X", __FUNCTION__, p->port, strerror(errno));
/* Which size */
strncpy(size, drvthis->config_get_string(drvthis->name, "Size", 0, DEFAULT_SIZE), sizeof(size));
size[sizeof(size)-1] = '\0';
if ((sscanf(size, "%dx%d", &w, &h) != 2)
|| (w <= 0) || (w > LCD_MAX_WIDTH)
|| (h <= 0) || (h > LCD_MAX_HEIGHT)) {
report(RPT_WARNING, "%s: cannot parse size: %s; using default %s",
drvthis->name, size, DEFAULT_SIZE);
sscanf(DEFAULT_SIZE, "%dx%d", &w, &h);
}
p->width = w;
p->height = h;
/* Which backlight brightness */
tmp = drvthis->config_get_int(drvthis->name, "Brightness", 0, DEFAULT_ON_BRIGHTNESS);
debug(RPT_INFO, "%s: Brightness (in config) is '%d'", __FUNCTION__, tmp);
if ((tmp < 0) || (tmp > 1000)) {
report(RPT_WARNING, "%s: Brightness must be between 0 and 1000; using default %d",
drvthis->name, DEFAULT_ON_BRIGHTNESS);
tmp = DEFAULT_ON_BRIGHTNESS;
}
p->on_brightness = tmp;
/* Which backlight-off "brightness" */
tmp = drvthis->config_get_int(drvthis->name, "OffBrightness", 0, DEFAULT_OFF_BRIGHTNESS);
debug(RPT_INFO, "%s: OffBrightness (in config) is '%d'", __FUNCTION__, tmp);
if ((tmp < 0) || (tmp > 1000)) {
report(RPT_WARNING, "%s: OffBrightness must be between 0 and 1000; using default %d",
drvthis->name, DEFAULT_OFF_BRIGHTNESS);
tmp = DEFAULT_OFF_BRIGHTNESS;
}
p->off_brightness = tmp;
/* ISO 8859 1 */
p->ISO_8859_1 = drvthis->config_get_bool(drvthis->name, "ISO_8859_1", 0, 1);
/* Which displaytype */
p->display_type = drvthis->config_get_int(drvthis->name, "Type", 0, DEFAULT_DISPLAYTYPE);
/* Number of custom characters */
tmp = drvthis->config_get_int(drvthis->name, "Custom-Characters", 0, -83);
if ((tmp < 0) || (tmp > 99)) {
report(RPT_WARNING, "%s: The number of Custom-Characters must be between 0 and 99. Using default",
drvthis->name, 0);
tmp = -83;
}
p->customchars = tmp;
// report(RPT_ERR, "%s: Port: %X", __FUNCTION__, p->port, strerror(errno));
// Do connection type specific io-port init
if (Port_Function[p->use_parallel].init_fkt(drvthis) == -1) {
report(RPT_ERR, "%s: unable to initialize io-port", drvthis->name);
return -1;
}
// setup frame buffer and backing store
/* make sure the frame buffer is there... */
p->framebuf = (unsigned char *) malloc(p->width * p->height);
if (p->framebuf == NULL) {
report(RPT_ERR, "%s: unable to create framebuffer", drvthis->name);
return -1;
}
memset(p->framebuf, ' ', p->width * p->height);
/* make sure the framebuffer backing store is there... */
p->backingstore = (unsigned char *) malloc(p->width * p->height);
if (p->backingstore == NULL) {
report(RPT_ERR, "%s: unable to create framebuffer backing store", drvthis->name);
return -1;
}
memset(p->backingstore, 0, p->width * p->height);
//setup displayspecific data
memset(p->usr_chr_mapping, 0, 31);
memset(p->usr_chr_load_mapping, 0, 31);
if (serialVFD_load_display_data(drvthis) != 0) {
report(RPT_WARNING, "%s: Type %d not defined; using default %d",
drvthis->name, p->display_type, DEFAULT_DISPLAYTYPE);
p->display_type = DEFAULT_DISPLAYTYPE;
if (serialVFD_load_display_data(drvthis) != 0) {
report(RPT_ERR, "%s: unable to load display_data", drvthis->name);
return -1;
}
}
/* parallel port wait */
tmp = p->para_wait;
p->para_wait = drvthis->config_get_int(drvthis->name, "PortWait", 0, p->para_wait);
if ((p->usr_chr_load_mapping[0] == 0) && (p->usr_chr_load_mapping[1] == 0)){ //this should not happen if usr_chr_load_mapping had been set
memcpy(p->usr_chr_load_mapping, p->usr_chr_mapping, 31);
}
// report(RPT_ERR, "%s: Port: %X", drvthis->name, p->port, strerror(errno));
//initialise display
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[reset][1],p->hw_cmd[reset][0]);
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[init_cmds][1],p->hw_cmd[init_cmds][0]);
serialVFD_backlight(drvthis, 1);
report(RPT_DEBUG, "%s: init() done", drvthis->name);
return 0;
}
/*
* Retrieves brightness (in promille)
*/
MODULE_EXPORT int
serialVFD_get_brightness(Driver *drvthis, int state)
{
PrivateData *p = drvthis->private_data;
return (state == BACKLIGHT_ON) ? p->on_brightness : p->off_brightness;
}
/*
* Sets on/off brightness (in promille)
*/
MODULE_EXPORT void
serialVFD_set_brightness(Driver *drvthis, int state, int promille)
{
PrivateData *p = drvthis->private_data;
/* Check it */
if (promille < 0 || promille > 1000)
return;
/* store the software value since there is not get */
if (state == BACKLIGHT_ON) {
p->on_brightness = promille;
}
else {
p->off_brightness = promille;
}
}
/*
* Sets the backlight on or off.
* The hardware support any value between 0 and 100.
*/
MODULE_EXPORT void
serialVFD_backlight (Driver *drvthis, int on)
{
PrivateData *p = drvthis->private_data;
int hardware_value = (on == BACKLIGHT_ON)
? p->on_brightness
: p->off_brightness;
// map range [0, 1000] -> [0, 4] that the hardware understands
//(4 steps 0-250, 251-500, 501-750, 751-1000)
hardware_value /= 251;
if (hardware_value != p->hw_brightness) {
p->hw_brightness = hardware_value;
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[p->hw_brightness][1],\
p->hw_cmd[p->hw_brightness][0]);
}
}
/////////////////////////////////////////////////////////////////
// Draws a vertical bar...
//
MODULE_EXPORT void
serialVFD_vbar (Driver *drvthis, int x, int y, int len, int promille, int options)
{
PrivateData *p = drvthis->private_data;
if (p->customchars >= p->cellheight || p->predefined_vbar == 1) {
serialVFD_init_vbar(drvthis);
lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, p->vbar_cc_offset);
}
else {
lib_vbar_static(drvthis, x, y, len, promille, options, 2, 0x5E);
}
}
/////////////////////////////////////////////////////////////////
// Draws a horizontal bar to the right.
//
MODULE_EXPORT void
serialVFD_hbar (Driver *drvthis, int x, int y, int len, int promille, int options)
{
PrivateData *p = drvthis->private_data;
if (p->customchars >= p->cellwidth || p->predefined_hbar == 1) {
serialVFD_init_hbar(drvthis);
lib_hbar_static(drvthis, x, y, len, promille, options, p->cellwidth, p->hbar_cc_offset);
}
else {
lib_hbar_static(drvthis, x, y, len, promille, options, 2, 0x2C);
}
}
/////////////////////////////////////////////////////////////////
// Sets a custom character from 0-7...
//
// For input, value 1 mean "on" and 0 is "off".
//
// The input is just an array of characters...
//
MODULE_EXPORT void
serialVFD_set_char (Driver *drvthis, int n, unsigned char *dat)
{ //set char in p->custom_char
PrivateData *p = drvthis->private_data;
unsigned int byte, bit;
if (n < 0 || n > p->customchars-1)
return;
if (!dat)
return;
for (byte = 0; byte < p->usr_chr_dot_assignment[0]; byte++) {
int letter = 0;
for (bit = 0; bit < 8; bit++) {
int pos = (int) p->usr_chr_dot_assignment[bit+8*byte+1];
if (pos > 0) {
int posbyte = (pos-1) / 5;
int posbit = 4 - ((pos-1) % 5);
letter |= ((dat[posbyte] >> posbit) & 1) << bit;
}
}
p->custom_char[n][byte] = letter;
}
}
static void
serialVFD_put_char (Driver *drvthis, int n)
{ // put userchar in display
PrivateData *p = drvthis->private_data;
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[set_user_char][1],\
p->hw_cmd[set_user_char][0]);// substitute and select Character to overwrite
Port_Function[p->use_parallel].write_fkt(drvthis, (unsigned char *) &p->usr_chr_load_mapping[n], 1);
Port_Function[p->use_parallel].write_fkt(drvthis, &p->custom_char[n][0], p->usr_chr_dot_assignment[0]);// overwrite selected Character
}
/////////////////////////////////////////////////////////////
// Blasts a single frame onscreen, to the lcd...
//
// Input is a character array, sized serialVFD->width*serialVFD->height
//
MODULE_EXPORT void
serialVFD_flush (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
int i, j, last_chr = -10;
char custom_char_changed[32]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
for (i = 0; i < p->customchars; i++) {
for (j = 0; j < p->usr_chr_dot_assignment[0]; j++) {
if (p->custom_char[i][j] != p->custom_char_store[i][j]) {
custom_char_changed[i] = 1;
// report(RPT_ERR, "%s: Char: %X %i", __FUNCTION__, i, j, strerror(errno));
}
p->custom_char_store[i][j] = p->custom_char[i][j];
}
}
if (p->refresh_timer > 500) { // Do a full refresh every 500 refreshs.
// With this it is possible to switch the display on and off while LCDd is running
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[init_cmds][1],p->hw_cmd[init_cmds][0]);
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[p->hw_brightness][1],\
p->hw_cmd[p->hw_brightness][0]); // restore brightness
memset(p->backingstore, 0, p->width * p->height); // clear Backing-store
for (i = 0; i < p->customchars; i++) // refresh all customcharacters
custom_char_changed[i] = 1;
p->refresh_timer = 0;
}
p->refresh_timer++;
if (p->display_type == 1) { // KD Rev 2.1 only
if (custom_char_changed[p->last_custom])
p->last_custom = -10;
}
else { // other Displays
for (i = 0; i < p->customchars; i++) // set customcharacters
if (custom_char_changed[i])
serialVFD_put_char(drvthis, i);
}
if (p->hw_cmd[next_line][0] == 0) { // normal mode Display
if (p->hw_cmd[mv_cursor][0] == 0) { // Workaround for Displays that doesn't support mv_cursor command
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[pos1_cursor][1], p->hw_cmd[pos1_cursor][0]);
last_chr = -1;
}
for (i = 0; i < (p->height * p->width); i++) {
/* Backing-store implementation. If it's already
* on the screen, don't put it there again
*/
if ((p->framebuf[i] != p->backingstore[i]) || (p->hw_cmd[hor_tab][0] == 0) ||
((p->framebuf[i] <= 30) && (custom_char_changed[(int)p->framebuf[i]] != 0))) {
if (last_chr < i-1) { // if not last char written cursor has to be moved.
if (((p->hw_cmd[hor_tab][0] * (i-1-last_chr)) > (p->hw_cmd[mv_cursor][0]+1)) && (p->hw_cmd[mv_cursor][0] != 0)) {
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[mv_cursor][1],
p->hw_cmd[mv_cursor][0]);
Port_Function[p->use_parallel].write_fkt(drvthis, (unsigned char *) &i, 1);
//report(RPT_WARNING, "%s: move %d", drvthis->name, i);
}
else {
for (j = last_chr; j < (i-1); j++)
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[hor_tab][1], p->hw_cmd[hor_tab][0]);
//report(RPT_WARNING, "%s: TAB %d", drvthis->name, j-last_chr);
}
}
serialVFD_hw_write(drvthis, i);
last_chr = i;
}
}
}
else { // line mode Display (partitially borrowed from serialPOS.c)
for (j = 0; j < p->height; j++) {
// set pointers to start of the line in frame buffer & backing store
unsigned char *sp = p->framebuf + (j * p->width);
unsigned char *sq = p->backingstore + (j * p->width);
if (j == 0) {
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[pos1_cursor][1], p->hw_cmd[pos1_cursor][0]);
}
else {
Port_Function[p->use_parallel].write_fkt(drvthis, &p->hw_cmd[next_line][1], p->hw_cmd[next_line][0]);
}
// skip over identical lines
if ( memcmp(sp, sq, p->width) == 0) {
/* The lines are the same. */
continue;
}
for (i = 0; i < p->width; i++) {
serialVFD_hw_write(drvthis, (i + (j * p->width)));
}
last_chr = 10;
}
}
if (last_chr >= 0) { // update backingstore if something changed
memcpy(p->backingstore, p->framebuf, p->height * p->width);
//report(RPT_WARNING, "%s: memcpy", drvthis->name);
}
}
static void
serialVFD_hw_write (Driver *drvthis, int i)
{ // finally write character/usercharacter on the display
PrivateData *p = drvthis->private_data;
if (p->framebuf[i] <= 30) { // custom character
if (p->display_type == 1) { // KD Rev 2.1 only
if (p->last_custom != p->framebuf[i]) {
// substitute and select character to overwrite (237)
Port_Function[p->use_parallel].write_fkt(drvthis, (unsigned char *)"\x1A\xDB", 2);
// overwrite selected character
Port_Function[p->use_parallel].write_fkt(drvthis, &p->custom_char[(int)p->framebuf[i]][0], 7);
}
Port_Function[p->use_parallel].write_fkt(drvthis, (unsigned char *)"\xDB", 1); // write character
p->last_custom = p->framebuf[i];
}
else { // all other displays
Port_Function[p->use_parallel].write_fkt(drvthis, (unsigned char *) &p->usr_chr_mapping[(int)p->framebuf[i]], 1);
}
}
else if ((p->framebuf[i] == 127) || ((p->framebuf[i] > 127) && (p->ISO_8859_1 != 0))) { // ISO_8859_1 translation for 129 ... 255
Port_Function[p->use_parallel].write_fkt(drvthis, &p->charmap[p->framebuf[i] - 127], 1);
}
else {
Port_Function[p->use_parallel].write_fkt(drvthis, &p->framebuf[i], 1);
}
}
MODULE_EXPORT void
serialVFD_num(Driver * drvthis, int x, int num)
{
PrivateData *p = drvthis->private_data;
int do_init = 0;
if (p->ccmode != CCMODE_BIGNUM) { // Are the customcharacters set up correctly? If not:
do_init = 1; // Lib_adv_bignum has to set the customcharacters.
p->ccmode = CCMODE_BIGNUM; // Switch customcharactermode to bignum.
}
// Lib_adv_bignum does everything needed to show the bignumbers.
lib_adv_bignum(drvthis, x, num, 0, do_init);
}
/*
* Places an icon on screen
*/
MODULE_EXPORT int
serialVFD_icon (Driver *drvthis, int x, int y, int icon)
{
PrivateData *p = drvthis->private_data;
static unsigned char heart_open[] =
{ b__XXXXX,
b__X_X_X,
b_______,
b_______,
b_______,
b__X___X,
b__XX_XX,
b__XXXXX };
static unsigned char heart_filled[] =
{ b__XXXXX,
b__X_X_X,
b___X_X_,
b___XXX_,
b___XXX_,
b__X_X_X,
b__XX_XX,
b__XXXXX };
// Yes we know, this is a VERY BAD implementation :-)
switch (icon) {
case ICON_BLOCK_FILLED:
serialVFD_chr(drvthis, x, y, 127);
break;
case ICON_HEART_FILLED:
if (p->customchars > 0) {
p->ccmode = CCMODE_STANDARD;
serialVFD_set_char(drvthis, 0, heart_filled);
serialVFD_chr(drvthis, x, y, 0);
}
else
serialVFD_icon(drvthis, x, y, ICON_BLOCK_FILLED);
break;
case ICON_HEART_OPEN:
if (p->customchars > 0) {
p->ccmode = CCMODE_STANDARD;
serialVFD_set_char(drvthis, 0, heart_open);
serialVFD_chr(drvthis, x, y, 0);
}
else
serialVFD_chr(drvthis, x, y, 0x23);
break;
default:
return -1; // Let the core do other icons
}
return 0;
}
/////////////////////////////////////////////////////////////////
// Sets up for vertical bars. Call before serialVFD->vbar()
//
static void
serialVFD_init_vbar (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
if (p->ccmode != CCMODE_VBAR) {
unsigned char vBar[p->cellheight];
int i;
p->ccmode = CCMODE_VBAR;
memset(vBar, 0x00, sizeof(vBar));
for (i = 1; i < p->cellheight; i++) {
// add pixel line per pixel line ...
vBar[p->cellheight - i] = 0xFF;
serialVFD_set_char(drvthis, i, vBar);
}
}
}
/////////////////////////////////////////////////////////////////
// Inits horizontal bars...
//
static void
serialVFD_init_hbar (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
if (p->ccmode != CCMODE_HBAR) {
unsigned char hBar[p->cellheight];
int i;
p->ccmode = CCMODE_HBAR;
for (i = 1; i < p->cellwidth; i++) {
// fill pixel columns from left to right.
memset(hBar, 0xFF & ~((1 << (p->cellwidth - i)) - 1), sizeof(hBar));
serialVFD_set_char(drvthis, i, hBar);
}
}
}
/////////////////////////////////////////////////////////////////
// Clears the LCD screen
//
MODULE_EXPORT void
serialVFD_clear (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
memset(p->framebuf, ' ', p->width * p->height);
}
/////////////////////////////////////////////////////////////////
// Prints a string on the lcd display, at position (x,y). The
// upper-left is (1,1), and the lower right should be (20,4).
//
MODULE_EXPORT void
serialVFD_string (Driver *drvthis, int x, int y, const char string[])
{
PrivateData *p = drvthis->private_data;
int i;
x--;
y--;
for (i = 0; string[i] != '\0'; i++) {
// Check for buffer overflows...
if ((y * p->width) + x + i > (p->width * p->height))
break;
p->framebuf[(y * p->width) + x + i] = string[i];
}
}
/////////////////////////////////////////////////////////////////
// Clean-up
//
MODULE_EXPORT void
serialVFD_close (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
if (p != NULL) {
Port_Function[p->use_parallel].close_fkt(drvthis);
if (p->framebuf)
free(p->framebuf);
if (p->backingstore)
free(p->backingstore);
free(p);
}
drvthis->store_private_ptr(drvthis, NULL);
}
/*
* Returns the display width in characters
*/
MODULE_EXPORT int
serialVFD_width (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->width;
}
/*
* Returns the display height in characters
*/
MODULE_EXPORT int
serialVFD_height (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->height;
}
/////////////////////////////////////////////////////////////////
// Returns the maximum number of custom char slots (not how many
// are free at a moment, maybe this isn't even needed...
//
MODULE_EXPORT int
serialVFD_get_free_chars (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->customchars;
}
/*
* Returns the width of a character in pixels
*/
MODULE_EXPORT int
serialVFD_cellwidth (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->cellwidth;
}
/*
* Returns the height of a character in pixels
*/
MODULE_EXPORT int
serialVFD_cellheight (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->cellheight;
}
/////////////////////////////////////////////////////////////////
// Prints a character on the lcd display, at position (x,y). The
// upper-left is (1,1), and the lower right should be (20,4).
//
MODULE_EXPORT void
serialVFD_chr (Driver *drvthis, int x, int y, char c)
{
PrivateData *p = drvthis->private_data;
if ((x > p->width) || (y > p->height))
return;
y--;
x--;
p->framebuf[(y * p->width) + x ] = c;
}
/////////////////////////////////////////////////////////////////
// provides some info about this driver
//
MODULE_EXPORT const char *
serialVFD_get_info (Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
strcpy(p->info, "Driver for many serialVFDs from NEC(all FIPC based), Noritake, Futaba and the \"KD Rev2.1\"VFD");
return p->info;
}