summaryrefslogtreecommitdiffstats
path: root/firmware/pcf8574.c
blob: dd9bfebf8d1a236fb27404bbac06bc04c4802fd7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * PCF-8574 shift register
 *
 * Copyright (c) 2013 Michael Buesch <m@bues.ch>
 *
 * 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);
}
bues.ch cgit interface