Problem with I2C

.....

First *ASSUMPTION* the first data byte of the 16 data bytes to send should be 0x80 ?

Second assumption you are coding in C.

This suggests that you have not coded properly for interupt routines and all the variables used.

Are the variables used by other functions and interupt routine defined as volatile?

Are you sure that the index(pointer) starts at beginning of buffer? Are you sure that the index/pointer is reset correctly at end of buffer to beginning of buffer?

Are you sure nothing else changes the index/pointer?

Are you sure the data is correct in the table?

Are you sure the index/pointer is incrementing correctly and not been optimised away. You need a way to get this value every time used and be sure it is a real value.

There are so many things this could be that the actual code needs to be checked including variables and function declarations as interupt function.

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter
Loading thread data ...

RECEIVE

device

register

No, it is 0x00 to 0x10 in sequence. 0x80 isn't part of the data array at all. In fact, I tried sending dummy constants but I always get 0x80 instead.

Yes

Variables modified by the ISR are declared volatile. Should those that are simply read but not written to by the ISR also be declared volatile?

Yes it is initialized to 0 in the init function

What do you mean? The index never goes beyond the end of the buffer

Pretty sure but I'll check

Yes most definitely.

You mean optimized by the compiler? How do I prevent this? By declaring it as volatile?

Reply to
galapogos

.....

Are you sending a block of 16 bytes or 16 single byte read equences?

Check when changing to send first byte you change to transmit and have set up everything and be sure it is in transmit mode before writing data to controller.

....

YES if they are defined elsewhere (e.g. base pointer)

Well anything could be happening.

If the index is incremented once EACH interupt call it is possible the compiler optimises the increment away, for interupt routines and values used there and elsewhere I would always make those variables volatile to be on the safe side as a start point.

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter

I interfaced a microchip I2C EEPROM to the master ASIC, and then triggered the master to request the 16byte data from the EEPROM. I'm not sure what's in the EEPROM but it appears to be all 0xFFs. Anyway, the timing diagram is exactly like the ASIC's datasheet says. Here's the sequence:

1) Master(ASIC) sends start bit and device address and WRITE direction bit(0xa0) 2) Slave(EEPROM) ACKs 3) Master sends word address(0x00) 4) Slave ACKs 5) Master sends another start bit and device address and READ direction bit(0xa1) 6) Slave ACKs 7) Slave sends 1 byte of data(0xFF) 8) Master ACKs 9) Repeat 7-8 until all 16 bytes have been received 10) Master(?) sends NOACK and stop bit

This also corresponds with the EEPROM's datasheet timing diagram so I assume this is the correct handshaking protocol to follow.

I refered to the I2C bus specification 2.1(Jan 2000) trying to find this handshaking protocol, and this seems to be a "combined format" for serial EEPROMs.

I then tried to access the EEPROM with my MCU set as a master. I was able to get all the way to step 6, but after that, my MCU was still in master transmitter mode rather than master receiver mode. I have to set the master to transmit mode for it to send the 2nd device address in step 5. I thought that the direction bit sent in step 5 would reset the MCU to be in receiver mode but it didn't. Therefore I'm unable to continue on to step 7. In fact, I had to hack things up quite a bit to even get it working till step 6. For example, I didn't know how to correctly detect when to restart in step 5, so I detected this by having a global interrupt counter and restarting when the counter is a certain value.

I'm sure there must be a proper way of detecting when to restart but I can't figure out how. As far as my MCU goes, it still thinks it's in master transmitter mode after sending the device/word address, so I don't know how to tell it to restart at that point.

Is it so hard to handle the handshaking for every MCU or am I just unlucky to be working with a Toshiba?

Reply to
galapogos

I enter the MCU's ISR after each ACK sent/received. Each time I enter the ISR I send or receive 1 byte, so I guess I'm sending 16 single byte sequences?

Actually I'm not even sure if I can even change the transmit direction on the fly. The control register that the transmit direction is at is shared with the status register, so within my ISR, I check to see if it's in transmit mode. If it is, then I'll send data, if not I'll receive. It's really up to the hardware to tell me if it's in transmit or receive mode AFAIK.

I understand. Better be safe than sorry. I've modified all the isr variable declarations to be volatile, but it didn't change the program behavior.

Reply to
galapogos

I'm confused. I thought you said that your MCU was a slave.

-- Michael N. Moran (h) 770 516 7918

5009 Old Field Ct. (c) 678 521 5460 Kennesaw, GA, USA 30144
formatting link

