View previous topic :: View next topic |
Author |
Message |
rovtech
Joined: 24 Sep 2006 Posts: 270
|
i2c between RaspberryPi and PIC |
Posted: Sat Apr 19, 2025 2:01 pm |
|
|
I have been using i2c between PICs for years. However I cannot get a RaspberryPi 4b to receive data from a PIC 16F1938.
Here is my PIC software:
Code: | /* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=16000000)
#use i2c (SLAVE, FORCE_HW, SCL=PIN_C3, SDA=PIN_C4, address=0x14)
#byte portc = getenv ("SFR:PORTC")
#bit CKP=getenv("BIT:CKP")
// Global variables
int status = 0xc1;
int ct_req = 0x00;
// Interrupt on I2C
#INT_SSP
void ssp_interrupt () // have an interrupt
{
int incoming, state; // variables
state = i2c_isr_state (); // get state
if (state < 0x80) // master is sending data
{
incoming = i2c_read (); // throw away device address if state = 0
if (state == 1) // first data received
ct_req = incoming;
}
if (state >= 0x80) // master is requesting data from slave
{
i2c_write (status);
CKP = TRUE;
}
}
// ****************************************************************************
/* The main function */
void main(void)
{
// initialize I2C. These did not help:
// i2c_init(100000); // baud = 100,000
// # define read_port 0xff // all bus pins input
// # define write_port 0x00 // all bus pins output
// Initialize port directions
set_tris_c(0b00011000); // i2c as inputs
// setup interrupts
enable_interrupts (INT_SSP); // enable I2C interrupt
enable_interrupts (GLOBAL);
// *******************************************************************************
while (1) // loop continuously
{
delay_ms (250); // wait
output_toggle(PIN_C5); // flash LED if IRC not hung
} // end of while loop
} // end of main function |
Here is my Python software:
(sudo i2cdetect -y 1 returns address 0x0a)
Code: | from smbus import SMBus
import time
i2cbus = SMBus(1) # create a new i2c bus
i2caddress = 0x0A # address of Curve Tracer
status = 0xf0
status = i2cbus.read_byte(i2caddress) # read the status
print(status) |
Here is my schematic:
[img]https://drive.google.com/file/d/1u70PVtwvevMCdCCHkvuvc9dFPeL8fnu9/view?usp=sharing[/img]
Problems:
• The PIC address is 0x14 or 0001 0100 but the RaspberryPi reads it as 0x0A or 0000 1010. See explanation below.
• How does the RaspberryPi send the address for a Read or Write?
• How do I tell the RaspberryPi to be Master?
• The PIC uses 5v on the i2c bus whereas the RaspberryPi and other devices use a 3.3v bus. This problem is solved by using a level shifter as seen in the schematic.
• The PIC requires pullup resistors, the RaspberryPi does not. Corrected with the circuit.
The I2C addresses are 7 bits. When sending out the 7 bit address, we still always send 8 bits. The extra bit is used to inform the slave if the master is writing (0) to it or reading from it (1). The 7 bit address is placed in the upper 7 bits of the byte and the Read/Write (R/W) bit is in the LSB (Least Significant Bit).
The address of the PIC is declared as 0x14 which is 0x0a if the LSB is removed.
These are the signals on the i2c bus:
I don’t understand the first group. This does not always happen.
The second group sends 000101010 which is 0x14 with the direction 1 for a read then the 9th clock has SDA held low. So far so good but the PIC does not return the data or end the transaction.
I have an LED blinking every time through the while loop but it stops when the RPi sends the request.
The PIC is hanging the i2c bus. HELP
Waveforms
[img]https://drive.google.com/file/d/1vPvl56nYrz-NLs6C4nHMCqNe7dbPbABf/view?usp=sharing[/img] |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 19782
|
|
Posted: Sun Apr 20, 2025 4:26 am |
|
|
Since you are trying to do the read without sending a register address,
you need to _preload_ the byte to be sent, when the device address is
received with the read bit set.
The byte has to be already 'in' the PIC I2C data register when the read
is performed, otherwise it has nothing to send......
Normally when doing a standard read when a register address has been sent,
this is done by doing a read(0) to read the address, and then the write
in the same INT routine.. You need to do the same for the device address
since you are not sending a register address. |
|
 |
