/* * PCF-8574 shift register * * Copyright (c) 2013 Michael Buesch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "pcf8574.h" #include "twi_master.h" #include "util.h" /* I2C base-address of the PCF-8574 */ #define PCF8574_ADDR_BASE 0x20 /* I2C base-address of the PCF-8574-A */ #define PCF8574A_ADDR_BASE 0x38 /* Wait for the scheduled transfer to finish. * If there is no scheduled transfer, this function * returns immediately. */ void pcf8574_wait(struct pcf8574_chip *chip) { twi_transfer_wait(&chip->xfer, 100); } /* Asynchronously write the output states of a PCF-8574 chip. * The actual write might not have completed after return * from this function. * chip: The PCF-8574 chip instance. * write_value: The 8-bit value to write. */ void pcf8574_write(struct pcf8574_chip *chip, uint8_t write_value) { /* Wait for previous transfer to finish, if any. */ pcf8574_wait(chip); /* Prepare the I2C transfer context. */ chip->xfer.buffer = &write_value; chip->xfer.write_size = sizeof(write_value); chip->xfer.read_size = 0; /* Trigger the I2C transfer. */ twi_transfer(&chip->xfer); } /* Synchronously read the input states of a PCF-8574 chip. * chip: The PCF-8574 chip instance. * Returns the 8-bit chip-input value. */ uint8_t pcf8574_read(struct pcf8574_chip *chip) { uint8_t read_value; /* Wait for previous transfer to finish, if any. */ pcf8574_wait(chip); /* Prepare the I2C transfer context. */ chip->xfer.buffer = &read_value; chip->xfer.write_size = 0; chip->xfer.read_size = sizeof(read_value); /* Trigger the I2C transfer and wait for it to finish. */ twi_transfer(&chip->xfer); pcf8574_wait(chip); return read_value; } /* Synchronously write the output states and read the input states * of a PCF-8574 chip. * This function first writes and then reads. * chip: The PCF-8574 chip instance. * write_value: The 8-bit value to write. * Returns the 8-bit chip-input value. */ uint8_t pcf8574_write_read(struct pcf8574_chip *chip, uint8_t write_value) { uint8_t buffer = write_value; /* Wait for previous transfer to finish, if any. */ pcf8574_wait(chip); /* Prepare the I2C transfer context. */ chip->xfer.buffer = &buffer; chip->xfer.write_size = sizeof(buffer); chip->xfer.read_size = sizeof(buffer); /* Trigger the I2C transfer and wait for it to finish. */ twi_transfer(&chip->xfer); pcf8574_wait(chip); return buffer; } /* Initialize a PCF-8574 context structure. * address: The I2C sub-address that is physically * configured on the chip pins (Lower 3 bits). * chipversion_A: 1: The chip is a PCF-8574-A. * 0: The chip is a PCF-8574. * initial_state: 1: All bits are initialized to 1. * 0: All bits are initialized to 0. */ void pcf8574_init(struct pcf8574_chip *chip, uint8_t address, bool chipversion_A, bool initial_state) { /* Initialize the I2C transfer structure. */ twi_transfer_init(&chip->xfer); /* Calculate the I2C address, based on the * Chip version and the sub-address. */ if (chipversion_A) chip->xfer.address = PCF8574A_ADDR_BASE + (address & 7); else chip->xfer.address = PCF8574_ADDR_BASE + (address & 7); /* Initially write all output states. */ pcf8574_write(chip, initial_state ? 0xFF : 0); }