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 support@ccsinfo.com

i2c communication issues

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







i2c communication issues
PostPosted: Wed Aug 28, 2002 11:36 am     Reply with quote

<font face="Courier New" size=-1>Hello all,

I am currently using two PIC 16c771 devices for a single master/slave i2c configuration.

Right now, I'm mainly concerned with writing two known good bytes of data to my slave and am using slave i2c hardware mode, not via software.

I have cut & pasted my code samples below.

I2C MASTER:

#INT_RTCC
clk_isr()
{
rb5=0;
set_timer0(98); // interrupt 995 Hz
set_adc_channel(0); // select pin 1 as analog to digital channel
delay_us(5); // delay required capacitor charge time
value = read_adc(); // Take a sample of analog input


switch (slave_driver){
case 0:
I2C_START(); // generate i2c start condition
if(!I2C_WRITE(0xA0)) // write to slave 0xA0 and expect a 0 for acknowledge
{
if(!I2C_WRITE(DATA1a)) // write first 8 bits
{
rb5=1;
I2C_WRITE(DATA2a); // write second 8 bits
slave_driver = 1; // move to next case
}
}
i2c_stop(); // generate i2c stop condition
break; // end case 0

case 1:
I2C_START(); // generate i2c start condition
if(!I2C_WRITE(0xB0)) // write to slave 0xB0 and expect a 0 for acknowledge
{
if(!I2C_WRITE(DATA1b)) // write first 8 bits
{
I2C_WRITE(DATA2b); // write second 8 bits
slave_driver = 0; // move to next case
}
}
i2c_stop(); // generate i2c stop condition
break; // end case 1
}

i2c_reset(); // reset the i2c when done

}

I2C SLAVE:

#INT_SSP
void ssp_isr()
{ // begin void

switch(datacatcher)
{
case ADDRESS: // begin case address
{
addr=SSPBUF; // read i2c buffer to a dummy variable, this clears the buffer
// and prepares for next data byte
datacatcher = byte1; // transition for byte1 read
if (addr != 0xA0)
i2c_reset(); // reset SSP to continue to search for addres 0xA0
}
break;

case byte1:
DATA1 = i2c_read();
datacatcher = byte2;
break;

case byte2:
DATA2 = i2c_read();
break;

i2c_reset();
} // end switch

} // end void

So far, my master is trying to address my slave, but unfortunately, there is no data transfer and I receive a !ACK and nothing from that point on.

Any help would be greatly appreciated.

Thanks!
Jim</font>
___________________________
This message was ported from CCS's old forum
Original Post ID: 6679
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

Re: i2c communication issues
PostPosted: Wed Aug 28, 2002 1:56 pm     Reply with quote

Here is an example of some code that I did based on ACCESS.Bus protocol (it was actually modifed to include a checksum that is read back from the slave device). I did not see where you are setting your address or the mode of operation. The hardware will not receive any data if it is not addressed. You should have something like this in your main()

/* setup ABUS */
SSPADD = Fixed_Address;
SSPCON1 = (ABUS_SLAVE_MASK | ABUS_CLK_ENABLE | ABUS_MODE_SETUP);
empty = SSPBUF;
/* turn on access bus interrupt and set the abus address */
PIR1bits.SSPIF=0;
PIE1bits.SSPIE=1;


#INT_SSP
void ABus_Int(void)
{
/* Make sure the buffer has not overflowed */
if (SSPCON1bits.SSPOV)
{
I2CStatus = SSPBUF;
/* Clear the register */
SSPCON1bits.SSPOV=0;
I2CStatus = I2CIDLE;
}
else
{
Wait_Time = 5;

if (Rx_Msg.hdr.stat != PROCESS_OK) /* don't recieve a byte */
ABUS_Rx_Byte(); /* if we have a msg */
}

}



/****************************************************************************
* NAME: ABUS_Rx_Byte
* DESCRIPTION: Handles the Access.bus reception.
* PARAMETERS: none
* RETURN: none
* ALGORITHM: none
* NOTES: none
*****************************************************************************/
void ABUS_Rx_Byte(void)
{
static unsigned char rx_count = 0; /* num of bytes received */
static unsigned char rx_msg_len = 0; /* msg length */
static unsigned char checksum = 0; /* message checksum (XOR) */
static unsigned char data = 0; /* data read from SSPBUF */


if (SSPSTATbits.P)
{
I2CStatus = I2CIDLE;
Wait_Time = 1;
I2C_Timeout = 0;
}
else
{
/* were we addressed in read mode */
if (SSPSTATbits.R_W)
{
if (Rx_Msg.hdr.stat == CHECKSUM_OK)
{
Rx_Msg.hdr.stat = PROCESS_OK;
/* Load acknowledgement value */
SSPBUF = MSGACK;
}
else
{
SSPBUF = MSGNACK;
}
/* Setup to allow data to be read from us */
SSPCON1bits.CKP=1;
I2CStatus = I2CSLAVETxING;
I2C_Timeout = 25;
}
/* See if we were addressed */
else if (SSPSTATbits.BF)
{
/* continue recieving bytes */
/* process the first byte */
if (!SSPSTATbits.D_A)
{
/* Read the data */
data = SSPBUF;
Turn_On_Abus_LED();
I2CStatus = I2CRxING;
Rx_Msg.hdr.stat = INVALID_MSG;
Rx_Msg.hdr.dest = data;
rx_msg_len = 2;
rx_count = 1;
checksum = data;
}
else
{
/* Read the data */
data = SSPBUF;
++rx_count; /* keep track of how many bytes */
if (rx_count == 3) /* test for length of msg byte */
rx_msg_len = data & 0x7F; /* set length counter, ignore MSB*/
else
--rx_msg_len;
if (rx_msg_len == -1) /* check for end of msg */
{
I2CStatus = I2CWAITING;
if ((checksum ^ data) == 0)
Rx_Msg.hdr.stat = CHECKSUM_OK;
else
Rx_Msg.hdr.stat = INVALID_MSG;
}
checksum ^= data;
if (rx_count < sizeof(Rx_Msg.buf))
Rx_Msg.buf[rx_count] = data;
}
I2C_Timeout = 25;
}
}
}


