This commit is contained in:
2020-06-11 11:25:19 +02:00
parent 5cb02880b3
commit 5177cd5cbd
15 changed files with 1090 additions and 411 deletions

View File

@@ -6,10 +6,284 @@
#include "GPIO.h"
/** new implementation */
template <int PIN_SDA, int PIN_SCL, bool fast> class SoftI2C {
static constexpr const char* NAME = "softI2C";
inline void sdaDirOut() {MyGPIO::setOutput(PIN_SDA);}
inline void sdaDirIn() {MyGPIO::setInput(PIN_SDA);}
inline void sclDirOut() {MyGPIO::setOutput(PIN_SCL);}
inline void sdaHi() {MyGPIO::set(PIN_SDA);}
inline void sdaLo() {MyGPIO::clear(PIN_SDA);}
inline bool sdaRead() {return MyGPIO::get(PIN_SDA);}
inline void sclHi() {MyGPIO::set(PIN_SCL);}
inline void sclLo() {MyGPIO::clear(PIN_SCL);}
inline void waitLong() {
for (uint8_t i = 0; i < 240; ++i) {
__asm__ __volatile__("nop");
__asm__ __volatile__("nop");
}
}
inline void waitShort() {
for (uint8_t i = 0; i < 240; ++i) {
__asm__ __volatile__("nop");
__asm__ __volatile__("nop");
}
}
void init() {
debugMod2(NAME, "init. SDA: %d, SCL: %d", PIN_SDA, PIN_SCL);
}
public:
/** ctor */
SoftI2C() {
init();
}
/** start communication */
inline void start() {
sdaDirOut();
sclDirOut();
sdaHi();
sclHi();
waitLong();
sdaLo();
waitLong();
sclLo();
waitLong();
}
/** stop communication */
inline void stop() {
sdaDirOut();
sclDirOut();
sdaLo();
sclLo();
waitLong();
sclHi();
waitLong();
sdaHi();
sdaDirIn(); // free the bus
waitLong();
}
/** read the given number of bytes from the slave and generate ACK/NACK as needed */
void readBytes(uint8_t* dst, uint8_t len) {
while(len) {
*dst = _readByte();
--len;
++dst;
if (len) {_writeAck();} else {_writeNAck();} // done? or want more?
}
}
// write one byte to the bus and check slave's ACK/NACK
bool IN_FLASH writeByteAndCheck(const uint8_t byte) {
_writeByte(byte);
return _readAck();
}
// write several bytes to the bus and check slave's ACK/NACK
bool IN_FLASH writeBytesAndCheck(const uint8_t* src, uint8_t len) {
while(len) {
const bool ok = writeByteAndCheck(*src);
if (!ok) {return false;}
--len; ++src;
}
return true;
}
inline bool startWrite(const uint8_t addr7) {
start();
_writeByte( (addr7<<1) | 0 );
const bool ok = _readAck();
return ok;
}
inline bool startRead(const uint8_t addr7) {
start();
_writeByte( (addr7<<1) | 1 );
const bool ok = _readAck();
return ok;
}
inline bool query(const uint8_t addr7) {
const bool ok = startWrite(addr7);
stop();
return ok;
}
public:
uint8_t readReg8(const uint8_t addr, const uint8_t reg) {
uint8_t dst = 0xFF;
readReg(addr, reg, 1, &dst);
return dst;
}
bool readReg(const uint8_t addr, const uint8_t reg, const uint8_t len, uint8_t* dst) {
bool ok;
// select register(s) to read
ok = startWrite(addr);
if (!ok) {debugMod(NAME, "failed start write1\n"); return false;}
ok = writeByteAndCheck(reg);
if (!ok) {debugMod(NAME, "failed select register\n"); return false;}
stop();
// read register(s)
ok = startRead(addr);
if (!ok) {debugMod(NAME, "failed start write2\n"); return false;}
readBytes(dst, len);
stop();
return true;
}
bool writeReg(const uint8_t addr, const uint8_t reg, const uint8_t len, const uint8_t* src) {
bool ok;
// address the slave in write mode and select the first register to read
ok = startWrite(addr);
if (!ok) {debugMod(NAME, "failed start write\n"); return false;}
ok = writeByteAndCheck(reg);
if (!ok) {debugMod1(NAME, "failed to select register %d\n", addr); return false;}
ok = writeBytesAndCheck(src, len);
if (!ok) {debugMod(NAME, "failed to write register contents \n"); return false;}
// done
stop();
return true;
}
bool writeReg8(const uint8_t addr, const uint8_t reg, const uint8_t val) {
return writeReg(addr, reg, 1, &val);
}
private:
/** write one bit to the bus */
inline void _writeBit(const bool out) {
//sdaDirOut(); // switch to output mode
if(out) {sdaHi();} else {sdaLo();} // apply data
sclHi(); // clock pulse
waitShort();
sclLo();
waitShort();
//sdaDirIn(); // free the bus
}
/** read one bit from the bus */
inline uint8_t _readBit() {
//sdaDirIn(); // switch to input mode
sclHi(); // clock pulse and read
waitShort();
const uint8_t val = sdaRead();
sclLo();
waitShort();
return val;
}
// read 8 bits from the bus, WITHOUT sending ACK/NACK
uint8_t _readByte() {
sdaDirIn(); // switch SDA to input mode
return
(_readBit() << 7) |
(_readBit() << 6) |
(_readBit() << 5) |
(_readBit() << 4) |
(_readBit() << 3) |
(_readBit() << 2) |
(_readBit() << 1) |
(_readBit() << 0);
}
inline bool _readAck() {
sdaDirIn(); // switch to input mode
const uint8_t res = _readBit();
return res == 0; // ACK when input pulled low
}
// write one byte to the bus
void _writeByte(const uint8_t byte) {
sdaDirOut(); // switch SDA to output mode
_writeBit(byte & (1<<7));
_writeBit(byte & (1<<6));
_writeBit(byte & (1<<5));
_writeBit(byte & (1<<4));
_writeBit(byte & (1<<3));
_writeBit(byte & (1<<2));
_writeBit(byte & (1<<1));
_writeBit(byte & (1<<0));
}
inline void _writeAck() {
sdaDirOut(); // switch SDA to output mode
_writeBit(0);
}
inline void _writeNAck() {
sdaDirOut(); // switch SDA to output mode
_writeBit(1);
}
};
/*
#if ESP8266
#include "fastGPIO.h"
#elif ESP32
# error "Not yet supported"
#include "GPIO.h"
#endif
// https://www.best-microcontroller-projects.com/i2c-tutorial.html
@@ -42,6 +316,8 @@ namespace i2c {
#elif ESP32
//# error "Not yet supported"
#endif
@@ -127,7 +403,7 @@ namespace i2c {
}
/** write one bit to the bus */
// write one bit to the bus
static inline void writeBit(const bool out) {
sdaDirOut(); // switch to output mode
if(out) {sdaHi();} else {sdaLo();} // apply data
@@ -138,7 +414,7 @@ namespace i2c {
sdaDirIn(); // free the bus
}
/** read one bit from the bus */
// read one bit from the bus
static inline uint8_t readBit() {
sdaDirIn(); // switch to input mode
sclHi(); // clock pulse and read
@@ -163,7 +439,7 @@ namespace i2c {
writeBit(1);
}
/** read 8 bits from the bus, WITHOUT sending ACK/NACK */
// read 8 bits from the bus, WITHOUT sending ACK/NACK
static uint8_t IN_FLASH readByte() {
return
(readBit() << 7) |
@@ -176,7 +452,7 @@ namespace i2c {
(readBit() << 0);
}
/** read the given number of bytes from the slave and generate ACK/NACK as needed */
// read the given number of bytes from the slave and generate ACK/NACK as needed
static void readBytes(uint8_t* dst, uint8_t len) {
while(len) {
*dst = readByte();
@@ -186,7 +462,7 @@ namespace i2c {
}
}
/** write one byte to the bus */
// write one byte to the bus
static void IN_FLASH writeByte(const uint8_t byte) {
writeBit(byte & BIT( 7));
writeBit(byte & BIT( 6));
@@ -198,13 +474,13 @@ namespace i2c {
writeBit(byte & BIT( 0));
}
/** write one byte to the bus and check slave's ACK/NACK */
// write one byte to the bus and check slave's ACK/NACK
static bool IN_FLASH writeByteAndCheck(const uint8_t byte) {
writeByte(byte);
return readAck();
}
/** write several bytes to the bus and check slave's ACK/NACK */
// write several bytes to the bus and check slave's ACK/NACK
static bool IN_FLASH writeBytesAndCheck(const uint8_t* src, uint8_t len) {
while(len) {
const bool ok = writeByteAndCheck(*src);
@@ -236,5 +512,6 @@ namespace i2c {
}
}
*/
#endif // SOFTSPI_H