temtronic
Joined: 01 Jul 2010 Posts: 9458 Location: Greensville,Ontario
|
|
Posted: Sun Apr 20, 2025 6:09 am |
|
|
curious..
The Pi has ...i2cbus = SMBus(1) .
soooo...
would the PIC have to have it's I2C port configured for SMBus as well ??
never worked with Pies,so don't know what their 'I2C port' is. |
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 19782
|
|
Posted: Sun Apr 20, 2025 6:33 am |
|
|
Shouldn't matter. Smbus, and I2C are 99% compatible. Smbus allows
lower voltage thresholds, and has a timeout on a transaction, but the basic
protocols are pretty much the same.
However he should select SMBUS on the PIC. One key is that the Pi
I2C bus is pulled up to 3.3v inside the unit. He says he is using level
translation, so he should be getting 5v at the PIC, but better to be safe
on this.
His state==0x80, need to perform an i2c_read(0), then an I2Cwrite.
He will never get a state below this, since he is starting with the address
write with the read bit set. |
|
 |
rovtech
Joined: 24 Sep 2006 Posts: 270
|
|
Posted: Sun Apr 20, 2025 9:00 am |
|
|
Thanks Ttelmah. I did not fully understand your reply about sending the register address. However it did jog my memory and I realized I was not using the same ISR that had worked in the past. I normally use i2c_read(2) but because I left it blank ( ) it defaulted to (1). I don't understand why it held the SCL line low because i2c_read(2) indicates do not release clock. Since SCL is controlled by the RaspberryPi Master the default i2c_read(1) does an ACK. I'm obviosly confused.
However muddling through I changed my ISR to
Code: | // Interrupt on I2C
#INT_SSP
void ssp_interrupt () // have an interrupt
{
int incoming, state; // variables
state = i2c_isr_state (); // get state
if (state <= 0x80) // master is sending data
{
if(state==0x80) // throw away device address
incoming = i2c_read (2); // but do not release i2c bus
else
incoming = i2c_read();
if (state == 1) // first data received
ct_req = incoming;
}
if (state >= 0x80) // master is requesting data from slave
{
i2c_write (status);
CKP = TRUE;
}
} |
and now it works and I get the reply I expected and the bus behaves and closes.
https://drive.google.com/file/d/1oe7T8ukD8uBrNVG7DYbWFySW8CnviSRn/view?usp=sharing
Perhaps you can explain the stream parameter in i2c_read(stream,ack) and how I would use it.
There are a couple of other things in my code that bother me. Should the
be
|
|
 |
Ttelmah
Joined: 11 Mar 2010 Posts: 19782
|
|
Posted: Sun Apr 20, 2025 11:02 am |
|
|
Good.
Yes that is right.
Shows the importance of looking at what you used before....
The pont about streams is the same as for serial. On chips with multiple
I2C peripherals, or if you use one software and one hardware I2C, you can
have multiple #use I2C setups, with different stream names. You then use
this names in the read, write or state commands so that the compiler knows
which of the ports you want to talk to. |
|
 |
rovtech
Joined: 24 Sep 2006 Posts: 270
|
|
Posted: Mon Apr 21, 2025 8:11 am |
|
|
I need to send an array[512] from slave PIC to master RaspberryPi.
Am I correct in assuming that the Pi has to make 512 requests in a loop?
Is there a way that the PIC can simply send 512 writes in a loop?
I am just learning Python and I see there is a read_i2c_block_data(addr,cmd) but I have no idea how to use it.
Looking at Ttelmah's last post in https://www.ccsinfo.com/forum/viewtopic.php?t=59131&highlight=i2c+array
he seems to be incrementing the array for each request from the master, so 16 transactions.
Code: | #INT_SSP
void ssp_interrupt(){
int8 incoming, state;
static int8 count=0;
state = i2c_isr_state();
if(state < 0x80)
{ // Master is sending data
incoming = i2c_read();
}
if(state == 0x80)
{
count=0;
incoming = i2c_read(2); //perform the read on state==0x80
//note special read that does not release the CKP
}
if (state>=0x80)
{ // Master is requesting data from slave
i2c_write(sensorArray[count++]);
}
} |
But the master is not a Pi running Python so may not be as versatile.
I don't understand how count can be incremented if it is reset to 0 each interrupt.
I started a new thread:
https://www.ccsinfo.com/forum/viewtopic.php?p=245698#245698 |
|
 |
|