Regards
Mark



:= :=
:=I am currently using two PIC 16c771 devices for a single master/slave i2c configuration.
:=
:=Right now, I'm mainly concerned with writing two known good bytes of data to my slave and am using slave i2c hardware mode, not via software.
:=
:=I have cut & pasted my code samples below.
:=
:=I2C MASTER:
:=
:=#INT_RTCC
:= clk_isr()
:= {
:= rb5=0;
:= set_timer0(98); // interrupt 995 Hz
:= set_adc_channel(0); // select pin 1 as analog to digital channel
:= delay_us(5); // delay required capacitor charge time
:= value = read_adc(); // Take a sample of analog input
:=
:=
:= switch (slave_driver){
:= case 0:
:= I2C_START(); // generate i2c start condition
:= if(!I2C_WRITE(0xA0)) // write to slave 0xA0 and expect a 0 for acknowledge
:= {
:= if(!I2C_WRITE(DATA1a)) // write first 8 bits
:= {
:= rb5=1;
:= I2C_WRITE(DATA2a); // write second 8 bits
:= slave_driver = 1; // move to next case
:= }
:= }
:= i2c_stop(); // generate i2c stop condition
:= break; // end case 0
:=
:= case 1:
:= I2C_START(); // generate i2c start condition
:= if(!I2C_WRITE(0xB0)) // write to slave 0xB0 and expect a 0 for acknowledge
:= {
:= if(!I2C_WRITE(DATA1b)) // write first 8 bits
:= {
:= I2C_WRITE(DATA2b); // write second 8 bits
:= slave_driver = 0; // move to next case
:= }
:= }
:= i2c_stop(); // generate i2c stop condition
:= break; // end case 1
:= }
:=
:= i2c_reset(); // reset the i2c when done
:=
:= }
:=
:=I2C SLAVE:
:=
:=#INT_SSP
:=void ssp_isr()
:= { // begin void
:=
:= switch(datacatcher)
:= {
:= case ADDRESS: // begin case address
:= {
:= addr=SSPBUF; // read i2c buffer to a dummy variable, this clears the buffer
:= // and prepares for next data byte
:= datacatcher = byte1; // transition for byte1 read
:= if (addr != 0xA0)
:= i2c_reset(); // reset SSP to continue to search for addres 0xA0
:= }
:= break;
:=
:= case byte1:
:= DATA1 = i2c_read();
:= datacatcher = byte2;
:= break;
:=
:= case byte2:
:= DATA2 = i2c_read();
:= break;
:=
:= i2c_reset();
:= } // end switch
:=
:= } // end void
:=
:=So far, my master is trying to address my slave, but unfortunately, there is no data transfer and I receive a !ACK and nothing from that point on.
:=
:=Any help would be greatly appreciated.
:=
:=Thanks!
:=Jim</font>
___________________________
This message was ported from CCS's old forum
Original Post ID: 6689
JamesT
Guest







Re: i2c communication issues
PostPosted: Thu Aug 29, 2002 10:56 am     Reply with quote

Mark,

Thank you for the information, and I will try to implement snippettes of your work into my code. Actually, my code was pretty much just the ISRs. So far, I've pretty much narrowed it down to a slave side issue. I've cut/pasted my entire slave code. I agree with you, nothing will occur unless the hardware is addressed.

Here is my slave code:

#include <16c771.h>

#fuses BROWNOUT,HS,NOWDT,MCLR
#use delay(clock = 12000000)
#use I2C(slave, sda=pin_b4, scl=pin_b2,address=0xA0,noforce_sw,restart_wdt)

// memory map: portA
#byte PORTA = 0x05
#bit ra0 = 0x05.0
#bit ra1 = 0x05.1
#bit ra2 = 0x05.2
#bit ra3 = 0x05.3
#bit ra4 = 0x05.4
#bit ra5 = 0x05.5
#bit ra6 = 0x05.6
#bit ra7 = 0x05.7

// memory map: portB
#byte PORTA = 0x06
#bit rb0 = 0x06.0
#bit rb1 = 0x06.1
#bit rb2 = 0x06.2
#bit rb3 = 0x06.3
#bit rb4 = 0x06.4
#bit rb5 = 0x06.5
#bit rb6 = 0x06.6
#bit rb7 = 0x06.7

// A/D setup
//#byte AD

//--------------------------------------------------------------
// memory map: i2c operation (SSP)
//--------------------------------------------------------------
// i2c control register1
#byte SSPCON = 0x14
#bit SSPM0 = 0x14.0
#bit SSPM1 = 0x14.1
#bit SSPM2 = 0x14.2
#bit SSPM3 = 0x14.3
#bit CKP = 0x14.4 // clock polarity
#bit SSPEN = 0x14.5 // i2c enable
#bit SSPOV = 0x14.6 // i2c overflow bit
#bit WCOL = 0x14.7 // wr collision detect bit

// i2c control register2
#byte SSPCON2 = 0x91
#bit SEN = 0x91.0
#bit RSEN = 0x91.1
#bit PEN = 0x91.2
#bit RCEN = 0x91.3
#bit ACKEN = 0x91.4 // ACK enable bit
#bit ACKDT = 0x91.5 // ACK data bit
#bit ACKSTAT = 0x91.6 // ACK status bit
#bit GCEN = 0x91.7

