I2C on Port 32A7 and 32A8

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
GeorgeIoak
Active Member
Posts: 56
Joined: Wed May 19, 2010 9:42 pm

I2C on Port 32A7 and 32A8

Post by GeorgeIoak »

I'm working on a custom board and trying some new modifications which require I2C on 32A7 and 328 (SDA and SCL). Am I going to have to bit bang the protocol since I'm on the 32-bit port and if so is there any code already written for this?

Thanks in advance for any help as I'm more of a hardware guy than software guy!


User avatar
Bianco
XCore Expert
Posts: 754
Joined: Thu Dec 10, 2009 6:56 pm
Contact:

Post by Bianco »

I'm afraid it is not easy to implement I2C on a 32-bit port because you needs pins in different directions at the same time (while the master clocks out SCL, it should also be able to receive data from the slave at the same time) while the whole port can only be in one direction at one time.
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

But you can PEEK to read it while it is in output mode. Not exactly ideal but
this is a slow interface anyway, and you're already bit-banging.

Be sure to drive it as open drain though... Might be best to use two pins
for SDA, one for input and one for output (via a FET for example).
User avatar
GeorgeIoak
Active Member
Posts: 56
Joined: Wed May 19, 2010 9:42 pm

Post by GeorgeIoak »

I was afraid I'd get an answer like this! Would something like this code work:

Code: Select all

void MyBang (int cycle, int highlow)
{
	unsigned tmps;
	unsigned mytmp;
	unsigned portDat;

	tmps = port32A_peek();
	mytmp = tmps;
	portDat = tmps;
	if (highlow)
	{
		portDat |= P32A_SDIN;
	}
	else
	{
		portDat &= (~P32A_SDIN);
	}
	port32A_out(portDat);

	for (int i=0; i<cycle; i++)
	{
		Wait250us();
		port32A_out(portDat | P32A_SCK);
		Wait250us();
		mytmp = port32A_peek();
		mytmp &= (~P32A_SCK);
		port32A_out(mytmp);
		//Wait250us();
	}
	port32A_out(tmps);
}
I had used this code for SPI bit banging but of course that has separate in and out pins. I'm not exactly sure how I can change the P32A_SDIN to an input to wait for the ACK to come back from the slave device.
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

You cannot switch a single pin of a port between input and output; you
can only change the whole port.

You can always use PEEK, and it will always return what is actually on the
pins right now, not what you put there with some OUT instruction.

With I2C, you should never drive SDA to a high, only ever to a low, an
external pullup does the high for you: an external I2C device can (and will)
pull SDA low. So you need a way to tristate the SDA. You can tristate
a whole port (by inputting from it), but that is not what you want, so
you really should add a FET or something else to convert P/P to O/D for
you.
User avatar
GeorgeIoak
Active Member
Posts: 56
Joined: Wed May 19, 2010 9:42 pm

Post by GeorgeIoak »

Thanks for the reply. I'm trying to salvage a current board design but it's looking like I might not have a choice but to re-spin it and move the current connection over to a couple of unused single pin ports.

I was looking at github at the I2C module and it had a note that the pins used had to be at bit 0 of the port. Does that mean if I currently have Port4E available that I would have to use X0D26 and X0D27?

Assume I don't need to do any reads on the I2C port. Could I still make the writes and use peek to check for the ACK?
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

GeorgeIoak wrote:I'm trying to salvage a current board design but it's looking like I might not have a choice but to re-spin it and move the current connection over to a couple of unused single pin ports.
Either use a 1-bit port for SDA or use some external pulldown device, yeah.
I was looking at github at the I2C module and it had a note that the pins used had to be at bit 0 of the port.
That sounds like simply a code restriction, the hardware does not care. I don't know that
code or what it does though.
Assume I don't need to do any reads on the I2C port.
You _always_ have to do reads on IIC: every byte gets an ACK or NAK back.
If you cheat and output on SDA during that time, and your (slave) device
outputs the other polarity, you have a nice short-circuit. You can work
around that with some more resistors or something, but you said you
cannot do reworks so even the ugly rubber band solution is out ;-)
Could I still make the writes and use peek to check for the ACK?
You could try, and see for yourself if there is blue smoke, or how much
current flows... It's up to you if that's acceptable. You *can* just output
a 0 or 1 and see (with PEEK) what the actual line state ends up as, if you
accept the consequences.
User avatar
Ross
XCore Expert
Posts: 962
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

I2C master can be supported on a single port with the restriction of not supporting devices that want to stretch the clock or multiple bus masters.

There is some code here: https://github.com/xcore/sc_i2c/blob/ma ... /i2c-sp.xc
User avatar
GeorgeIoak
Active Member
Posts: 56
Joined: Wed May 19, 2010 9:42 pm

Post by GeorgeIoak »

Thanks again for hanging in there with me. I guess I had my mind wrapped around the incorrect assumption that the slave would always issue a ACK but you're right, if for some reason it tries to issue a NAK, then I've got trouble brewing.

With the XS1-SU1 coming out soon I guess I'll just roll this design change into that board spin but I was hoping for some quick "fix" for the exisiting design to support this new idea. So I don't make another bad pin choice decision could you help clarify what would work for us.

We currently are using X0D56 and X0D57 (pins 33 & 35 of the TQ128). P4A and P4B are unused right now and the lower bits are physically close to pins 33/35 so using them would be a simple board layout change. Would it be better to put the clock on say P4A and data on P4B. I haven't looked closely at the new XS1-SU1 but if the pinout is drastically different then that new design will take a little more time and I might have to do an interim board change.

looking at the I2C code on github I found the bit 0 comment I remembered seeing:

Code: Select all

/** Struct that holds the data for instantiating the I2C module - it just
 * comprises two ports (the clock port and the data port), and the speed of
 * the bus which is a compile time define.
 */
struct r_i2c {
    port scl;      /**< Port on which clock wire is attached. Must be on bit 0 */
    port sda;      /**< Port on which data wire is attached. Must be on bit 0 */
    unsigned int clockTicks; /**< Number of reference clocks per I2C clock,
                              *   set to 1000 for 100 Khz. */
};


which is in I2c.h of the master and simple versions. Only in the single port can you define which bits are used.
User avatar
segher
XCore Expert
Posts: 844
Joined: Sun Jul 11, 2010 1:31 am
Contact:

Post by segher »

Ross wrote:I2C master can be supported on a single port
Yeah, but you change the whole port to input in places, which I presume
the OP cannot do as he has other things on that 32-bit port :-)
Post Reply