Skip to content

Commit

Permalink
Initial support for TM1638 modules
Browse files Browse the repository at this point in the history
  • Loading branch information
conejoninja committed Feb 11, 2018
1 parent d5e7393 commit f3150c7
Show file tree
Hide file tree
Showing 3 changed files with 440 additions and 0 deletions.
288 changes: 288 additions & 0 deletions drivers/gpio/tm1638_driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
package gpio

import (
"math"

"strings"

"gobot.io/x/gobot"
)

const (
TM1638None = iota
TM1638Red
TM1638Green
)

const (
TM1638DataCmd = 0x40
TM1638DispCtrl = 0x80
TM1638AddrCmd = 0xC0

TM1638WriteDisp = 0x00
TM1638ReadKeys = 0x02
TM1638FixedAddr = 0x04
)

// TM1638Driver is the gobot driver for modules based on the TM1638, which has 8 7-segment displays, 8 LEDs and 8 buttons
// Buttons are not supported
//
// Datasheet EN: https://retrocip.cz/files/tm1638.pdf
// Datasheet CN: http://www.datasheetspdf.com/pdf/775356/TitanMicro/TM1638/1
//
// Ported from the Arduino driver https://github.com/rjbatista/tm1638-library

type TM1638Driver struct {
pintClock *DirectPinDriver
pinData *DirectPinDriver
pinStrobe *DirectPinDriver
fonts map[string]byte
name string
connection gobot.Connection
gobot.Commander
}

// NewTM1638Driver return a new TM1638Driver given a gobot.Connection and the clock, data and strobe pins
func NewTM1638Driver(a gobot.Connection, clockPin string, dataPin string, strobePin string) *TM1638Driver {
t := &TM1638Driver{
name: gobot.DefaultName("TM1638"),
pintClock: NewDirectPinDriver(a, clockPin),
pinData: NewDirectPinDriver(a, dataPin),
pinStrobe: NewDirectPinDriver(a, strobePin),
fonts: NewTM1638Fonts(),
connection: a,
Commander: gobot.NewCommander(),
}

/* TODO : Add commands */

return t
}

// Start initializes the tm1638, it uses a SPI-like communication protocol
func (t *TM1638Driver) Start() (err error) {

t.pinStrobe.On()
t.pintClock.On()

t.sendCommand(TM1638DataCmd)
t.sendCommand(TM1638DispCtrl | 8 | 7)

t.pinStrobe.Off()
t.send(TM1638AddrCmd)
for i := 0; i < 16; i++ {
t.send(TM1638WriteDisp)
}
t.pinStrobe.On()

return
}

// Halt implements the Driver interface
func (t *TM1638Driver) Halt() (err error) { return }

// Name returns the TM1638Drivers name
func (t *TM1638Driver) Name() string { return t.name }

// SetName sets the TM1638Drivers name
func (t *TM1638Driver) SetName(n string) { t.name = n }

// Connection returns the TM1638Driver Connection
func (t *TM1638Driver) Connection() gobot.Connection {
return t.connection
}

// sendCommand is an auxiliary function to send commands to the TM1638 module
func (t *TM1638Driver) sendCommand(cmd byte) {
t.pinStrobe.Off()
t.send(cmd)
t.pinStrobe.On()
}

// send writes data on the module
func (t *TM1638Driver) send(data byte) {
for i := 0; i < 8; i++ {
t.pintClock.Off()

if (data & 1) > 0 {
t.pinData.On()
} else {
t.pinData.Off()
}
data >>= 1

t.pintClock.On()
}
}

// sendData is an auxiliary function to send data to the TM1638 module
func (t *TM1638Driver) sendData(address byte, data byte) {
t.sendCommand(TM1638DataCmd | TM1638FixedAddr)
t.pinStrobe.Off()
t.send(TM1638AddrCmd | address)
t.send(data)
t.pinStrobe.On()
}

// SetLED changes the color (TM1638None, TM1638Red, TM1638Green) of the specific LED
func (t *TM1638Driver) SetLED(color byte, pos byte) {
if pos > 7 {
return
}
t.sendData((pos<<1)+1, color)
}

// SetDisplay cuts and sends a byte array to the display (without dots)
func (t *TM1638Driver) SetDisplay(data []byte) {
minLength := int(math.Min(8, float64(len(data))))
for i := 0; i < minLength; i++ {
t.SendChar(byte(i), data[i], false)
}
}

// SetDisplayText cuts and sends a string to the display (without dots)
func (t *TM1638Driver) SetDisplayText(text string) {
data := t.fromStringToByteArray(text)
minLength := int(math.Min(8, float64(len(data))))
for i := 0; i < minLength; i++ {
t.SendChar(byte(i), data[i], false)
}
}

// SendChar sends one byte to the specific position in the display
func (t *TM1638Driver) SendChar(pos byte, data byte, dot bool) {
if pos > 7 {
return
}
var dotData byte
if dot {
dotData = TM1638DispCtrl
}
t.sendData(pos<<1, data|(dotData))
}

// fromStringToByteArray translates a string to a byte array with the corresponding representation for the 7-segment LCD, return and empty character if the font is not available
func (t *TM1638Driver) fromStringToByteArray(str string) []byte {
chars := strings.Split(str, "")
data := make([]byte, len(chars))

for index, char := range chars {
if val, ok := t.fonts[char]; ok {
data[index] = val
}
}
return data
}

