i2C on digital only pins?

yeahtuna

Well-known member
Looking at the pinout for T4.1, I see that i2C SCL/SDA are on analog pins. For my current project I need all my analog pins. Assuming in MUX chip is out of the question, is there are amount of software hacking that would allow me to move SCL/SDA to digital only pins so I don't need to sacrifice two analog pins? Or is the i2C protocol designed in such a way that it needs to use analog pins? I only need very slow one-way communication.

Interestingly, this seems to indicate that it is possible:
https://sites.google.com/site/marth...system/app/templates/print/&showPrintDialog=1
 
Last edited:
Could also maybe use flexio. I have not tried it but they show an example in the reference manual
 
I'm pressed for digital pins too. There's not a lot of info on FlexIO. Can FlexIO run on only two digital pins? I'm still in prototyping. What two pins might I reserve so I can look at implementing flexIO in the future?
 
Kurt's big pinout chart has the easiest info

https://forum.pjrc.com/threads/6614...-use-third-SPI?p=268934&viewfull=1#post268934

Choose 2 pins with the same FlexIO peripheral (3 separate ones exist). For example, pins 4 and 5 are have FlexIO1 bits 6 and 8.

Bitbanging I2C on any 2 digital pins is always an option, if you're willing to spend the CPU time. If you use that code meant for Arduino Uno, you will need to sprinkle "delayMicroseconds(5);" in several places.
 
There is some information about Flexio up on my github project: https://github.com/kurte/flexio_t4
The readme shows the different pins for the T4.x boards.

Code:
The Teensy 4.1 (ARDUINO_TEENSY41) Will have additional IO pins.
FlexIO 1 - The three rows are: Teensy pin, Flex IO pin, and MUX setting for that pin:

    2,       3,    4,    5,  33,    49,   50,   52,   54
    4,       5,    6,    8,  7,     13,   14,   12,   15
    0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14
Ranges: 4-8,12-15

FlexIO 2

    6,       7,    8,    9,  10,    11,   12,   13,   32,   34,   35,   36,   37
    10,     17,   16,   11,  0,      2,    1,    3,   12,   29,   28,   18,   19
    0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14
Ranges 0-3, 10-12, 16-19, 28-29

FlexIO 3 - Note Flex IO 3 does not have DMA support

    7,       8,   14,   15,   16,   17,   18,   19,   20,  21,    22,   23,   26,   27,   34,   35,   36,   37,   38,   39,   40,   41
    17,     16,    2,    3,    7,    6,    1,    0,   10,   11,    8,    9,   14,   15,   29,   28,   18,   19,   12,   13,    4,    5 
    0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 
Ranges: 0-19, 28-29
Probably should update my comment there that it will have... In each of these groups first line is Teensy pin, the second line is the
FlexIO pin number within that FlexIO object and the 3rd line is the Mux setting.

Note this library is now in the TD releases. Although I don't think he picks up the readme.
 
I wrote out a very low level Bit Bang IC2 header only class. I think this will take care of my needs, but perhaps I'll try to tackle FlexIO further down the road.

Code:
#ifndef __BIT_BANG_IC2__
#define __BIT_BANG_IC2__


#define BIT_BANG_DEBUG


class BitBang_IC2 {
public:
	BitBang_IC2(uint8_t SCL_PIN, uint8_t SDA_PIN, bool use_pullups = false) {

		BB_SDA = SDA_PIN;
		BB_SCL = SCL_PIN;
		usePullup = use_pullups;

		#ifdef BIT_BANG_DEBUG
		Serial.println("Bit Bang I2C: Initializing...");
		#endif
	}

	void init() {
		pinMode(BB_SDA, OUTPUT);
		pinMode(BB_SCL, OUTPUT);
		sda_high();
		scl_high();
		dly();
	}

	void start() {
		sda_high();
		dly();
		scl_high();
		dly();
		sda_low();
		dly();
		scl_low();
		dly();
	}

	void stop() {
		sda_low();
		dly();
		scl_high();
		dly();
		sda_high();
		dly();
	}

	/* Transmit 8 bit data to slave */
	bool tx(uint8_t data) {
		sda_set_write_mode();
		for (int i = 7; i >= 0; i--) {

			if (data & (0x01 << i)) sda_high(); else sda_low();
			dly();
			scl_high();
			dly();
			scl_low();
			dly();
		}

		sda_set_read_mode();
		scl_high();
		dly();
		bool ack = !digitalRead(BB_SDA);    // Acknowledge bit
		scl_low();
		sda_set_write_mode();
		sda_high();
		return ack;
	}

