View previous topic :: View next topic |
Author |
Message |
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
i2c Slave and RS232 printf |
Posted: Sat Aug 08, 2015 2:48 am |
|
|
Hi,
Quick one, (18F4620)
Could someone explain what is happening on the hardware when I printf something and in the same time I get i2c call from Master.
Is this affecting printf routine? Is i2c able to work in parallel when processor is printing to serial port in the same time?
I do have a board with 2 processors, one is master and second one is slave. I do print out to the LCD screen from the slave processor.
So here is the scenario: master is ordering slave to print out string to LCD, slave takes it and starts printing out the data, in the middle of this process we receive another call from master cpu sending some other instruction to slave. I just try to understand if this will affect the printf routine once it is underway in the middle of execution.
thnx 4 help _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Sat Aug 08, 2015 3:03 am |
|
|
That is totally down to your software.
There is no magic. The serial has just a couple of characters of buffering in each direction (the actual output shift register being 'sent', and the next byte waiting to send. The I2C, again has a buffer register and in the slave, this has to be read when the master sends a byte.
The serial driver always 'loads ahead', so the buffer register is kept full.
If you stopped and didn't listen to the I2C, then it'd be held till the byte is read. However if you are using interrupt driven I2C, as in the example, then the interrupt will be called, the data read, and meanwhile the byte in the shift register will be sending. If this finishes, then the next byte that is waiting, will be loaded. So you have a 'minimum' of a complete character time, that the serial will keep sending even if not 'serviced'. Given that typically serial rate may be something like 9600bps, and I2C, 100Kbps, the time needed to handle the byte is easily covered.
So it is possible to hang either the serial, or the I2C, if your software doesn't always handle things, but the hardware contains enough buffering to give time for this, provided you use interrupt driven comms. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Mon Aug 10, 2015 2:00 am |
|
|
thnx, that is what I was after. I run 40MHz 18F4620 and could not catch why I miss on some transmission on i2c. In the end it looks like it was too fast, had to slow it down to 100000 and it did fix the issue. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Mon Aug 10, 2015 2:27 am |
|
|
The rate shouldn't matter.
Are you sure your bus capacitance is low enough, and the pull up's good enough to handle 400K?.
What does matter is the delay after sending a byte, before the next. Lowering the rate will give the slave a little more time here. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Mon Aug 10, 2015 2:32 am |
|
|
1.8K the bus is fairly good, 6 chips on it.
I have two different boards, different design, the same thing happening.
It seem to lose every second, third message. I was able to pin it to lost byte in between. I post my routines later on after I clean it. On 100 it is more stable but still playing up occasionally. Thnx _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Mon Aug 10, 2015 3:07 am |
|
|
Code: | #use i2c(master, sda=I2CSDA, scl=I2CSCL, FORCE_HW, SMBUS, FAST=100000) |
Code: | void send_to_i2c_worker(BYTE destinationi2c_, BYTE command_, BYTE data1_, BYTE data2_, BYTE data3_, BYTE data4_, BYTE data5_, BYTE data6_, BYTE data7_, BYTE data8_)
{
i2c_start (); //begin transmission
i2c_write (destinationi2c_); //select address of device to communicate with
i2c_write (command_); //send actual command
i2c_write (data1_); //send actual data
i2c_write (data2_); //send actual data
i2c_write (data3_); //send actual data
i2c_write (data4_); //send actual data
i2c_write (data5_); //send actual data
i2c_write (data6_); //send actual data
i2c_write (data7_); //send actual data
i2c_write (data8_); //send actual data
i2c_stop (); //terminate communication
delayms (5);
} |
Code: | void read_i2c(BYTE destinationi2c, BYTE rcommand, BYTE rdata)
{
int a = 0;
i2c_start () ;
i2c_write (destinationi2c) ;
i2c_write (rcommand);
i2c_write (rdata);
i2c_start () ;
i2c_write (destinationi2c + 1);
i2creadbuff = i2c_read (0);
i2c_stop ();
delayms (5);
} |
Code: | #use i2c(slave, sda=I2CSDA, scl=I2CSCL, address=0xA0, FORCE_HW, SMBUS) |
Code: | #INT_SSP
void ssp_interupt(VOID)
{
byte c;
state = i2c_isr_state ();
IF (state <= 0x80) // send data here
{
//master is sending data
IF (state == 0)
{
//address
c = i2c_read ();
i2cbuffer[0] = c;
IF (c == 0xA0)
{
process_i2c = 1;
} //IF
} //IF
IF (state == 1)
{
//command
c = i2c_read ();
i2cbuffer[1] = c;
} //IF
IF (state == 2)
{
//data1
c = i2c_read ();
i2cbuffer[2] = c;
} //IF
IF (state == 3)
{
//data1
c = i2c_read ();
i2cbuffer[3] = c;
} //IF
IF (state == 4)
{
//data1
c = i2c_read ();
i2cbuffer[4] = c;
} //IF
IF (state == 5)
{
//data1
c = i2c_read ();
i2cbuffer[5] = c;
} //IF
IF (state == 6)
{
//data1
c = i2c_read ();
i2cbuffer[6] = c;
} //IF
IF (state == 7)
{
//data1
c = i2c_read ();
i2cbuffer[7] = c;
} //IF
IF (state == 8)
{
//data1
c = i2c_read ();
i2cbuffer[8] = c;
} //IF
IF (state == 9)
{
//data1
c = i2c_read ();
i2cbuffer[9] = c;
} //IF
IF (state > 9)
{
}
} //IF
IF (state >= 0x80) // read data from here
{
//master is requesting data
IF (i2cbuffer[0] == 0xA0)
{
check_data_to_i2c (); //switch selecting data from VARs
i2cdata = read_back;
i2c_write (i2cdata); //send requested data
SSP_locked = 0;
} //IF
} //IF
} //VOID |
_________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Mon Aug 10, 2015 4:10 am |
|
|
Seriously, with six devices, you are probably straining at 1.8KR.
For 5v Operation (assumed since you have a 5v PIC), with standard bus transition points and 400K operation, your maximum bus capacitance allowed would be about 190pF. Now depending on the wire you have joining the devices, and the specs of the devices themselves, I can see a six device bus, straining this..... |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Mon Aug 10, 2015 4:12 am |
|
|
Thnx, I put a scope on it later on and let you know where I sit on the transmission. Last time I have tested it it was ok but you right it was on prototype in 2009, many things are changed since. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Mon Aug 17, 2015 4:07 am |
|
|
Hey,
1.8K - 2.2K are fairly spot on on the scope. Rising and fall times are in norm.
So circuit is OK.
I think my problem is in the data being overrun by incoming. Slave is not fast enough to process commands and new ones do override the old data in the middle of processing. So I need to slow the master chip down or do a ring buffer on i2c.
thnx for help. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Mon Aug 17, 2015 4:52 am |
|
|
There is one thing 'wrong' that might be causing problems:
On the I2C_read, in state 0x80, the clock will normally release on the read, but in this one state, should not until the write. Hence on this particular read you should use i2c_read(stream_name,2); - this tells the chip _not_ to release the clock. It then gets released on the write.
You should also check that the SEN bit is being set high by the compiler in the slave.
The other thing you can do is simply pause for the slave latency time after each read/write on the master. Should not be necessary. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9245 Location: Greensville,Ontario
|
|
Posted: Mon Aug 17, 2015 4:59 am |
|
|
re: slave code.
this...
Code: |
IF (state == 1)
{
//command
c = i2c_read ();
i2cbuffer[1] = c;
} //IF
|
seems to be 'kinda' repeated 9 times( if I'm reading it right..) perhaps just one 'c=i2c_read(); will be faster ? Also maybe using 'switch' will create better (faster) code ?
I know ISRs must be fast so might be worth cutting test code,compiling, dump the listing to see if it 'looks' better then real world test and check the performance.
Jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Mon Aug 17, 2015 11:16 am |
|
|
Also you say the rise time looks OK. Spec is 300nSec max for fast mode. Is it less than this?. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Tue Aug 18, 2015 12:37 am |
|
|
Ttelmah wrote: | Also you say the rise time looks OK. Spec is 300nSec max for fast mode. Is it less than this?. |
580ns/40ns at 100KHz _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Tue Aug 18, 2015 1:53 am |
|
|
Ttelmah wrote: | There is one thing 'wrong' that might be causing problems:
On the I2C_read, in state 0x80, the clock will normally release on the read, but in this one state, should not until the write. Hence on this particular read you should use i2c_read(stream_name,2); - this tells the chip _not_ to release the clock. It then gets released on the write.
You should also check that the SEN bit is being set high by the compiler in the slave.
The other thing you can do is simply pause for the slave latency time after each read/write on the master. Should not be necessary. |
...here you gave me a bit of Googling to do, anyway it is simple and not, will take me some time to figure this out how to do it properly. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
Linuxbuilders
Joined: 20 Mar 2010 Posts: 193 Location: Auckland NZ
|
|
Posted: Tue Aug 18, 2015 2:09 am |
|
|
temtronic wrote: | re: slave code.
this...
Code: |
IF (state == 1)
{
//command
c = i2c_read ();
i2cbuffer[1] = c;
} //IF
|
seems to be 'kinda' repeated 9 times( if I'm reading it right..) perhaps just one 'c=i2c_read(); will be faster ? Also maybe using 'switch' will create better (faster) code ?
I know ISRs must be fast so might be worth cutting test code,compiling, dump the listing to see if it 'looks' better then real world test and check the performance.
Jay |
Code: | IF (state <= 0x80)
{
c = i2c_read ();
IF (state == 0)
{
i2cbuffer[0] = c;
if (c == 0xA0)
{
process_i2c = 1;
}
else
{
process_i2c = 0;
}
}
if (state <= 9)
{
i2cbuffer[state] = c;
}
} |
I had a bug, if my comms would be more than 9 (like reading time of RTC) then I was not clearing the buffer. This does fix the issue, however for some reason the code is unstable. _________________ Help "d" others and then you shell receive some help from "d" others. |
|
|
|