ArduinoAlphanumeric

Changeset

0:a9ad2b31a8ff
2016-01-10 Paul Boddie raw files shortlog changelog graph A test of the ElecFreaks alphanumeric display brick with an Arduino Duemilanove.
Alphanumeric.cpp (file) Makefile (file) README.txt (file) client.py (file)
     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