// i2c status register
#byte SSPSTAT = 0x94
#bit BF = 0x94.0 // buffer full flag
#bit RW = 0x94.2 // read_write bit
#bit CKE = 0x94.6 //
#bit SMP = 0x94.7 // sample phase bit


// i2c address register
#byte SSPADD = 0x93

// SSP receive buffer/transmit register
#byte SSPBUF = 0x13

// i2c shift register
// #byte SSPSR = 0x?? // not readily accessible

// control bits
#bit SSPIF = 0x0C.3 // clear the SSP interrupt flag
#bit SSPIE = 0x8C.3 // enable the SSP interrupt
#bit PEIE = 0x8b.6 // enable ALL periph intrpt

//ANALOG TO DIGITAL SETUP
#byte ADref = 0x9F // reference setup
#byte ADsel = 0x1F // a/d setup
#byte ANsel = 0x9D // PORTA ANALOG SELECT DI/O or ANALOG IN
#byte ADout = 0x9E // UPPER PORTION OF A/D 12 BIT OUTPUT
#bit ADon = 0x1F.2 // INITIATE A/D BIT
//--------------------------------------------------------------
//--------------------------------------------------------------

// global variables
int addr; //store incoming address
long DATA1; //i2c data
long DATA2; //i2c data
long value; //temp variable
long adc_value; //temp variable
long Incoming1; //i2c temp
long Incoming2; //i2c temp
int i2cfail; //i2c hang counter

int datacatcher = 0; //i2c data flow variable
void i2c_reset(void); //function prototype for i2c reset

// i2c states
#define address 0
#define byte1 1
#define byte2 2

// initializes i2c bus
void init_i2c()
{
SSPCON = 0x36; //enable i2c slave mode & 7bit addr
BF = 0; //RESET buffer
SSPOV = 0; //RESET OVERFLOW BIT
SSPADD = 0xA0; //set i2c address
SSPIE = 1; //enable ssp interrupts
SSPIF = 0; //reset interrupt flag
PEIE = 1; //enable all peripheral interrupts
SSPEN = 1; //enable i2c
//SMP = 0; //slew rate control 400khz
}

// i2c reset routine, courtesy: Justin S.
void i2c_reset()
{
SSPEN = 0; //reset i2c enable
SSPEN = 1; //enable i2c
SSPOV = 0; //reset overflow bit
BF = 0; //reset buffer full
i2cfail = 0; //reset i2c hang counter
}

// clock interupt service routine, courtesy: Justin S.
#INT_RTCC
void clock_isr()
{
set_timer0(98); //interrupt 995 Hz
set_adc_channel(0); //select pin 1 as A/D channel
delay_us(5); // delay required cap charge time
adc_value = read_adc(); // Take a sample of analog input
while(ADon) //Wait for req. samples to fill register
{
//wait for a/d complete
}
i2cfail = i2cfail+1; //increment i2c counter
if (i2cfail>2) //is i2c bus hanging?
{ //yes
i2c_reset(); //resets i2c
}
//enter transfer function
}

#INT_SSP
void ssp_isr()
{ // begin ssp_isr

switch(datacatcher)
{
case address: // begin case address
{
addr=SSPBUF; // read i2c buffer to a
// dummy variable
// this clears the buf
// and prepares for next
// data byte
datacatcher = byte1; // switch to byte1 read
if (addr != 0xA0)
i2c_reset(); // reset SSP
}
break;

case byte1:
if (i2c_poll() == TRUE)
{
DATA1 = SSPBUF; // clear SSP and stuff
// stuff variable
//CKP = 1; // clk polarity (omit)
DATA1 = i2c_read(1); // read from variable
datacatcher = byte2; // next state: byte2
}
else datacatcher = address;
break;

case byte2:
if (i2c_poll() == TRUE)
{
DATA2 = SSPBUF; // clear SSP and stuff
// variable
DATA2 = i2c_read(0); // read from variable
}
datacatcher = address; // take it from the top
break;

i2c_reset();
} // end switch


SSPIF = 0; // reset the i2c interrupt
//SSPOV = 0; // reset the over-flow bit
BF = 0; // reset the buffer full bit
i2cfail = 0; // reset the hang counter

} // end ssp_isr

void main(void)
{ // begin main
set_tris_a(0xFF); //set port a as all inputs
set_tris_b(0x14); //set port b pins19 & 11
//as inputs for i2c/all
//others are outputs

set_rtcc(0); //Clock = 12,000,000Hz
setup_counters(rtcc_internal,
rtcc_div_32); //20,000,000/4/32 = 156,250

init_i2c(); //initialize i2c
enable_interrupts(INT_RTCC); //flag timer 0 int
enable_interrupts(INT_SSP); //flag i2c interrupts
enable_interrupts(GLOBAL); //enable all flagged
//interrupts
} // end main


By the way, this will eventually be set for A/D conversions, but for the sake of testibility for right now, they have been omitted. Right now, the problem that I'm having is that it seems as if my slave is holding the SCL line low after my first byte of data is received. For some reason, it seems as if the SSPBUF is waiting to receive the second byte, and after a while the slave then allows the SCL to be generated by the master. From that point on the second byte is received.

In all honesty, I'm not all too sure about my code and whether or not I am using the i2c hardware functions correctly. It was my impression that the i2c_poll, if returned as a "true" would then allow me to read directly from the SSPBUF, which is what I did...or at least I think I did...

Any input? =\

Once again, thank you!

Jim
___________________________
This message was ported from CCS's old forum
Original Post ID: 6726
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

Re: i2c communication issues
PostPosted: Thu Aug 29, 2002 8:23 pm     Reply with quote

<pre>
For starters, don't use CCS's built in functions. You are using a mixture of both in your code. Here are some problems as I see it:

