1 /* 2 * Manual GPIO-based I2C communication. 3 * 4 * Copyright (C) 2013, 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <unistd.h> 23 #include "i2c-gpio.h" 24 25 /* Declare the pins for initial I2C communications. */ 26 27 I2c_gpio::I2c_gpio(Hw::Gpio_chip *scl_chip, int scl, Hw::Gpio_chip *sda_chip, int sda) 28 { 29 _scl_chip = scl_chip; 30 _sda_chip = sda_chip; 31 I2C_SCL = scl; 32 I2C_SDA = sda; 33 } 34 35 void I2c_gpio::CLR(int pin) 36 { 37 (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Output, 0); 38 } 39 40 void I2c_gpio::SET(int pin) 41 { 42 (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Output, 1); 43 } 44 45 void I2c_gpio::IN(int pin) 46 { 47 (pin == I2C_SCL ? _scl_chip : _sda_chip)->setup(pin, Hw::Gpio_chip::Input, 0); 48 } 49 50 int I2c_gpio::PIN(int pin) 51 { 52 return (pin == I2C_SCL ? _scl_chip : _sda_chip)->get(pin); 53 } 54 55 void I2c_gpio::wait() 56 { 57 usleep(4); 58 } 59 60 /* Initiate an I2C transaction. */ 61 62 void I2c_gpio::start() 63 { 64 /* Set up the data signal. */ 65 66 CLR(I2C_SCL); 67 wait(); 68 SET(I2C_SDA); 69 70 /* During a clock pulse, produce the start condition. */ 71 72 SET(I2C_SCL); 73 wait(); 74 CLR(I2C_SDA); 75 } 76 77 /* Terminate an I2C transaction. */ 78 79 void I2c_gpio::stop() 80 { 81 /* Set up the data signal. */ 82 83 CLR(I2C_SCL); 84 wait(); 85 CLR(I2C_SDA); 86 87 /* During a clock pulse, produce the stop condition. */ 88 89 SET(I2C_SCL); 90 wait(); 91 SET(I2C_SDA); 92 } 93 94 /* Send an I2C acknowledgement to a transmitting device. */ 95 96 void I2c_gpio::ack(bool ack) 97 { 98 if (ack) 99 CLR(I2C_SDA); 100 else 101 SET(I2C_SDA); 102 103 SET(I2C_SCL); 104 wait(); 105 CLR(I2C_SCL); 106 107 IN(I2C_SDA); 108 } 109 110 /* Receive a single byte from an I2C device as part of a transaction. */ 111 112 uint8_t I2c_gpio::recv() 113 { 114 uint8_t mask, result = 0; 115 116 IN(I2C_SDA); 117 CLR(I2C_SCL); 118 119 for (mask = 0x80; mask; mask >>= 1) 120 { 121 SET(I2C_SCL); 122 wait(); 123 124 if (PIN(I2C_SDA)) 125 result |= mask; 126 127 CLR(I2C_SCL); 128 wait(); 129 } 130 131 return result; 132 } 133 134 /* Receive into a buffer a transmission of the given length in bytes. */ 135 136 void I2c_gpio::recvmany(uint8_t *data, uint8_t len) 137 { 138 uint8_t *end = data + len; 139 140 for (; data != end; data++, len--) 141 { 142 *data = recv(); 143 ack(len > 1); 144 } 145 } 146 147 /* Send a single byte of data to an I2C device as part of a transaction, 148 returning whether the transmission succeeded. */ 149 150 bool I2c_gpio::send(uint8_t data) 151 { 152 uint8_t mask; 153 bool status; 154 155 CLR(I2C_SCL); 156 157 for (mask = 0x80; mask; mask >>= 1) 158 { 159 wait(); 160 161 if (data & mask) 162 SET(I2C_SDA); 163 else 164 CLR(I2C_SDA); 165 166 SET(I2C_SCL); 167 wait(); 168 CLR(I2C_SCL); 169 } 170 171 /* Wait for acknowledgement, failing if none is given. */ 172 173 IN(I2C_SDA); 174 SET(I2C_SCL); 175 wait(); 176 177 status = PIN(I2C_SDA); 178 CLR(I2C_SCL); 179 return !status; 180 } 181 182 /* Send from the buffer provided a transmission with the given length to an I2C 183 device. */ 184 185 bool I2c_gpio::sendmany(uint8_t *data, uint8_t len) 186 { 187 uint8_t *end = data + len; 188 189 for (; data != end; data++) 190 { 191 if (!send(*data)) 192 return false; 193 194 /* NOTE: Should test for the slave holding the clock signal low. */ 195 } 196 197 return true; 198 } 199 200 201 202 // C language interface functions. 203 204 void *i2c_gpio_get_channel(void *scl_chip, int scl, void *sda_chip, int sda) 205 { 206 return (void *) new I2c_gpio(reinterpret_cast<Hw::Gpio_chip *>(scl_chip), scl, 207 reinterpret_cast<Hw::Gpio_chip *>(sda_chip), sda); 208 } 209 210 void i2c_gpio_start(void *channel) 211 { 212 static_cast<I2c_gpio *>(channel)->start(); 213 } 214 215 void i2c_gpio_stop(void *channel) 216 { 217 static_cast<I2c_gpio *>(channel)->stop(); 218 } 219 220 void i2c_gpio_ack(void *channel, bool ack) 221 { 222 static_cast<I2c_gpio *>(channel)->ack(ack); 223 } 224 225 uint8_t i2c_gpio_recv(void *channel) 226 { 227 return static_cast<I2c_gpio *>(channel)->recv(); 228 } 229 230 void i2c_gpio_recvmany(void *channel, uint8_t *data, uint8_t len) 231 { 232 static_cast<I2c_gpio *>(channel)->recvmany(data, len); 233 } 234 235 bool i2c_gpio_send(void *channel, uint8_t data) 236 { 237 return static_cast<I2c_gpio *>(channel)->send(data); 238 } 239 240 bool i2c_gpio_sendmany(void *channel, uint8_t *data, uint8_t len) 241 { 242 return static_cast<I2c_gpio *>(channel)->sendmany(data, len); 243 }