ht16k33: add support for LED alphanumeric display (#298)

Add support to rainbowhat which is a composite of multiple devices.
pull/1/head
Alvaro Viebrantz 8 years ago committed by M-A
parent 38804e4864
commit 28f7e8009b

@ -0,0 +1,167 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package ht16k33
import (
"periph.io/x/periph/conn/i2c"
)
var digitValues = map[rune]uint16{
' ': 0x0,
'!': 0x6,
'"': 0x220,
'#': 0x12ce,
'$': 0x12ed,
'%': 0xc24,
'&': 0x235d,
'\'': 0x400,
'(': 0x2400,
')': 0x900,
'*': 0x3fc0,
'+': 0x12c0,
',': 0x800,
'-': 0xc0,
'.': 0x4000,
'/': 0xc00,
'0': 0xc3f,
'1': 0x6,
'2': 0xdb,
'3': 0x8f,
'4': 0xe6,
'5': 0x2069,
'6': 0xfd,
'7': 0x7,
'8': 0xff,
'9': 0xef,
':': 0x1200,
';': 0xa00,
'<': 0x2400,
'=': 0xc8,
'>': 0x900,
'?': 0x1083,
'@': 0x2bb,
'A': 0xf7,
'B': 0x128f,
'C': 0x39,
'D': 0x120f,
'E': 0xf9,
'F': 0x71,
'G': 0xbd,
'H': 0xf6,
'I': 0x1200,
'J': 0x1e,
'K': 0x2470,
'L': 0x38,
'M': 0x536,
'N': 0x2136,
'O': 0x3f,
'P': 0xf3,
'Q': 0x203f,
'R': 0x20f3,
'S': 0xed,
'T': 0x1201,
'U': 0x3e,
'V': 0xc30,
'W': 0x2836,
'X': 0x2d00,
'Y': 0x1500,
'Z': 0xc09,
'[': 0x39,
'\\': 0x2100,
']': 0xf,
'^': 0xc03,
'_': 0x8,
'`': 0x100,
'a': 0x1058,
'b': 0x2078,
'c': 0xd8,
'd': 0x88e,
'e': 0x858,
'f': 0x71,
'g': 0x48e,
'h': 0x1070,
'i': 0x1000,
'j': 0xe,
'k': 0x3600,
'l': 0x30,
'm': 0x10d4,
'n': 0x1050,
'o': 0xdc,
'p': 0x170,
'q': 0x486,
'r': 0x50,
's': 0x2088,
't': 0x78,
'u': 0x1c,
'v': 0x2004,
'w': 0x2814,
'x': 0x28c0,
'y': 0x200c,
'z': 0x848,
'{': 0x949,
'|': 0x1200,
'}': 0x2489,
'~': 0x520,
}
// Display is a handler to control an alphanumeric display based on ht16k33.
type Display struct {
dev *Dev
}
// NewAlphaNumericDisplay returns a Display object that communicates over I2C to ht16k33.
//
// To use on the default address, ht16k33.I2CAddr must be passed as argument.
func NewAlphaNumericDisplay(bus i2c.Bus, address uint16) (*Display, error) {
dev, err := NewI2C(bus, address)
if err != nil {
return nil, err
}
return &Display{dev: dev}, nil
}
// SetDigit at position to provided value.
func (d *Display) SetDigit(pos int, digit rune, decimal bool) error {
val := digitValues[digit]
if decimal {
val |= digitValues['.']
}
return d.dev.WriteColumn(pos, val)
}
// WriteString print string of values to the display.
//
// Characters in the string should be any ASCII value 32 to 127 (printable ASCII).
func (d *Display) WriteString(s string) (int, error) {
if err := d.dev.Halt(); err != nil {
return 0, err
}
pos := (4 - len(s))
if pos < 0 {
pos = 0
}
// Go through each character and print it on the display.
for _, ch := range s {
if ch == '.' {
// Print decimal points on the previous digit.
c := rune(s[pos-1])
if err := d.SetDigit(pos-1, c, true); err != nil {
return pos, err
}
} else {
if err := d.SetDigit(pos, ch, false); err != nil {
return pos, err
}
pos++
}
}
return pos, nil
}
// Halt clear all the display.
func (d *Display) Halt() error {
return d.dev.Halt()
}

@ -0,0 +1,16 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
// Package ht16k33 implements interfacing code to Holtek HT16K33 Alphanumeric 16x8 LED driver.
//
// More Details
//
// Datasheets
//
// http://www.holtek.com/documents/10179/116711/HT16K33v120.pdf
//
// Product Page
//
// http://www.holtek.com/productdetail/-/vg/HT16K33
package ht16k33

@ -0,0 +1,63 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package ht16k33_test
import (
"fmt"
"log"
"time"
"periph.io/x/periph/conn/i2c/i2creg"
"periph.io/x/periph/experimental/devices/ht16k33"
"periph.io/x/periph/host"
)
func Example() {
// Make sure periph is initialized.
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
bus, err := i2creg.Open("")
if err != nil {
log.Fatal(err)
}
defer bus.Close()
display, err := ht16k33.NewAlphaNumericDisplay(bus, ht16k33.I2CAddr)
if err != nil {
log.Fatal(err)
}
defer display.Halt()
if _, err := display.WriteString("ABCD"); err != nil {
log.Fatal(err)
}
time.Sleep(1 * time.Second)
if _, err := display.WriteString("GO"); err != nil {
log.Fatal(err)
}
time.Sleep(1 * time.Second)
if _, err := display.WriteString(fmt.Sprintf("%d", 1234)); err != nil {
log.Fatal(err)
}
time.Sleep(1 * time.Second)
if _, err := display.WriteString(fmt.Sprintf("%d", 60)); err != nil {
log.Fatal(err)
}
time.Sleep(1 * time.Second)
if _, err := display.WriteString(fmt.Sprintf("%5f", 23.99)); err != nil {
log.Fatal(err)
}
time.Sleep(1 * time.Second)
if _, err := display.WriteString(fmt.Sprintf("%5f", 1.45)); err != nil {
log.Fatal(err)
}
time.Sleep(1 * time.Second)
}

@ -0,0 +1,113 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package ht16k33
import (
"errors"
"periph.io/x/periph/conn/i2c"
)
// I2CAddr i2c default address.
const I2CAddr uint16 = 0x70
const (
cmdRAM = 0x00
cmdKeys = 0x40
displaySetup = 0x80
displayOff = 0x00
displayOn = 0x01
systemSetup = 0x20
oscillatorOff = 0x00
oscillatorOn = 0x01
cmdBrightness = 0xE0
)
// BlinkFrequency display frequency must be a value allowed by the HT16K33.
type BlinkFrequency byte
// Blinking frequencies.
const (
BlinkOff = 0x00
Blink2Hz = 0x02
Blink1Hz = 0x04
BlinkHalfHz = 0x06
)
// Dev is a handler to ht16k33 controller
type Dev struct {
dev i2c.Dev
}
// NewI2C returns a Dev object that communicates over I2C.
//
// To use on the default address, ht16k33.I2CAddr must be passed as argument.
func NewI2C(bus i2c.Bus, address uint16) (*Dev, error) {
dev := &Dev{dev: i2c.Dev{Bus: bus, Addr: address}}
if err := dev.init(); err != nil {
return nil, err
}
return dev, nil
}
func (d *Dev) init() error {
// Turn on the oscillator.
if _, err := d.dev.Write([]byte{systemSetup | oscillatorOn}); err != nil {
return err
}
// Turn on display
if _, err := d.dev.Write([]byte{displaySetup | displayOn}); err != nil {
return err
}
// Set no blinking.
if err := d.SetBlink(BlinkOff); err != nil {
return err
}
// Set display to full brightness.
if err := d.SetBrightness(15); err != nil {
return err
}
return nil
}
// SetBlink Blink display at specified frequency.
func (d *Dev) SetBlink(freq BlinkFrequency) error {
if _, err := d.dev.Write([]byte{displaySetup | displayOn | byte(freq)}); err != nil {
return err
}
return nil
}
// SetBrightness of entire display to specified value.
//
// Supports 16 levels, from 0 to 15.
func (d *Dev) SetBrightness(brightness int) error {
if brightness < 0 || brightness > 15 {
return errors.New("ht16k33: brightness must be between 0 and 15")
}
_, err := d.dev.Write([]byte{cmdBrightness | byte(brightness)})
return err
}
// WriteColumn set data in a given column.
func (d *Dev) WriteColumn(column int, data uint16) error {
_, err := d.dev.Write([]byte{byte(column * 2), byte(data & 0xFF), byte(data >> 8)})
return err
}
// Halt clear the contents of display buffer.
func (d *Dev) Halt() error {
for i := 0; i < 4; i++ {
if err := d.WriteColumn(i, 0); err != nil {
return err
}
}
return nil
}

@ -0,0 +1,28 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
// Package rainbowhat implements interfacing code to Pimoroni's Rainbow hat.
//
// This driver provides easy access to the peripherals available on the Rainbow Hat for Android Things:
//
// BMP280 temperature and pressure sensor (I2C)
//
// HT16K33 segment display (I2C)
//
// Capacitive buttons (GPIO)
//
// LEDs (GPIO)
//
// APA102 RGB LEDs (SPI)
//
// Piezo Buzzer (PWM)
//
// Servo header (PWM)
//
// More details
//
// Product Page
//
// https://shop.pimoroni.com/products/rainbow-hat-for-android-things
package rainbowhat

@ -0,0 +1,113 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package rainbowhat_test
import (
"fmt"
"image"
"image/color"
"log"
"os"
"os/signal"
"syscall"
"time"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/physic"
"periph.io/x/periph/devices/apa102"
"periph.io/x/periph/experimental/devices/rainbowhat"
"periph.io/x/periph/host"
)
func Example() {
// Make sure periph is initialized.
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
hat, err := rainbowhat.NewRainbowHat(&apa102.DefaultOpts)
if err != nil {
log.Fatal(err)
}
defer hat.Halt()
handleButton := func(btn gpio.PinIn, led gpio.PinOut) {
ledState := false
if err := led.Out(gpio.Low); err != nil {
log.Fatal(err)
}
for {
btn.WaitForEdge(-1)
if btn.Read() == gpio.Low {
if ledState {
if err := led.Out(gpio.High); err != nil {
log.Fatal(err)
}
} else {
if err := led.Out(gpio.Low); err != nil {
log.Fatal(err)
}
}
ledState = !ledState
}
}
}
go handleButton(hat.GetButtonA(), hat.GetLedR())
go handleButton(hat.GetButtonB(), hat.GetLedG())
go handleButton(hat.GetButtonC(), hat.GetLedB())
ledstrip := hat.GetLedStrip()
ledstrip.Intensity = 50
img := image.NewNRGBA(image.Rect(0, 0, ledstrip.Bounds().Dx(), 1))
img.SetNRGBA(0, 0, color.NRGBA{148, 0, 211, 255})
img.SetNRGBA(1, 0, color.NRGBA{75, 0, 130, 255})
img.SetNRGBA(2, 0, color.NRGBA{0, 0, 255, 255})
img.SetNRGBA(3, 0, color.NRGBA{0, 255, 0, 255})
img.SetNRGBA(4, 0, color.NRGBA{255, 255, 0, 255})
img.SetNRGBA(5, 0, color.NRGBA{255, 127, 0, 255})
img.SetNRGBA(6, 0, color.NRGBA{255, 0, 0, 255})
if err := ledstrip.Draw(ledstrip.Bounds(), img, image.Point{}); err != nil {
log.Fatalf("failed to draw: %v", err)
}
display := hat.GetDisplay()
sensor := hat.GetBmp280()
ticker := time.NewTicker(3 * time.Second)
go func() {
for {
var envi physic.Env
if err := sensor.Sense(&envi); err != nil {
log.Fatal(err)
}
temp := fmt.Sprintf("%5s", envi.Temperature)
fmt.Printf("Pressure %8s \n", envi.Pressure)
fmt.Printf("Temperature %8s \n", envi.Temperature)
if _, err := display.WriteString(temp); err != nil {
log.Fatal(err)
}
<-ticker.C
}
}()
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
go func() {
sig := <-sigs // Wait for signal
log.Println(sig)
done <- true
}()
log.Println("Press ctrl+c to stop...")
<-done // Wait
}

@ -0,0 +1,184 @@
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package rainbowhat
import (
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/i2c/i2creg"
"periph.io/x/periph/conn/spi/spireg"
"periph.io/x/periph/devices/apa102"
"periph.io/x/periph/devices/bmxx80"
"periph.io/x/periph/experimental/devices/ht16k33"
"periph.io/x/periph/host/rpi"
)
// Dev represents a Rainbow HAT (https://shop.pimoroni.com/products/rainbow-hat-for-android-things)
type Dev struct {
ledstrip *apa102.Dev
bmp280 *bmxx80.Dev
display *ht16k33.Display
buttonA gpio.PinIn
buttonB gpio.PinIn
buttonC gpio.PinIn
ledR gpio.PinOut
ledG gpio.PinOut
ledB gpio.PinOut
buzzer gpio.PinOut
servo gpio.PinOut
}
// NewRainbowHat returns a rainbowhat driver.
func NewRainbowHat(ao *apa102.Opts) (*Dev, error) {
i2cPort, err := i2creg.Open("/dev/i2c-1")
if err != nil {
return nil, err
}
spiPort, err := spireg.Open("/dev/spidev0.0")
if err != nil {
return nil, err
}
bmp280, err := bmxx80.NewI2C(i2cPort, 0x77, &bmxx80.DefaultOpts)
if err != nil {
return nil, err
}
display, err := ht16k33.NewAlphaNumericDisplay(i2cPort, ht16k33.I2CAddr)
if err != nil {
return nil, err
}
opts := *ao
opts.NumPixels = 7
ledstrip, err := apa102.New(spiPort, &opts)
if err != nil {
return nil, err
}
dev := &Dev{
ledstrip: ledstrip,
bmp280: bmp280,
display: display,
buttonA: rpi.P1_40, // GPIO21
buttonB: rpi.P1_38, // GPIO20
buttonC: rpi.P1_36, // GPIO16
ledR: rpi.P1_31, // GPIO06
ledG: rpi.P1_35, // GPIO19
ledB: rpi.P1_37, // GPIO26
buzzer: rpi.P1_33, // PWM1
servo: rpi.P1_32, // PWM0
}
if err := dev.buttonA.In(gpio.PullUp, gpio.BothEdges); err != nil {
return nil, err
}
if err := dev.buttonB.In(gpio.PullUp, gpio.BothEdges); err != nil {
return nil, err
}
if err := dev.buttonC.In(gpio.PullUp, gpio.BothEdges); err != nil {
return nil, err
}
return dev, nil
}
// GetLedStrip returns apa102.Dev seven addressable led strip.
func (d *Dev) GetLedStrip() *apa102.Dev {
return d.ledstrip
}
// GetBmp280 returns bmxx80.Dev handler.
func (d *Dev) GetBmp280() *bmxx80.Dev {
return d.bmp280
}
// GetDisplay returns ht16k33.Display with four alphanumeric digits.
func (d *Dev) GetDisplay() *ht16k33.Display {
return d.display
}
// GetButtonA returns gpio.PinIn corresponding to the A capacitive button.
func (d *Dev) GetButtonA() gpio.PinIn {
return d.buttonA
}
// GetButtonB returns gpio.PinIn corresponding to the B capacitive button.
func (d *Dev) GetButtonB() gpio.PinIn {
return d.buttonB
}
// GetButtonC returns gpio.PinIn corresponding to the C capacitive button.
func (d *Dev) GetButtonC() gpio.PinIn {
return d.buttonC
}
// GetLedR returns gpio.PinOut corresponding to the red LED.
func (d *Dev) GetLedR() gpio.PinOut {
return d.ledR
}
// GetLedG returns gpio.PinOut corresponding to the green LED.
func (d *Dev) GetLedG() gpio.PinOut {
return d.ledG
}
// GetLedB returns gpio.PinOut corresponding to the blue LED.
func (d *Dev) GetLedB() gpio.PinOut {
return d.ledB
}
// GetBuzzer returns gpio.PinOut corresponding to the buzzer pin.
func (d *Dev) GetBuzzer() gpio.PinOut {
return d.buzzer
}
// GetServo returns gpio.PinOut corresponding to the servo pin.
func (d *Dev) GetServo() gpio.PinOut {
return d.servo
}
// Halt all internal devices.
func (d *Dev) Halt() error {
if err := d.bmp280.Halt(); err != nil {
return err
}
if err := d.ledstrip.Halt(); err != nil {
return err
}
if err := d.display.Halt(); err != nil {
return err
}
if err := d.ledR.Halt(); err != nil {
return err
}
if err := d.ledG.Halt(); err != nil {
return err
}
if err := d.ledB.Halt(); err != nil {
return err
}
if err := d.buttonA.Halt(); err != nil {
return err
}
if err := d.buttonB.Halt(); err != nil {
return err
}
if err := d.buttonC.Halt(); err != nil {
return err
}
return nil
}
Loading…
Cancel
Save