Merge pull request #40 from earlephilhower/i2c0

Fix INPUT_PULLUP/DOWN, I2C 0-len probes, timeout
This commit is contained in:
Earle F. Philhower, III 2021-03-27 21:31:33 -07:00 committed by GitHub
commit 33dc16d033
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 24 deletions

View file

@ -21,6 +21,8 @@
#include "Arduino.h" #include "Arduino.h"
#include <hardware/gpio.h> #include <hardware/gpio.h>
static PinMode _pm[30];
extern "C" void pinMode( pin_size_t ulPin, PinMode ulMode ) { extern "C" void pinMode( pin_size_t ulPin, PinMode ulMode ) {
switch (ulMode) { switch (ulMode) {
case INPUT: case INPUT:
@ -31,11 +33,13 @@ extern "C" void pinMode( pin_size_t ulPin, PinMode ulMode ) {
gpio_init(ulPin); gpio_init(ulPin);
gpio_set_dir(ulPin, false); gpio_set_dir(ulPin, false);
gpio_pull_up(ulPin); gpio_pull_up(ulPin);
gpio_put(ulPin, 0);
break; break;
case INPUT_PULLDOWN: case INPUT_PULLDOWN:
gpio_init(ulPin); gpio_init(ulPin);
gpio_set_dir(ulPin, false); gpio_set_dir(ulPin, false);
gpio_pull_down(ulPin); gpio_pull_down(ulPin);
gpio_put(ulPin, 1);
break; break;
case OUTPUT: case OUTPUT:
gpio_init(ulPin); gpio_init(ulPin);
@ -43,16 +47,23 @@ extern "C" void pinMode( pin_size_t ulPin, PinMode ulMode ) {
break; break;
default: default:
// Error // Error
break; return;
} }
_pm[ulPin] = ulMode;
} }
extern "C" void digitalWrite( pin_size_t ulPin, PinStatus ulVal ) { extern "C" void digitalWrite( pin_size_t ulPin, PinStatus ulVal ) {
if (!gpio_is_dir_out(ulPin)) { if (_pm[ulPin] == INPUT_PULLDOWN) {
if (ulVal == LOW) { if (ulVal == LOW) {
gpio_pull_down(ulPin); gpio_set_dir(ulPin, false);
} else { } else {
gpio_pull_up(ulPin); gpio_set_dir(ulPin, true);
}
} else if (_pm[ulPin] == INPUT_PULLUP) {
if (ulVal == HIGH) {
gpio_set_dir(ulPin, false);
} else {
gpio_set_dir(ulPin, true);
} }
} else { } else {
gpio_put(ulPin, ulVal == LOW ? 0 : 1); gpio_put(ulPin, ulVal == LOW ? 0 : 1);

View file

@ -180,7 +180,7 @@ size_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit) {
} }
size_t byteRead = 0; size_t byteRead = 0;
_buffLen = i2c_read_blocking(_i2c, address, _buff, quantity, !stopBit); _buffLen = i2c_read_blocking_until(_i2c, address, _buff, quantity, !stopBit, make_timeout_time_ms(50));
_buffOff = 0; _buffOff = 0;
return _buffLen; return _buffLen;
} }
@ -189,6 +189,63 @@ size_t TwoWire::requestFrom(uint8_t address, size_t quantity) {
return requestFrom(address, quantity, true); return requestFrom(address, quantity, true);
} }
static bool _clockStretch(pin_size_t pin) {
auto end = time_us_64() + 100;
while ((time_us_64() < end) && (!digitalRead(pin))) { /* noop */ }
return digitalRead(pin);
}
bool _probe(int addr, pin_size_t sda, pin_size_t scl, int freq) {
int delay = (1000000 / freq) / 2;
bool ack = false;
pinMode(sda, INPUT_PULLUP);
pinMode(scl, INPUT_PULLUP);
gpio_set_function(scl, GPIO_FUNC_SIO);
gpio_set_function(sda, GPIO_FUNC_SIO);
digitalWrite(sda, HIGH);
sleep_us(delay);
digitalWrite(scl, HIGH);
if (!_clockStretch(scl)) goto stop;
digitalWrite(sda, LOW);
sleep_us(delay);
digitalWrite(scl, LOW);
sleep_us(delay);
for (int i=0; i<8; i++) {
addr <<= 1;
digitalWrite(sda, (addr & (1<<7)) ? HIGH : LOW);
sleep_us(delay);
digitalWrite(scl, HIGH);
sleep_us(delay);
if (!_clockStretch(scl)) goto stop;
digitalWrite(scl, LOW);
sleep_us(5); // Ensure we don't change too close to clock edge
}
digitalWrite(sda, HIGH);
sleep_us(delay);
digitalWrite(scl, HIGH);
if (!_clockStretch(scl)) goto stop;
ack = digitalRead(sda) == LOW;
sleep_us(delay);
digitalWrite(scl, LOW);
stop:
sleep_us(delay);
digitalWrite(sda, LOW);
sleep_us(delay);
digitalWrite(scl, HIGH);
sleep_us(delay);
digitalWrite(sda, HIGH);
sleep_us(delay);
gpio_set_function(scl, GPIO_FUNC_I2C);
gpio_set_function(sda, GPIO_FUNC_I2C);
return ack;
}
// Errors: // Errors:
// 0 : Success // 0 : Success
// 1 : Data too long // 1 : Data too long
@ -196,14 +253,19 @@ size_t TwoWire::requestFrom(uint8_t address, size_t quantity) {
// 3 : NACK on transmit of data // 3 : NACK on transmit of data
// 4 : Other error // 4 : Other error
uint8_t TwoWire::endTransmission(bool stopBit) { uint8_t TwoWire::endTransmission(bool stopBit) {
if (!_running || !_txBegun || !_buffLen) { if (!_running || !_txBegun) {
return 4; return 4;
} }
auto len = _buffLen;
auto ret = i2c_write_blocking(_i2c, _addr, _buff, _buffLen, !stopBit);
_buffLen = 0;
_txBegun = false; _txBegun = false;
if (!_buffLen) {
// Special-case 0-len writes which are used for I2C probing
return _probe(_addr, _sda, _scl, _clkHz) ? 0 : 2;
} else {
auto len = _buffLen;
auto ret = i2c_write_blocking_until(_i2c, _addr, _buff, _buffLen, !stopBit, make_timeout_time_ms(50));
_buffLen = 0;
return (ret == len) ? 0 : 4; return (ret == len) ? 0 : 4;
}
} }
uint8_t TwoWire::endTransmission() { uint8_t TwoWire::endTransmission() {

View file

@ -40,11 +40,11 @@ public:
TwoWire(i2c_inst_t *i2c, pin_size_t sda, pin_size_t scl); TwoWire(i2c_inst_t *i2c, pin_size_t sda, pin_size_t scl);
// Start as Master // Start as Master
void begin(); void begin() override;
// Start as Slave // Start as Slave
void begin(uint8_t address); void begin(uint8_t address) override;
// Shut down the I2C interface // Shut down the I2C interface
void end(); void end() override;
// Select IO pins to use. Call before ::begin() // Select IO pins to use. Call before ::begin()
bool setSDA(pin_size_t sda); bool setSDA(pin_size_t sda);
@ -52,20 +52,20 @@ public:
void setClock(uint32_t freqHz) override; void setClock(uint32_t freqHz) override;
void beginTransmission(uint8_t); void beginTransmission(uint8_t) override;
uint8_t endTransmission(bool stopBit); uint8_t endTransmission(bool stopBit) override;
uint8_t endTransmission(void); uint8_t endTransmission(void) override;
size_t requestFrom(uint8_t address, size_t quantity, bool stopBit); size_t requestFrom(uint8_t address, size_t quantity, bool stopBit) override;
size_t requestFrom(uint8_t address, size_t quantity); size_t requestFrom(uint8_t address, size_t quantity) override;
size_t write(uint8_t data); size_t write(uint8_t data) override;
size_t write(const uint8_t * data, size_t quantity); size_t write(const uint8_t * data, size_t quantity) override;
virtual int available(void); virtual int available(void) override;
virtual int read(void); virtual int read(void) override;
virtual int peek(void); virtual int peek(void) override;
virtual void flush(void); virtual void flush(void) override;
void onReceive(void(*)(int)); void onReceive(void(*)(int));
void onRequest(void(*)(void)); void onRequest(void(*)(void));