LCDproc development and user support list

Text archives Help


[Lcdproc] CwLnx display corruption - possible cause and fix


Chronological Thread 
  • From: dplatt AT radagast.org (Dave Platt)
  • Subject: [Lcdproc] CwLnx display corruption - possible cause and fix
  • Date: Mon Apr 30 05:49:02 2007

Peter Marschall
<peter AT adpm.de>
wrote:

> That explains why it works for the 1-char case.
> Here we only have to distinguish between 1 (= success)
> and all other cases that are considered as EAGAIN.
>
> With multiple chars the 1 - (size-1) case is falsely
> treated like EAGAIN.

That makes complete sense.

> Please post the patch to the ML so that I can be fixed in CVS.

See below... I've made a few other changes which I think will prove
beneficial as well.

> This really sounds weird.
> I haven't touched that part of the code and have no idea too why
> it is coded this way.

> Sorry no idea. Same for the double open or the too short SETUP_DELAY.

After a bunch of experimentation, I think I've figured out much of
what's going on here. It appears that the CW12232, at least, doesn't
immediately honor a "change speed" command. You have to send the
command, wait for it to reach the device, and then send a long-space
to the device to "prod" its serial interface into changing speed. I'm
not sure why this is the case, but that seems to be what it takes to
make it work... without the long-space it doesn't actually switch speed.

> If you have patches that make the initialization part of the code
> easier to understand, don't hesitate to send them.
>
> I doubt that a cleaner initialization that works on a 12232 might
> negatively affect the initialization of a 1602. The 1602 even works
> with the current code. So it should be able to profit from a saner
> initialization too.

Also included below. I've generalized the previous code a bit, so
that it can run at either 9600 or 19200 as the configuration file
requests. It figures out what the desired speed is, sets the serial
port to the opposite speed, sends a "change to the right speed"
command, drains the port, waits a while, sends a long-space, and then
sets the port to the correct speed.

As far as I can tell, this strategy is quite reliable. I've run a
test which starts up and terminates LCDd, switching speeds on about
half of the startups. So far, so good... I've run a few dozen test
cycles and haven't seen the device fail to talk correctly to the host,
no matter which speed was corrected.

> Hey, you're right.
> I haven't thought of that before.
> This optimization might apply (with varying thresholds of the move command)
> for other drivers as well.

'Tis done. The version in the attached patch depends on the device
having automatic-wrap-to-the-next-line capability - it'll optimize
right across line boundaries. Feel free to use the code in other
drivers, as applicable... the cost-of-a-move is a #define. It should
be pretty trivial to rearrange it slightly so that it will treat each
line independently and won't need autowrap.

> So, same as above: patches are very welcome.

Here's the results of a few days of fiddling. I've

- Fixed the nonblocking-write problem

- Optimized the optimized framebuffer-update code to eliminate
excessive move-insertion-point commands

- Cleaned up (I think) the initialization sequence so it'll actually
talk at 19200 if requested to do so

- Added code to the framebuffer-flushing routine to actually propagate
changes in the backlight status and brightness to the device. The
backlight now goes off at low system-load levels (if lcdproc is
configured to enable this) and the blink/flash feature is working
(my digital-audio-recorder client uses this to signal the end of a
recording).

- Made "SEAMLESS_HBAR" mode work on the 12232. This required adding
an alternative set_char routine which doesn't limit the hbar
characters to 5 columns on this device. [I'm tempted to add
support for the native draw-bar commands in the 1602 and 12232 but
that would involve making the framebuffer management quite a lot
more complex.]

- Added some newlines to the Debian init scripts' PRINTF commands
so that they can be run interactively without leaving the subsequent
prompt on the same line as the script output.

- I put the soft-reset-BIOS support code back in, but am not actually
using it in the code at the moment.

The patch is against the CVS tree as of a day or so before 0.5.2 was
released... ought to apply fairly easily, I think.

Comments, criticisms, and suggestions are most welcome!

