LIBS =
CORE = bdfe
-OBJS = main.o ossd_i2c.o pi2c.o bdf.o rterm.o
-HFILES = Makefile pi2c.h ossd_i2c.h rterm.h font88.h font816.h
+OBJS = main.o ossd_i2c.o bdf.o rterm.o li2c.o
+HFILES = Makefile li2c.h ossd_i2c.h rterm.h font88.h font816.h
CFILES = ossd_i2c.c bdf.c rterm.c main.c
all: $(CORE)
rm -f *.o
%.o: %.c $(HFILES)
- $(CXX) -c $(CFLAGS) $< -o $@
+ $(CXX) -c $(CFLAGS) -DOSSD_TARGET=OSSD_IF_LINUX $< -o $@
So here it is - bdf font converter which can export glyphs from bdf and present them as array of bytes. It also can rotate 8x8 and 8x16 glyphs CCW so they can be used on displays with SSD1306 controller.
-I'm running it with my Raspberry Pi (same RPi I use to program MMR70 ATmega) but it should run on any other Linux machine as well.
+I'm running it with on Raspberry Pi (same RPi I use to program MMR70 ATmega) but latest version can run Intel Edison with native Linux I2C interface or Arduino-like Wire. With the native Linux I2C interface it should run on any other Linux machine as well.
Command line options
native: do not adjust font height 8 pixels
ascender H: add extra ascender of H pixels per glyph
rotate: rotate glyphs' bitmaps CCW
- display A: show converted font on SSD1306 compatible display
- using I2C bus 1, hexadecimal address A (default 3C)
+ display show converted font on SSD1306 compatible display
+ i2c_bus B: I2C bus for SSD1306 compatible display (default 1)
+ i2c_addr A: I2C address for SSD1306 compatible display (default 0x3C)
updown: display orientation is upside down
```
There is no output file option - just redirect bdfe output to a file: ```bdfe -h -v -l -a -n font.bdf > font.h```
-OLED display is not needed for conversion but useful to have as you can immediately see if font is suitable with under-/over-line or inverse attributes. By default I2C slave address x3C is used, on my display this address is selected by connecting DC to GND, connecting it to Vcc switches address to x3D. If you do not know address of your SSD1306 display use ```i2cdetect
+OLED display is not needed for conversion but useful to have as you can immediately see if font is suitable with under-/over-line or inverse attributes. By default I2C slave address x3C is used, on my display this address is selected by connecting DC to GND, connecting it to Vcc switches address to x3D. If you do not know address of your SSD1306 display use ```i2cdetect```
Source code
-----------
```bdfe.h, bdfe.c``` - BDF file parser, single (quite big) function.
```rterm.h, rterm.c``` - raw terminal input, kind of old nice getch().
```ossd_i2c.h, ossd_i2c.c``` - OLED SSD1306 controller interface using I2C bus. Simple text mode only with direct access to SSD1306 registers, no shadow graphics buffer - memory on MMR70 ATmega32 MCU is a precious resource and anyway MMR70 will update screen only once in a while. Currently only fonts 8 and 16 bits high are supported. Underline, overline and inverse attributes can be used.
-```pi2c.h, pi2c.c``` - basic I2C wrapper for i2c-dev library, allows to write to SSD1306 connected to I2C bus.
+```li2c.h, li2c.c``` - basic I2C wrapper for Linux i2c-dev library, provides communication with SSD1306 connected to I2C bus.
+```wi2c.h, wi2c.c``` - basic I2C wrapper for Arduino Wire object, not used in this project.
```font8x8.h, font8x16.h``` - converted bdf files.
```main.c``` - puts all of above together and does the work.
--- /dev/null
+/* BSD License
+ Copyright (c) 2015 Andrey Chilikin https://github.com/achilikin
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ Basic Linux I2C wrapper for Raspberry Pi and Intel Edison,
+ only write is supported
+
+ For Raspberry Pi make sure that RPi i2c driver is enabled, check following
+ files:
+ /etc/modprobe.d/raspi-blacklist.conf
+ #blacklist i2c-bcm2708
+ /etc/modules
+ i2c-dev
+ /etc/modprobe.d/i2c.conf
+ options i2c_bcm2708 baudrate=400000
+*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/i2c-dev.h>
+
+#include "li2c.h"
+
+/* by default RPi mode, so bus 1 is valid for RPi and Edison */
+static li2c_mode_t i2c_mode = LI2C_RPI;
+
+/** i2c bus file descriptors */
+static int i2c_bus[LI2C_MAX_BUS];
+
+static int is_valid_bus(uint8_t bus)
+{
+ if (i2c_mode == LI2C_RPI) {
+ if (bus > PI2C_MAX_BUS)
+ return 0;
+ return 1;
+ }
+ if (i2c_mode == LI2C_EDISON) {
+ if (bus > EDI2C_MAX_BUS)
+ return 0;
+ return 1;
+ }
+ return 0;
+}
+
+/* select I2C mode - Raspberry or Edison for bus index validation */
+int li2c_init(li2c_mode_t mode)
+{
+ for(int i = 0; i < LI2C_MAX_BUS; i++)
+ i2c_bus[i] = -1;
+ i2c_mode = mode;
+ return 0;
+}
+
+/** open I2C bus if not opened yet and store file descriptor */
+int li2c_open(uint8_t bus)
+{
+ char bus_name[64];
+
+ if (!is_valid_bus(bus))
+ return -1;
+
+ // already opened?
+ if (i2c_bus[bus] >= 0)
+ return 0;
+
+ // open i2c bus and store file descriptor
+ sprintf(bus_name, "/dev/i2c-%u", bus);
+
+ if ((i2c_bus[bus] = open(bus_name, O_RDWR)) < 0)
+ return -1;
+
+ return 0;
+}
+
+/** close I2C bus file descriptor */
+int li2c_close(uint8_t bus)
+{
+ if (!is_valid_bus(bus))
+ return -1;
+
+ if (i2c_bus[bus] >= 0)
+ close(i2c_bus[bus]);
+ i2c_bus[bus] = -1;
+
+ return 0;
+}
+
+/** select I2C device for li2c_write() calls */
+int li2c_select(uint8_t bus, uint8_t slave)
+{
+ if (!is_valid_bus(bus) || (i2c_bus[bus] < 0))
+ return -1;
+
+ return ioctl(i2c_bus[bus], I2C_SLAVE, slave);
+}
+
+/** write to I2C device selected by li2c_select() */
+int li2c_write(uint8_t bus, const uint8_t *data, uint32_t len)
+{
+ if (!is_valid_bus(bus) || (i2c_bus[bus] < 0))
+ return -1;
+
+ if (write(i2c_bus[bus], data, len) != (ssize_t)len)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/* BSD License
+ Copyright (c) 2015 Andrey Chilikin https://github.com/achilikin
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ Basic Linux I2C wrapper for Raspberry Pi and Intel Edison,
+ only write is supported
+
+ For Raspberry Pi make sure that RPi i2c driver is enabled, check following
+ files:
+ /etc/modprobe.d/raspi-blacklist.conf
+ #blacklist i2c-bcm2708
+ /etc/modules
+ i2c-dev
+ /etc/modprobe.d/i2c.conf
+ options i2c_bcm2708 baudrate=400000
+*/
+
+#ifndef __LI2C_H__
+#define __LI2C_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PI2C_BUS0 0 /*< P5 header I2C bus */
+#define PI2C_BUS1 1 /*< P1 header I2C bus */
+#define PI2C_MAX_BUS PI2C_BUS1
+
+#define EDI2C_BUS1 1
+#define EDI2C_BUS6 6
+#define EDI2C_MAX_BUS EDI2C_BUS6
+
+#define LI2C_MAX_BUS 8
+
+typedef enum li2c_mode { LI2C_RPI, LI2C_EDISON } li2c_mode_t;
+
+int li2c_init(li2c_mode_t mode); /*< init I2C bus */
+int li2c_open(uint8_t bus); /*< open I2C bus */
+int li2c_close(uint8_t bus); /*< close I2C bus */
+int li2c_select(uint8_t bus, uint8_t slave); /*< select I2C slave */
+int li2c_write(uint8_t bus, const uint8_t *data, uint32_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
/* BSD License
- Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+ Copyright (c) 2015 Andrey Chilikin https://github.com/achilikin
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
#include <unistd.h>
#include "bdf.h"
-#include "pi2c.h"
+#include "li2c.h"
#include "rterm.h"
#include "ossd_i2c.h"
printf(" native: do not adjust font height 8 pixels\n");
printf(" ascender H: add extra ascender of H pixels per glyph\n");
printf(" rotate: rotate glyphs' bitmaps CCW\n");
- printf(" display A: show converted font on SSD1306 compatible display\n");
- printf(" using I2C bus 1, hexadecimal address A (default 3C)\n");
+ printf(" display show converted font on SSD1306 compatible display\n");
+ printf(" i2c_bus B: I2C bus for SSD1306 compatible display (default 1)\n");
+ printf(" i2c_addr A: I2C address for SSD1306 compatible display (default 0x3C)\n");
printf(" updown: display orientation is upside down\n");
}
{
char *file;
bdfe_t *font;
- int flags = 0;
+ int flags = 0;
+ uint8_t i2c_bus = 1;
uint8_t i2c_address = 0x3C;
uint8_t orientation = 0;
uint32_t gidx = 0;
if (arg_is(argv[i], "-r", "rotate"))
flags |= BDF_ROTATE;
- if (arg_is(argv[i], "-d", "display")) {
- flags |= DISPLAY_FONT;
- if (i < argc && isxdigit(*argv[i+1])) {
- i++;
- uint32_t i2ca = strtoul(argv[i], NULL, 16);
- if (i2ca && (i2ca < 0x78))
- i2c_address = i2ca;
- }
+ if (arg_is(argv[i], "-B", "i2c_bus")) {
+ i++;
+ uint32_t i2bus = strtoul(argv[i], NULL, 16);
+ if (i2bus && (i2bus < LI2C_MAX_BUS))
+ i2c_bus = i2bus;
}
+ if (arg_is(argv[i], "-A", "i2c_addr")) {
+ i++;
+ uint32_t i2ca = strtoul(argv[i], NULL, 16);
+ if (i2ca && (i2ca < 0x78))
+ i2c_address = i2ca;
+ }
+
+ if (arg_is(argv[i], "-d", "display"))
+ flags |= DISPLAY_FONT;
+
if (arg_is(argv[i], "-u", "updown"))
orientation = OSSD_UPDOWN;
}
return 0;
}
- if (pi2c_open(PI2C_BUS) < 0) {
- fprintf(stderr, "Unable to open i2c bus %d\n", PI2C_BUS);
+ if (i2c_bus > PI2C_MAX_BUS)
+ li2c_init(LI2C_EDISON);
+ else
+ li2c_init(LI2C_RPI);
+
+ if (li2c_open(i2c_bus) < 0) {
+ fprintf(stderr, "Unable to open i2c bus %d\n", i2c_bus);
+ free(font);
+ return -1;
+ }
+ if (li2c_select(i2c_bus, i2c_address) < 0) {
+ fprintf(stderr, "Unable to open select device at %02X\n", i2c_address);
free(font);
return -1;
}
- pi2c_select(PI2C_BUS, i2c_address);
ossd_font_t of;
of.gw = font->gw;
of.gn = (uint8_t)font->chars;
of.font = font->font;
- ossd_init(orientation);
+ ossd_init(i2c_bus, orientation);
ossd_set_user_font(&of, NULL);
ossd_select_font(OSSD_FONT_USER);
exit:
stdin_mode(TERM_MODE_CAN);
- pi2c_close(PI2C_BUS);
+ li2c_close(i2c_bus);
free(font);
return 0;
/* BSD License
- Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+ Copyright (c) 2015 Andrey Chilikin https://github.com/achilikin
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
#include "ossd_i2c.h"
-#if (OSSD_TARGET == OSSD_AVR)
+#if (OSSD_TARGET == OSSD_IF_AVR)
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/atomic.h>
#define pgm_read_byte(x) (*((uint8_t *)x))
#include <alloca.h>
#include <memory.h>
-#if (OSSD_TARGET == OSSD_RPI)
- #include "pi2c.h"
+ #if (OSSD_TARGET == OSSD_IF_WIRE)
+ #include "wi2c.h"
+ #else
+ #include "li2c.h"
#endif
#endif
static uint8_t _cfont;
static uint8_t _mode;
+static uint8_t _i2c_val;
uint8_t ossd_select_font(uint8_t font)
{
_ofont[OSSD_FONT_USER].font = nfont->font;
}
-#if (OSSD_TARGET == OSSD_AVR)
+#if (OSSD_TARGET == OSSD_IF_LINUX)
static void ossd_send_byte(uint8_t dc, uint8_t data)
{
- i2c_start(I2C_OSSD | I2C_WRITE);
+ uint8_t buf[2];
+ buf[0] = dc;
+ buf[1] = data;
+
+ li2c_write(_i2c_val, buf, 2);
+}
+
+static void ossd_cmd_arg(uint8_t cmd, uint8_t arg)
+{
+ uint8_t data[3];
+ data[0] = OSSD_CMD;
+ data[1] = cmd;
+ data[2] = arg;
+ li2c_write(_i2c_val, data, 3);
+}
+
+static void ossd_cmd_arg2(uint8_t cmd, uint8_t arg1, uint8_t arg2)
+{
+ uint8_t data[4];
+ data[0] = OSSD_CMD;
+ data[1] = cmd;
+ data[2] = arg1;
+ data[3] = arg2;
+ li2c_write(_i2c_val, data, 4);
+}
+
+static void ossd_fill_line(uint8_t data, uint8_t num)
+{
+ uint8_t *buf = (uint8_t *)alloca(num+1);
+ memset(buf, data, num+1);
+ buf[0] = OSSD_DATA;
+ li2c_write(_i2c_val, buf, num+1);
+}
+
+#else
+
+static void ossd_send_byte(uint8_t dc, uint8_t data)
+{
+ i2c_start(_i2c_val);
i2c_write(dc);
i2c_write(data);
i2c_stop();
static void ossd_cmd_arg(uint8_t cmd, uint8_t arg)
{
- i2c_start(I2C_OSSD | I2C_WRITE);
+ i2c_start(_i2c_val);
i2c_write(OSSD_CMD);
i2c_write(cmd);
i2c_write(arg);
static void ossd_cmd_arg2(uint8_t cmd, uint8_t arg1, uint8_t arg2)
{
- i2c_start(I2C_OSSD | I2C_WRITE);
+ i2c_start(_i2c_val);
i2c_write(OSSD_CMD);
i2c_write(cmd);
i2c_write(arg1);
i2c_stop();
}
+#if (OSSD_TARGET == OSSD_IF_AVR)
+
static void ossd_fill_line(uint8_t data, uint8_t num)
{
- i2c_start(I2C_OSSD | I2C_WRITE);
+ uint8_t i;
+ i2c_start(_i2c_val);
i2c_write(OSSD_DATA);
- for(uint8_t i = 0; i < num; i++)
+ for(i = 0; i < num; i++)
i2c_write(data);
i2c_stop();
}
#else
-static void ossd_send_byte(uint8_t dc, uint8_t data)
-{
- uint8_t buf[2];
- buf[0] = dc;
- buf[1] = data;
-
- pi2c_write(PI2C_BUS, buf, 2);
-}
-
-static void ossd_cmd_arg(uint8_t cmd, uint8_t arg)
-{
- uint8_t data[3];
- data[0] = OSSD_CMD;
- data[1] = cmd;
- data[2] = arg;
- pi2c_write(PI2C_BUS, data, 3);
-}
-
-static void ossd_cmd_arg2(uint8_t cmd, uint8_t arg1, uint8_t arg2)
-{
- uint8_t data[4];
- data[0] = OSSD_CMD;
- data[1] = cmd;
- data[2] = arg1;
- data[3] = arg2;
- pi2c_write(PI2C_BUS, data, 4);
-}
+static inline void ossd_data(uint8_t cmd);
static void ossd_fill_line(uint8_t data, uint8_t num)
{
- uint8_t *buf = (uint8_t *)alloca(num+1);
- memset(buf, data, num+1);
- buf[0] = OSSD_DATA;
- pi2c_write(PI2C_BUS, buf, num+1);
+ uint8_t i;
+ for(i = 0; i < num; i++)
+ ossd_data(data);
}
+#endif
#endif
void ossd_fill_screen(uint8_t data)
{
// fill full screen line by line
- for(uint8_t line = 0; line < 8; line++) {
+ uint8_t line;
+ for(line = 0; line < 8; line++) {
ossd_goto(line, 0);
ossd_fill_line(data, 128);
}
// in case if new text is shorter than previous one
// we clean line up to x position
if (x) {
- for(uint8_t i = 0; i < (gh+7)/8; i++) {
+ uint8_t i;
+ for(i = 0; i < (gh+7)/8; i++) {
ossd_goto(line + i, 0);
ossd_fill_line(0, x);
}
// in case if new text is shorter than previous one
// we clean to the end of the line
if (x) {
- for(uint8_t i = 0; i < (gh+7)/8; i++) {
+ uint8_t i;
+ for(i = 0; i < (gh+7)/8; i++) {
ossd_goto(line + i, x + len);
ossd_fill_line(0, x);
}
line = (line + (gh+7)/8) & 0x07;
}
ossd_goto(line, x);
- for(uint8_t i = 0; i < gb; i++) {
+ uint8_t i;
+ for(i = 0; i < gb; i++) {
uint8_t d = pgm_read_byte(&font[idx+i]);
d ^= rev;
if (under && (gh == 8 || i > (gw - 1)))
ossd_set_addr_mode(cmode);
}
-void ossd_init(uint8_t orientation)
+void ossd_init(uint8_t i2c_val, uint8_t orientation)
{
_mode = 0xFF;
+ _i2c_val = i2c_val;
+#if (OSSD_TARGET == OSSD_IF_AVR)
+ _i2c_val = (_i2c_val << 1) | I2C_WRITE;
+#endif
// set all default values
ossd_cmd(OSSD_SET_SLEEP_ON);
ossd_cmd_arg(OSSD_SET_MUX_RATIO, 63);
ossd_cmd(OSSD_SET_INVERSE_OFF);
ossd_cmd_arg(OSSD_SET_CHARGE_PUMP, OSSD_CHARGE_PUMP_ON);
ossd_set_addr_mode(OSSD_ADDR_MODE_PAGE);
- ossd_fill_screen(0);
+ ossd_fill_screen(0x00);
ossd_cmd(OSSD_SET_SLEEP_OFF);
ossd_goto(0, 0);
}
/* BSD License
- Copyright (c) 2014 Andrey Chilikin https://github.com/achilikin
+ Copyright (c) 2015 Andrey Chilikin https://github.com/achilikin
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
#endif
#endif
-/** Target platform */
-#define OSSD_AVR 1 /*< AVR compiler */
-#define OSSD_RPI 2 /*< Raspberry Pi */
-#define OSSD_GALILEO 3 /*< Reserved for Intel Galileo */
+/** Target interface */
+#define OSSD_IF_AVR 1 /*< AVR compiler */
+#define OSSD_IF_LINUX 2 /*< Linux native Intel Edison or Raspberry Pi */
+#define OSSD_IF_WIRE 3 /*< Arduino Wire Interface on Intel Edison */
+#ifndef OSSD_TARGET
#ifdef __AVR_ARCH__
- #define OSSD_TARGET OSSD_AVR
+ #define OSSD_TARGET OSSD_IF_AVR
#else
- #define OSSD_TARGET OSSD_RPI
+ #define OSSD_TARGET OSSD_IF_LINUX
+#endif
#endif
-#if (OSSD_TARGET == OSSD_AVR)
- #define I2C_OSSD (0x3C << 1)
-#else
+#if (OSSD_TARGET != OSSD_IF_AVR)
#include <stdint.h>
#endif
use ossd_init(OSSD_UPDOWN) to rotate screen
*/
#define OSSD_UPDOWN 0x09
+#define OSSD_NORMAL 0x00
-/** set default parameters */
-void ossd_init(uint8_t orientation);
+/**
+ set default parameters
+ for AVR i2c_val is I2C address
+ for Linux (Edison, RPi) i2c_val is I2C bus
+ */
+void ossd_init(uint8_t i2c_val, uint8_t orientation);
/** fill screen with specified pattern */
void ossd_fill_screen(uint8_t data);