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 problems with PIC18F47K42

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



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

I2C problems with PIC18F47K42
PostPosted: Wed Feb 13, 2019 8:31 pm     Reply with quote

Using I2C Hardware on PIC18F46K42 and PIC18F47K42.

I was using software I2C code but now need to get more speed.

I have the hardware I2C port working for simple transfers, but I don't understand how the new i2c_transfer() set of functions can deal with special cases.

Example: Reading RTC chip PCF85263A using Old I2C code:

Code:

   i2c_start();
   i2c_write(0XA2); // COMMAND to write = 0XA2
   i2c_write(0X00); // Start reading at register 0
   // no i2c_stop()
   i2c_start();
   i2c_write(0XA3); // COMMAND get ready to READ  the command is 0XA3
   for (i = 0; i < 8; i++)
   {
      buf[i] = i2c_read(TRUE); // read 8 bytes including DoW
   }
   // the 9th Byte
   buf[i] = i2c_read(FALSE);  // last read no ack
   i2c_stop();


How is this done with i2c_transfer() ?
The help file suggests Start Stop are all done in the i2c_transfer() Ok but what about ack or no ack ?

How do I do a read with ack and then the last read without ack ?

May be it's all so simple I just can't see the forest because of the trees!
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Thu Feb 14, 2019 1:48 am     Reply with quote

The chip's data sheet is your friend.

The I2C_transfer mode is basically designed to use the I2C (rather than
MSSP) peripheral that exists on this and a few other new chips.
This peripheral says:
Quote:

Certain conditions will cause a not-ACK (NACK) to be
sent automatically. If any of the RXRE, TXWE, RXO, or
TXU bits is set, the hardware response is forced to
NACK.


Which tells you it'll send a NACK, when on the last byte (no more space left
in the buffer).
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

PostPosted: Thu Feb 14, 2019 9:50 pm     Reply with quote

Ttelmah wrote:
The chip's data sheet is your friend.

The I2C_transfer mode is basically designed to use the I2C (rather than
MSSP) peripheral that exists on this and a few other new chips.
This peripheral says:
Quote:

Certain conditions will cause a not-ACK (NACK) to be
sent automatically. If any of the RXRE, TXWE, RXO, or
TXU bits is set, the hardware response is forced to
NACK.


Which tells you it'll send a NACK, when on the last byte (no more space left
in the buffer).

Thanks for the response, but now I'm even more confused.
Yes I know the RTC chip is using I2C and I thought the 47K42 is using I2C or at least that's what it's named as in the data sheet.

I'm confused about the NAK/NONAK but even more about how CCS library i2c_transfer can be used for reading the RTC.

My code sample (original posting) to read the RTC using the "old i2c" CCS lib. works.

The first part of the code calls for the chip address and the register address, but NO i2CStop....

How do I instruct the i2c_transfer() not to send a Stop ?

CCS explanation of the i2c_transfer() function/s says it takes care of start and stop but the function can't know what the device requires !

Reading the NXP data sheet and coding with the old i2c function calls was easy, but the new i2c_transfer() functions are a black box that I don't see follow the rtc chip requirements.

I'll call CCS and see what they can tell me.
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Fri Feb 15, 2019 1:26 am     Reply with quote

OK.
First thing you can perfectly well use the 'old' syntax.

Just use 'FORCE_SW' in #use I2C. The software library works well
and still merrily uses the old syntax. On a reasonably fast chip it
is just as quick for a chip only supporting 100K I2C, and avoids having
to get involved in I2C_transfer... Smile

Now if you do feel you must use I2C_transfer look at the example for
this that is now in the examples. This shows how to do a read and a write
using the transfer.

Unfortunately, transfer is forced on you when using this new I2C peripheral.
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

PostPosted: Fri Feb 15, 2019 10:04 am     Reply with quote

Ttelmah wrote:
OK.
First thing you can perfectly well use the 'old' syntax.

Just use 'FORCE_SW' in #use I2C. The software library works well
and still merrily uses the old syntax. On a reasonably fast chip it
is just as quick for a chip only supporting 100K I2C, and avoids having
to get involved in I2C_transfer... Smile

Now if you do feel you must use I2C_transfer look at the example for
this that is now in the examples. This shows how to do a read and a write
using the transfer.

Unfortunately, transfer is forced on you when using this new I2C peripheral.


Yes I have been using the old style with FORCE_SW but as I said it's time to have more speed.

At 12MHz the cpu is running at 3Mhz and throughput using software i2c is about 32kbs Using Hardware and writing to a FRAM chip with i2c_transfer() the speed jump to about 120kbs.

I need the extra speed in the i2c.

I have the example code you mention for using i2c_transfer() but it is for a simple straight forward read and write which as I said works, and is the code I used to measure the speed difference.

The NXP rtc ship is not the only chip that has straenge i2c requirements, but considering NXP are the originators of the i2c protocol I assume other OEM's would have to be compliant to use the term/trademark i2c !

All call ccs and see what can be done.