	uint8_t rx() {
		sda_set_read_mode();
		dly();
		uint8_t data = 0;

		for (int i = 7; i >= 0; i--) {
			#ifdef BIT_BANG_DEBUG
			Serial.print(".");
			#endif

			if (digitalRead(BB_SDA) == HIGH) data |= (0x01 << i);

			scl_high();
			dly();
			scl_low();
			dly();
		}

		#ifdef BIT_BANG_DEBUG
		Serial.println(" Byte Read");
		#endif

		scl_low();
		sda_set_write_mode();
		sda_high();
		return(data);
	}

	void setDelayTime(int micro_seconds = 5) {
		delay_us = micro_seconds;
	}

protected:

	inline void dly() { delayMicroseconds(delay_us); }

	inline void sda_high() { digitalWrite(BB_SDA, HIGH); }
	inline void sda_low() { digitalWrite(BB_SDA, LOW); }
	inline void scl_high() { digitalWrite(BB_SCL, HIGH); }
	inline void scl_low() { digitalWrite(BB_SCL, LOW); }

	inline void sda_set_write_mode() { pinMode(BB_SDA, OUTPUT); }
	void sda_set_read_mode() {
		if (usePullup)
			pinMode(BB_SDA, INPUT_PULLUP);
		else
			pinMode(BB_SDA, INPUT);
	}

	bool usePullup;
	uint8_t BB_SDA;
	uint8_t BB_SCL;

	int delay_us = 5;
};



#endif
 
Curious if you tried your bit-bang I2C and whether it worked? Seems like a useful thing!
Yes, it works. I used it read some values from an EEPROM chip connected to a Teensy 3.2. It's bare bones. To make it useful for communicating with chips, you would likely want to extend the class. For example, here's the class I've written for communicating with 4 digital rheostats (hasn't been tested yet as I'm still waiting for my proto board to arrive).

Code:
#ifndef __MCP4332_BIT_BANG_I2C__
#define __MCP4332_BIT_BANG_I2C__

#define MCP4332_BASE_ID 0xB


#include "AF_BitBang.h"
#include <stdint.h>

struct MCP4332_DeviceID {
	uint8_t writeAddress;
	uint8_t readAddress;
};


class MCP4332_Bit_Bang_IC2 {
public:
	MCP4332_Bit_Bang_IC2(uint8_t SCL_PIN, uint8_t SDA_PIN, bool use_pullups = false)
	: bitBang(SCL_PIN, SDA_PIN, use_pullups) {

		writeAddress = (MCP4332_BASE_ID << 3);
		readAddress = writeAddress | 0x01;

		wiperAddress[0] = 0;
		wiperAddress[1] = 0x01 << 4;
		wiperAddress[2] = 0x06 << 4;
		wiperAddress[3] = 0x07 << 4;

	}

	void init() {
		bitBang.init();
	}

	//SET THE GAIN OF THE RHEOSTAT
	void write(uint8_t devIndex, uint8_t wiperIndex, uint8_t gain) {

		if (devIndex < 4 && wiperIndex < 4) {
			bitBang.start();
			bitBang.tx(dev[devIndex].writeAddress);
			bitBang.tx(wiperAddress[wiperIndex]);
			bitBang.tx(gain);
			bitBang.stop();
		}
	}

	void setDeviceID(uint8_t index, uint8_t a0, uint8_t a1) {
		if (index < 4) {
			dev[index].writeAddress = writeAddress | (a1 << 2) | (a0 << 1);
			dev[index].readAddress = writeAddress | 0x01;
		}
	}
	
protected:

	BitBang_IC2 bitBang;

	int writeAddress;
	int readAddress;

	MCP4332_DeviceID dev[4];

	uint8_t wiperAddress[4];
};

#endif
 
Last edited:
Yes, it works. I used it read some values from an EEPROM chip connected to a Teensy 3.2. It's bare bones. To make it useful for communicating with chips, you would likely want to extend the class. For example, here's the class I've written for communicating with 4 digital rheostats (hasn't been tested yet as I'm still waiting for my proto board to arrive).

Thanks, that's pretty cool. Nice thing to have in the toolbox.
 
Thanks, that's pretty cool. Nice thing to have in the toolbox.

This is just to confirm that it's working to control my 4 quad digipots. I used to use SPI with CS pins, which worked out to 6 pins total. Nice to see that I can now accomplish the same thing using just 2 digital pins (didn't even need external pullups).
 
This is just to confirm that it's working to control my 4 quad digipots. I used to use SPI with CS pins, which worked out to 6 pins total. Nice to see that I can now accomplish the same thing using just 2 digital pins (didn't even need external pullups).

Good news!
 
Back
Top