tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/seesaw/seesaw.go (about)

     1  // Package seesaw provides a driver implementation to communicate with Adafruit's seesaw chip.
     2  // There are many Adafruit boards that use a seesaw. Soil moisture sensors, LED keyboards, etc.
     3  //
     4  //   - Documentation: https://learn.adafruit.com/adafruit-seesaw-atsamd09-breakout/overview
     5  //   - Arduino driver: https://github.com/adafruit/Adafruit_Seesaw
     6  //   - Seesaw firmware: https://github.com/adafruit/seesaw
     7  package seesaw
     8  
     9  import (
    10  	"errors"
    11  	"strconv"
    12  	"time"
    13  
    14  	"tinygo.org/x/drivers"
    15  )
    16  
    17  // DefaultAddress is the I2C address the chips have by default. Most boards
    18  // built on top of it come with their own respective default addresses.
    19  const DefaultAddress = 0x49
    20  
    21  // DefaultReadDelay is an empirically determined delay used when reading from the device,
    22  // the one from the official library seems to be too short (250us)
    23  const DefaultReadDelay = 100 * time.Millisecond
    24  
    25  const (
    26  	seesawHwIdCodeSAMD09  = 0x55 // HW ID code for SAMD09
    27  	seesawHwIdCodeTINY8x7 = 0x87 // HW ID code for ATtiny817
    28  )
    29  
    30  type Device struct {
    31  	bus       drivers.I2C
    32  	Address   uint16
    33  	ReadDelay time.Duration
    34  }
    35  
    36  func New(bus drivers.I2C) *Device {
    37  	return &Device{
    38  		bus:       bus,
    39  		Address:   DefaultAddress,
    40  		ReadDelay: DefaultReadDelay,
    41  	}
    42  }
    43  
    44  // SoftReset triggers a soft-reset of seesaw and waits for it to be ready
    45  func (d *Device) SoftReset() error {
    46  	err := d.WriteRegister(ModuleStatusBase, FunctionStatusSwrst, 0xFF)
    47  	if err != nil {
    48  		return errors.New("failed sending soft-reset command: " + err.Error())
    49  	}
    50  
    51  	return d.waitForReset()
    52  }
    53  
    54  func (d *Device) waitForReset() error {
    55  	// give the device a little bit of time to reset
    56  	time.Sleep(time.Second)
    57  
    58  	var lastErr error
    59  	tries := 0
    60  	for ; tries < 20; tries++ {
    61  		_, err := d.readHardwareID()
    62  		if err == nil {
    63  			return nil
    64  		}
    65  		lastErr = err
    66  		time.Sleep(20 * time.Millisecond)
    67  	}
    68  	return errors.New("failed to wait for device to start: " + lastErr.Error())
    69  }
    70  
    71  func (d *Device) readHardwareID() (byte, error) {
    72  	hwid, err := d.ReadRegister(ModuleStatusBase, FunctionStatusHwId)
    73  	if err != nil {
    74  		return 0, err
    75  	}
    76  
    77  	if hwid == seesawHwIdCodeSAMD09 || hwid == seesawHwIdCodeTINY8x7 {
    78  		return hwid, nil
    79  	}
    80  
    81  	return 0, errors.New("unknown hardware ID: " + strconv.FormatUint(uint64(hwid), 16))
    82  }
    83  
    84  // WriteRegister writes a single seesaw register
    85  func (d *Device) WriteRegister(module ModuleBaseAddress, function FunctionAddress, value byte) error {
    86  	var buf [3]byte
    87  	buf[0] = byte(module)
    88  	buf[1] = byte(function)
    89  	buf[2] = value
    90  	return d.bus.Tx(d.Address, buf[:], nil)
    91  }
    92  
    93  // ReadRegister reads a single register from seesaw
    94  func (d *Device) ReadRegister(module ModuleBaseAddress, function FunctionAddress) (byte, error) {
    95  	var buf [1]byte
    96  	err := d.Read(module, function, buf[:])
    97  	if err != nil {
    98  		return 0, err
    99  	}
   100  	return buf[0], nil
   101  }
   102  
   103  // Read reads a number of bytes from the device after sending the read command and waiting 'ReadDelay'. The delays depend
   104  // on the module and function and are documented in the seesaw datasheet
   105  func (d *Device) Read(module ModuleBaseAddress, function FunctionAddress, buf []byte) error {
   106  	var cmd [2]byte
   107  	cmd[0] = byte(module)
   108  	cmd[1] = byte(function)
   109  
   110  	err := d.bus.Tx(d.Address, cmd[:], nil)
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	// This is needed for the client seesaw device to flush its RX buffer and process the command.
   116  	// See seesaw datasheet for timings for specific modules.
   117  	time.Sleep(d.ReadDelay)
   118  
   119  	return d.bus.Tx(d.Address, nil, buf)
   120  }
   121  
   122  // Write writes data into a given module and function
   123  func (d *Device) Write(module ModuleBaseAddress, function FunctionAddress, buf []byte) error {
   124  	cmd := []byte{byte(module), byte(function)}
   125  	data := append(cmd, buf...)
   126  	return d.bus.Tx(d.Address, data, nil)
   127  }