"So often times it happens, that we live our lives in chains and we never even know we have the key." The Eagles, "Already Gone"

The Beatles were wrong: 1 & 1 & 1 is 1

Reply to
Michael N. Moran

Yes it is, when I interface it with the master ASIC. The previous post that you replied to was a case where I was trying to interface with an external I2C EEPROM just to test things out and hopefully get a better understanding of how I2C works. In this particular setup the MCU was setup as a master and the EEPROM a slave.

Reply to
galapogos

.....

You are getting 0xFF which shows

a) The EEPROM is erased b) The hardware and wiring works c) You can see that the transactions can be done. d) You have a reference for REAL that matches what should happen.

Yes that is correct.

NO direction changes YOU must code, I would suggest when you are sending a READ address (last bit of address = 1), after the ACK then YOU must change the direction.

You send the start condition using the controller to send a start then the Read address. When your MCU is master you do not detect it you generate it.

You do the following steps

generate a start coindition transmit the read address, get ACK Change mode to receive Receive bytes last byte Set NOACK Send Stop

I think it is more to do with understanding of I2C and which end is transmitting at what stage.

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter

.....

No you are sending a block of 16 bytes. Just that I2C is slower than the MCU.

16 single byte reads would have an address sequence for each byte read of one byte.

No it is up to you to change direction once you have acked the read address being received.

....

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter

OK

So at the interrupt after the read address when i have something like junk = buffer, i should do a if (junk = address) { change direction } ?

Reply to
galapogos

Cool, it means at least that works

How do I detect this though. I send the READ address at interrupt n, but I expect the ACK at interrupt n+1, so I would need some way of detecting something at n+1 to change direction, and the address is long gone by then so I can't detect. Other than using the global counter or some other global variable I can't think of a way to do so.

I think you misunderstood me. I know when conceptually to start and restart, and I'm doing this in main(). However, in my ISR, I do a bunch of status bit detection to determine what I should do at any given time.

On my restart, when I'm sending the READ address in main(), I have to keep my status as a master transmitter since I'm sending the READ address to the master. However, I think because of this, at the end of the ACK for the READ address, I'm still going into the isr code section for master transmitter, which is to send more data. Instead, I should be going into the master receive code section where the serial buffer is free to accept incoming data. Makes sense? Or am I thinking of it totally the wrong way?

Aren't you missing the 1st start condition? i.e.

1) generate start condition 2) transmit WRITE address 3) get ACK 4) transmit word address 5) get ACK 6) generate restart 7) transmit READ address 8) receive ACK 9) receive bytes 10) last byte set NOACK, send stop(automatic)

Like I said, I'm doing fine until step 5. I can do step 7, but then I fall into the wrong code segment inside my ISR(master transmitter instead of master receiver), and I'm not sure where exactly I can change my control/status bit to receive. What I'm thinking is I can't change it at the start of step 7 since I'm still transmitting, but the next point I can change it is in in the ISR after step 8, and that's too late since I'm already supposed to be in receive mode in that stage right? I can't change it in the ISR after step 8 since my ISR wouldn't know that the READ address was previously sent. All it knows about the outside world is what the MCU status bits reveal, which are the following bits: MST - master or slave TRX - transmit or receive AL - arbitration lost AAS - slave address match AD0 - GENERAL CALL address match BB - bus free PIN - serial interrupt cancel What else can I do to detect that my previous transmission was a "transmit READ address"?

