diff --git a/ChangeLog b/ChangeLog index c789fef..262e209 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,7 +17,8 @@ v0.5dev (ongoing development) * lcdproc client: Add option to turn off title on OldTime screen + glcd driver: Connection type 'x11' that draws to a X window (S. Meharg) + glcd driver: 'picolcdgfx' for picoLCD 256x64 from Mini-Box.com (S. Meharg) - * picolcd: Improvements to IR processing (M. Jones). + * picolcd: Improvements to IR processing (M. Jones) + + hd44780: Added 'spi' connection type (S. Dawson) v0.5.6 - Remove deprecated CFontz633 driver. Use CFontzPacket with Model=633 instead! diff --git a/LCDd.conf b/LCDd.conf index 896198a..20ca8ac 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -538,7 +538,7 @@ ConnectionType=4bit # For I2C connections this sets the slave address (usually 0x20). Port=0x378 -# Device of the serial or I2C interface [default: /dev/lcd] +# Device of the serial, I2C, or SPI interface [default: /dev/lcd] Device=/dev/ttyS0 # Bitrate of the serial port (0 for interface default) diff --git a/acinclude.m4 b/acinclude.m4 index ea8b99a..7317615 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -232,6 +232,9 @@ dnl else HD44780_DRIVERS="$HD44780_DRIVERS hd44780-hd44780-i2c.o" HD44780_DRIVERS="$HD44780_DRIVERS hd44780-hd44780-piplate.o" fi + if test "$x_ac_have_spi" = yes; then + HD44780_DRIVERS="$HD44780_DRIVERS hd44780-hd44780-spi.o" + fi dnl The hd4470-rpi driver only works on a Raspberry Pi, dnl which is an ARM platform. Require people to compile on dnl (or for) ARM to get it. diff --git a/configure.ac b/configure.ac index abd5099..7c053d2 100644 --- a/configure.ac +++ b/configure.ac @@ -167,6 +167,12 @@ if test "$x_ac_have_i2c" = yes; then AC_DEFINE(HAVE_I2C,[1],[Define to 1 if you have the i2c headers]) fi +x_ac_have_spi=no +AC_CHECK_HEADERS([linux/spi/spidev.h], [x_ac_have_spi=yes]) +if test "$x_ac_have_spi" = yes; then + AC_DEFINE(HAVE_SPI,[1],[Define to 1 if you have the spi headers]) +fi + dnl Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC diff --git a/docs/LCDd.8.in b/docs/LCDd.8.in index 0420be7..dd36610 100644 --- a/docs/LCDd.8.in +++ b/docs/LCDd.8.in @@ -182,6 +182,9 @@ USS-720 USB-to-IEEE 1284 Bridge (Belkin F5U002 USB Parallel Printer Adapters) .B i2c LCD in 4-bit mode driven by PCF8574(A) / PCA9554(A), connected via I2C bus .TP +.B spi +LCD with KS0073 or equivalent in serial mode, connected via SPI bus +.TP .B ftdi USB connection via a FTDI FT2232D chip in bitbang mode .TP diff --git a/docs/lcdproc-user/drivers/hd44780.docbook b/docs/lcdproc-user/drivers/hd44780.docbook index 881cf6a..15927fc 100644 --- a/docs/lcdproc-user/drivers/hd44780.docbook +++ b/docs/lcdproc-user/drivers/hd44780.docbook @@ -37,7 +37,7 @@ various special features. The following table lists the available connection types, sorted by the kind -of connection (parallel port, serial port, USB, I2C or others). +of connection (parallel port, serial port, USB, I2C, SPI or others). @@ -155,6 +155,14 @@ of connection (parallel port, serial port, USB, I2C or others). piplate Adafruit RGB Positive 16x2 LCD+Keypad for Raspberry Pi + + + SPI (Serial Peripheral Interface): + + + spi + Display using a KS0073 or similar in serial mode accessed via Linux SPI device + Other connection types: @@ -1757,6 +1765,27 @@ that. The BL output below should be connected to the BL input in the + +spi + + +This connection type drives a LCD in serial mode (supported on Hitachi HD66712, +Samsung KS0073 and KS0074 (and clones) controllers) connected to some Linux +SPI device. + + +This connection type is currently only supported on Linux. It is strongly +recommended to use a SPI implementation with hardware support, e.g. on the +Raspberry Pi. + + +As the SPI communication to the display cannot drive a backlight switch, this +connection type features a BacklightDevice option +that can use a GPIO pin exposed via sysfs (see eLinux GPIO). + + + + FTDI FT2232D USB chip "ftdi" @@ -3122,8 +3151,8 @@ This can be done by specifying or by inclu 4bit | 8bit | - winamp | serialLpt | + winamp | picanlcd | lcdserializer | los-panel | @@ -3141,6 +3170,7 @@ This can be done by specifying or by inclu ftdi | i2c | piplate | + spi | ethlcd | raspberrypi } diff --git a/server/drivers/Makefile.am b/server/drivers/Makefile.am index d998ba2..534608c 100644 --- a/server/drivers/Makefile.am +++ b/server/drivers/Makefile.am @@ -95,7 +95,7 @@ EXTRA_glcd_SOURCES = glcd-t6963.c t6963_low.c t6963_low.h glcd-png.c glcd-serdis glcdlib_SOURCES = lcd.h lcd_lib.h glcdlib.h glcdlib.c report.h glk_SOURCES = lcd.h glk.c glk.h glkproto.c glkproto.h report.h hd44780_SOURCES = lcd.h lcd_lib.h hd44780.h hd44780.c hd44780-drivers.h hd44780-low.h hd44780-charmap.h report.h adv_bignum.h -EXTRA_hd44780_SOURCES = port.h lpt-port.h timing.h lcd_sem.c lcd_sem.h hd44780-4bit.c hd44780-4bit.h hd44780-ext8bit.c hd44780-ext8bit.h hd44780-serialLpt.c hd44780-serialLpt.h hd44780-serial.c hd44780-serial.h hd44780-winamp.c hd44780-winamp.h hd44780-bwct-usb.c hd44780-bwct-usb.h hd44780-lcd2usb.c hd44780-lcd2usb.h hd44780-usbtiny.c hd44780-usbtiny.h hd44780-lis2.c hd44780-lis2.h hd44780-i2c.c hd44780-i2c.h hd44780-ftdi.c hd44780-ftdi.h hd44780-ethlcd.c hd44780-ethlcd.h hd44780-uss720.c hd44780-uss720.h hd44780-usblcd.c hd44780-usblcd.h hd44780-usb4all.c hd44780-usb4all.h hd44780-rpi.c hd44780-rpi.h hd44780-piplate.c hd44780-piplate.h +EXTRA_hd44780_SOURCES = port.h lpt-port.h timing.h lcd_sem.c lcd_sem.h hd44780-4bit.c hd44780-4bit.h hd44780-bwct-usb.c hd44780-bwct-usb.h hd44780-ethlcd.c hd44780-ethlcd.h hd44780-ext8bit.c hd44780-ext8bit.h hd44780-ftdi.c hd44780-ftdi.h hd44780-i2c.c hd44780-i2c.h hd44780-lcd2usb.c hd44780-lcd2usb.h hd44780-lis2.c hd44780-lis2.h hd44780-piplate.c hd44780-piplate.h hd44780-rpi.c hd44780-rpi.h hd44780-serial.c hd44780-serial.h hd44780-serialLpt.c hd44780-serialLpt.h hd44780-spi.c hd44780-spi.h hd44780-usb4all.c hd44780-usb4all.h hd44780-usblcd.c hd44780-usblcd.h hd44780-usbtiny.c hd44780-usbtiny.h hd44780-uss720.c hd44780-uss720.h hd44780-winamp.c hd44780-winamp.h i2500vfd_SOURCES = lcd.h i2500vfd.c i2500vfd.h glcd_font5x8.h report.h icp_a106_SOURCES = lcd.h lcd_lib.h icp_a106.c icp_a106.h report.h imon_SOURCES = lcd.h lcd_lib.h hd44780-charmap.h imon.h imon.c report.h adv_bignum.h diff --git a/server/drivers/hd44780-drivers.h b/server/drivers/hd44780-drivers.h index b155451..c4b50d4 100644 --- a/server/drivers/hd44780-drivers.h +++ b/server/drivers/hd44780-drivers.h @@ -33,6 +33,9 @@ # include "hd44780-i2c.h" # include "hd44780-piplate.h" #endif +#ifdef HAVE_SPI +# include "hd44780-spi.h" +#endif #ifdef WITH_ETHLCD # include "hd44780-ethlcd.h" #endif @@ -82,6 +85,9 @@ static const ConnectionMapping connectionMapping[] = { #ifdef HAVE_I2C { "i2c", HD44780_CT_I2C, IF_TYPE_I2C, hd_init_i2c }, { "piplate", HD44780_CT_PIPLATE, IF_TYPE_I2C, hd_init_i2c_piplate }, +#endif +#ifdef HAVE_SPI + { "spi", HD44780_CT_SPI, IF_TYPE_SPI, hd_init_spi }, #endif /* TCP socket connection types */ #ifdef WITH_ETHLCD diff --git a/server/drivers/hd44780-low.h b/server/drivers/hd44780-low.h index 4914dca..3bb1b8b 100644 --- a/server/drivers/hd44780-low.h +++ b/server/drivers/hd44780-low.h @@ -55,6 +55,7 @@ #define HD44780_CT_USB4ALL 21 #define HD44780_CT_RASPBERRYPI 22 #define HD44780_CT_PIPLATE 23 +#define HD44780_CT_SPI 24 /**@}*/ /** \name Symbolic names for interface types @@ -65,6 +66,7 @@ #define IF_TYPE_USB 3 #define IF_TYPE_I2C 4 #define IF_TYPE_TCP 5 +#define IF_TYPE_SPI 6 /**@}*/ /** \name Symbolic default values diff --git a/server/drivers/hd44780-spi.c b/server/drivers/hd44780-spi.c new file mode 100644 index 0000000..5f94333 --- /dev/null +++ b/server/drivers/hd44780-spi.c @@ -0,0 +1,215 @@ +/** \file server/drivers/hd44780-spi.c + * \c SPI connection type of \c hd44780 driver for Hitachi HD44780 based LCD displays. + * + * The LCD is operated in its serial mode to be connected via the SPI bus + * using the Linux kernel spidev interface. The LCD SPI device should be + * declared in your board setup code. + */ + +/*- + * Copyright (C) 2013 Simon Dawson + * + * Based mostly on the hd44780-i2c module, see there for a complete history. + * + * This file is released under the GNU General Public License. Refer to the + * COPYING file distributed with this package. + * + * Configuration: + * Device=/dev/spidev0.0 # the device file of the spi bus (spi 0, chipselect 0) + * BacklightDevice=(undef) # the device file for the backlight (1 = on, 0 = off) + */ + +#include "hd44780-spi.h" +#include "hd44780-low.h" +#include "report.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +void spi_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch); +void spi_HD44780_backlight(PrivateData *p, unsigned char state); + +#define DEFAULT_DEVICE "/dev/spidev0.0" + +/** KS0073 5-bit sync lead-in on SPI interface */ +#define SYNC 0xF8u +/** KS0073 Read Write bit: 1 = read selected data/register, 0 = write to selected data/register */ +#define RW 0x04u +/** KS0073 Register Select bit: 0 = instruction register follows, 1 = data register follows */ +#define RS 0x02u + + +/** + * Reverses the bits of \a u8. + * \param u8 byte of which to reverse the bits. + * \return Value resulting from \a u8 with reversed bits. + */ +static inline uint8_t +bit_reverse8(uint8_t u8) +{ + /* + * See + * http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits + * for how this works and. This code snippet is in the public domain. + */ + return (uint8_t) (((u8 * 0x0802LU & 0x22110LU) | (u8 * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); +} + + +/** + * Do a SPI transfer by sending \c length bytes of \c outbuf and read the same + * number of bytes into \c inbuf. + * \param p Pointer to driver's private data structure. + * \param outbuf Buffer holding data to send. + * \param inbuf Buffer to store incoming data (or NULL). + * \param length Length of buffers (both). + * \return Status from ioctl. + */ +static int +spi_transfer(PrivateData *p, const unsigned char *outbuf, unsigned char *inbuf, unsigned length) +{ + struct spi_ioc_transfer xfer; + int status; + + static unsigned char no_more_errormsgs = 0; + + memset(&xfer, 0, sizeof(xfer)); + xfer.tx_buf = (unsigned long) outbuf; + xfer.rx_buf = (unsigned long) inbuf; + xfer.len = length; + + p->hd44780_functions->drv_debug(RPT_DEBUG, "SPI sending %02x %02x %02x", + outbuf[0], outbuf[1], length > 2 ? outbuf[2] : 0xFFu); + status = ioctl(p->fd, SPI_IOC_MESSAGE(1), &xfer); + if (status < 0) { + p->hd44780_functions->drv_report(no_more_errormsgs ? RPT_DEBUG : RPT_ERR, + "HD44780: SPI: spidev write data %u failed: %s", + status, strerror(errno)); + no_more_errormsgs = 1; + } +#ifdef DEBUG + if (inbuf) + p->hd44780_functions->drv_debug(RPT_DEBUG, "SPI returned data %02x %02x %02x", + inbuf[0], inbuf[1], length > 2 ? inbuf[2] : 0xFFu); +#endif + + return status; +} + + +/** + * Initialize the driver. + * \param drvthis Pointer to driver structure. + * \retval 0 Success. + * \retval -1 Error. + */ +int +hd_init_spi(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + HD44780_functions *hd44780_functions = p->hd44780_functions; + + char device[256] = DEFAULT_DEVICE; + char backlight_device[256] = ""; + + /* READ CONFIG FILE */ + + /* Get and open SPI device to use */ + strncpy(device, drvthis->config_get_string(drvthis->name, "Device", 0, DEFAULT_DEVICE), + sizeof(device)); + device[sizeof(device) - 1] = '\0'; + report(RPT_INFO, "HD44780: SPI: Using device '%s'", device); + + p->fd = open(device, O_RDWR); + if (p->fd < 0) { + report(RPT_ERR, "HD44780: SPI: open spidev device '%s' failed: %s", device, + strerror(errno)); + return -1; + } + + /* Get and open the backlight device */ + p->backlight_bit = -1; + strncpy(backlight_device, + drvthis->config_get_string(drvthis->name, "BacklightDevice", 0, ""), + sizeof(backlight_device)); + backlight_device[sizeof(backlight_device) - 1] = '\0'; + + if (strlen(backlight_device) > 0) { + report(RPT_INFO, "HD44780: SPI: Using backlight_device '%s'", backlight_device); + + p->backlight_bit = open(backlight_device, O_RDWR); + if (p->backlight_bit < 0) { + report(RPT_ERR, "HD44780: SPI: open backlight_device '%s' failed: %s", + backlight_device, strerror(errno)); + } + else { + hd44780_functions->backlight = spi_HD44780_backlight; + } + } + + hd44780_functions->senddata = spi_HD44780_senddata; + common_init(p, IF_8BIT); + + return 0; +} + + +/** + * Send data or commands to the display. + * \param p Pointer to driver's private data structure. + * \param displayID ID of the display (or 0 for all) to send data to. + * \param flags Defines whether to end a command or data. + * \param ch The value to send. + */ +void +spi_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch) +{ + unsigned char buf[3]; + unsigned char reverse; + + p->hd44780_functions->drv_report(RPT_DEBUG, "HD44780: SPI: sending %s %02x", + RS_INSTR == flags ? "CMD" : "DATA", ch); + + if (flags == RS_INSTR) + buf[0] = SYNC; + else + buf[0] = SYNC | RS; + + /* KS0073 wants Least Significant Bit first, with the added twist of + * peculiar splitting of a byte across 4 nibbles. If we ever need to + * read from the device, remember to bit_reverse8() each byte (note + * that replies aren't split into nibbles). */ + reverse = bit_reverse8(ch); + buf[1] = reverse & 0xF0; + buf[2] = (reverse & 0x0F) << 4; + + spi_transfer(p, buf, NULL, sizeof(buf)); +} + + +/** + * Turn display backlight on or off. + * \param p Pointer to driver's private data structure. + * \param state New backlight status. + * + * KS0073 in SPI mode does not have a backlight control function. We + * assume instead that there is some device mapped to a file which we can + * write to. + */ +void +spi_HD44780_backlight(PrivateData *p, unsigned char state) +{ + if (p->backlight_bit != -1) { + unsigned char byte = (state == BACKLIGHT_ON) ? '1' : '0'; + if (write(p->backlight_bit, &byte, sizeof(byte)) < 0) + p->hd44780_functions->drv_report(RPT_ERR, + "HD44780: SPI: Cannot write to backlight device: %d (%s)", + errno, strerror(errno)); + } +} diff --git a/server/drivers/hd44780-spi.h b/server/drivers/hd44780-spi.h new file mode 100644 index 0000000..9b5f83d --- /dev/null +++ b/server/drivers/hd44780-spi.h @@ -0,0 +1,9 @@ +#ifndef HD_SPI_H +#define HD_SPI_H + +#include "lcd.h" /* for Driver */ + +/* initialise this particular driver */ +int hd_init_spi(Driver *drvthis); + +#endif diff --git a/server/drivers/hd44780.c b/server/drivers/hd44780.c index e53e48f..4e85b4c 100644 --- a/server/drivers/hd44780.c +++ b/server/drivers/hd44780.c @@ -427,6 +427,13 @@ HD44780_init(Driver *drvthis) (p->have_output?" out":"") ); break; + case IF_TYPE_SPI: + sprintf(buf, "SPI %s%s%s", + (p->have_backlight?" bl":""), + (p->have_keypad?" key":""), + (p->have_output?" out":"") + ); + break; case IF_TYPE_TCP: sprintf(buf, "TCP %s%s%s", (p->have_backlight?" bl":""),