|
|
View previous topic :: View next topic |
Author |
Message |
rovtech
Joined: 24 Sep 2006 Posts: 262
|
Has anyone successfully used a 16F1938 as slave I2C? |
Posted: Tue Sep 18, 2018 8:17 am |
|
|
I have been trying to migrate from WORKING software using a 16F722 as an I2C slave to a newer PIC16F1938. Has anyone successfully used a 16F1938 as slave I2C?
Can I see your ISR software please? Also the Master request software?
What was your version of CCS compiler and was it PCM?
This thread started as "16F882 used as slave i2c" which resolved the fact that a 16F882 cannot be used as a slave I2C. It also shows the problems after I discovered this and tried a 16F1938 which should work but does not.
I need to be convinced that the 16F1938 can be used as a slave I2C using the CCS compiler. It is likely that I'm doing something wrong but I was not in the case of the 16F882. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Sep 18, 2018 11:46 am |
|
|
I have a 16F1937. It's in the same overall family as the 16F1938.
Do you want me to test it in hardware as an i2c slave ? I can do so
at your request. I'd prefer to test it with my own master and slave
PIC programs (in CCS code), because I know they work.
I don't have two 16F1937's so for a master I'd have to use something
else. I would prefer to run master and slave at +5v, with 4.7K pullups.
Let me know if you want this test done. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Tue Sep 18, 2018 12:53 pm |
|
|
I appreciate the offer but hold off for a bit.
I am just starting to build a test circuit using two 16F1938 where the master will read 8 DIP switches and the slave will display on 8 LEDs. I will post the code here.
Could you see anything wrong with my code in the old posting? Particularly how I set the PIC frequency and my ISR code?
You could post an example of what has worked for you, just the relevant code for send and receive including your Interrupt Service Routine, ISR.
The Master does not matter, I have migrated all the way from 16C74A to 16F886 and now the 16F1938, all with no problems using various chips as slave until I tried the 16F882 and 16F1938 which don't work..
Update: I have a circuit built using two 16F1938 and the master is passing 3 bytes successfully to the slave. I will try returning data later today (car service). Also studying I2C basics and Ttelmah's comments in my original post. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19552
|
|
Posted: Wed Sep 19, 2018 7:15 am |
|
|
Good.
It is the 'turn round' that gives issues when reading data from a slave.
Remember the master sends a new 'address' with the bottom bit changed to say 'I am going to read', and then as it's next transaction starts reading. The slave has to read the address (still holding the clcok), and then 'preload' the byte ready for the subsequent read, and release the clock.
Now there is an issue with some PIC's not releasing the clock on the write (there are about three generations of I2C peripheral on the PIC - the oldest doesn't have this issue and the newest also doesn't, but the middle one does). On these you have to explicitly release the clock:
Code: |
#bit CKP=getenv("BIT:CKP")
{
unsigned int8 state, incoming;
state = i2c_isr_state();
//Note <= here
if(state <= 0x80) //Master is sending data
{
if(state == 0x80) //Now note == here
incoming = i2c_read(2); //Passing 2 as parameter, causes the function to read the SSPBUF without releasing the clock
else
incoming = i2c_read();
if(state == 1) //First received byte is address
address = incoming;
else if(state >= 2 && state != 0x80) //Received byte is data
buffer[address++] = incoming;
}
if(state >= 0x80) //Master is requesting data
{
i2c_write(buffer[address++]);
CKP=TRUE; //Add this for chips that don't release the clock....
}
}
|
It's also vital that the master device, when reading from the slave does not send ACK on the last byte transferred.
So if transferring three bytes:
Code: |
//start the read
b0=i2c_read();
b1=i2c_read();
b2=i2c_read(0); //this tells the slave this is the last byte
I2c_stop();
|
Keeping the two internal state machines in the I2C peripheral 'happy' is a matter of ensuring all phases are done at the right point!...
(I managed to post '1' not '0' above - having a bad morning! - corrected now). |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Wed Sep 19, 2018 12:12 pm |
|
|
You are absolutely right Ttelmah.
I had to simplify my Master PIC hardware and software as it was complicated by RS488 communications with a control console and all sorts of other tasks with multiple I2C to the ROV and robotic arm. Now I have a simple hardware with two PICs talking to each other. The Master has 8 LEDs on the A port to display what is happening. Two 2k pullups on I2C, a 10k pullup on MCLR, a programming ICSP connector, and 5v and ground.
Note to others; This software is almost identical to other posts under various titles and will work with, including others, a PIC16F722 as slave with the comments // removed. It will not work with a 16F1938 which is what this post is about. I suggest reading a description of how I2C works and also pay particular attention to "i2c_isr_state()" in the CCS manual under "Built-in Functions" and other references to i2c in the manual.
My resources for understanding I2C are Circuit Cellar #228 July 2009 and #233 Dec 2009, and Signetics Linear Data Manual (old), and of course this forum.
Note that this is where I'm starting from in getting this software working. I will post the correct working version later so bear with me. I am no expert.
The Master I2C software at present is below. It works as long as the reply lines are commented out as shown.
Code: | /* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=8000000)
#use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
#byte porta = getenv ("SFR:PORTA")
#byte portb = getenv ("SFR:PORTB")
#byte portc = getenv ("SFR:PORTC")
// define I2C addresses
#define SLAVE_WRT_ADDR 0X14 // slave PIC
#define SLAVE_READ_ADDR 0x15
/* The main function */
void main(void)
{
// declare variables
int count, reply = 0;
int ii = 3;
int jj = 5;
// Initialize port directions
set_tris_a(0xff); // Port A all inputs
set_tris_b(0b11001001); // B1, B2, B4, B5 outputs, the rest inputs
set_tris_c(0b10011000); // set Port C3, C4, C7 inputs, the rest outputs
while (1)
{
count = count + 1;
// send data
I2C_START (); // start I2C
I2C_WRITE (SLAVE_WRT_ADDR); // addr of Slave PIC
I2C_WRITE (count); // send count
I2C_WRITE (ii); // send ii
I2C_WRITE (jj); // send jj
I2C_STOP (); // stop I2C
// read side thruster status
// I2C_START (); // start I2C
// I2C_WRITE (SIDE_READ_ADDR); // addr of slave PIC
// reply = I2C_READ (0); // get reply (no ack)
// I2C_STOP (); // stop I2C
output_toggle (PIN_B2); // flash LED each pass
delay_ms(1000); // allow time to see results
} // end of while loop
} // end of main function
// end |
The Slave PIC software is below and works as long as the Master is as above. Next I will try Ttelmah's suggestions and post the results.
Code: | /* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=32 MHZ)
// #use fast_io (B)
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4, address=0x14)
// global variables
int count, ii, jj; // data from master
int reply; // data returned from slave
// Interrupt on I2C (Interrupt Service Routine)
#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 is count
count = incoming;
if (state == 2) // second data received is ii
ii = incoming;
if (state == 3) // third data received is jj
jj = incoming;
}
if (state >= 0x80) // master is requesting data from slave
{
i2c_write (reply); // send reply
}
}
/* The main function */
void main(void)
{
// declare variables
// initialize port directions
set_tris_a (0x00); // set Port RA3 output, the rest inputs
set_tris_b (0xC9); // set Port B7, B6, B3, B0 inputs, B5, B4, B2, B1 outputs
set_tris_c (0xff); // set Port C3 & C4 inputs, the rest outputs
enable_interrupts (INT_SSP);
enable_interrupts (GLOBAL);
// main loop
while (1) // endless loop
{
output_a(count);
delay_ms (1000);
output_a(ii);
delay_ms (1000);
output_a(jj);
delay_ms (1000);
} // end of endless while loop
} // end of main function |
Last edited by rovtech on Thu Sep 20, 2018 6:57 am; edited 1 time in total |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Wed Sep 19, 2018 6:40 pm |
|
|
Thank you Ttelmah and PCM Programmer for help in this and other problems.
The program is now working. I think you meant state 1 is first data byte and I have adjusted the program to reflect that. I also added displaying the addr(ess). The 16F1938 needs the forced release of CKP.
The Master sends count which is my switch settings, ii which is set to 3, and jj which is what is read back from the Slave.
The Slave displays on 8 LEDs in order: addr, count, ii, jj (which is what it returned to the Master). With my switches set to 0x81 (count), and reply set to 0xc8 as in the program I get LEDs showing: 0x15 (Slave read address), 0x81 (switches), 0x03 (ii), and 0xc8 (reply). Sorry if this is confusing but, along with an LED on B2 of the Master, these displays made sure everything was being sent and received properly.
This is the Master software
Code: | /// Mster I2C PIC FIRMWARE Main PIC v1.1.c Last modified: 19 Sept 2018 ///
/* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=8000000)
#use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
#byte porta = getenv ("SFR:PORTA")
#byte portb = getenv ("SFR:PORTB")
#byte portc = getenv ("SFR:PORTC")
// define I2C addresses
#define SLAVE_WRT_ADDR 0X14 // slave PIC
#define SLAVE_READ_ADDR 0x15
/* The main function */
void main(void)
{
// declare variables
int count, reply = 0;
int ii = 3;
int jj = 5;
// Initialize port directions
set_tris_a(0xff); // Port A all inputs
set_tris_b(0b11001001); // B1, B2, B4, B5 outputs, the rest inputs
set_tris_c(0b10011000); // set Port C3, C4, C7 inputs, the rest outputs
while (1)
{
count = input_A(); // switches on port A
// send data
I2C_START (); // start I2C
I2C_WRITE (SLAVE_WRT_ADDR); // addr of Slave PIC
I2C_WRITE (count); // send count
I2C_WRITE (ii); // send ii
I2C_WRITE (jj); // send jj
I2C_STOP (); // stop I2C
// read reply
I2C_START (); // start I2C
I2C_WRITE (SLAVE_READ_ADDR); // addr of slave PIC
reply = I2C_READ (0); // get reply (no ack)
I2C_STOP (); // stop I2C
jj = reply; // programmed in slave
output_toggle (PIN_B2); // flash LED each pass
delay_ms(1000); // allow time to see results
} // end of while loop
} // end of main function
// end |
The Slave software is:
Code: | /// Slave PIC TEST 16F1938 Last modified: 19 Sept 2018 ///
/* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=32 MHZ)
// #use fast_io (B)
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4, address=0x14)
#bit CKP=getenv("BIT:CKP")
// global variables
int addr, count, ii, jj; // data from master
int reply = 0xc8; // data returned from slave
// Interrupt on I2C (Interrupt Service Routine)
#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) // address match
{
incoming = i2c_read(2); // read address without releasing clock
addr = incoming;
}
else
incoming = i2c_read();
if (state == 1) // first byte received is count
count = incoming;
if (state == 2) // second data received is ii
ii = incoming;
if (state == 3) // third data received is jj
jj = incoming; // the reply from this sent back
}
if (state >= 0x80) // master is requesting data from slave
{
i2c_write (reply); // send reply, set in this program
CKP=TRUE; // release clock
}
}
/* The main function */
void main(void)
{
// declare variables
// initialize port directions
set_tris_a (0x00); // set Port RA3 output, the rest inputs
set_tris_b (0xC9); // set Port B7, B6, B3, B0 inputs, B5, B4, B2, B1 outputs
set_tris_c (0xff); // set Port C3 & C4 inputs, the rest outputs
enable_interrupts (INT_SSP);
enable_interrupts (GLOBAL);
// main loop
while (1) // endless loop
{
output_a(addr);
delay_ms (1000);
output_a(count);
delay_ms (1000);
output_a(ii);
delay_ms (1000);
output_a(jj);
delay_ms (1000);
} // end of endless while loop
} // end of main function |
Last edited by rovtech on Fri Sep 21, 2018 8:40 am; edited 1 time in total |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Thu Sep 20, 2018 10:19 am |
|
|
The software in my ROV is now working using a 16F1938 as a slave.
It is very important to check the Silicon version of the PIC and look at the Errata for it.
The Device ID Revision = 00000003 for the 16F1938 that I am using. The earlier revisions have issues described in the Errata. They may not work with this software.
The Errata for the 16F882 that I initially tried indicates that none of them will work as an I2C Slave! It is important to check your PIC. The revision shows up right after "Target Detected", on my ICD3 anyway.
My PCM compiler version 5.064 uses software I2C by default so that is what is being used in the above software and initially on my ROV. I forced the hardware I2C with
Code: | #use i2c (SLAVE, FORCE_HW, SCL=PIN_C3, SDA=PIN_C4, address=0x14) |
in my ROV and it works fine.
I believe newer compilers use hardware I2C by default so if it is important it is best to force your choice. |
|
|
|
|
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
|