summaryrefslogtreecommitdiff
path: root/src/driver
diff options
context:
space:
mode:
Diffstat (limited to 'src/driver')
-rw-r--r--src/driver/soft_i2c.cc162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/driver/soft_i2c.cc b/src/driver/soft_i2c.cc
index 33f77ab..99a88aa 100644
--- a/src/driver/soft_i2c.cc
+++ b/src/driver/soft_i2c.cc
@@ -2,6 +2,13 @@
#include "driver/gpio.h"
#include "arch.h"
+#ifdef SOFTI2C_TIMER
+#ifdef TIMER_CYCLES
+#error "SOFTI2C_TIMER and TIMER_CYCLES are mutually exclusive"
+#endif
+#include "driver/timer.h"
+#endif
+
#ifdef SOFTI2C_PULLUP
#define SDA_HIGH gpio.input(sda, 1)
#define SDA_LOW gpio.output(sda, 0)
@@ -14,6 +21,8 @@
#define SCL_LOW gpio.output(scl)
#endif
+#ifndef SOFTI2C_TIMER
+
signed char SoftI2C::setup()
{
SDA_HIGH;
@@ -136,6 +145,159 @@ signed char SoftI2C::xmit(unsigned char address,
return 0;
}
+#else
+
+#ifndef F_I2C
+#define F_I2C 100000
+#endif
+
+volatile unsigned char timer_done = 0;
+
+inline void await_timer()
+{
+ timer_done = 0;
+ timer.start(1);
+ while (!timer_done) {
+ arch.idle();
+ }
+ timer.stop();
+}
+
+signed char SoftI2C::setup()
+{
+ SDA_HIGH;
+ SCL_HIGH;
+ /*
+ * I2C frequency is the time between two SCL low->high transitions
+ * (or high->low, whatever you prefer). For the timer, we need to set the
+ * time between SCL low->high and the following high->low transition
+ * (and vice versa), which is twice the desired I2C frequency. Also,
+ * timer.setup wants kHz and not Hz, so we have
+ * Timer Freq [kHz] = I2C Freq [Hz] * 2 / 1000
+ */
+ timer.setup(F_I2C / 500);
+ return 0;
+}
+
+void SoftI2C::start()
+{
+ SDA_HIGH;
+ SCL_HIGH;
+ await_timer();
+ SDA_LOW;
+ await_timer();
+ SCL_LOW;
+ await_timer();
+}
+
+void SoftI2C::stop()
+{
+ SCL_LOW;
+ SDA_LOW;
+ await_timer();
+ SCL_HIGH;
+ await_timer();
+ SDA_HIGH;
+}
+
+bool SoftI2C::tx(unsigned char byte)
+{
+ unsigned char got_ack = 0;
+ for (unsigned char i = 0; i <= 8; i++) {
+ if ((byte & 0x80) || (i == 8)) {
+ SDA_HIGH;
+ } else {
+ SDA_LOW;
+ }
+ byte <<= 1;
+ SCL_HIGH;
+ await_timer();
+ while (!gpio.read(scl)) ;
+ if (i == 8) {
+ if (!gpio.read(sda)) {
+ got_ack = 1;
+ }
+ }
+ SCL_LOW;
+ await_timer();
+ }
+ return got_ack;
+}
+
+unsigned char SoftI2C::rx(bool send_ack)
+{
+ unsigned char byte = 0;
+ SDA_HIGH;
+ for (unsigned char i = 0; i <= 8; i++) {
+ SCL_HIGH;
+ await_timer();
+ while (!gpio.read(scl)) ;
+ if ((i < 8) && gpio.read(sda)) {
+ byte |= 1 << (7 - i);
+ }
+ SCL_LOW;
+ await_timer();
+ if ((i == 7) && send_ack) {
+ SDA_LOW;
+ } else if ((i == 8) && send_ack) {
+ SDA_HIGH;
+ }
+ }
+ return byte;
+}
+
+void SoftI2C::scan(unsigned int *results)
+{
+ unsigned char i2caddr;
+ for (unsigned char address = 0; address < 128; address++) {
+
+ i2caddr = (address << 1) | 0;
+
+ start();
+
+ if (tx(i2caddr)) {
+ results[address / (8 * sizeof(unsigned int))] |= 1 << (address % (8 * sizeof(unsigned int)));
+ stop();
+ }
+ }
+ stop();
+}
+
+signed char SoftI2C::xmit(unsigned char address,
+ unsigned char tx_len, unsigned char *tx_buf,
+ unsigned char rx_len, unsigned char *rx_buf)
+{
+ unsigned char i;
+
+ if (tx_len) {
+ start();
+ tx((address << 1) | 0);
+
+ for (i = 0; i < tx_len; i++) {
+ tx(tx_buf[i]);
+ }
+ }
+ if (rx_len) {
+ start();
+ tx((address << 1) | 1);
+
+ for (i = 1; i <= rx_len; i++) {
+ rx_buf[i-1] = rx((i < rx_len) * 1);
+ }
+ }
+
+ stop();
+
+ return 0;
+}
+
+ON_TIMER_INTERRUPT
+{
+ timer_done = 1;
+}
+
+#endif
+
#ifdef MULTIPASS_ARCH_esp8266
SoftI2C i2c(GPIO::d7, GPIO::d8);
#elif MULTIPASS_ARCH_arduino_nano