diff options
Diffstat (limited to 'src/driver')
| -rw-r--r-- | src/driver/soft_i2c.cc | 162 | 
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 | 
