Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- TCA9534: Added I2C GPIO expander driver (`src/dev/tca9534.h`) with shared-bus and owned-bus init, plus `TCA9534_GPIO` example.

## v8.1.0

### Features
Expand Down
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_subdirectory(SerialRead)
add_subdirectory(SR_4021)
add_subdirectory(Switch)
add_subdirectory(Switch3)
add_subdirectory(TCA9534_GPIO)
add_subdirectory(TIM_SingleCallback)

# Folders
Expand Down
3 changes: 3 additions & 0 deletions examples/TCA9534_GPIO/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
set(FIRMWARE_NAME TCA9534_GPIO)
set(FIRMWARE_SOURCES main.cpp)
include(DaisyProject)
12 changes: 12 additions & 0 deletions examples/TCA9534_GPIO/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Project Name
TARGET = TCA9534_GPIO

# Sources
CPP_SOURCES = main.cpp

# Library Locations
LIBDAISY_DIR = ../..

# Core location, and generic Makefile.
SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
include $(SYSTEM_FILES_DIR)/Makefile
65 changes: 65 additions & 0 deletions examples/TCA9534_GPIO/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/** TCA9534 GPIO expander demo
*
* Wiring (Daisy Seed I2C1 defaults):
* - SCL = D11 (PB8), SDA = D12 (PB9), +3V3, GND
* - TCA9534 at address 0x20 (A0/A1/A2 = GND), or change kAddress
* - External pull-ups on SDA/SCL (typically 2.2k–4.7k to 3V3)
* - External pull-ups on input pins (chip has no internal pull-ups)
*
* P0 is driven as an output (blink). P1 is read as an input and printed over USB serial.
*/
#include "daisy_seed.h"

using namespace daisy;

DaisySeed hw;
Tca9534 expander;

// 7-bit I2C address — do not left-shift (libDaisy v8+).
static constexpr uint8_t kAddress = 0x20;

int main(void)
{
hw.Init(true);
hw.StartLog(false);

Tca9534::Config cfg;
cfg.Defaults();
cfg.i2c_address = kAddress;
expander.Init(cfg);

// P0 = output, P1–P7 = inputs
expander.SetConfig(0xFE);
expander.WritePin(0, false);

uint32_t last_print = System::GetNow();
bool led_on = false;

while(1)
{
const uint32_t now = System::GetNow();
if(now - last_print > 250)
{
last_print = now;
led_on = !led_on;
expander.WritePin(0, led_on);

uint8_t port = 0;
bool p1 = false;
if(expander.ReadInput(port))
{
expander.ReadPin(1, p1);
hw.PrintLine("OUT0:%d IN:%02X P1:%d ok:%d",
led_on ? 1 : 0,
port,
p1 ? 1 : 0,
expander.LastTransferOk() ? 1 : 0);
}
else
{
hw.PrintLine("TCA9534 read failed (check wiring / address)");
}
}
hw.SetLed((now & 511) < 255);
}
}
1 change: 1 addition & 0 deletions src/daisy.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "dev/dps310.h"
#include "dev/lcd_hd44780.h"
#include "dev/mcp23x17.h"
#include "dev/tca9534.h"
#include "dev/max11300.h"
#include "dev/tlv493d.h"
#include "dev/dotstar.h"
Expand Down
222 changes: 222 additions & 0 deletions src/dev/tca9534.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
#pragma once
#ifndef DSY_TCA9534_H
#define DSY_TCA9534_H

#include "per/gpio.h"
#include "per/i2c.h"