1. The BF bit is read only. To clear it, you must read the SSPBUF.
2. The i2c_read() function. What does this do? The manual says that it will wait for a byte to come in? Does it check for the BF bit and if set then return immediately else sit and wait forever? I really can't tell from the manual. What should this function do, this

retval = SSPBUF
3. Never wait in an isr. If you are only using one channel, then there is no need to keep setting it. You only need to start the conversion and either poll for the completion in your main or use an interrupt.



See below for changes that I have made. Please note that you might have to adjust the delays based on your frequency. The ones given were used on a device @ 20MHz.
Here are some notes from one of my designs. Refer to the datasheet for more information. Also, some of the #defines need values.


/****************************************************************************
* Analog-to-Digital Converter (ADC) NOTES:
*
* The A/D conversion clock (TAD) is generated from the oscillator
* via a programmable divider. The divider choices are 2, 8, or 32.
* The minimum allowable TAD period is 1.6uS for the PIC16C76, so
* the divider must be chosen carefully. Given a 20MHz oscillator,
* choosing a divider of 32 would result in a TAD period of 1.6u,
* and a CONVERSION TIME of 16uS.
*
* Three time delays must be observed:
*
* SAMPLING TIME: The time required for the sample & hold (S&H)
* circuit to acquire the new voltage level after the analog input
* channel is changed. The minimum required sampling time, given
* a source impedance of 10K ohms and an ambient temp of 50 deg C,
* (worst case conditions) is 12uS. At the same temp with a zero
* ohm source impedance, the minimum sampling time works out to be
* 8.8uS. See formula in Microchip Microcontroller Data Book.
*
* CONVERSION TIME: The time required for the ADC to calculate the
* digital value corresponding to the analog input after the
* conversion is started. Sampling time = 10 x TAD period.
*
* POST-CONVERSION TIME: After a conversion has been completed, 2
* TAD cycles must elapse before sampling can begin again. During
* this time the S&H capacitor is not connected to the selected
* ADC input channel.
*
*
* The delays associated with this design are:
* SAMPLING TIME: 12uS
* CONVERSION TIME: 16uS
* POST-CONVERSION: 3.2uS
*
* (1 instruction cycle time = 200ns)
*
*****************************************************************************/


If you need any more help, let me know. I do alot of work with i2c. Some engineer thought it was a good means of communicating between devices on a small network!

Here is a link to one of our systems. The big cabinet in the picture can have as many as 18 devices on the i2c bus in a multi-master mode.
<a href="http://www.lithonia.com/controls/SynergyOnlineBrochure/SystemArchitecture.pdf" TARGET="_blank">http://www.lithonia.com/controls/SynergyOnlineBrochure/SystemArchitecture.pdf</a>

Regards
Mark

:=Mark,
:=
:=Thank you for the information, and I will try to implement snippettes of your work into my code. Actually, my code was pretty much just the ISRs. So far, I've pretty much narrowed it down to a slave side issue. I've cut/pasted my entire slave code. I agree with you, nothing will occur unless the hardware is addressed.

:=
:=Here is my slave code:
:=
:=#include <16c771.h>
:=
:=#fuses BROWNOUT,HS,NOWDT,MCLR
:=#use delay(clock = 12000000)

Don't need this - we won't use CCS.
:=//#use I2C(slave, sda=pin_b4, scl=pin_b2,address=0xA0,noforce_sw,restart_wdt)
:=
:=// memory map: portA
:=#byte PORTA = 0x05
:= #bit ra0 = 0x05.0
:= #bit ra1 = 0x05.1
:= #bit ra2 = 0x05.2
:= #bit ra3 = 0x05.3
:= #bit ra4 = 0x05.4
:= #bit ra5 = 0x05.5
:= #bit ra6 = 0x05.6
:= #bit ra7 = 0x05.7
:=
:=// memory map: portB
:=#byte PORTA = 0x06
:= #bit rb0 = 0x06.0
:= #bit rb1 = 0x06.1
:= #bit rb2 = 0x06.2
:= #bit rb3 = 0x06.3
:= #bit rb4 = 0x06.4
:= #bit rb5 = 0x06.5
:= #bit rb6 = 0x06.6
:= #bit rb7 = 0x06.7
:=
:=// A/D setup
:=//#byte AD
:=
:=//--------------------------------------------------------------
:=// memory map: i2c operation (SSP)
:=//--------------------------------------------------------------
:=// i2c control register1

