CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

i2c_transfer_in() stucks in read out loop

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
FloDu



Joined: 18 Sep 2024
Posts: 5

View user's profile Send private message

i2c_transfer_in() stucks in read out loop
PostPosted: Wed Sep 18, 2024 6:24 am     Reply with quote

Hey guys,

I'm trying to write and read data over I2C FORCE_HW on a PIC18F27Q43 and the CCS 5v115. The code is running perfectly using the FORCE_SW directive with the i2c_start, i2c_write, i2c_read, i2c_stop functions. The GPIOs are the C3 and C4 (which are the HW I2C pins on my controller).
Now I added a new device to the I2C bus which requires me to use the hardware I2C. Using the FORCE_HW directive forces me to use the i2c_transfer (_in/_out) function. There is no problem writing data to the bus using the i2c_transfer_out funtion, but if I want to read data from the device using i2c_transfer_in, I got stuck in a read out loop.
I want to read out a single byte of the I2C slave. The controller starts the read out by sending the address, the slave sends an ACK and its data. Afterwards the controller keeps reading out the slave until the slave pulls down the SDL line.
What is happening here? Is there something wrong with my code? Or is there a known bug with the i2c_transfere_in function?

My code is the following:
Code:

    static uint8_t WriteData = NEXTCMD(ST_CB_INST);
    static uint8_t ReadData[2];
   
    i2c_transfer_out(RD(ST_ADDRESS), &WriteData, 1);
    delay_us(100);
    i2c_transfer_in(RD(ST_ADDRESS), &ReadData, 2);




Thank you very much for you help and hints.

Greeting
FloDu[/code]


Last edited by FloDu on Wed Sep 18, 2024 8:06 am; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Wed Sep 18, 2024 7:37 am     Reply with quote

What compiler version do you have?.
When I2C_transfer_in was originally written it required the number of
bytes to be the total number of bytes in the transaction.
This was changed a while ago, so it is now the number of bytes to read.
The manual was changed to reflect this.
FloDu



Joined: 18 Sep 2024
Posts: 5

View user's profile Send private message

PostPosted: Wed Sep 18, 2024 8:10 am     Reply with quote

Hey, thanks for oyu reply. Sorry for the missing information. I use the compiler V5.115.
I tried both variants of length, the payload length + one byte for the address and only the payload length. It's the same result in both cases.

Greetings
FloDu
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Wed Sep 18, 2024 9:14 am     Reply with quote

I'm wondering if the driver is setting the ACNT bit in the peripheral.
This autoloads the byte count with the first byte received after the read
is started. Designed for devices that reply with the number of bytes
they are sending at the start of the read. If the ACNT bit is set this is
done. This ability doesn't exist in most of the I2C peripherals, but your
one has this. The driver may have missed this and left it set.
Might be worth trying clearing this yourself.
FloDu



Joined: 18 Sep 2024
Posts: 5

View user's profile Send private message

PostPosted: Mon Sep 23, 2024 6:25 am     Reply with quote

Hey,

I created a new project which only reads in data over i2c. Using the FORCE_SW it is still working. But if I change to FORCE_HW, I see the same behaviour as in my production project.

The big advantage in the new project is a nice and clean list file, which makes it easy to follow the asm code. And there is something confusing:

Code:
!    i2c_transfer_out(RD(ST_ADDRESS), &WriteData, 1);
0xAE: MOVLB 0x2
0xB0: BCF SPI2BAUD, 4, BANKED
0xB2: MOVLW 0x1
0xB4: MOVWF SPI2RXB, BANKED
0xB6: MOVLW 0x7C
0xB8: MOVWF SPI2TCNT, BANKED
0xBA: CLRF 0x8, ACCESS
0xBC: MOVLW 0x5
0xBE: MOVWF 0xA, ACCESS
0xC0: MOVLW 0x4
0xC2: MOVWF 0x9, ACCESS
0xC4: MOVLB 0x0
0xC6: BRA I2C_TRANSFER_OUT_HW_3190
!    delay_us(100);
0xC8: CLRWDT
0xCA: MOVLW 0x10
0xCC: MOVWF 0x0, ACCESS
0xCE: DECFSZ 0x0, F, ACCESS
0xD0: BRA 0xCE
!    i2c_transfer_in(RD(ST_ADDRESS), &ReadData, 2);
0xD2: MOVLB 0x2
0xD4: BCF SPI2BAUD, 4, BANKED
0xD6: MOVLW 0x7D
0xD8: MOVWF SPI2TCNT, BANKED
0xDA: MOVLW 0x2
0xDC: MOVWF SPI2RXB, BANKED
0xDE: CLRF 0x8, ACCESS
0xE0: MOVLW 0x5
0xE2: MOVWF 0xA, ACCESS
0xE4: MOVWF 0x9, ACCESS
0xE6: MOVLB 0x0
0xE8: BRA I2C_TRANSFER_IN_HW_3190


Why is there an access to the SFRs of the SPI2 peripheral before calling the I2C_TRANSFER functions?
On the old PIC controllers (K22) there is the MSSP, where the I2C bus is part of the MSSP module, but the Q43 has a dedicated I2C module.
On the Q43 controller I would expect writing the I2C1CNT register.

Is this a bug in the compilers functions for my Q43 controller? Who can help me with this question?

Thanks you
_________________
--------------------
Greetings
FloDu

I'm using CCS V5.115 on a PIC18FXXQ43
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Mon Sep 23, 2024 7:45 am     Reply with quote

There's not!.... Very Happy

