View previous topic :: View next topic |
Author |
Message |
soonc
Joined: 03 Dec 2013 Posts: 215
|
I2C problems with PIC18F47K42 |
Posted: Wed Feb 13, 2019 8:31 pm |
|
|
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
|
|
Posted: Thu Feb 14, 2019 1:48 am |
|
|
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
|
|
Posted: Thu Feb 14, 2019 9:50 pm |
|
|
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
|
|
Posted: Fri Feb 15, 2019 1:26 am |
|
|
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...
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
|
|
Posted: Fri Feb 15, 2019 10:04 am |
|
|
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...
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
|
|
Posted: Fri Feb 15, 2019 11:57 am |
|
|
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
|
|
Posted: Fri Feb 15, 2019 12:37 pm |
|
|
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
|
|
Posted: Fri Feb 15, 2019 1:57 pm |
|
|
OK. I've edited my post to reflect that. Thanks. |
|
|
soonc
Joined: 03 Dec 2013 Posts: 215
|
|
Posted: Fri Feb 15, 2019 10:26 pm |
|
|
It's late I'll try this this weekend.
Thanks for the response. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Sat Feb 16, 2019 3:48 am |
|
|
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
|
|
Posted: Sat Feb 16, 2019 10:20 pm |
|
|
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
|
|
Posted: Sun Feb 17, 2019 12:58 am |
|
|
Please post what actually worked for you, so other people will be able
to see what is needed. Helps everybody. |
|
|
|