:=#byte SSPCON = 0x14
:= #bit SSPM0 = 0x14.0
:= #bit SSPM1 = 0x14.1
:= #bit SSPM2 = 0x14.2
:= #bit SSPM3 = 0x14.3
:= #bit CKP = 0x14.4 // clock polarity
:= #bit SSPEN = 0x14.5 // i2c enable
:= #bit SSPOV = 0x14.6 // i2c overflow bit
:= #bit WCOL = 0x14.7 // wr collision detect bit
:=
:=// i2c control register2
:=#byte SSPCON2 = 0x91
:= #bit SEN = 0x91.0
:= #bit RSEN = 0x91.1
:= #bit PEN = 0x91.2
:= #bit RCEN = 0x91.3
:= #bit ACKEN = 0x91.4 // ACK enable bit
:= #bit ACKDT = 0x91.5 // ACK data bit
:= #bit ACKSTAT = 0x91.6 // ACK status bit
:= #bit GCEN = 0x91.7
:=
:=// i2c status register
:=#byte SSPSTAT = 0x94
:= #bit BF = 0x94.0 // buffer full flag
:= #bit RW = 0x94.2 // read_write bit
:= #bit CKE = 0x94.6 //
:= #bit SMP = 0x94.7 // sample phase bit
#bit S = 0x94.? // start condition
#bit P = 0x94.? // stop condition
#bit DA = 0x94.? // data or address
:=
:=
:=// i2c address register
:=#byte SSPADD = 0x93
:=
:=// SSP receive buffer/transmit register
:=#byte SSPBUF = 0x13
:=
:=// i2c shift register
:=// #byte SSPSR = 0x?? // not readily accessible
:=
:=// control bits
:=#bit SSPIF = 0x0C.3 // clear the SSP interrupt flag
:=#bit SSPIE = 0x8C.3 // enable the SSP interrupt
:=#bit PEIE = 0x8b.6 // enable ALL periph intrpt
:=
#byte ADRES = 0x??
#byte ADCON0 = 0x??
#byte ADCON1 = 0x??
:=//ANALOG TO DIGITAL SETUP
:=#byte ADref = 0x9F // reference setup
:=#byte ADsel = 0x1F // a/d setup
:=#byte ANsel = 0x9D // PORTA ANALOG SELECT DI/O or ANALOG IN
:=#byte ADout = 0x9E // UPPER PORTION OF A/D 12 BIT OUTPUT
:=#bit ADon = 0x1F.2 // INITIATE A/D BIT
:=//--------------------------------------------------------------
:=//--------------------------------------------------------------
:=
:=// global variables
:=int addr; //store incoming address
:=long DATA1; //i2c data
:=long DATA2; //i2c data
:=long value; //temp variable
:=long adc_value; //temp variable
:=long Incoming1; //i2c temp
:=long Incoming2; //i2c temp
:=int i2cfail; //i2c hang counter
:=
:=int datacatcher = 0; //i2c data flow variable
:=void i2c_reset(void); //function prototype for i2c reset
:=
:=// i2c states
:=#define address 0
:=#define byte1 1
:=#define byte2 2
:=

#define ALL_ANALOG 0x00
#define ADC_CLOCK_DIV_32 0x81




:=// initializes i2c bus
:=void init_i2c()
:={
int dummy;
:= SSPCON = 0x36; //enable i2c slave mode & 7bit addr
// BF is Read only!!!
// BF = 0; //RESET buffer
dummy = SSPBUF; //RESET buffer

:= SSPOV = 0; //RESET OVERFLOW BIT
:= SSPADD = 0xA0; //set i2c address
//I would clear the flag before I enable the int
SSPIF = 0; //reset interrupt flag
SSPIE = 1; //enable ssp interrupts
:= PEIE = 1; //enable all peripheral interrupts
:= SSPEN = 1; //enable i2c
:= //SMP = 0; //slew rate control 400khz
:=}
:=
:=// i2c reset routine, courtesy: Justin S.
:=void i2c_reset()
:={
int dummy;
:= SSPEN = 0; //reset i2c enable
:= SSPEN = 1; //enable i2c
:= SSPOV = 0; //reset overflow bit
Read only!!!
// BF = 0; //reset buffer full
dummy = SSPBUF; //RESET buffer
:= i2cfail = 0; //reset i2c hang counter
:=}
:=
:=// clock interupt service routine, courtesy: Justin S.
:=#INT_RTCC
:=void clock_isr()
:= {
:= set_timer0(98); //interrupt 995 Hz
// moved A/D to main
// set_adc_channel(0); //select pin 1 as A/D channel
// delay_us(5); // delay required cap charge time
// adc_value = read_adc(); // Take a sample of analog input
// while(ADon) //Wait for req. samples to fill register
// {
// //wait for a/d complete
// }
// did not implement anything here for the reset.
// you can add it back if you want but you should only hang up if
// the device is accessed in read mode and you don't set the CKE back high
// i2cfail = i2cfail+1; //increment i2c counter
// if (i2cfail>2) //is i2c bus hanging?
// { //yes
/ i2c_reset(); //resets i2c
// }
:= //enter transfer function
:= }
:=
:=#INT_SSP
:=void ssp_isr()
:= { // begin ssp_isr
char dummy;

// Make sure the buffer has not overflowed
if (SSPOV)
{
// Clear the register
dummy = SSPBUF;
SSPOV=0;
return();
}

//Look for a stop
if (P)
{
return();
}
else
{
// were we addressed in read mode
if (RW)
{
//Note that the code below is how you would send back a value
//You could check the data written to you below and set a flag
//or something to know what to send back
return();
// Load our response
SSPBUF = adc_value;
// Stop stretching the clock
CKE=1;
}
// Do we have any data
else if (BF)
{
// See if we were addressed
if (!D_A)
{
// Read the address
// Note that it is really not necessary to check the address
// Since you would not receive any data if the address did not
// Match your SSPADDR!
data = SSPBUF;
}
else
{
// Read the data
data = SSPBUF;
}
}
}
//Note that I did not clear the SSPIF. The compiler does this for you.
//Also you cannot clear the BF bit. It is done when you read the SSPBUF
//I am not sure what your intention here is, but I think you are planning to
//Read an ADC value from this slave device.

:= } // end ssp_isr
:=
:=void main(void)
:= { // begin main

char ADC_Chan_Num = 0;


:= set_tris_a(0xFF); //set port a as all inputs
// Note for Channel 0 you could just use
//ADCON0 = ADC_CLOCK_DIV_32;
ADCON0 = (ADC_CLOCK_DIV_32 | (ADC_Chan_Num << 3));

ADCON1 = ALL_ANALOG;




:= set_tris_b(0x14); //set port b pins19 & 11
:= //as inputs for i2c/all
:= //others are outputs
:=
:= set_rtcc(0); //Clock = 12,000,000Hz
:= setup_counters(rtcc_internal,
:= rtcc_div_32); //20,000,000/4/32 = 156,250
:=
:= init_i2c(); //initialize i2c
:= enable_interrupts(INT_RTCC); //flag timer 0 int
//Not needed - you did this in init_i2c
// enable_interrupts(INT_SSP); //flag i2c interrupts
:= enable_interrupts(GLOBAL); //enable all flagged
:= //interrupts

delay_us(12); // delay required cap charge time
// Start the ADC conversion
ADon = 1;


// You need and endless loop here
while(1)
{

if (!(ADon))
{
adc_value=ADRES;
//Post conversion time
delay_us(3);
//Start the next conversion
ADon = 1;
}

}
:= } // end main
:=
:=
:=By the way, this will eventually be set for A/D conversions, but for the sake of testibility for right now, they have been omitted. Right now, the problem that I'm having is that it seems as if my slave is holding the SCL line low after my first byte of data is received. For some reason, it seems as if the SSPBUF is waiting to receive the second byte, and after a while the slave then allows the SCL to be generated by the master. From that point on the second byte is received.