Thanks for you input it is always much welcomed.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Feb 15, 2019 11:57 am     Reply with quote

Based on your posted code and the CCS example file, it looks like this
would work:
Code:

#include <18F46K42.h>
#fuses NOWDT, BROWNOUT
#use delay(internal=4M)

#pin_select U1TX=PIN_C6
#pin_select U1RX=PIN_C7
#use rs232(baud=9600, UART1, ERRORS)

#pin_select SCL1OUT = PIN_C0
#pin_select SCL1IN  = PIN_C0
#pin_select SDA1OUT = PIN_C1
#pin_select SDA1IN  = PIN_C1 
#use i2c(Master, I2C1)

#define PCF85263A_I2C_ADDRESS  0xA2
#define PCF85263A_REG_0        0x00

//======================================
void main()
{                                                                     
int8 wdata[1] = PCF85263A_REG_0;
int8 rdata[9] = {0};
int i;

i2c_transfer(PCF85263A_I2C_ADDRESS, wData, sizeof(wdata) +1, rData, sizeof(rdata));

for(i=0; i < sizeof(rdata); i++)
   printf("%x ", rdata[i]);
printf("\n\r");

while(TRUE);
}


----------
Edited to add 1 to the write data size, to include the address byte (per Ttelmah).


Last edited by PCM programmer on Fri Feb 15, 2019 1:57 pm; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Fri Feb 15, 2019 12:37 pm     Reply with quote

We found before that the transmitted size, has to include the address byte.
So I think you need to use:

sizeof(wdata)+1

This agrees with what the data sheet says about this.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Feb 15, 2019 1:57 pm     Reply with quote

OK. I've edited my post to reflect that. Thanks.
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

PostPosted: Fri Feb 15, 2019 10:26 pm     Reply with quote

It's late I'll try this this weekend.
Thanks for the response.
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Sat Feb 16, 2019 3:48 am     Reply with quote

OK. Iv'e just walked through the assembler being generated. It looks basically
'right', and the code is automatically setting the read bit and reloading the
address after the register address is sent. However it looks as if the same
'extra byte' is needed on the read transfer as for the write. The data sheet
says the count is decremented after each byte is read or written, and the
chips receive address is sent before the data packet is read, so it appears
the same 'extra' count is needed:
Code:

i2c_transfer(PCF85263A_I2C_ADDRESS, wData, sizeof(wdata)+1, rData, sizeof(rdata)+1);


Everything then looks correct (cross things....).

The example given by CCS appears to be missing the need for this extra
count. Will be interesting to see if I am right on this!.
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

PostPosted: Sat Feb 16, 2019 10:20 pm     Reply with quote

Ttelmah wrote:
OK. Iv'e just walked through the assembler being generated. It looks basically
'right', and the code is automatically setting the read bit and reloading the
address after the register address is sent. However it looks as if the same
'extra byte' is needed on the read transfer as for the write. The data sheet
says the count is decremented after each byte is read or written, and the
chips receive address is sent before the data packet is read, so it appears
the same 'extra' count is needed:
Code:

i2c_transfer(PCF85263A_I2C_ADDRESS, wData, sizeof(wdata)+1, rData, sizeof(rdata)+1);


Everything then looks correct (cross things....).

The example given by CCS appears to be missing the need for this extra
count. Will be interesting to see if I am right on this!.


Thanks to both PCM and Ttelmah.

It does work.

i2c_transfer() is not as fast as I originally thought. My measurement was wrong.

Using hardware or software i2c with FAST as the speed, the clock cycle time it the same but the software version is slower I suspect it has more code to execute.

software i2c
// setting FAST 1 clock cycle = 6.2uS
// setting FAST=100000 1 clock cycle = 11.6uS

hardware i2c
// declaring FAST
// 1 clock cycle = 6.2uS

// declaring FAST=100000
// 1 clock cycle = 10.2uS

////////////////////////////////////////////////////
// Added later: This works
#define SCL_PIN PIN_B1
#define SDA_PIN PIN_B2
#pin_select SCL2OUT = SCL_PIN
#pin_select SDA2OUT = SDA_PIN

#use i2c(MASTER, scl=SCL_PIN, sda=SDA_PIN, FAST)

int8 n8Reg=0; // start reading from register zero
i2c_transfer(0XA2, &n8Reg, 1, rtcbuf, 8);

Using this declaration:
#use i2c(Master, Fast, sda = PIN_B2, scl = PIN_B1, FAST, force_sw)
Allows using old style and i2c_transfer().

As the measured speed is so very similar I opted to keep the old style as the code is debugged and working, rather than rewrite all the calls to i2c_transfer().


Note!
My original code was reading 9 bytes from the rtc, that was an error, it worked just fine but the last read was never used.

Thanks again for all the help.


Last edited by soonc on Mon Feb 18, 2019 5:09 pm; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19496

View user's profile Send private message

PostPosted: Sun Feb 17, 2019 12:58 am     Reply with quote

Please post what actually worked for you, so other people will be able
to see what is needed. Helps everybody. Smile
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