|
|
View previous topic :: View next topic |
Author |
Message |
Seaborgium
Joined: 23 Jul 2012 Posts: 14
|
I2C 10-bit Addressing |
Posted: Wed Jul 25, 2012 6:02 pm |
|
|
Hi,
I'm trying to implement 10-bit I2C addressing with a PIC16LF1503 master and PIC16F886 slave, using CCS 4.133. I have a totally functional 7-bit addressing version. I've included the relevant parts here, for the working version:
Master.c:
Code: | #include <Master.h>
#define LED PIN_A5
#define LED2 PIN_A4
#define LED3 PIN_C5
#use i2c(Master, Slow, sda=PIN_C3, scl=PIN_C4, force_sw, stream=module)
void flash(int ledsToFlash) {
if ((ledsToFlash&0b1)==0b1)
output_high(LED);
if ((ledsToFlash&0b10)==0b10)
output_high(LED2);
if ((ledsToFlash&0b100)==0b100)
output_high(LED3);
delay_ms(500);
output_low(LED);
output_low(LED2);
output_low(LED3);
delay_ms(200);
}
//Communicates with a certain slave address by delivering a certain
//data address, then returns the received data.
int16 requestData(int16 slaveAddress, unsigned int dataAddress) {
int16 data;
//Instruct slave that you wish to write
i2c_start(module);
flash(0b100|i2c_write(module, slaveAddress));
//Give data address
flash(0b100|i2c_write(module, dataAddress));
//Restart; instruct slave that you wish to read
i2c_start(module);
flash(0b100|i2c_write(module, slaveAddress|1));
//Receive data and finish
data=i2c_read(module, 1)<<8; //Read high byte and ACK
data+=i2c_read(module, 0); //Read low byte and end transmission
i2c_stop(module);
flash(0b111);
return data;
}
void main() {
setup_comparator(NC_NC_NC_NC); // This device COMP currently not supported by the PICWizard
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
delay_ms(3000);
int i;
i=0;
while (TRUE) {
flash(requestData(0xa0, i));
i++;
delay_ms(1000);
}
} |
Master.h:
Code: | #include <16LF1503.h>
#device adc=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES WDT_SW //No Watch Dog Timer, enabled in Software
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(int=16000000) |
Slave.c:
Code: | #include <Slave.h>
//Reserve a 14-bit word for saving data address
#define ADDRESSLOC 0x500
#ORG 0x500, 0x500 {}
//Define output pins
#define LED PIN_A7
#define LED2 PIN_A6
#define LED0 PIN_C5
//Define states
#define READY 0 //Ready for next command (change address order, or sensor request)
#define CHANGEADDR 1 //Next byte received will be new assigned address
#define SENDLOW 2 //Sent high bit of data; next send low bit
//General call bit
#BYTE SSPCON2=0x91
#BIT GCEN=SSPCON2.7
//10-bit addressing bit
#BYTE SSPCON=0x14
#BIT SSPM0=SSPCON.0
//Change # of sensors per module to correct number in sensorVals
int writeBuffer [2]; //Holds one 10-bit integer to write through i2c
int16 sensorVals [16];
int orderState;
void flash(int ledsToFlash) {
output_high(LED0);
if ((ledsToFlash&0b1)==0b1)
output_high(LED);
if ((ledsToFlash&0b10)==0b10)
output_high(LED2);
delay_ms(400);
output_low(LED);
output_low(LED2);
output_low(LED0);
delay_ms(200);
}
#int_SSP
void i2c_interrupt() {
int state;
unsigned int message;
state = i2c_isr_state();
if (state==0||state==0x80) //Master has issued start command
i2c_read(); //Clear address from read buffer.
if (state>=0x80) //Master is waiting for data
i2c_write(writeBuffer[state - 0x80]); //Write appropriate byte, based on how many have already been written
else if(state>0) { //Master has sent data; read.
message=i2c_read();
if (orderState==READY) {
if (message==0x20) { //Master wants to assign address.
orderState=CHANGEADDR;
}
else { //Master requests sensor data.
int16 toSend;
toSend=sensorVals[message]; //Grab appropriate sensor value.
writeBuffer[0] = toSend>>8;
writeBuffer[1] = toSend&0xFF;
}
}
else if (orderState==CHANGEADDR) { //Master has just sent new address
i2c_slaveaddr(message); //Change address
write_program_eeprom(ADDRESSLOC, message); //Set address in eeprom
orderState=READY;
GCEN=FALSE; //Disable general calls for remainder of runtime
}
}
}
void main() {
setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
enable_interrupts(INT_SSP); //Enable interrupts
enable_interrupts(GLOBAL);
GCEN=TRUE; //Allow general calls
i2c_slaveAddr(0xa0);
orderState=READY; //Ready for orders
while (true) { //Constantly refresh sensor values
sensorVals[0]=0b101;
sensorVals[1]=0b011;
sensorVals[2]=0b110;
sensorVals[3]=0b100;
sensorVals[4]=0b010;
}
} |
Slave.h:
Code: | #include <16F886.h>
#device adc=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOPROTECT //No read protection
#FUSES NOWRT //No write protection
#use delay(int=8000000)
#use i2c(Slave, Slow, sda=PIN_C4, scl=PIN_C3, force_hw, address=0xa0) |
In this code, the master asks the slave for "sensor values" (really just an array for testing purposes). The master has 3 LEDs, which it flashes to indicate certain states. It is supposed to flash LED 3 to indicate a successful write (0b100 is OR'ed with the return value of i2c_write, so if there was an unsuccessful write, it would flash LED 3 and LED 1 simultaneously). It does this three times. Then it flashes all three LEDs at the end of a successful i2c transaction; and then it flashes the value given to it by the slave.
In this version, it flashes: 3-3-3-123-13; 3-3-3-123-23; 3-3-3-123-12; and so on, with the last value matching up perfectly with the array values that the slave is supposed to send.
I made certain changes to implement 10-bit addressing. On the slave, I set SSPM0 to 1 (I also outputted SSPM to the LEDs and verified that it was 0111, I2C slave mode with 10-bit address). On the master, I made the appropriate calculations to transform the 7-bit address into a 10-bit address. This did not work, so I tried manually specifying the addresses bit-by-bit. I've included below the relevant changes.
Master.c
Code: | int16 requestData(int16 slaveAddress, unsigned int dataAddress) {
int16 data;
//int slaveHigh; //High byte of slave address
//int slaveLow; //Low byte of slave address
//Split slave address into low and high byte
//slaveHigh = (slaveAddress>>8)&0xFE; //Shift over first 3 bits; clear R/W bit
//slaveHigh = slaveHigh|0xF0; //"11110XXX" signifies 10-bit address; add this.
//slaveLow = slaveAddress>>1; //Get lower 8 bits, without R/W bit
//Instruct slave that you wish to write
i2c_start(module);
flash(0b100|i2c_write(module, 0b11110000)); //High byte
flash(0b100|i2c_write(module, 0b00001111)); //Low byte |
Slave.c
Code: |
void i2c_interrupt() {
int state;
unsigned int message;
state = i2c_isr_state();
flash(0b11);
void main() {
SSPM0=TRUE; //10-bit addressing
i2c_slaveAddr(0b00011110); |
The slave responds to general calls, but will not recognize its 10-bit address at all - no interrupt is even generated. I've looked through the CCS documentation and it seems that there is no support for 10-bit addresses (e.g. i2c_slaveAddr specifies an 8-bit address as input). However, since I'm using force_hw, I assume that the compiler uses the PIC16F886's internal hardware to check for address matches, and the PIC16F886 supports 10-bit addressing. How would I use 10-bit addresses? I've looked at the PIC16F866 data sheet and it's a little confusing - would I have to manually load the second byte of the address as described in section 13.4.1.1? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Jul 26, 2012 1:47 am |
|
|
The I2c address in the slave, needs to be set to look for the first address byte. So: 0b11110000
and bit 0 of CCPCON has to be set (I see you do this).
Then in the interrupt code, you need to test the UA bit (before _anything_ else), and if this is set, change the I2C address for the second byte, and immediately return.
The rest of the slave code remains the same, once this test has been passed, _except_ you must reset the address byte ready for the first value. So usually on the 'address match' code, you add a set address, back to look for the first byte.
Basically with 10bit, there are two address bytes, one after the other, with after the first, an interrupt, with the UA bit set to say 'second address needed'. The code behaves just as normal, except when you arrive in the ISR, if this bit is set, you just load the new address and get out again. Then when the second match is made, read the direction flag, and reset the address ready for the first match again.
Best Wishes |
|
|
Seaborgium
Joined: 23 Jul 2012 Posts: 14
|
|
Posted: Thu Jul 26, 2012 2:06 pm |
|
|
Ah, I see. I managed to get it working. Thank you! |
|
|
|
|
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
|