It puzzled me too, but if you look the addresses it accesses is the correct
addresses for the I2C registers.
The name being used is wrong. Historically of course I2C was handled
by the MSSP peripheral, which did both I2C and SPI. looks as if whoever
coded this still hasn't got their head around the peripherals being separate
on these chips.
However I walked through the code and it is accessing the I2C registers
not the SPI registers.
Switch the compiler so it doesn't use the symbolic notation and look at
the actual addresses being used. A pain I know, but it does look right.
I can confirm it does still just transfer the 'count' value directly into the
chip register.

Try being sneaky and use i2C_transfer, rather than i2c_transfer_in.

So:

i2c_trasnfer(RD(ST_ADDRESS), NULL, 0, &ReadData, 2);

You have not answered about what RD(ST_ADDRESS) actually is?.

Have you tried what happens if you use i2c_read and i2c_write with the
hardware, rather than i2C_transfer?.
Historically this didn't work on chips with the I2C peripheral, but I know
on some other chips CCS does now allow these to be used.

I think the fact the address naming is wrong says you really ought to
talk to CCS about this. It is a fault and should be fixed, and they will
know if anything else is wrong.

However I have to ask why you say you 'have to use hardware'?. The
only advantage of hardware over software is it allows faster I2C speeds
when you are clocking the processor slowly, and it supports use as a slave,
which the software can't do.
FloDu



Joined: 18 Sep 2024
Posts: 5

View user's profile Send private message

PostPosted: Mon Sep 23, 2024 9:39 am     Reply with quote

Hey Ttelmah,

I double checked this and figured out that the correct registers are accessed. I was confused by the alias, but the BSR is loaded with 2, so the correct registers are accessed.
As I dived deep in the code and the CCS functions (assembler code) I figured out that the transfer_in function is waiting for the I2CSTAT0->MMA Bit is cleared, which is not cleared by the hardware and the code hangs up between the line 0094 and 009C. The MMA bit should be cleared when a stop condition is issued, which should happen after the I2CCNT reaches zero and a NACK is sent to the bus by the mastern.
In my case there is no NACK on the bus.
I have no idea why this is happening, but I will try to figure this out by writing my own transfer_in function based on the CCS's function. Below is the assembler code of the transfer_in function.

Code:

00066:  MOVLB  2
00068:  BCF    x97.4
0006A:  BCF    x99.3
0006C:  BSF    x99.2
0006E:  BCF    x95.6
00070:  BSF    x95.7
00072:  MOVFF  50A,4EA
00076:  MOVFF  509,4E9
0007A:  BSF    x94.5
0007C:  BTFSC  x94.5
0007E:  BRA    007C
00080:  BCF    x94.6
00082:  BTFSC  x97.4
00084:  BRA    0094
00086:  BTFSS  x99.0
00088:  BRA    0082
0008A:  MOVFF  28B,4EE
0008E:  MOVF   x8D,W
00090:  BZ    0094
00092:  BRA    0082
00094:  BTFSC  x99.0
00096:  MOVFF  28B,4EE
0009A:  BTFSC  x98.5
0009C:  BRA    0094
0009E:  CLRF   501
000A0:  BTFSC  x98.3
000A2:  BRA    00A8
000A4:  BTFSC  x97.4
000A6:  INCF   501,F
000A8:  MOVLB  0
000AA:  GOTO   00EA (RETURN)


To answer you questions:
- the i2c_transfer function is also not working as it should
- RD(ST_ADDRESS) is the slave address 0x3E
- The i2c_read and _write functions are not available when using FORCE_HW.
- I am using a graphical display with 160*120 pixels (4 pixel per byte results in 4800 byte). To reduce the CPU load I want to write the display using HW I2C and DMA. I've already written the driver using the XC8 compiler and it's working very smooth. Now I have to port it, because the production firmware is using the CCS.
_________________
--------------------
Greetings
FloDu

I'm using CCS V5.115 on a PIC18FXXQ43
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Mon Sep 23, 2024 10:49 am     Reply with quote

That makes sense. As you say the master is meant to send the NACK
when the count reaches zero. I repeat to talk to CCS. On things like
this they are very good, and may know what is going wrong.
DMA makes sense.
I'm very surprised the device allows a read like this. Normally on I2C,
you have to send the register to be accessed to the device before you
can do a read or write. So the normal transfer is:

START
device address(W)
register address
data to write.
STOP

Or for a read:

START
device address(W)
register address
RESTART
device address(R)
read data
STOP

Are you sure your device correctly handles the write/read without a
register address?.

Not having this could be leaving the device not able to handle this.
FloDu



Joined: 18 Sep 2024
Posts: 5

View user's profile Send private message

PostPosted: Tue Sep 24, 2024 12:26 am     Reply with quote

Hi,

of course I'm writing the register before reading data from the device (see my code listing in the first post). However the master should stop the read transaction after the given count of bytes.

Just to understand you correctly, should I get in contact with the CCS guys? Or are you part of the CCS team and can get more information about this behaviour?

Thank you for your help so far Smile
_________________
--------------------
Greetings
FloDu

I'm using CCS V5.115 on a PIC18FXXQ43
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Tue Sep 24, 2024 3:09 am     Reply with quote

I think you are misunderstanding about addresses.

Your post does not show a register address being written. Or is this
what you send in your write?. If so, this is wrong. You should not stop
between sending the address and switching to reading.
Code:

    static uint8_t WriteData = NEXTCMD(ST_CB_INST);
    static uint8_t ReadData[2];
   
    i2c_transfer(RD(ST_ADDRESS), &WriteData, 1,  &ReadData, 2);

Normally there should not be a stop between sending the register address
and starting the read. There should be a restart. Some devices will accept
a stop, but a lot won't. They set their pointers back to zero when a STOP
is seen. This is why the combined command is there.

Yes. If you look at the header of the forum, it tells you CCS does not
monitor this forum on a regular basis. Given there is a definate issue
with at least the register names, you need to be talking to them.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group