1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Alphanumeric.cpp Sun Jan 10 00:30:41 2016 +0100
1.3 @@ -0,0 +1,163 @@
1.4 +/*
1.5 +Interfacing the Arduino Duemilanove to the ElecFreaks alphanumeric display
1.6 +brick.
1.7 +
1.8 +Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
1.9 +
1.10 +This program is free software; you can redistribute it and/or modify it under
1.11 +the terms of the GNU General Public License as published by the Free Software
1.12 +Foundation; either version 3 of the License, or (at your option) any later
1.13 +version.
1.14 +
1.15 +This program is distributed in the hope that it will be useful, but WITHOUT ANY
1.16 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
1.17 +PARTICULAR PURPOSE. See the GNU General Public License for more details.
1.18 +
1.19 +You should have received a copy of the GNU General Public License along
1.20 +with this program. If not, see <http://www.gnu.org/licenses/>.
1.21 +*/
1.22 +
1.23 +#include <Wire.h>
1.24 +
1.25 +const int BUFSIZE = 17;
1.26 +char inbuffer[BUFSIZE];
1.27 +uint8_t nread = 0;
1.28 +
1.29 +uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1.30 +
1.31 +void enable_clock()
1.32 +{
1.33 + Wire.beginTransmission(0x70);
1.34 + Wire.write(0x21);
1.35 + Wire.endTransmission();
1.36 +}
1.37 +
1.38 +void set_row_output()
1.39 +{
1.40 + Wire.beginTransmission(0x70);
1.41 + Wire.write(0xa0);
1.42 + Wire.endTransmission();
1.43 +}
1.44 +
1.45 +void set_dimming()
1.46 +{
1.47 + Wire.beginTransmission(0x70);
1.48 + Wire.write(0xe3); // pulse_width = 4/16
1.49 + Wire.endTransmission();
1.50 +}
1.51 +
1.52 +void enable_display()
1.53 +{
1.54 + Wire.beginTransmission(0x70);
1.55 + Wire.write(0x81); // no blinking
1.56 + Wire.endTransmission();
1.57 +}
1.58 +
1.59 +void disable_display()
1.60 +{
1.61 + Wire.beginTransmission(0x70);
1.62 + Wire.write(0x80);
1.63 + Wire.endTransmission();
1.64 +}
1.65 +
1.66 +void init_alphanumeric()
1.67 +{
1.68 + enable_clock();
1.69 + set_row_output();
1.70 + set_dimming();
1.71 +}
1.72 +
1.73 +void write_digits(uint8_t data[], uint8_t len)
1.74 +{
1.75 + uint8_t i;
1.76 +
1.77 + Wire.beginTransmission(0x70);
1.78 + Wire.write(0x00); // address = 0
1.79 +
1.80 + for (i = 0; i < len; i++)
1.81 + {
1.82 + Wire.write(data[i]);
1.83 + }
1.84 +
1.85 + Wire.endTransmission();
1.86 +}
1.87 +
1.88 +/* User interface functions. */
1.89 +
1.90 +uint8_t fromHex(char c)
1.91 +{
1.92 + if ((c >= 48) && (c <= 57))
1.93 + return c - 48;
1.94 + if ((c >= 65) && (c <= 70))
1.95 + return c - 65 + 10;
1.96 + if ((c >= 97) && (c <= 102))
1.97 + return c - 97 + 10;
1.98 + return 0;
1.99 +}
1.100 +
1.101 +void to_digits(char buffer[], uint8_t data[], uint8_t len)
1.102 +{
1.103 + uint8_t i, j, p, high, low;
1.104 +
1.105 + for (i = 0, j = 0; j < len; j += 2)
1.106 + {
1.107 + for (p = 2; p > 0; p--, i += 2)
1.108 + {
1.109 + high = fromHex(buffer[i]);
1.110 + if (high == 17)
1.111 + return;
1.112 + low = fromHex(buffer[i+1]);
1.113 + if (low == 17)
1.114 + return;
1.115 +
1.116 + /* Switch to little-endian. */
1.117 +
1.118 + data[j+p-1] = (high << 4) + low;
1.119 + }
1.120 + }
1.121 +}
1.122 +
1.123 +void setup()
1.124 +{
1.125 + Wire.begin();
1.126 + Serial.begin(115200);
1.127 +
1.128 + init_alphanumeric();
1.129 + write_digits(data, 8);
1.130 + enable_display();
1.131 +
1.132 + // Interface loop.
1.133 +
1.134 + Serial.println("?");
1.135 +}
1.136 +
1.137 +void loop()
1.138 +{
1.139 + /* Read bytes, obtaining the number read excluding any newline terminator. */
1.140 +
1.141 + if (nread += Serial.readBytesUntil('\n', inbuffer + nread, BUFSIZE - nread))
1.142 + {
1.143 + /* Handle each command, waiting for the newline. */
1.144 +
1.145 + if (nread >= 16)
1.146 + {
1.147 + to_digits(inbuffer, data, 8);
1.148 + write_digits(data, 8);
1.149 + nread = 0;
1.150 + Serial.println("OK");
1.151 + }
1.152 + else
1.153 + {
1.154 + Serial.print(nread);
1.155 + Serial.println("...");
1.156 + }
1.157 +
1.158 + Serial.flush();
1.159 + }
1.160 +}
1.161 +
1.162 +extern "C" void __cxa_pure_virtual(void) {
1.163 + while(1);
1.164 +}
1.165 +
1.166 +// tabstop=4 expandtab shiftwidth=4
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/Makefile Sun Jan 10 00:30:41 2016 +0100
2.3 @@ -0,0 +1,146 @@
2.4 +TARGET = $(notdir $(CURDIR))
2.5 +INSTALL_DIR = ../arduino-1.0.5
2.6 +PORT = /dev/ttyUSB0
2.7 +UPLOAD_RATE = 57600 # 19200
2.8 +AVRDUDE_PROGRAMMER = stk500v1
2.9 +MCU = atmega328p # atmega168
2.10 +F_CPU = 16000000
2.11 +
2.12 +LIBRARIES = $(INSTALL_DIR)/libraries
2.13 +
2.14 +EXTRA_SRC = $(LIBRARIES)/Wire/utility/twi.c
2.15 +EXTRA_CXXSRC = $(LIBRARIES)/Wire/Wire.cpp
2.16 +EXTRA_CINCS =
2.17 +EXTRA_CXXINCS = -I$(LIBRARIES)/Wire -I$(LIBRARIES)/Wire/utility
2.18 +
2.19 +
2.20 +### Internal definitions.
2.21 +
2.22 +
2.23 +ARDUINO = $(INSTALL_DIR)/hardware/arduino/cores/arduino
2.24 +VARIANT = $(INSTALL_DIR)/hardware/arduino/variants/standard
2.25 +
2.26 +SRC = $(ARDUINO)/wiring.c $(ARDUINO)/wiring_digital.c \
2.27 + $(EXTRA_SRC)
2.28 +CXXSRC = $(ARDUINO)/HardwareSerial.cpp \
2.29 + $(ARDUINO)/Print.cpp \
2.30 + $(ARDUINO)/WString.cpp \
2.31 + $(ARDUINO)/Stream.cpp \
2.32 + $(EXTRA_CXXSRC)
2.33 +FORMAT = ihex
2.34 +
2.35 +# Name of this Makefile (used for "make depend").
2.36 +MAKEFILE = Makefile
2.37 +
2.38 +# Debugging format.
2.39 +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2.
2.40 +# AVR (extended) COFF requires stabs, plus an avr-objcopy run.
2.41 +DEBUG = stabs
2.42 +
2.43 +# Place -D or -U options here
2.44 +CDEFS = -DF_CPU=$(F_CPU)
2.45 +CXXDEFS = -DF_CPU=$(F_CPU)
2.46 +
2.47 +# Place -I options here
2.48 +CINCS = -I$(ARDUINO) -I$(VARIANT) $(EXTRA_CINCS)
2.49 +CXXINCS = -I$(ARDUINO) -I$(VARIANT) $(EXTRA_CXXINCS)
2.50 +
2.51 +# Compiler flag to set the C Standard level.
2.52 +# c89 - "ANSI" C
2.53 +# gnu89 - c89 plus GCC extensions
2.54 +# c99 - ISO C99 standard (not yet fully implemented)
2.55 +# gnu99 - c99 plus GCC extensions
2.56 +CSTANDARD = -std=gnu99
2.57 +CDEBUG = -g$(DEBUG)
2.58 +CWARN = -Wall -Wstrict-prototypes
2.59 +
2.60 +OPT = s
2.61 +
2.62 +CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA)
2.63 +CXXFLAGS = $(CXXDEFS) $(CXXINCS) -O$(OPT) -ffunction-sections -fdata-sections
2.64 +LDFLAGS = -lm -Wl,--gc-sections
2.65 +
2.66 +# Combine all necessary flags and optional flags.
2.67 +# Add target processor to flags.
2.68 +ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS)
2.69 +ALL_CXXFLAGS = -mmcu=$(MCU) -I. $(CXXFLAGS)
2.70 +
2.71 +# Programming support using avrdude. Settings and variables.
2.72 +AVRDUDE_PORT = $(PORT)
2.73 +AVRDUDE_WRITE_FLASH = -U flash:w:applet/$(TARGET).hex
2.74 +AVRDUDE_FLAGS = -V -F -C /etc/avrdude.conf \
2.75 + -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \
2.76 + -b $(UPLOAD_RATE)
2.77 +
2.78 +# Program settings
2.79 +CC = avr-gcc
2.80 +CXX = avr-g++
2.81 +LD = avr-ld
2.82 +OBJCOPY = avr-objcopy
2.83 +OBJDUMP = avr-objdump
2.84 +AR = avr-ar
2.85 +SIZE = avr-size
2.86 +NM = avr-nm
2.87 +AVRDUDE = avrdude
2.88 +REMOVE = rm -f
2.89 +MV = mv -f
2.90 +
2.91 +# Define all object files.
2.92 +OBJ = $(SRC:.c=.o) $(CXXSRC:.cpp=.o)
2.93 +
2.94 +# Default target.
2.95 +all: applet_files build sizeafter
2.96 +
2.97 +build: elf hex
2.98 +
2.99 +applet_files: $(TARGET).cpp
2.100 + test -d applet || mkdir applet
2.101 + cat $(ARDUINO)/main.cpp > applet/$(TARGET).cpp
2.102 + cat $(TARGET).cpp >> applet/$(TARGET).cpp
2.103 +
2.104 +elf: applet/$(TARGET).elf
2.105 +hex: applet/$(TARGET).hex
2.106 +
2.107 +# Program the device.
2.108 +upload: applet/$(TARGET).hex
2.109 + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
2.110 +
2.111 +# Display size of file.
2.112 +HEXSIZE = $(SIZE) --target=$(FORMAT) applet/$(TARGET).hex
2.113 +ELFSIZE = $(SIZE) applet/$(TARGET).elf
2.114 +
2.115 +sizebefore:
2.116 + @if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(HEXSIZE); echo; fi
2.117 +
2.118 +sizeafter:
2.119 + @if [ -f applet/$(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(HEXSIZE); echo; fi
2.120 +
2.121 +
2.122 +### File-specific processing.
2.123 +
2.124 +
2.125 +.SUFFIXES: .elf .hex .eep .lss .sym
2.126 +
2.127 +.elf.hex:
2.128 + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
2.129 +
2.130 +applet/$(TARGET).elf: applet/$(TARGET).cpp applet/core.a
2.131 + $(CXX) $(ALL_CXXFLAGS) -o $@ applet/$(TARGET).cpp -L. applet/core.a $(LDFLAGS)
2.132 +
2.133 +applet/core.a: $(OBJ)
2.134 + @for i in $(OBJ); do echo $(AR) rcs applet/core.a $$i; $(AR) rcs applet/core.a $$i; done
2.135 +
2.136 +# Compile: create object files from C++ source files.
2.137 +.cpp.o:
2.138 + $(CXX) -c $(ALL_CXXFLAGS) $< -o $@
2.139 +
2.140 +# Compile: create object files from C source files.
2.141 +.c.o:
2.142 + $(CC) -c $(ALL_CFLAGS) $< -o $@
2.143 +
2.144 +# Target: clean project.
2.145 +clean:
2.146 + $(REMOVE) applet/$(TARGET).hex applet/$(TARGET).elf applet/$(TARGET).cpp \
2.147 + applet/$(TARGET).map applet/core.a $(OBJ)
2.148 +
2.149 +.PHONY: all build elf hex eep lss sym program coff extcoff clean applet_files sizebefore sizeafter
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/README.txt Sun Jan 10 00:30:41 2016 +0100
3.3 @@ -0,0 +1,136 @@
3.4 +From the HT16K33 datasheet [1] and EF4058 schematic [2].
3.5 +
3.6 +Addressing
3.7 +----------
3.8 +
3.9 + RAM addresses
3.10 + ROW0..7 ROW8..15
3.11 +COM0 -> digit 1, tube 1 00 01
3.12 +COM1 -> digit 2, tube 1 02 03
3.13 +COM2 -> digit 1, tube 2 04 05
3.14 +COM3 -> digit 2, tube 2 06 07
3.15 +
3.16 +ROW0/A2 |-> connected to both tubes
3.17 +ROW1/A1 |
3.18 +ROW2/A0 |
3.19 +ROW3..14 |
3.20 +
3.21 +Tube and Digit Layout
3.22 +---------------------
3.23 +
3.24 +By observing the behaviour, the tubes and digits have the following layout:
3.25 +
3.26 +tubes
3.27 +2 1
3.28 +digits
3.29 +1 2 1 2
3.30 +
3.31 +Communications
3.32 +--------------
3.33 +
3.34 +I2C commands:
3.35 +
3.36 + D15 D14 D13 D12 D11 D10 D9 D8
3.37 +setup 0 0 1 0 X X X S S=0 => standby; S=1 => operational
3.38 +ROW/INT 1 0 1 0 X X A I I=0 => ROW driver output;
3.39 + I=1 => INT output
3.40 + A=0 => active low
3.41 + A=1 => active high
3.42 +display 1 0 0 0 X B1 B0 D D=0 => display off; D=1 => display on
3.43 + B1..B0 = 0 => blinking off
3.44 + B1..B0 = 2 - log2 (blinking frequency)
3.45 + (1 => 2Hz; 2 => 1Hz; 3 => 0.5Hz)
3.46 +address 0 0 0 0 A3 A2 A1 A0 A3..A0 => set row address pointer
3.47 +dimming 1 1 1 0 P3 P2 P1 P0 P3..P0 = 16 * pulse_width - 1
3.48 + (0 => 1/16; 15 => 1)
3.49 +
3.50 +I2C data format:
3.51 +
3.52 + D7 D6 D5 D4 D3 D2 D1 D0
3.53 +ROW 7 6 5 4 3 2 1 0
3.54 +ROW 15 14 13 12 11 10 9 8
3.55 +
3.56 +When converting values to the I2C format, the most significant byte is sent
3.57 +after the least significant byte. For example, the following hexadecimal
3.58 +representation is converted as follows:
3.59 +
3.60 + 8000400020001000
3.61 +-> 80 00 40 00 20 00 10 00 (bytes in MSB, LSB order)
3.62 +-> 00 80 00 40 00 20 00 10 (bytes in LSB, MSB order)
3.63 +
3.64 +LED Digit Layout
3.65 +----------------
3.66 +
3.67 +The digits each have the following layout:
3.68 +
3.69 +LED ROWx data value remark
3.70 +
3.71 +--- ROW0 1 upper horizontal
3.72 +
3.73 +
3.74 +
3.75 + | ROW1 2 upper-right vertical
3.76 +
3.77 +
3.78 +
3.79 + ROW2 4 lower-right vertical
3.80 +
3.81 + |
3.82 +
3.83 + ROW3 8 lower horizontal
3.84 +
3.85 +---
3.86 +
3.87 + ROW4 10 lower-left vertical
3.88 +
3.89 +|
3.90 +
3.91 +| ROW5 20 upper-left vertical
3.92 +
3.93 +
3.94 +
3.95 + ROW6 40 left-middle horizontal
3.96 +-
3.97 +
3.98 +
3.99 + ROW7 80 right-middle horizontal
3.100 + -
3.101 +
3.102 +
3.103 +\ ROW8 100 top-left diagonal
3.104 +
3.105 +
3.106 +
3.107 + | ROW9 200 upper-middle vertical
3.108 +
3.109 +
3.110 +
3.111 + / ROW10 400 top-right diagonal
3.112 +
3.113 +
3.114 +
3.115 + ROW11 800 lower-right diagonal
3.116 +
3.117 + \
3.118 +
3.119 + ROW12 1000 lower-middle vertical
3.120 +
3.121 + |
3.122 +
3.123 + ROW13 2000 lower-left diagonal
3.124 +
3.125 +/
3.126 +
3.127 + ROW14 4000 decimal point
3.128 +
3.129 + .
3.130 +
3.131 + ROW15 8000 (not connected)
3.132 +
3.133 +
3.134 +
3.135 +References
3.136 +----------
3.137 +
3.138 +[1] EF4058-OCTOPUS_Alphanumeric_V1.0_SCH.pdf
3.139 +[2] EF4058-ht16K33v110.pdf
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/client.py Sun Jan 10 00:30:41 2016 +0100
4.3 @@ -0,0 +1,69 @@
4.4 +#!/usr/bin/env python
4.5 +
4.6 +"""
4.7 +A client for updating the display of the ElecFreaks alphanumeric display brick.
4.8 +
4.9 +Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk>
4.10 +
4.11 +This program is free software; you can redistribute it and/or modify it under
4.12 +the terms of the GNU General Public License as published by the Free Software
4.13 +Foundation; either version 3 of the License, or (at your option) any later
4.14 +version.
4.15 +
4.16 +This program is distributed in the hope that it will be useful, but WITHOUT ANY
4.17 +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
4.18 +PARTICULAR PURPOSE. See the GNU General Public License for more details.
4.19 +
4.20 +You should have received a copy of the GNU General Public License along
4.21 +with this program. If not, see <http://www.gnu.org/licenses/>.
4.22 +"""
4.23 +
4.24 +from serial import Serial
4.25 +from time import sleep
4.26 +import sys
4.27 +
4.28 +port = "/dev/ttyUSB0"
4.29 +rate = 115200
4.30 +
4.31 +def openPort():
4.32 + global s
4.33 + s = Serial(port, rate)
4.34 + s.setTimeout(5)
4.35 + s.readline()
4.36 +
4.37 +def closePort():
4.38 + global s
4.39 + s.close()
4.40 + s = None
4.41 +
4.42 +def writeToPort(data):
4.43 + s.write(data)
4.44 + s.flush()
4.45 + print >>sys.stderr, data.rstrip()
4.46 +
4.47 +def readFromPort():
4.48 + resp = s.readline().rstrip("\r\n")
4.49 + if resp == "?":
4.50 + resp = s.readline().rstrip("\r\n")
4.51 + return resp
4.52 +
4.53 +def console():
4.54 + try:
4.55 + while 1:
4.56 + cmd = raw_input("> ")
4.57 + if cmd:
4.58 + s.write(cmd + "\n")
4.59 + s.flush()
4.60 + print s.readline().rstrip("\r\n")
4.61 + except EOFError:
4.62 + print "Session closed."
4.63 +
4.64 +if __name__ == "__main__":
4.65 + openPort()
4.66 +
4.67 + try:
4.68 + console()
4.69 + finally:
4.70 + closePort()
4.71 +
4.72 +# vim: tabstop=4 expandtab shiftwidth=4