diff -du -b -r lcdproc-orig/scripts/init-LCDd.debian.in
lcdproc/scripts/init-LCDd.debian.in
--- lcdproc-orig/scripts/init-LCDd.debian.in 2007-04-25 20:46:01.000000000
-0700
+++ lcdproc/scripts/init-LCDd.debian.in 2007-04-29 21:26:30.000000000 -0700
@@ -30,12 +30,12 @@
start)
printf "Starting ${DESC}: "
start-stop-daemon --start --quiet --exec ${DAEMON} -- ${OPTIONS}
- printf "${NAME}."
+ printf "${NAME}.\n"
;;
stop)
printf "Stopping ${DESC}: "
start-stop-daemon --stop --oknodo --quiet --exec ${DAEMON}
- printf "${NAME}."
+ printf "${NAME}.\n"
;;
restart|force-reload)
$0 stop
diff -du -b -r lcdproc-orig/scripts/init-lcdproc.debian.in
lcdproc/scripts/init-lcdproc.debian.in
--- lcdproc-orig/scripts/init-lcdproc.debian.in 2007-04-25 20:46:01.000000000
-0700
+++ lcdproc/scripts/init-lcdproc.debian.in 2007-04-29 21:26:44.000000000
-0700
@@ -30,12 +30,12 @@
start)
printf "Starting ${DESC}: "
start-stop-daemon --start --quiet --exec ${DAEMON} -- ${OPTIONS}
- printf "${NAME}."
+ printf "${NAME}.\n"
;;
stop)
printf "Stopping ${DESC}: "
start-stop-daemon --stop --oknodo --quiet --exec ${DAEMON}
- printf "${NAME}."
+ printf "${NAME}.\n"
;;
restart|force-reload)
$0 stop
diff -du -b -r lcdproc-orig/server/drivers/CwLnx.c
lcdproc/server/drivers/CwLnx.c
--- lcdproc-orig/server/drivers/CwLnx.c 2007-04-25 20:46:01.000000000 -0700
+++ lcdproc/server/drivers/CwLnx.c 2007-04-29 22:16:39.000000000 -0700
@@ -122,6 +122,7 @@
static void CwLnx_linewrap(int fd, int on);
static void CwLnx_autoscroll(int fd, int on);
static void CwLnx_hidecursor(int fd);
+static void CwLnx_set_char_unrestricted(Driver *drvthis, int n, unsigned
char *dat);


#define LCD_CMD 254
@@ -140,31 +141,37 @@
#define LCD_SETCHAR 78
#define LCD_ENABLE_SCROLL 81
#define LCD_DISABLE_SCROLL 82
+#define LCD_SOFT_RESET 86
#define LCD_OFF_CURSOR 72

#define LCD_PUT_PIXEL 112
#define LCD_CLEAR_PIXEL 113

#define DELAY 2000 /* 2 milli sec */
-#define UPDATE_DELAY 0 /* 1 imicro sec */
-#define SETUP_DELAY 1 /* 2 micro sec */
-
+#define UPDATE_DELAY 20000 /* 20 milliseconds */
+#define SETUP_DELAY 20000 /* 20 milliseconds */

+#define MOVE_COST 5 /* # bytes for most move-to ops */

static int Write_LCD(int fd, char *c, int size)
{
- int rc;
+ int rc, wrote = 0;
int retries = 30;

do {
rc = write(fd, c, size);
- if (rc == size)
- break;
+ if (rc > 0) {
+ c += rc;
+ size -= rc;
+ wrote += rc;
+ } else if (rc == 0 || (rc < 0 && errno == EAGAIN)) { /* would have
blocked */
usleep(DELAY);
+ } else {
+ break;
+ }
+ } while (size > 0 && --retries > 0);

- } while (--retries > 0);
-
- return rc;
+ return wrote;
}


@@ -395,6 +402,17 @@
Disable_Cursor(fd);
}

+/********************************************************************
+ * Reset the display bios
+ */
+static void CwLnx_reboot(int fd)
+{
+ char cmd[] = { LCD_CMD, LCD_SOFT_RESET, LCD_CMD_END };
+ Write_LCD(fd, cmd, 3);
+ usleep(SETUP_DELAY);
+ return;
+}
+

/*****************************************************
* Here start the API function
@@ -410,8 +428,6 @@
MODULE_EXPORT int
CwLnx_init(Driver *drvthis)
{
- struct termios portset_save;
-
char device[200] = DEFAULT_DEVICE;
int speed = DEFAULT_SPEED;
char size[200] = DEFAULT_SIZE;
@@ -576,23 +592,26 @@
}
report(RPT_INFO, "%s: opened display on %s", drvthis->name, device);

+ /*
+ Since we don't know what speed the display is using when
+ we first connect to it, configure the port for the speed
+ we don't want to use, send a command to switch the display
+ to the speed we want to use, and flush the command.
+ */
+
Init_Port(p->fd);
- tcgetattr(p->fd, &portset_save);
- speed = B19200;
- Setup_Port(p->fd, speed);
+ if (speed == B9600) {
+ Setup_Port(p->fd, B19200);
Set_9600(p->fd);
- close(p->fd);
-
- 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));
- return -1;
+ } else {
+ Setup_Port(p->fd, B9600);
+ Set_19200(p->fd);
}
- report(RPT_INFO, "%s: opened display on %s", drvthis->name, device);
-
+ tcdrain(p->fd);
+ usleep(SETUP_DELAY);
Init_Port(p->fd);
- speed = B9600;
Setup_Port(p->fd, speed);
+
CwLnx_hidecursor(p->fd);
CwLnx_linewrap(p->fd, 1);
CwLnx_autoscroll(p->fd, 0);
@@ -712,7 +731,8 @@
PrivateData *p = drvthis->private_data;

int i, j;
- int move = 1;
+ int iUpdate = 0, jUpdate = 0;
+ unsigned char *firstUpdate = NULL, *lastUpdate = NULL;