:=
:=In all honesty, I'm not all too sure about my code and whether or not I am using the i2c hardware functions correctly. It was my impression that the i2c_poll, if returned as a "true" would then allow me to read directly from the SSPBUF, which is what I did...or at least I think I did...

:=
:=Any input? =\
:=
:=Once again, thank you!
:=
:=Jim
:=
:=
___________________________
This message was ported from CCS's old forum
Original Post ID: 6732
JamesT
Guest







Re: i2c communication issues
PostPosted: Fri Sep 06, 2002 10:24 am     Reply with quote

:=<pre>
:=For starters, don't use CCS's built in functions. You are using a mixture of both in your code. Here are some problems as I see it:
:=
:=1. The BF bit is read only. To clear it, you must read the SSPBUF.
:=2. The i2c_read() function. What does this do? The manual says that it will wait for a byte to come in? Does it check for the BF bit and if set then return immediately else sit and wait forever? I really can't tell from the manual. What should this function do, this
:=
:= retval = SSPBUF
:=3. Never wait in an isr. If you are only using one channel, then there is no need to keep setting it. You only need to start the conversion and either poll for the completion in your main or use an interrupt.
:=
:=
:=
:=See below for changes that I have made. Please note that you might have to adjust the delays based on your frequency. The ones given were used on a device @ 20MHz.
:=Here are some notes from one of my designs. Refer to the datasheet for more information. Also, some of the #defines need values.
:=
:=
:=/****************************************************************************
:=* Analog-to-Digital Converter (ADC) NOTES:
:=*
:=* The A/D conversion clock (TAD) is generated from the oscillator
:=* via a programmable divider. The divider choices are 2, 8, or 32.
:=* The minimum allowable TAD period is 1.6uS for the PIC16C76, so
:=* the divider must be chosen carefully. Given a 20MHz oscillator,
:=* choosing a divider of 32 would result in a TAD period of 1.6u,
:=* and a CONVERSION TIME of 16uS.
:=*
:=* Three time delays must be observed:
:=*
:=* SAMPLING TIME: The time required for the sample & hold (S&H)
:=* circuit to acquire the new voltage level after the analog input
:=* channel is changed. The minimum required sampling time, given
:=* a source impedance of 10K ohms and an ambient temp of 50 deg C,
:=* (worst case conditions) is 12uS. At the same temp with a zero
:=* ohm source impedance, the minimum sampling time works out to be
:=* 8.8uS. See formula in Microchip Microcontroller Data Book.
:=*
:=* CONVERSION TIME: The time required for the ADC to calculate the
:=* digital value corresponding to the analog input after the
:=* conversion is started. Sampling time = 10 x TAD period.
:=*
:=* POST-CONVERSION TIME: After a conversion has been completed, 2
:=* TAD cycles must elapse before sampling can begin again. During
:=* this time the S&H capacitor is not connected to the selected
:=* ADC input channel.
:=*
:=*
:=* The delays associated with this design are:
:=* SAMPLING TIME: 12uS
:=* CONVERSION TIME: 16uS
:=* POST-CONVERSION: 3.2uS
:=*
:=* (1 instruction cycle time = 200ns)
:=*
:=*****************************************************************************/
:=
:=
:=If you need any more help, let me know. I do alot of work with i2c. Some engineer thought it was a good means of communicating between devices on a small network!
:=
:=Here is a link to one of our systems. The big cabinet in the picture can have as many as 18 devices on the i2c bus in a multi-master mode.
:= <a href="http://www.lithonia.com/controls/SynergyOnlineBrochure/SystemArchitecture.pdf" TARGET="_blank"> <a href="http://www.lithonia.com/controls/SynergyOnlineBrochure/SystemArchitecture.pdf" TARGET="_blank">http://www.lithonia.com/controls/SynergyOnlineBrochure/SystemArchitecture.pdf</a></a>
:=
:=Regards
:=Mark
:=
:=:=Mark,
:=:=
:=:=Thank you for the information, and I will try to implement snippettes of your work into my code. Actually, my code was pretty much just the ISRs. So far, I've pretty much narrowed it down to a slave side issue. I've cut/pasted my entire slave code. I agree with you, nothing will occur unless the hardware is addressed.
:=
:=:=
:=:=Here is my slave code:
:=:=
:=:=#include <16c771.h>
:=:=
:=:=#fuses BROWNOUT,HS,NOWDT,MCLR
:=:=#use delay(clock = 12000000)
:=
:=Don't need this - we won't use CCS.
:=:=//#use I2C(slave, sda=pin_b4, scl=pin_b2,address=0xA0,noforce_sw,restart_wdt)
:=:=
:=:=// memory map: portA
:=:=#byte PORTA = 0x05
:=:= #bit ra0 = 0x05.0
:=:= #bit ra1 = 0x05.1
:=:= #bit ra2 = 0x05.2
:=:= #bit ra3 = 0x05.3
:=:= #bit ra4 = 0x05.4
:=:= #bit ra5 = 0x05.5
:=:= #bit ra6 = 0x05.6
:=:= #bit ra7 = 0x05.7
:=:=
:=:=// memory map: portB
:=:=#byte PORTA = 0x06
:=:= #bit rb0 = 0x06.0
:=:= #bit rb1 = 0x06.1
:=:= #bit rb2 = 0x06.2
:=:= #bit rb3 = 0x06.3
:=:= #bit rb4 = 0x06.4
:=:= #bit rb5 = 0x06.5
:=:= #bit rb6 = 0x06.6
:=:= #bit rb7 = 0x06.7
:=:=
:=:=// A/D setup
:=:=//#byte AD
:=:=
:=:=//--------------------------------------------------------------
:=:=// memory map: i2c operation (SSP)
:=:=//--------------------------------------------------------------
:=:=// i2c control register1
:=
:=:=#byte SSPCON = 0x14
:=:= #bit SSPM0 = 0x14.0
:=:= #bit SSPM1 = 0x14.1
:=:= #bit SSPM2 = 0x14.2
:=:= #bit SSPM3 = 0x14.3
:=:= #bit CKP = 0x14.4 // clock polarity
:=:= #bit SSPEN = 0x14.5 // i2c enable
:=:= #bit SSPOV = 0x14.6 // i2c overflow bit
:=:= #bit WCOL = 0x14.7 // wr collision detect bit
:=:=
:=:=// i2c control register2
:=:=#byte SSPCON2 = 0x91
:=:= #bit SEN = 0x91.0
:=:= #bit RSEN = 0x91.1
:=:= #bit PEN = 0x91.2
:=:= #bit RCEN = 0x91.3
:=:= #bit ACKEN = 0x91.4 // ACK enable bit
:=:= #bit ACKDT = 0x91.5 // ACK data bit
:=:= #bit ACKSTAT = 0x91.6 // ACK status bit
:=:= #bit GCEN = 0x91.7
:=:=
:=:=// i2c status register
:=:=#byte SSPSTAT = 0x94
:=:= #bit BF = 0x94.0 // buffer full flag
:=:= #bit RW = 0x94.2 // read_write bit
:=:= #bit CKE = 0x94.6 //
:=:= #bit SMP = 0x94.7 // sample phase bit
:= #bit S = 0x94.? // start condition
:= #bit P = 0x94.? // stop condition
:= #bit DA = 0x94.? // data or address
:=:=
:=:=
:=:=// i2c address register
:=:=#byte SSPADD = 0x93
:=:=
:=:=// SSP receive buffer/transmit register
:=:=#byte SSPBUF = 0x13
:=:=
:=:=// i2c shift register
:=:=// #byte SSPSR = 0x?? // not readily accessible
:=:=
:=:=// control bits
:=:=#bit SSPIF = 0x0C.3 // clear the SSP interrupt flag
:=:=#bit SSPIE = 0x8C.3 // enable the SSP interrupt
:=:=#bit PEIE = 0x8b.6 // enable ALL periph intrpt
:=:=
:=#byte ADRES = 0x??
:=#byte ADCON0 = 0x??
:=#byte ADCON1 = 0x??
:=:=//ANALOG TO DIGITAL SETUP
:=:=#byte ADref = 0x9F // reference setup
:=:=#byte ADsel = 0x1F // a/d setup
:=:=#byte ANsel = 0x9D // PORTA ANALOG SELECT DI/O or ANALOG IN
:=:=#byte ADout = 0x9E // UPPER PORTION OF A/D 12 BIT OUTPUT
:=:=#bit ADon = 0x1F.2 // INITIATE A/D BIT
:=:=//--------------------------------------------------------------
:=:=//--------------------------------------------------------------
:=:=
:=:=// global variables
:=:=int addr; //store incoming address
:=:=long DATA1; //i2c data
:=:=long DATA2; //i2c data
:=:=long value; //temp variable
:=:=long adc_value; //temp variable
:=:=long Incoming1; //i2c temp
:=:=long Incoming2; //i2c temp
:=:=int i2cfail; //i2c hang counter
:=:=
:=:=int datacatcher = 0; //i2c data flow variable
:=:=void i2c_reset(void); //function prototype for i2c reset
:=:=
:=:=// i2c states
:=:=#define address 0
:=:=#define byte1 1
:=:=#define byte2 2
:=:=
:=
:=#define ALL_ANALOG 0x00
:=#define ADC_CLOCK_DIV_32 0x81
:=
:=
:=
:=
:=:=// initializes i2c bus
:=:=void init_i2c()
:=:={
:= int dummy;
:=:= SSPCON = 0x36; //enable i2c slave mode & 7bit addr
:=// BF is Read only!!!
:=// BF = 0; //RESET buffer
:= dummy = SSPBUF; //RESET buffer
:=
:=:= SSPOV = 0; //RESET OVERFLOW BIT
:=:= SSPADD = 0xA0; //set i2c address
:=//I would clear the flag before I enable the int
:= SSPIF = 0; //reset interrupt flag
:= SSPIE = 1; //enable ssp interrupts
:=:= PEIE = 1; //enable all peripheral interrupts
:=:= SSPEN = 1; //enable i2c
:=:= //SMP = 0; //slew rate control 400khz
:=:=}
:=:=
:=:=// i2c reset routine, courtesy: Justin S.
:=:=void i2c_reset()
:=:={
:= int dummy;
:=:= SSPEN = 0; //reset i2c enable
:=:= SSPEN = 1; //enable i2c
:=:= SSPOV = 0; //reset overflow bit
:=Read only!!!
:=// BF = 0; //reset buffer full
:= dummy = SSPBUF; //RESET buffer
:=:= i2cfail = 0; //reset i2c hang counter
:=:=}
:=:=
:=:=// clock interupt service routine, courtesy: Justin S.
:=:=#INT_RTCC
:=:=void clock_isr()
:=:= {
:=:= set_timer0(98); //interrupt 995 Hz
:=// moved A/D to main
:=// set_adc_channel(0); //select pin 1 as A/D channel
:=// delay_us(5); // delay required cap charge time
:=// adc_value = read_adc(); // Take a sample of analog input
:=// while(ADon) //Wait for req. samples to fill register
:=// {
:=// //wait for a/d complete
:=// }
:=// did not implement anything here for the reset.
:=// you can add it back if you want but you should only hang up if
:=// the device is accessed in read mode and you don't set the CKE back high
:=// i2cfail = i2cfail+1; //increment i2c counter
:=// if (i2cfail>2) //is i2c bus hanging?
:=// { //yes
:=/ i2c_reset(); //resets i2c
:=// }
:=:= //enter transfer function
:=:= }
:=:=
:=:=#INT_SSP
:=:=void ssp_isr()
:=:= { // begin ssp_isr
:= char dummy;
:=
:= // Make sure the buffer has not overflowed
:= if (SSPOV)
:= {
:= // Clear the register
:= dummy = SSPBUF;
:= SSPOV=0;
:= return();
:= }
:=
:= //Look for a stop
:= if (P)
:= {
:= return();
:= }
:= else
:= {
:= // were we addressed in read mode
:= if (RW)
:= {
:= //Note that the code below is how you would send back a value
:= //You could check the data written to you below and set a flag
:= //or something to know what to send back
:= return();
:= // Load our response
:= SSPBUF = adc_value;
:= // Stop stretching the clock
:= CKE=1;
:= }
:= // Do we have any data
:= else if (BF)
:= {
:= // See if we were addressed
:= if (!D_A)
:= {
:= // Read the address
:= // Note that it is really not necessary to check the address
:= // Since you would not receive any data if the address did not
:= // Match your SSPADDR!
:= data = SSPBUF;
:= }
:= else
:= {
:= // Read the data
:= data = SSPBUF;
:= }
:= }
:= }
:=//Note that I did not clear the SSPIF. The compiler does this for you.
:=//Also you cannot clear the BF bit. It is done when you read the SSPBUF
:=//I am not sure what your intention here is, but I think you are planning to
:=//Read an ADC value from this slave device.
:=
:=:= } // end ssp_isr
:=:=
:=:=void main(void)
:=:= { // begin main
:=
:=char ADC_Chan_Num = 0;
:=
:=
:=:= set_tris_a(0xFF); //set port a as all inputs
:=// Note for Channel 0 you could just use
:=//ADCON0 = ADC_CLOCK_DIV_32;
:=ADCON0 = (ADC_CLOCK_DIV_32 | (ADC_Chan_Num << 3));
:=
:=ADCON1 = ALL_ANALOG;
:=
:=
:=
:=
:=:= set_tris_b(0x14); //set port b pins19 & 11
:=:= //as inputs for i2c/all
:=:= //others are outputs
:=:=
:=:= set_rtcc(0); //Clock = 12,000,000Hz
:=:= setup_counters(rtcc_internal,
:=:= rtcc_div_32); //20,000,000/4/32 = 156,250
:=:=
:=:= init_i2c(); //initialize i2c
:=:= enable_interrupts(INT_RTCC); //flag timer 0 int
:=//Not needed - you did this in init_i2c
:=// enable_interrupts(INT_SSP); //flag i2c interrupts
:=:= enable_interrupts(GLOBAL); //enable all flagged
:=:= //interrupts
:=
:= delay_us(12); // delay required cap charge time
:= // Start the ADC conversion
:= ADon = 1;
:=
:=
:= // You need and endless loop here
:= while(1)
:= {
:=
:= if (!(ADon))
:= {
:= adc_value=ADRES;
:= //Post conversion time
:= delay_us(3);
:= //Start the next conversion
:= ADon = 1;
:= }
:=
:=}
:=:= } // end main
:=:=
:=:=
:=:=By the way, this will eventually be set for A/D conversions, but for the sake of testibility for right now, they have been omitted. Right now, the problem that I'm having is that it seems as if my slave is holding the SCL line low after my first byte of data is received. For some reason, it seems as if the SSPBUF is waiting to receive the second byte, and after a while the slave then allows the SCL to be generated by the master. From that point on the second byte is received.
:=
:=:=
:=:=In all honesty, I'm not all too sure about my code and whether or not I am using the i2c hardware functions correctly. It was my impression that the i2c_poll, if returned as a "true" would then allow me to read directly from the SSPBUF, which is what I did...or at least I think I did...
:=
:=:=
:=:=Any input? =\
:=:=
:=:=Once again, thank you!
:=:=
:=:=Jim
:=:=
:=:=
:=
Mark,

I apologize for the delay as my labor day weekend was longer than I expected. Trust me, it was definately a good thing!

I've decided to dump the CCS built in functions for my slave. For some reason, they work like a charm with my master, but not for my slaves...go figure. You are right, dumping the functions and transitioning towards "bit banging" so to speak was a step in the right direction. Also, I am surprised that the BF bit is "read only." I could've swore for the life of me that it was user accessible. Nonetheless, you are correct and that has helped me as well. Since last we spoke, I have almost completely dumped my i2c slave and started out fresh. Upon looking at your slave, I never thought to look for a stop condition. I have just now been able to look for your response and have already attacked the problem from a new angle. But, clearing the buffer by reading it to a dummy variable works just fine.

I greatly appreciate your help!

Jim
___________________________
This message was ported from CCS's old forum
Original Post ID: 6951
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