cap1xxx: generalize cap1188 to support more devices

cap1166 can be found in Display-O-Tron HAT by Pimoroni.
pull/1/head
Marc-Antoine Ruel 8 years ago
parent c0f73d6f9a
commit f7ddfe8f13

@ -2,19 +2,40 @@
// Use of this source code is governed under the Apache License, Version 2.0 // Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
// Package cap1188 controls a Microchip cap1188 device over I²C. // Package cap1xxx controls a Microchip
// cap1105/cap1106/cap1114/cap1133/cap1126/cap1128/cap1166/cap1188 device over
// I²C.
// //
// The cap1188 device is a 8 channel capacitive touch sensor with 8 LED drivers. // The cap1xxx devices are a 3/5/6/8/14 channel capacitive touch sensor with
// 2/3/6/8/11 LED drivers.
// //
// Datasheet // Datasheet
// //
// The official data sheet can be found here: // 3 sensors, 3 LEDs:
// http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1133.pdf
// //
// 5-6 sensors, no LED:
// http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1105_CAP1106.pdf
//
// 6 sensors, 2 LEDs:
// http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1126.pdf
//
// 6 sensors, 6 LEDs:
// http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1166.pdf
//
// 8 sensors, 2 LEDs:
// http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1128.pdf
//
// 8 sensors, 8 LEDs:
// http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1188.pdf // http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1188.pdf
package cap1188 //
// 14 sensors, 11 LEDs:
// http://ww1.microchip.com/downloads/en/DeviceDoc/CAP1114.pdf
package cap1xxx
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"log" "log"
"strconv" "strconv"
@ -53,7 +74,8 @@ func (i TouchStatus) String() string {
return touchStatusName[touchStatusIndex[i]:touchStatusIndex[i+1]] return touchStatusName[touchStatusIndex[i]:touchStatusIndex[i+1]]
} }
// NewI2C returns a new device that communicates over I²C to cap1188. // NewI2C returns a new device that communicates over I²C to
// one of the supported cap1xxx device.
// //
// Use default options if nil is used. // Use default options if nil is used.
func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) { func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) {
@ -68,36 +90,37 @@ func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Printf("cap1188: Connected via I²C address: %#x", addr)
return d, nil return d, nil
} }
/* /*
// NewSPI returns an object that communicates over SPI to cap1188 environmental // NewSPI returns an object that communicates over SPI to cap1xxx touch
// sensor. // sensor.
// //
// TODO(mattetti): Expose once implemented and tested. // TODO(mattetti): Expose once implemented and tested.
func NewSPI(p spi.Port, opts *Opts) (*Dev, error) { func NewSPI(p spi.Port, opts *Opts) (*Dev, error) {
return nil, fmt.Errorf("cap1188: not implemented") return nil, fmt.Errorf("cap1xxx: not implemented")
} }
*/ */
// Dev is a handle to a cap1188. // Dev is a handle to a device of the cap1xxx family.
type Dev struct { type Dev struct {
c mmr.Dev8 c mmr.Dev8
opts Opts opts Opts
isSPI bool isSPI bool
inputStatuses [8]TouchStatus inputStatuses []TouchStatus
numLEDs int
lastReset time.Time lastReset time.Time
} }
func (d *Dev) String() string { func (d *Dev) String() string {
return fmt.Sprintf("cap1188{%s}", d.c.Conn) return fmt.Sprintf("cap1xxx{%s}", d.c.Conn)
} }
// Halt is a noop for the cap1188. // Halt is a noop for the cap1xxx.
func (d *Dev) Halt() error { func (d *Dev) Halt() error {
// TODO(maruel): Turn off the LEDs?
return nil return nil
} }
@ -176,13 +199,13 @@ func (d *Dev) AllLEDs(on bool) error {
if d.opts.LinkedLEDs { if d.opts.LinkedLEDs {
return wrapf("can't manually set LEDs when they are linked to sensors") return wrapf("can't manually set LEDs when they are linked to sensors")
} }
// TODO(maruel): support > 8 LEDs.
var v byte
if on { if on {
if err := d.c.WriteUint8(regLEDOutputControl, 0xff); err != nil { v = 0xFF
return wrapf("failed to turn all LEDs on: %v", err)
}
} }
if err := d.c.WriteUint8(regLEDOutputControl, 0x00); err != nil { if err := d.c.WriteUint8(regLEDOutputControl, v); err != nil {
return wrapf("failed to turn all LEDs off: %v", err) return wrapf("failed to set all LEDs: %v", err)
} }
return nil return nil
} }
@ -194,12 +217,13 @@ func (d *Dev) SetLED(idx int, state bool) error {
if d.opts.LinkedLEDs { if d.opts.LinkedLEDs {
return wrapf("can't manually set LEDs when they are linked to sensors") return wrapf("can't manually set LEDs when they are linked to sensors")
} }
if idx > 7 || idx < 0 { if idx >= d.numLEDs || idx < 0 {
return wrapf("invalid led idx %d", idx) return wrapf("invalid led idx %d", idx)
} }
if d.opts.Debug { if d.opts.Debug {
log.Printf("cap1188: Set LED state %d - %t", idx, state) log.Printf("cap1xxx: Set LED state %d - %t", idx, state)
} }
// TODO(maruel): support > 8 LEDs.
if state { if state {
if err := d.setBit(regLEDOutputControl, idx); err != nil { if err := d.setBit(regLEDOutputControl, idx); err != nil {
return wrapf("failed to set LED #%d to %t: %v", idx, state, err) return wrapf("failed to set LED #%d to %t: %v", idx, state, err)
@ -218,7 +242,7 @@ func (d *Dev) Reset() error {
} }
if d.opts.ResetPin != nil { if d.opts.ResetPin != nil {
if d.opts.Debug { if d.opts.Debug {
log.Println("cap1188: Resetting the device using the reset pin") log.Println("cap1xxx: Resetting the device using the reset pin")
} }
if err := d.opts.ResetPin.Out(gpio.Low); err != nil { if err := d.opts.ResetPin.Out(gpio.Low); err != nil {
return wrapf("failed to set reset pin low: %v", err) return wrapf("failed to set reset pin low: %v", err)
@ -259,10 +283,62 @@ func makeDev(c conn.Conn, isSPI bool, opts *Opts) (*Dev, error) {
} }
// Read the product id to confirm it matches our expectations. // Read the product id to confirm it matches our expectations.
if productID, err := d.c.ReadUint8(0xFD); err != nil { productID, err := d.c.ReadUint8(0xFD)
if err != nil {
return nil, wrapf("failed to read product id: %v", err) return nil, wrapf("failed to read product id: %v", err)
} else if productID != 0x50 { }
return nil, wrapf("unexpected chip id %x; is this a cap1188?", productID) switch productID {
case 0x3A: // cap1114
d.inputStatuses = make([]TouchStatus, 14)
d.numLEDs = 11
return nil, errors.New("cap1xxx: cap1114 is not yet supported")
case 0x50: // cap1188
d.inputStatuses = make([]TouchStatus, 8)
d.numLEDs = 8
case 0x51: // cap1166
d.inputStatuses = make([]TouchStatus, 6)
d.numLEDs = 6
case 0x52: // cap1128
d.inputStatuses = make([]TouchStatus, 8)
d.numLEDs = 2
case 0x53: // cap1126
d.inputStatuses = make([]TouchStatus, 6)
d.numLEDs = 2
case 0x54: // cap1133
d.inputStatuses = make([]TouchStatus, 3)
d.numLEDs = 3
case 0x55: // cap1106
// No LED.
d.inputStatuses = make([]TouchStatus, 6)
case 0x56: // cap1105
// No LED.
d.inputStatuses = make([]TouchStatus, 5)
case 0x67: // cap1206
// http://ww1.microchip.com/downloads/en/DeviceDoc/00001567B.pdf
d.inputStatuses = make([]TouchStatus, 6)
return nil, errors.New("cap1xxx: cap1206 is not yet supported")
case 0x69: // cap1296
// http://ww1.microchip.com/downloads/en/DeviceDoc/00001569B.pdf
d.inputStatuses = make([]TouchStatus, 6)
return nil, errors.New("cap1xxx: cap1296 is not yet supported")
case 0x6B: // cap1208
// http://ww1.microchip.com/downloads/en/DeviceDoc/00001570C.pdf
d.inputStatuses = make([]TouchStatus, 8)
return nil, errors.New("cap1xxx: cap1208 is not yet supported")
case 0x6D: // cap1203
// http://ww1.microchip.com/downloads/en/DeviceDoc/00001572B.pdf
d.inputStatuses = make([]TouchStatus, 3)
return nil, errors.New("cap1xxx: cap1203 is not yet supported")
case 0x6F: // cap1293
// http://ww1.microchip.com/downloads/en/DeviceDoc/00001566B.pdf
d.inputStatuses = make([]TouchStatus, 3)
return nil, errors.New("cap1xxx: cap1293 is not yet supported")
case 0x71: // cap1298
// http://ww1.microchip.com/downloads/en/DeviceDoc/00001571B.pdf
d.inputStatuses = make([]TouchStatus, 8)
return nil, errors.New("cap1xxx: cap1298 is not yet supported")
default:
return nil, wrapf("unexpected chip id %x; is this a cap1xxx?", productID)
} }
// manufacturer ID on 0xFE, should be 0x5D // manufacturer ID on 0xFE, should be 0x5D
// revision ID on 0xFF, should be 0x83 // revision ID on 0xFF, should be 0x83
@ -331,7 +407,7 @@ func makeDev(c conn.Conn, isSPI bool, opts *Opts) (*Dev, error) {
byte(0)<<1 | byte(0)<<1 |
byte(0)<<0) byte(0)<<0)
if d.opts.Debug { if d.opts.Debug {
log.Printf("cap1188: Sampling config mask: %08b", samplingConfig) log.Printf("cap1xxx: Sampling config mask: %08b", samplingConfig)
} }
if err := d.c.WriteUint8(0x24, samplingConfig); err != nil { if err := d.c.WriteUint8(0x24, samplingConfig); err != nil {
return nil, wrapf("failed to enable multitouch: %v", err) return nil, wrapf("failed to enable multitouch: %v", err)
@ -352,7 +428,7 @@ func makeDev(c conn.Conn, isSPI bool, opts *Opts) (*Dev, error) {
byte(1)<<6 | byte(0)<<5 | byte(1)<<4 | byte(1)<<6 | byte(0)<<5 | byte(1)<<4 |
byte(0)<<3 | byte(0)<<2 | byte(0)<<1 | byte(0)<<0) byte(0)<<3 | byte(0)<<2 | byte(0)<<1 | byte(0)<<0)
if d.opts.Debug { if d.opts.Debug {
log.Printf("cap1188: Sensitivity mask: %08b", sensitivity) log.Printf("cap1xxx: Sensitivity mask: %08b", sensitivity)
} }
if err := d.c.WriteUint8(0x1F, sensitivity); err != nil { if err := d.c.WriteUint8(0x1F, sensitivity); err != nil {
return nil, wrapf("failed to set sensitivity: %v", err) return nil, wrapf("failed to set sensitivity: %v", err)
@ -410,7 +486,7 @@ func makeDev(c conn.Conn, isSPI bool, opts *Opts) (*Dev, error) {
byte(0)<<1 | byte(0)<<1 |
byte(0)<<0) byte(0)<<0)
if d.opts.Debug { if d.opts.Debug {
log.Printf("cap1188: Config mask: %08b", config) log.Printf("cap1xxx: Config mask: %08b", config)
} }
if err := d.c.WriteUint8(0x20, config); err != nil { if err := d.c.WriteUint8(0x20, config); err != nil {
return nil, wrapf("failed to set the device configuration: %v", err) return nil, wrapf("failed to set the device configuration: %v", err)
@ -459,7 +535,7 @@ func makeDev(c conn.Conn, isSPI bool, opts *Opts) (*Dev, error) {
// repeat rate but not when a release is detected. // repeat rate but not when a release is detected.
intOnRel<<0) intOnRel<<0)
if d.opts.Debug { if d.opts.Debug {
log.Printf("cap1188: Config2 mask: %08b", config2) log.Printf("cap1xxx: Config2 mask: %08b", config2)
} }
if err := d.c.WriteUint8(0x44, config2); err != nil { if err := d.c.WriteUint8(0x44, config2); err != nil {
return nil, wrapf("failed to set the device configuration 2: %v", err) return nil, wrapf("failed to set the device configuration 2: %v", err)
@ -516,7 +592,7 @@ const (
var sleep = time.Sleep var sleep = time.Sleep
func wrapf(format string, a ...interface{}) error { func wrapf(format string, a ...interface{}) error {
return fmt.Errorf("cap1188: "+format, a...) return fmt.Errorf("cap1xxx: "+format, a...)
} }
var _ conn.Resource = &Dev{} var _ conn.Resource = &Dev{}

@ -4,7 +4,7 @@
// +build go1.8 // +build go1.8
package cap1188 package cap1xxx
import ( import (
"reflect" "reflect"

@ -2,7 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0 // Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
package cap1188 package cap1xxx
import ( import (
"errors" "errors"

@ -2,7 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0 // Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
package cap1188 package cap1xxx
import ( import (
"flag" "flag"
@ -46,7 +46,7 @@ func TestNewI2C(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if s := d.String(); s != "cap1188{playback(40)}" { if s := d.String(); s != "cap1xxx{playback(40)}" {
t.Fatal(s) t.Fatal(s)
} }
if err := d.Halt(); err != nil { if err := d.Halt(); err != nil {

@ -2,7 +2,7 @@
// Use of this source code is governed under the Apache License, Version 2.0 // Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
package cap1188_test package cap1xxx_test
import ( import (
"fmt" "fmt"
@ -11,7 +11,7 @@ import (
"periph.io/x/periph/conn/gpio" "periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/gpio/gpioreg" "periph.io/x/periph/conn/gpio/gpioreg"
"periph.io/x/periph/conn/i2c/i2creg" "periph.io/x/periph/conn/i2c/i2creg"
"periph.io/x/periph/experimental/devices/cap1188" "periph.io/x/periph/experimental/devices/cap1xxx"
) )
func Example() { func Example() {
@ -42,14 +42,14 @@ func Example() {
// We will configure the cap1188 by setting some options, we can start by the // We will configure the cap1188 by setting some options, we can start by the
// defaults. // defaults.
opts := cap1188.DefaultOpts opts := cap1xxx.DefaultOpts
opts.AlertPin = alertPin opts.AlertPin = alertPin
opts.ResetPin = resetPin opts.ResetPin = resetPin
// Open the device so we can detect touch events. // Open the device so we can detect touch events.
dev, err := cap1188.NewI2C(i2cBus, &opts) dev, err := cap1xxx.NewI2C(i2cBus, &opts)
if err != nil { if err != nil {
log.Fatalf("couldn't open cap1188: %v", err) log.Fatalf("couldn't open cap1xxx: %v", err)
} }
fmt.Println("Monitoring for touch events") fmt.Println("Monitoring for touch events")
@ -57,7 +57,7 @@ func Example() {
for maxTouches > 0 { for maxTouches > 0 {
if alertPin.WaitForEdge(-1) { if alertPin.WaitForEdge(-1) {
maxTouches-- maxTouches--
var statuses [8]cap1188.TouchStatus var statuses [8]cap1xxx.TouchStatus
if err := dev.InputStatus(statuses[:]); err != nil { if err := dev.InputStatus(statuses[:]); err != nil {
fmt.Printf("Error reading inputs: %v\n", err) fmt.Printf("Error reading inputs: %v\n", err)
continue continue
Loading…
Cancel
Save