Perhaps. I do appreciate your help, and sorry if I seem like a total noob(that's coz I am), but I do hope I can understand this whole thingamajig soon!

Reply to
galapogos

if( address & 1 ) /* Read address*/ { Change direction to transmit }

if( stop ÝÝ restart ) { Change direction back to receive /* idle as receive */ }

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter

....

Either from main program flow or knowing how many bytes are being sent/received. You must have some form of state knowledge, even if your higher level code is waiting for interupts to complete. I assume you have some form of flag for how many bytes received. The data in the data register at addressed or ACK time should be the address including a READ/WRITE bit to set flow for next bytes.

Most people run with simple functions and state variables, so they know that they are receiving an I2C address, having done that note what type of address and action accordingly.

Unless your MCU is very busy doing other things I would in this case test first using code that monitors an interupt flag to signify you have been Addressed as Slave, then drop to a linear function to test things first as in

If I2C address write mode Read 'n' bytes of EEPROM internal address SEND ACK Read 'n' bytes of write data Send ACK (if anytime stop or start detected exit) else switch mode to transmit send data byte Wait for ACK/NOACK if ACK repeat send data if NACK wait for stop/start condition (if anytime stop or start detected switch to receive mode exit)

The waits, sends and receive would be functions. get it working this way first then look at ways to put it into an interupt function with state variables about next state, current state, count of bytes if known.

Starting with interupt functions for I2C is probably trying to run before you can walk.

If you are master you should know from your program flow what you are trying to achieve. Write as a bunch of linear calls and code to test handling of I2C controller first, start as master talking to EEPROM to write a byte, then reset the address and read the byte back.

You need to determine as a aslave from the LRB (Last received bit) or a flag when as master that you have sent a read or write address I think you are expecting too much from a simple I2C controller, all the ones I have seen expect a lot of control software and state variables, especially for timeouts and other error trapping.

No I was talking about after the first Write transaction. The Toshiba manual does have sections on generating restarts (may not be well written but it is there)..

If detecting as a slave see the manual and BB bit for start/stop detection.

....

Whatever you do you will need some way of tracking either as a master talking to EEPROM, or as a slave emulating a EEPROM what type of transaction you are doing. probably what state you are in as well (waiting ack, receiving byte, receiving address, idle....).

The data in the data register when slave, and from your programme as a master you will have the Address used or status somwhere. Before ACK stage as slave LRB in status register will tell you as well. See manual for TMP91CP27U Table 3.11.1 .

I would start as linear code first of all to prove the methods of controlling the I2C controller, get that working then ARCHIVE it as a reference then look at creating interupt driven code.

Slave I2C devices are relatively easy in hardware but more complicated in software controlled situations.

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter

Is the above program flow for the MCU emulating as a slave EEPROM to the master ASIC? I was trying to get the MCU working as a master to an external slave EEPROM to get things working since I thought it would be easier. The above program flow doesn't make sense in this context.

I thought interrupts would be easier since the interrupts are well defined(occurs after every ACK), but I'll see if I can do it polling style.

I am having trouble with the MCU as a master talking to a slave EEPROM. I added a volatile i2c global state variable and tracking/changing it as the program runs, and I was again stuck at the point when the READ address is sent. If I try to change the direction from transmit to receive at that point, for some reason the MCU stops generating a clock and hence I don't receive anything from the EEPROM. I tried reading the buffer to a temp variable as well as pulling the PIN high. These did not work, contrary to Fig 3.11.15 of the datasheet where it claims that when TRX=0, setting PIN to 1 will cause the MCU to output a serial clock pulse to the SCL to transfer new 1-word of data. In fact, even if I set the TRX bit to what it already is, SCL becomes HIGH all the way. It seems like I can't even touch the TRX bit at all!

Now if I leave the TRX bit alone, SCL continues pumping, but the MCU is in master transmitter mode and will pump the buffer to SDA, which isn't what I want if I want to read from the EEPROM.

Reply to
galapogos

change

I tried coding it linear style rather than using interrupts, and again I'm getting stuck after the restart, right after the read address is sent by the MCU to the EEPROM. Whenever I modify the TRX bit, SCL will stop. I looked at the restart documentation, and it says that after a restart I should generate a new start condition by following 3.11.6(2) under Master. In that section it states the steps that I should follow to generate a start bit. This is identical to the first start bit that I generated, and consists of the following instructions

SBI0CR1 |= 0x10; // set SBI0CR1 to 1 SBI0DBR = 0xa1; // send READ address SBI0CR2 = BIN_11111000; // write "1" to MST, TRX, BB, PIN

That section also states that "when an interrupt request generate, the is changed according to the direction bit only when an acknowledge signal is returned from the slave device." This seems to suggest that TRX changes are automatic rather than me changing it. I verified that I'm indeed getting an ACK from the slave by looking at my logic analyzer timing waveform as well as checking that the LRB right after the read address is sent is "0". Since I'm getting an ACK and my direction bit is 1, why isn't TRX being reset to 0? Is there a bug with my emulator or am I misunderstanding the datasheet?

Reply to
galapogos

change

especially

I wish there was a way to edit posts, but anyway, after getting stuck in the above situation, I decided to give linear coding a go with the MCU as the slave and the ASIC as the master. After going at it a few times, I actually managed to get the MCU to send data to the ASIC! I didn't even use any state flags or byte count. The handshaking sequence is hardcoded as per the expected timing diagram, and the how many bytes is determined by checking the LRB status bit. Also, I didn't even need to change the TRX bit after the 2nd device address is sent by the ASIC. As I suspected, the MCU does this automatically, presumably by checking the 8th(direction) bit of the transmission and seeing if it's a 1 or a

  1. I don't know why this doesn't work when the MCU is in master mode though.

In any case, the current code, though lengthy and messy, is sufficient for my application, but I will probably try and clean it up and make it more general purpose rather than hard coded, and maybe implement it with interrupts.

I noticed that in the ASIC's datasheet as well as the logic analyzer, after the transmission is done and the ASIC sends its STOP bit, the SCL line is kept low, therefore the bus is not freed. Is this weird or normal? It doesn't really matter in this case I guess since there are only 2 devices on the bus and only 1 transaction being performed.

I also noticed that between the end of every MCU ACK and the start of every data byte sent, there is a glitch on the SDA line where it goes to HIGH for a very short period of time(

Reply to
galapogos

...

That sounds like you are changing the Master/Slave bit as well, even temporarily changing a controller from Master to Slave and back again will screw things up. Make sure you bit manipulations are doing what you think they are doing, even if you have to keep a memory copy of what was last written to the device.

I still think you are (even unintentionally changing the Master/Slave bit) that is the most likely reason that will stop 'clocks', actually probably resetting controller to idle state (waiting for start).

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter

...

So you have sent the read address here to the register, then IMMEDIATELY issue a start condition. The data will not have been shifted out of the register to the bus.

You need to wait for the PIN and other status bit changes to indicate the address has been sent and check the ACK bit, BEfore doing a RESTART.

Your processor is executing instructions many times faster than the bit rate of I2C.

NO NO it says it will change depending on the direction bit (in the register) and that it changes AFTER the ACK bit from a slave has been received.

You change the bit but the direction is read at ACK time.

The important thing is YOU have to change the bits direction.

The other important things is to do it at the RIGHT time, and WAIT for things to happen, and receive status bits back.

I have never come across an I2C controller that changes direction, without being instructed to, from software.

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter

Hmm, I didn't think of that. I was even worried that fetching the data from an array and putting it in SBI0DBR would be too slow. Anyway, the datasheet says the following regarding START condition generation:

Check a bus free status(when = 0)Set the SBI0CR1 to 1 and specify a slave address and direction bit to be transmitted to the SBI0DBR. When SBI0CR2 = 0, the start condition is generated by writing 1111 to SBI0CR2.

It doesn't say anything about any delay between setting SBDI0DBR and writing 1111 to SBI0CR2., only that I should check BB, which I already have. I don't understand what you mean by waiting for the status bits to change to indicate the address has been sent and check the ACK bit. If the read address has been sent and the slave's ACK has been received, I'm already 1 byte ahead of the RESTART aren't I? I think the whole point of setting those bits the 1111 is to trigger the sending of the read address. From my understanding no bits will change until I change those bits, so I can't be waiting for them to change when I'm supposed to change them?

Hmm isn't that what I said? So say I write the read address/direction bit of 0xA1 into SBI0DBR(the register/buffer), the address will be

1010000b and the direction bit is 1(read). When the slave ACKs this, TRX will change depending to 0 since direction bit is 1(doesn't seem to happen)? Or when the slave ACKs this(and PIN goes to 0), I must change TRX to 0(tried this but didn't work)?

I change the TRX bit? Why is the direction read at all if I'm the one who changes the TRX?

So when is the right time, and what things do I have to wait for? I'm really confused about this part. I don't have my code with me right now but here's the sequence of events that I can think of for a restart when a master MCU is addressing a slave EEPROM:

1) Wait for bus to be free( = 0) 2) Set to 1 3) Set SBI0DBR to read address(0xA1) 4) Write 1111 to 5) Wait for PIN to be 0(indicating ACK/NACK received) 6) Read SBI0DBR to data array 7) Wait for PIN to be 0 8) Back to 6 until all bytes have been received

If I'm understanding you correctly, somewhere along the line between steps 4 and 6 I have to change TRX to 0 to make the MCU a receiver? When exactly do I need to do so and what conditions do I have to wait for? are both 1 throughout the transmission, and changes to 0 only when I get an ACK/NACK, so those won't indicate anything. TRX is what I'm supposed to change so I can't wait on that. I can't think of any other status bits that will indicate when I need to set TRX to 0.

Reply to
galapogos

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.