// AddFonts adds new custom fonts or modify the representation of existing ones
func (t *TM1638Driver) AddFonts(fonts map[string]byte) {
for k, v := range fonts {
t.fonts[k] = v
}
}

// ClearFonts removes all the fonts from the driver
func (t *TM1638Driver) ClearFonts() {
t.fonts = make(map[string]byte)
}

// NewTM1638Fonts returns a map with fonts and their corresponding byte for proper representation on the 7-segment LCD
func NewTM1638Fonts() map[string]byte {
return map[string]byte{
" ": 0x00,
"!": 0x86,
"'": 0x22,
"#": 0x7E,
"$": 0x6D,
"%": 0x00,
"&": 0x00,
"\"": 0x02,
"(": 0x30,
")": 0x06,
"*": 0x63,
"+": 0x00,
",": 0x04,
"-": 0x40,
".": 0x80,
"/": 0x52,
"0": 0x3F,
"1": 0x06,
"2": 0x5B,
"3": 0x4F,
"4": 0x66,
"5": 0x6D,
"6": 0x7D,
"7": 0x27,
"8": 0x7F,
"9": 0x6F,
":": 0x00,
";": 0x00,
"<": 0x00,
"=": 0x48,
">": 0x00,
"?": 0x53,
"@": 0x5F,
"A": 0x77,
"B": 0x7F,
"C": 0x39,
"D": 0x3F,
"E": 0x79,
"F": 0x71,
"G": 0x3D,
"H": 0x76,
"I": 0x06,
"J": 0x1F,
"K": 0x69,
"L": 0x38,
"M": 0x15,
"N": 0x37,
"O": 0x3F,
"P": 0x73,
"Q": 0x67,
"R": 0x31,
"S": 0x6D,
"T": 0x78,
"U": 0x3E,
"V": 0x2A,
"W": 0x1D,
"X": 0x76,
"Y": 0x6E,
"Z": 0x5B,
"[": 0x39,
"\\": 0x64, // (this can't be the last char on a line, even in comment or it'll concat)
"]": 0x0F,
"^": 0x00,
"_": 0x08,
"`": 0x20,
"a": 0x5F,
"b": 0x7C,
"c": 0x58,
"d": 0x5E,
"e": 0x7B,
"f": 0x31,
"g": 0x6F,
"h": 0x74,
"i": 0x04,
"j": 0x0E,
"k": 0x75,
"l": 0x30,
"m": 0x55,
"n": 0x54,
"o": 0x5C,
"p": 0x73,
"q": 0x67,
"r": 0x50,
"s": 0x6D,
"t": 0x78,
"u": 0x1C,
"v": 0x2A,
"w": 0x1D,
"x": 0x76,
"y": 0x6E,
"z": 0x47,
"{": 0x46,
"|": 0x06,
"}": 0x70,
"~": 0x01,
}
}
72 changes: 72 additions & 0 deletions drivers/gpio/tm1638_driver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package gpio

import (
"strings"
"testing"

"gobot.io/x/gobot"
"gobot.io/x/gobot/gobottest"
)

var _ gobot.Driver = (*TM1638Driver)(nil)

// --------- HELPERS
func initTestTM1638Driver() (driver *TM1638Driver) {
driver, _ = initTestTM1638DriverWithStubbedAdaptor()
return
}

func initTestTM1638DriverWithStubbedAdaptor() (*TM1638Driver, *gpioTestAdaptor) {
adaptor := newGpioTestAdaptor()
return NewTM1638Driver(adaptor, "1", "2", "3"), adaptor
}

// --------- TESTS
func TestTM1638Driver(t *testing.T) {
var a interface{} = initTestTM1638Driver()
_, ok := a.(*TM1638Driver)
if !ok {
t.Errorf("NewTM1638Driver() should have returned a *TM1638Driver")
}
}

func TestTM1638DriverStart(t *testing.T) {
d := initTestTM1638Driver()
gobottest.Assert(t, d.Start(), nil)
}

func TestTM1638DriverHalt(t *testing.T) {
d := initTestTM1638Driver()
gobottest.Assert(t, d.Halt(), nil)
}

func TestTM1638DriverDefaultName(t *testing.T) {
d := initTestTM1638Driver()
gobottest.Assert(t, strings.HasPrefix(d.Name(), "TM1638"), true)
}

func TestTM1638DriverSetName(t *testing.T) {
d := initTestTM1638Driver()
d.SetName("mybot")
gobottest.Assert(t, d.Name(), "mybot")
}

func TestTM1638DriverFromStringToByteArray(t *testing.T) {
d := initTestTM1638Driver()
data := d.fromStringToByteArray("Hello World")
gobottest.Assert(t, []byte{0x76, 0x7B, 0x30, 0x30, 0x5C, 0x00, 0x1D, 0x5C, 0x50, 0x30, 0x5E}, data)
}

func TestTM1638DriverAddFonts(t *testing.T) {
d := initTestTM1638Driver()
d.AddFonts(map[string]byte{"µ": 0x1C, "ß": 0x7F})
data := d.fromStringToByteArray("µß")
gobottest.Assert(t, []byte{0x1C, 0x7F}, data)
}

func TestTM1638DriverClearFonts(t *testing.T) {
d := initTestTM1638Driver()
d.ClearFonts()
data := d.fromStringToByteArray("Hello World")
gobottest.Assert(t, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, data)
}
Loading

0 comments on commit f3150c7

Please sign in to comment.