namespace daisy
{
/** @addtogroup external
@{
*/

/**
* Registers for the TCA9534 8-bit I2C GPIO expander.
* See TI datasheet SCPS208 (TCA9534).
*
* Note: unlike the MCP23017, the TCA9534 has **no** internal pull-ups.
* Use external resistors on input pins.
*/
enum class Tca9534Register : uint8_t
{
INPUT = 0x00, /**< Input port */
OUTPUT = 0x01, /**< Output port */
POLARITY = 0x02, /**< Polarity inversion */
CONFIG = 0x03, /**< Configuration (1 = input, 0 = output) */
};

/** Pin direction for a single TCA9534 pin. */
enum class Tca9534Mode : uint8_t
{
INPUT,
OUTPUT,
};

/**
* Driver for the Texas Instruments TCA9534 (and TCA9534A) 8-bit I2C GPIO expander.
*
* Address range is typically 0x20–0x27 depending on A0/A1/A2 (TCA9534A uses 0x38–0x3F).
*
* Two init styles are supported:
* - Owned bus: `Init(Config)` creates/initializes an I2C peripheral (like Mcp23017).
* - Shared bus: `Init(I2CHandle&, address)` reuses an already-configured I2CHandle
* (useful when LEDs / codecs already own the same bus).
*
* Usage (shared bus):
* \code
* I2CHandle i2c;
* i2c.Init(...);
* Tca9534 exp;
* exp.Init(i2c, 0x21);
* exp.SetConfig(0xFE); // P0 output, P1–P7 inputs
* exp.WritePin(0, true);
* uint8_t port = 0;
* exp.ReadInput(port);
* \endcode
*/
class Tca9534
{
public:
struct Config
{
I2CHandle::Config i2c_config;
uint8_t i2c_address;

void Defaults()
{
i2c_config.periph = I2CHandle::Config::Peripheral::I2C_1;
i2c_config.speed = I2CHandle::Config::Speed::I2C_400KHZ;
i2c_config.mode = I2CHandle::Config::Mode::I2C_MASTER;
i2c_config.pin_config.scl = Pin(PORTB, 8);
i2c_config.pin_config.sda = Pin(PORTB, 9);
// TCA9534 default (A2/A1/A0 = 000). Do not left-shift: libDaisy
// Read/WriteDataAtAddress expect the 7-bit address.
i2c_address = 0x20;
}
};

Tca9534()
: i2c_(nullptr),
address_(0x20),
output_state_(0x00),
config_(0xFF),
last_ok_(false),
owns_i2c_(false)
{
}

/** Initialize with default Seed I2C1 pins and address 0x20. */
void Init()
{
Config cfg;
cfg.Defaults();
Init(cfg);
}

/** Initialize and own a dedicated I2C peripheral. */
void Init(const Config& config)
{
address_ = config.i2c_address;
owns_i2c_ = true;
owned_i2c_.Init(config.i2c_config);
i2c_ = &owned_i2c_;
// Datasheet power-on: all pins inputs (0xFF), outputs low.
config_ = 0xFF;
output_state_ = 0x00;
WriteReg(Tca9534Register::CONFIG, config_);
WriteReg(Tca9534Register::OUTPUT, output_state_);
}

/**
* Attach to an already-initialized I2CHandle (shared bus).
* Does not re-init the peripheral.
*/
void Init(I2CHandle& i2c, uint8_t address = 0x20)
{
i2c_ = &i2c;
address_ = address;
owns_i2c_ = false;
config_ = 0xFF;
output_state_ = 0x00;
}

/** 1 = input, 0 = output for each bit. */
bool SetConfig(uint8_t config)
{
config_ = config;
return WriteReg(Tca9534Register::CONFIG, config_);
}

bool SetOutputState(uint8_t output)
{
output_state_ = output;
return WriteReg(Tca9534Register::OUTPUT, output_state_);
}

bool PinMode(uint8_t pin, Tca9534Mode mode)
{
if(pin > 7)
return false;
if(mode == Tca9534Mode::INPUT)
config_ |= static_cast<uint8_t>(1u << pin);
else
config_ &= static_cast<uint8_t>(~(1u << pin));
return WriteReg(Tca9534Register::CONFIG, config_);
}

bool WritePin(uint8_t pin, bool high)
{
if(pin > 7)
return false;
if(high)
output_state_ |= static_cast<uint8_t>(1u << pin);
else
output_state_ &= static_cast<uint8_t>(~(1u << pin));
return WriteReg(Tca9534Register::OUTPUT, output_state_);
}

bool ReadInput(uint8_t& value)
{
last_ok_ = ReadReg(Tca9534Register::INPUT, value);
return last_ok_;
}

bool ReadPin(uint8_t pin, bool& high)
{
uint8_t value = 0;
if(!ReadInput(value) || pin > 7)
return false;
high = (value & static_cast<uint8_t>(1u << pin)) != 0;
return true;
}

bool LastTransferOk() const { return last_ok_; }
/** Alias matching early Cosmolab naming. */
bool LastReadOk() const { return last_ok_; }

uint8_t OutputState() const { return output_state_; }
uint8_t ConfigRegister() const { return config_; }
uint8_t Address() const { return address_; }

private:
bool WriteReg(Tca9534Register reg, uint8_t value)
{
if(i2c_ == nullptr)
{
last_ok_ = false;
return false;
}
last_ok_ = i2c_->WriteDataAtAddress(
address_, static_cast<uint8_t>(reg), 1, &value, 1, 10)
== I2CHandle::Result::OK;
return last_ok_;
}

bool ReadReg(Tca9534Register reg, uint8_t& value)
{
if(i2c_ == nullptr)
{
last_ok_ = false;
return false;
}
last_ok_ = i2c_->ReadDataAtAddress(
address_, static_cast<uint8_t>(reg), 1, &value, 1, 10)
== I2CHandle::Result::OK;
return last_ok_;
}

I2CHandle* i2c_;
I2CHandle owned_i2c_;
uint8_t address_;
uint8_t output_state_;
uint8_t config_;
bool last_ok_;
bool owns_i2c_;
};

/** @} */

} // namespace daisy

#endif // DSY_TCA9534_H