Add rawserial driver (by C. Wolf).

This commit is contained in:
mmdolze
2014-03-05 18:46:39 +00:00
parent a5cc44d51a
commit d61430decd
11 changed files with 627 additions and 13 deletions
+1
View File
@@ -21,6 +21,7 @@ v0.5dev (ongoing development)
+ hd44780: Added 'spi' connection type (S. Dawson)
* hd55780/serial: Change data escape handling (G. Smith)
+ hd44780: New connection type 'pifacecad' (T. Preston)
+ new driver: rawserial - dump framebuffer to serial port (C. Wolf)
v0.5.6
- Remove deprecated CFontz633 driver. Use CFontzPacket with Model=633 instead!
+27 -6
View File
@@ -44,12 +44,12 @@ DriverPath=server/drivers/
# driver specific section.
#
# The following drivers are supported:
# bayrad, CFontz, CFontzPacket, curses, CwLnx, ea65,
# EyeboxOne, g15, glcd, glcdlib, glk, hd44780, icp_a106, imon, imonlcd,
# IOWarrior, irman, joy, lb216, lcdm001, lcterm, lirc, lis, MD8800,
# mdm166a, ms6931, mtc_s16209x, MtxOrb, mx5000, NoritakeVFD, picolcd,
# pyramid, sdeclcd, sed1330, sed1520, serialPOS, serialVFD, shuttleVFD, sli,
# stv5730, svga, t6963, text, tyan, ula200, vlsys_m428, xosd
# bayrad, CFontz, CFontzPacket, curses, CwLnx, ea65, EyeboxOne, g15, glcd,
# glcdlib, glk, hd44780, icp_a106, imon, imonlcd,, IOWarrior, irman, joy,
# lb216, lcdm001, lcterm, lirc, lis, MD8800,, mdm166a, ms6931, mtc_s16209x,
# MtxOrb, mx5000, NoritakeVFD, picolcd,, pyramid, rawserial, sdeclcd,
# sed1330, sed1520, serialPOS, serialVFD, shuttleVFD, sli,, stv5730, svga,
# t6963, text, tyan, ula200, vlsys_m428, xosd
Driver=curses
# Tells the driver to bind to the given interface. [default: 127.0.0.1]
@@ -1056,6 +1056,27 @@ Device=/dev/ttyUSB0
## rawserial driver ##
[rawserial]
# Select the output device to use [default: /dev/cuaU0]
Device=/dev/ttyS0
# Serial port baudrate [default: 9600]
Speed=9600
# Specifies the size of the LCD. If this driver is loaded as a secondary driver
# it always adopts to the size of the primary driver. If loaded as the only
# (or primary) driver, the size can be set. [default: 40x4]
#Size=16x2
# How often to dump the LCD contents out the port, in Hertz (times per second)
# 1 = once per second, 4 is 4 times per second, 0.1 is once every 10 seconds.
# [default: 1; legal: 0.0005 - 10]
UpdateRate=1
## SDEC driver for Watchguard Firebox ##
[sdeclcd]
# No options
+10 -6
View File
@@ -8,21 +8,21 @@ AC_ARG_ENABLE(drivers,
[ --enable-drivers=<list> compile drivers for LCDs in <list>,]
[ which is a comma-separated list of drivers.]
[ Possible drivers are:]
[ bayrad,CFontz,CFontzPacket,curses,CwLnx,]
[ ea65,EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,]
[ bayrad,CFontz,CFontzPacket,curses,CwLnx,ea65,]
[ EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,]
[ icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,]
[ joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,]
[ ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,]
[ picolcd,pyramid,sdeclcd,sed1330,sed1520,serialPOS,]
[ serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,]
[ t6963,text,tyan,ula200,vlsys_m428,xosd]
[ picolcd,pyramid,rawserial,sdeclcd,sed1330,sed1520,]
[ serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,]
[ svga,t6963,text,tyan,ula200,vlsys_m428,xosd]
[ ]
[ 'all' compiles all drivers;]
[ 'all,!xxx,!yyy' de-selects previously selected drivers],
drivers="$enableval",
drivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,glk,lb216,lcdm001,MtxOrb,pyramid,text])
allDrivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,pyramid,sdeclcd,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,t6963,text,tyan,ula200,vlsys_m428,xosd]
allDrivers=[bayrad,CFontz,CFontzPacket,curses,CwLnx,ea65,EyeboxOne,g15,glcd,glcdlib,glk,hd44780,i2500vfd,icp_a106,imon,imonlcd,IOWarrior,irman,irtrans,joy,lb216,lcdm001,lcterm,lirc,lis,MD8800,mdm166a,ms6931,mtc_s16209x,MtxOrb,mx5000,NoritakeVFD,picolcd,pyramid,sdeclcd,sed1330,sed1520,serialPOS,serialVFD,shuttleVFD,sli,stv5730,SureElec,svga,t6963,text,tyan,ula200,vlsys_m428,xosd,rawserial]
if test "$debug" = yes; then
allDrivers=["${allDrivers},debug"]
fi
@@ -393,6 +393,10 @@ dnl else
DRIVERS="$DRIVERS NoritakeVFD${SO}"
actdrivers=["$actdrivers NoritakeVFD"]
;;
rawserial)
DRIVERS="$DRIVERS rawserial${SO}"
actdrivers=["$actdrivers rawserial"]
;;
picolcd)
if test "$enable_libusb" = yes ; then
DRIVERS="$DRIVERS picolcd${SO}"
+3
View File
@@ -274,6 +274,9 @@ LCD display on the Logitech MX5000 keyboard
.B NoritakeVFD
Noritake VFD Device CU20045SCPB-T28A
.TP
.B rawserial
Dumps the entire framebuffer to the serial port at a configurable rate.
.TP
.B picolcd
Mini-box.com USB LCD (PicoLCD 20x4 & picoLCD 20x2)
.TP
+1
View File
@@ -39,6 +39,7 @@ well as the configuration of LCDd.
&MtxOrb;
&mx5000;
&NoritakeVFD;
&rawserial;
&picolcd;
&pylcd;
&sdeclcd;
+1
View File
@@ -35,6 +35,7 @@ EXTRA_DIST = bayrad.docbook \
picolcd.docbook \
ppttrouble.docbook \
pylcd.docbook \
rawserial.docbook \
sdeclcd.docbook \
sed1330.docbook \
sed1520.docbook \
+134
View File
@@ -0,0 +1,134 @@
<sect1 id="howto">
<sect1info>
<author>
<firstname>Connor</firstname>
<surname>Wolf</surname>
</author>
</sect1info>
<!-- Only as a comment, as inline presentation is awful in Docbook 4.5
Imaginary Industries
imaginaryindustries.com
Email: lcdproc@imaginaryindustries.com
-->
<title>The *Really* Simple Serial interface (rawserial)</title>
<para>
This section talks about using LCDproc with custom hardware.
</para>
<para>
This driver is intended for using LCDproc with custom hardware. In the
original implementation, this was for a PFsense router with a homemade
arduino-style circuit that controlled analog panel meters to display network
traffic. It is intended for this sort of application, where the LCD control
characters emitted normally, as well as the partial screen drawing, make
parsing the serial stream a considerably complex affair, particularly on
small (generally 8 bit) microprocessors.
</para>
<sect2 id="features">
<title>Features</title>
<para>
The driver emulates a 40x4 display, and simply dumps the entire framebuffer
out the serial port at a rate configured in the config file (default of 1
Hz), post-pended with a single "\n" character. At 1 Hz, it therefore
generates 161 serial characters per second.
</para>
<para>
The maximum update-rate is limited by the internal update-rate of LCDproc,
which is currently 8 Hz, so speeds greater then 8 Hz will be simply limited
to 8 Hz. The update-rate is also granular at 1 second/8 time-intervals (125
ms), so interesting fractional update-rates will behave oddly. The
event-loop is rigidly deterministic, so fractional rates will *average* out
to the desired rate, but that will be achieved by dithering between nearby
available intervals.
</para>
<para>
The baud rate is configurable, with a default of 9600 baud. Baud rate will
have to be chosen with the required data-per-second in mind, since at 1 Hz,
there are 161 characters (40x4+1) per second, which requires that's a
theoretical minimum baud of 161*10 = 1610 baud. Higher frame-rates will
require higher baud rates.
</para>
</sect2>
<sect2 id="requirements">
<title>Requirements</title>
<para>
None. The only requirement is a serial port of some sort.
</para>
</sect2>
<sect2 id="config">
<title>Configuration in LCDd.conf</title>
<sect3 id="config-section">
<title>[rawserial]</title>
<variablelist>
<varlistentry>
<term>
<property>Device</property> =
<parameter><replaceable>DEVICE</replaceable></parameter>
</term>
<listitem><para>
Serial port to use. Default: <filename>/dev/cuaU0</filename>
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<property>Speed</property> =
<parameter><replaceable>NUMBER</replaceable></parameter>
</term>
<listitem><para>
Desired baud-rate. Possible values:
<literal>1200</literal>,
<literal>2400</literal>,
<literal>9600</literal> (default),
<literal>19200</literal>, and
<literal>115200</literal>.
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<property>Size</property> = &parameters.size;
</term>
<listitem><para>
Specifies the size of the LCD. If this driver is loaded as a secondary
driver it always adopts to the size of the primary driver. If loaded as
the only (or primary) driver, the size can be set.
Default: <literal>40x4</literal>.
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<property>UpdateRate</property> =
<parameter><replaceable>NUMBER</replaceable></parameter>
</term>
<listitem><para>
Desired update-rate in Hertz (e.g. updates per second). Fractional
values and values less than one are valid (e.g. 0.5). Legal values are
<literal>0.0005</literal> (equals 2000 seconds) - <literal>10</literal>,
with <literal>1.0 </literal> being the default.
</para></listitem>
</varlistentry>
</variablelist>
</sect3>
</sect2>
</sect1>
+1
View File
@@ -46,6 +46,7 @@
<!ENTITY MtxOrb SYSTEM "drivers/mtxorb.docbook">
<!ENTITY mx5000 SYSTEM "drivers/mx5000.docbook">
<!ENTITY NoritakeVFD SYSTEM "drivers/NoritakeVFD.docbook">
<!ENTITY rawserial SYSTEM "drivers/rawserial.docbook">
<!ENTITY picolcd SYSTEM "drivers/picolcd.docbook">
<!ENTITY pylcd SYSTEM "drivers/pylcd.docbook">
<!ENTITY sdeclcd SYSTEM "drivers/sdeclcd.docbook">
+2 -1
View File
@@ -23,7 +23,7 @@ AM_LDFLAGS = @LDSHARED@
lcdexecbindir = $(pkglibdir)
lcdexecbin_PROGRAMS = @DRIVERS@
EXTRA_PROGRAMS = bayrad CFontz CFontzPacket curses debug CwLnx ea65 EyeboxOne g15 glcd glcdlib glk hd44780 icp_a106 imon imonlcd IOWarrior irman joy lb216 lcdm001 lcterm lirc lis MD8800 mdm166a ms6931 mtc_s16209x MtxOrb mx5000 NoritakeVFD picolcd pyramid sdeclcd sed1330 sed1520 serialPOS serialVFD shuttleVFD stv5730 SureElec svga t6963 text tyan sli ula200 vlsys_m428 xosd i2500vfd irtrans
EXTRA_PROGRAMS = bayrad CFontz CFontzPacket curses CwLnx debug ea65 EyeboxOne g15 glcd glcdlib glk hd44780 i2500vfd icp_a106 imon imonlcd IOWarrior irman irtrans joy lb216 lcdm001 lcterm lirc lis MD8800 mdm166a ms6931 mtc_s16209x MtxOrb mx5000 NoritakeVFD picolcd pyramid rawserial sdeclcd sed1330 sed1520 serialPOS serialVFD shuttleVFD sli stv5730 SureElec svga t6963 text tyan ula200 vlsys_m428 xosd
noinst_LIBRARIES = libLCD.a libbignum.a
g15_CFLAGS = @LIBUSB_CFLAGS@ $(AM_CFLAGS)
@@ -116,6 +116,7 @@ mtc_s16209x_SOURCES = lcd.h lcd_lib.h mtc_s16209x.c mtc_s16209x.h report.h
MtxOrb_SOURCES = lcd.h lcd_lib.h MtxOrb.c MtxOrb.h report.h adv_bignum.h
mx5000_SOURCES = lcd.h mx5000.c mx5000.h report.h
NoritakeVFD_SOURCES = lcd.h lcd_lib.h NoritakeVFD.c NoritakeVFD.h report.h adv_bignum.h
rawserial_SOURCES = lcd.h rawserial.c rawserial.h
picolcd_SOURCES = lcd.h picolcd.h picolcd.c report.h
pyramid_SOURCES = lcd.h pylcd.c pylcd.h
sdeclcd_SOURCES = lcd.h sdeclcd.h sdeclcd.c lcd_lib.h report.h adv_bignum.h port.h lpt-port.h timing.h
+425
View File
@@ -0,0 +1,425 @@
/** \file server/drivers/rawserial.c
* LCDd \c rawserial driver.
* This is primarily of use when you want to use LCDproc as a data-source for
* an external MCU or other similar hardware, in a situation where the LCD
* control characters normally emitted are actually obnoxious, rather then
* beneficial.
* As such, it simply dumps the ENTIRE (no deltas or such) framebuffer at a
* configurable rate (default 1 Hz).
*/
/*-
* Copyright (C) 2014 Connor Wolf <lcdproc@imaginaryindustries.com>
*
* 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
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include "lcd.h"
#include "rawserial.h"
#include "report.h"
/** private data for the \c rawserial driver */
typedef struct rawserial_private_data {
int width; /**< display width in characters */
int height; /**< display height in characters */
char *framebuf; /**< frame buffer */
int fd; /**< handle to the device */
/** \name Event loop timing. refresh_time and refresh_delta form the
* event loop timing mechanism for configurable update rates.
*@{*/
uint refresh_time; /**< time at the last screen update */
uint refresh_delta; /**< time step to next screen update */
/**@}*/
} PrivateData;
/* Local prototypes */
static uint get_millisecond_time(void);
/* 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 = "rawserial_";
/**
* Initialize the driver.
* \param drvthis Pointer to driver structure.
* \retval 0 Success.
* \retval <0 Error.
*/
MODULE_EXPORT int
rawserial_init(Driver *drvthis)
{
PrivateData *p;
char buf[256];
char device[200];
int speed = DEFAULT_SPEED;
int tmp;
double tmpf;
struct termios portset;
/* Allocate and store private data */
p = (PrivateData *) calloc(1, sizeof(PrivateData));
if (p == NULL)
return -1;
if (drvthis->store_private_ptr(drvthis, p) != 0)
return -1;
/* initialize private data */
tmpf = drvthis->config_get_float(drvthis->name, "UpdateRate", 0, DEFAULT_UPDATE_RATE);
if (tmpf < 0.0005 || tmpf > 10) {
report(RPT_WARNING, "%s: UpdateRate out of range; using default %g",
drvthis->name, DEFAULT_UPDATE_RATE);
tmpf = DEFAULT_UPDATE_RATE;
}
p->refresh_delta = (int) ((double) SECOND_GRANULARITY / tmpf);
/* Subtract refresh delta so we update immediately on start */
p->refresh_time = get_millisecond_time() - p->refresh_delta;
report(RPT_INFO, "%s: start-up time: %u, refresh delta: %u ms per update", drvthis->name,
p->refresh_time, p->refresh_delta);
/* Which speed */
tmp = drvthis->config_get_int(drvthis->name, "Speed", 0, DEFAULT_SPEED);
switch (tmp) {
case 1200:
speed = B1200;
break;
case 2400:
speed = B2400;
break;
case 9600:
speed = B9600;
break;
case 19200:
speed = B19200;
break;
case 115200:
speed = B115200;
break;
default:
report(RPT_WARNING,
"%s: Speed must be 1200, 2400, 9600, 19200 or 115200; using default %d",
drvthis->name, DEFAULT_SPEED);
speed = DEFAULT_SPEED;
}
/* which serial device should be used */
strncpy(device, drvthis->config_get_string(drvthis->name, "Device", 0, DEFAULT_DEVICE),
sizeof(device));
device[sizeof(device) - 1] = '\0';
report(RPT_INFO, "%s: using Device %s at baud rate: %d (configured = %d)",
drvthis->name, device, speed, tmp);
/* Set display sizes */
if ((drvthis->request_display_width() > 0)
&& (drvthis->request_display_height() > 0)) {
/* Use size from primary driver */
p->width = drvthis->request_display_width();
p->height = drvthis->request_display_height();
}
else {
/* Use our own size from config file */
strncpy(buf,
drvthis->config_get_string(drvthis->name, "Size", 0, DISPLAY_DEFAULT_SIZE),
sizeof(buf));
buf[sizeof(buf) - 1] = '\0';
if ((sscanf(buf, "%dx%d", &p->width, &p->height) != 2)
|| (p->width <= 0) || (p->width > LCD_MAX_WIDTH)
|| (p->height <= 0) || (p->height > LCD_MAX_HEIGHT)) {
report(RPT_WARNING, "%s: cannot read Size: %s; using default %s",
drvthis->name, buf, DISPLAY_DEFAULT_SIZE);
sscanf(DISPLAY_DEFAULT_SIZE, "%dx%d", &p->width, &p->height);
}
}
report(RPT_INFO, "%s: using Size %dx%d", drvthis->name, p->width, p->height);
/* Allocate the framebuffer */
p->framebuf = malloc(p->width * p->height);
if (p->framebuf == NULL) {
report(RPT_ERR, "%s: unable to create framebuffer", drvthis->name);
goto err_out;
}
memset(p->framebuf, ' ', p->width * p->height);
/* Set up I/O port correctly, and open it... */
p->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
if (p->fd == -1) {
report(RPT_ERR, "%s: open(%s) failed (%s)", drvthis->name, device, strerror(errno));
if (errno == EACCES)
report(RPT_ERR, "%s: device %s could not be opened", drvthis->name, device);
goto err_out;
}
report(RPT_INFO, "%s: opened display on %s", drvthis->name, device);
tcgetattr(p->fd, &portset);
/* We use RAW mode */
#ifdef HAVE_CFMAKERAW
/* The easy way */
cfmakeraw(&portset);
#else
/* The hard way */
portset.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
portset.c_oflag &= ~OPOST;
portset.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
portset.c_cflag &= ~(CSIZE | PARENB | CRTSCTS);
portset.c_cflag |= CS8 | CREAD | CLOCAL;
#endif
/* Set timeouts */
portset.c_cc[VMIN] = 1;
portset.c_cc[VTIME] = 3;
/* Set port speed */
cfsetospeed(&portset, speed);
cfsetispeed(&portset, B0);
/* Do it... */
tcsetattr(p->fd, TCSANOW, &portset);
report(RPT_DEBUG, "%s: init() done", drvthis->name);
return 0;
err_out:
rawserial_close(drvthis);
return -1;
}
/**
* Close the driver (do necessary clean-up).
* \param drvthis Pointer to driver structure.
*/
MODULE_EXPORT void
rawserial_close(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
if (p != NULL) {
if (p->fd >= 0)
close(p->fd);
if (p->framebuf != NULL)
free(p->framebuf);
free(p);
}
drvthis->store_private_ptr(drvthis, NULL);
}
/**
* Return the display width in characters.
* \param drvthis Pointer to driver structure.
* \return Number of characters the display is wide.
*/
MODULE_EXPORT int
rawserial_width(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->width;
}
/**
* Return the display height in characters.
* \param drvthis Pointer to driver structure.
* \return Number of characters the display is high.
*/
MODULE_EXPORT int
rawserial_height(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->height;
}
/**
* Clear the screen.
* \param drvthis Pointer to driver structure.
*/
MODULE_EXPORT void
rawserial_clear(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
memset(p->framebuf, ' ', p->width * p->height);
}
/**
* Flush data on screen to the display.
*
* Output is written at every \c refresh_delta milliseconds to the device.
* However, updates do not occur more often than every 125 ms (8 Hz) which is
* the default rendering rate in LCDd.
*
* This function implements its own update scheduler which is independent of
* the main rendering loop to achieve a more constant output rate.
*
* \param drvthis Pointer to driver structure.
*/
MODULE_EXPORT void
rawserial_flush(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
char out[LCD_MAX_WIDTH * LCD_MAX_HEIGHT];
uint currentTime = get_millisecond_time();
int t_delta = currentTime - p->refresh_time;
/*
* Sanity checking in case of major time-shifts due to NTP updates or
* daylight savings time. (INT_MAX / 1e3) is 2147483 milliseconds, 2147
* seconds, or 35 minutes.
*/
if (((t_delta + 1) > (INT_MAX / 1e3)) || (t_delta < 0)) {
report(RPT_WARNING,
"%s: Major time-delta between flush calls! Old time: %d, new time: %d",
drvthis->name, p->refresh_time, currentTime);
p->refresh_time = currentTime;
}
if (currentTime > (p->refresh_time + p->refresh_delta)) {
int dataEnd = p->height * p->width;
/* Dump the contents of the framebuffer out the serial port.
* There is no processing and no control chars are emitted,
* just a plain-old newline at the end of the record. */
memcpy(out, p->framebuf, dataEnd);
write(p->fd, out, dataEnd);
write(p->fd, "\n", 1);
report(RPT_DEBUG,
"%s: flush exec time: %u, refresh delta: %u, current clock: %u, rendering loop overshoot: %d ms",
drvthis->name, p->refresh_time, p->refresh_delta, currentTime,
currentTime - p->refresh_time - p->refresh_delta);
/* Update the event timer so we'll trigger after the next
* p->refresh_delta milliseconds */
p->refresh_time += p->refresh_delta;
}
}
/**
* Print a string on the screen at position (x,y).
* The upper-left corner is (1,1), the lower-right corner is (p->width, p->height).
* \param drvthis Pointer to driver structure.
* \param x Horizontal character position (column).
* \param y Vertical character position (row).
* \param string String that gets written.
*/
MODULE_EXPORT void
rawserial_string(Driver *drvthis, int x, int y, const char string[])
{
PrivateData *p = drvthis->private_data;
int i;
x--;
y--; /* Convert 1-based coords to 0-based */
if ((y < 0) || (y >= p->height))
return;
for (i = 0; (string[i] != '\0') && (x < p->width); i++, x++) {
if (x >= 0) /* no write left of left border */
p->framebuf[(y * p->width) + x] = string[i];
}
}
/**
* Print a character on the screen at position (x,y).
* The upper-left corner is (1,1), the lower-right corner is (p->width, p->height).
* \param drvthis Pointer to driver structure.
* \param x Horizontal character position (column).
* \param y Vertical character position (row).
* \param c Character that gets written.
*/
MODULE_EXPORT void
rawserial_chr(Driver *drvthis, int x, int y, char c)
{
PrivateData *p = drvthis->private_data;
y--;
x--;
if ((x >= 0) && (y >= 0) && (x < p->width) && (y < p->height))
p->framebuf[(y * p->width) + x] = c;
}
/**
* Provide some information about this driver.
* \param drvthis Pointer to driver structure.
* \return Constant string with information.
*/
MODULE_EXPORT const char *
rawserial_get_info(Driver *drvthis)
{
static char *info_string =
"Raw Text mode driver for use as a source for sending data to external hardware";
return info_string;
}
/**
* Get current time in milliseconds, used for determining update event timing
*
* This WILL wrap fairly often, but we don't care, since we're just comparing
* two values, both of which will wrap at the same time. Note that gettimeofday
* is the wrong thing to use here, since it's not monotonic at all. We *really*
* should use `clock_gettime(CLOCK_MONOTONIC, &ts);` since that's both
* monotonic and immune to NTP updates, DST time changes, etc. However, that
* would require linking librealtime.
*
* \return int with current millisecond time.
*/
static uint
get_millisecond_time(void)
{
struct timeval ts;
gettimeofday(&ts, NULL);
return ((ts.tv_sec * SECOND_GRANULARITY) +
(ts.tv_usec / (1000000.0 / SECOND_GRANULARITY))) + 0.5;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef LCD_rawserial_H
#define LCD_rawserial_H
#define DEFAULT_DEVICE "/dev/cuaU0"
#define DISPLAY_DEFAULT_SIZE "40x4"
/* Default 9600 baud, 1 hz update rate, time resolution of 1 ms */
#define DEFAULT_SPEED B9600
#define DEFAULT_UPDATE_RATE 1.0
#define SECOND_GRANULARITY 1000
MODULE_EXPORT int rawserial_init(Driver *drvthis);
MODULE_EXPORT void rawserial_close(Driver *drvthis);
MODULE_EXPORT int rawserial_width(Driver *drvthis);
MODULE_EXPORT int rawserial_height(Driver *drvthis);
MODULE_EXPORT void rawserial_clear(Driver *drvthis);
MODULE_EXPORT void rawserial_flush(Driver *drvthis);
MODULE_EXPORT void rawserial_string(Driver *drvthis, int x, int y, const char string[]);
MODULE_EXPORT void rawserial_chr(Driver *drvthis, int x, int y, char c);
MODULE_EXPORT const char *rawserial_get_info(Driver *drvthis);
#endif