unsigned char *q = p->framebuf;
unsigned char *r = p->backingstore;
@@ -720,25 +740,43 @@
for (i = 0; i < p->height; i++) {
for (j = 0; j < p->width; j++) {
if ((*q == *r) && !((0 < *q) && (*q < 16))) {
- move = 1;
+ if (firstUpdate && q - lastUpdate > MOVE_COST) {
+ Set_Insert(p->fd, iUpdate, jUpdate);
+ Write_LCD(p->fd, (char *) firstUpdate,
+ lastUpdate - firstUpdate + 1);
+ firstUpdate = lastUpdate = NULL;
}
- else {
- /* Draw characters that have changed, as well
- * as custom characters. We know not if a custom
- * character has changed. */
- if (move == 1) {
- Set_Insert(p->fd, i, j);
- move = 0;
+ } else {
+ lastUpdate = q;
+ if (!firstUpdate) {
+ firstUpdate = q;
+ iUpdate = i;
+ jUpdate = j;
}
- Write_LCD(p->fd, (char *) q, 1);
}
q++;
r++;
}
}
+ if (firstUpdate) {
+ Set_Insert(p->fd, iUpdate, jUpdate);
+ Write_LCD(p->fd, (char *) firstUpdate,
+ lastUpdate - firstUpdate + 1);
+ }
+
memcpy(p->backingstore, p->framebuf, p->width * p->height);
-}

+ if (p->backlight != p->saved_backlight ||
+ p->brightness != p->saved_brightness) {
+ if (!p->backlight) {
+ Backlight_Brightness(p->fd, 1);
+ } else {
+ Backlight_Brightness(p->fd, 1 + p->brightness * 6 / 900); /* 90% and
up is full brightness */
+ }
+ p->saved_backlight = p->backlight;
+ p->saved_brightness = p->brightness;
+ }
+}

/**
* Print a character on the screen at position (x,y).
@@ -904,7 +942,11 @@
for (i = 1; i <= p->cellwidth; i++) {
// fill pixel columns from left to right.
memset(hBar, 0xFF & ~((1 << (p->cellwidth - i)) - 1),
sizeof(hBar));
+#if defined(SEAMLESS_HBARS)
+ CwLnx_set_char_unrestricted(drvthis, i+1, hBar);
+#else
CwLnx_set_char(drvthis, i+1, hBar);
+#endif
}
}

@@ -1020,6 +1062,65 @@
}


+/*
+ * Identical to CwLnx_set_char, but it doesn't restrict the 12232 to
+ * using only 5 of its 6 columns. Full 6-column mode is required
+ * for seamless H-bars.
+ */
+
+#if defined(SEAMLESS_HBARS)
+static void
+CwLnx_set_char_unrestricted(Driver *drvthis, int n, unsigned char *dat)
+{
+ PrivateData *p = drvthis->private_data;
+
+ char c;
+ int rc;
+
+ if ((n <= 0) || (n > CwLnx_get_free_chars(drvthis)))
+ return;
+ if (!dat)
+ return;
+
+ c = LCD_CMD;
+ rc = Write_LCD(p->fd, &c, 1);
+ c = LCD_SETCHAR;
+ rc = Write_LCD(p->fd, &c, 1);
+ c = (char) n;
+ rc = Write_LCD(p->fd, &c, 1);
+
+ if (p->model == 1602) { // the character model
+ unsigned char mask = (1 << p->cellwidth) - 1;
+ int row;
+
+ for (row = 0; row < p->cellheight; row++) {
+ c = dat[row] & mask;
+ Write_LCD(p->fd, &c, 1);
+ }
+ } else if (p->model == 12232) { // the graphical model
+ int col;
+
+ for (col = p->cellwidth - 1; col >= 0; col--) {
+ int letter = 0;
+ int row;
+
+ for (row = p->cellheight - 1; row >= 0; row--) {
+ letter <<= 1;
+ letter |= ((dat[row] >> col) & 1);
+ }
+
+ c = letter;
+
+ Write_LCD(p->fd, &c, 1);
+ }
+ }
+
+ c = LCD_CMD_END;
+ rc = Write_LCD(p->fd, &c, 1);
+}
+#endif
+
+
/**
* Place an icon on the screen.
* \param drvthis Pointer to driver structure.
diff -du -b -r lcdproc-orig/server/drivers/CwLnx.h
lcdproc/server/drivers/CwLnx.h
--- lcdproc-orig/server/drivers/CwLnx.h 2007-04-25 20:46:01.000000000 -0700
+++ lcdproc/server/drivers/CwLnx.h 2007-04-29 21:00:34.000000000 -0700
@@ -31,7 +31,7 @@

#define DEFAULT_DEVICE "/dev/lcd"
#define DEFAULT_BACKLIGHT 1
-#define DEFAULT_BRIGHTNESS 200
+#define DEFAULT_BRIGHTNESS 700

#define DEFAULT_CELL_WIDTH_1602 5
#define DEFAULT_CELL_WIDTH_12232 6





Archive powered by MHonArc 2.6.18.

Top of page