#include "driver/soft_i2c.h" #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) #define SCL_HIGH gpio.input(scl, 1) #define SCL_LOW gpio.output(scl, 0) #else #define SDA_HIGH gpio.input(sda) #define SDA_LOW gpio.output(sda) #define SCL_HIGH gpio.input(scl) #define SCL_LOW gpio.output(scl) #endif #ifndef SOFTI2C_TIMER signed char SoftI2C::setup() { SDA_HIGH; SCL_HIGH; return 0; } void SoftI2C::start() { SDA_HIGH; SCL_HIGH; // SDA_LOW; // SCL_LOW; } void SoftI2C::stop() { SCL_LOW; // SDA_LOW; // SCL_HIGH; // 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; while (!gpio.read(scl)) ; // if (i == 8) { if (!gpio.read(sda)) { got_ack = 1; } } SCL_LOW; // } 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; while (!gpio.read(scl)) ; // if ((i < 8) && gpio.read(sda)) { byte |= 1 << (7 - i); } // SCL_LOW; // 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; } #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 SoftI2C i2c(GPIO::pc4, GPIO::pc5); #elif MULTIPASS_ARCH_blinkenrocket SoftI2C i2c(GPIO::pc4, GPIO::pc5); #elif MULTIPASS_ARCH_msp430fr5969lp SoftI2C i2c(GPIO::p1_6, GPIO::p1_7); #endif