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] Help with multibyte read

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



Joined: 19 Apr 2016
Posts: 1
Location: Mexico

View user's profile Send private message

[I2C] Help with multibyte read
PostPosted: Tue Apr 19, 2016 4:43 pm     Reply with quote

Hello,

I'm trying to communicate 2 18F4550 by I2C multibyte read/write. It's kinda easy, the master PIC has to communicate a byte stream of "led status" to the slave (16 bytes). The slave PIC is basically, the matrix led multiplexer. It has to "render" the received bytes into led matrix.

The led matrix is made of 2 8x8 led matrix stacked "vertically". Thats why I send 16 bytes trough I2C. Every byte represents a row of the led matrix.

I based my program in the EX_SLAVE.c included with the CCS compiler.

Everything works but I cannot "read" (or save to the "status matrix" array) the first byte of info. In other words, matrixStatus[0] keeps its initial value forever.

TBH, I don't understand what the EX_SLAVE code is doing at the SSP interrupt nor understand what values are "address" and "incoming" taking, so I cannot debug by myself. Honestly, I made my function by guessing and trial/error.

I know master is sending the first byte because Proteus Debugger can read the info. In fact, every other byte saves correctly but byte 2 which has to save at the position 0 of the matrixStatus (byte 2, because byte 1 is address of the slave device).

How can I save the byte 2 (or first byte of data) to the position 0 of the matrixStatus array?

Also, if you can explain, what values are address and incoming taking? Why this code works where a "for" fails and "hangs" masters? How i2c_read() works and the parameters it can take?

This is an example of what Master sends every 50ms (S = start, P = stop, A = ack):

Code:
 S C0 A 00 A 3C A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A P


And here are the bytes of the Data Memory of the PIC where matrixStatus is located:

Code:
95 3C 00 00 00 00 00 00 00 00 00 00 00 00 00 00


NOTE: 95 should be 00. 95 is the initial value I give to matrixStatus[0].

Even when 3C is at the first byte of data, is not modified:

Code:
 S C0 A 3C A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A 00 A P


Code:
95 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00


SLAVE CODE:

Code:

#include <18f4550.h>

// <-- CONFIG BITS -->

#fuses INTHS        //Internal Oscillator, HS used by USB
#fuses PLL1         //No PLL PreScaler
#fuses CPUDIV1      //No System Clock Postscaler
#fuses USBDIV       //USB clock source comes from PLL divide by 2
#fuses VREGEN       //USB voltage regulator enabled
#fuses NOPROTECT    //Code not protected from reading
#fuses NOLVP        //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#fuses NOMCLR       //Master Clear pin used for I/O
#fuses NOWDT        //No Watch Dog Timer
#fuses NODEBUG      //No Debug mode for ICD

// <-- CONFIG MODULES -->
#use delay(clock=8000000)
#use i2c(SLAVE, sda=PIN_B0, scl=PIN_B1, address=0xC0, force_hw)

// <-- ACCESIBLE BYTES -->
#BYTE OSCCON = 0xFD3
#BYTE LATA = 0xF89
#BYTE LATB = 0xF8A
//#BYTE PORTA = 0xF80;
//#BYTE PORTC = 0xF82;

// <-- CONSTANT FUNCTIONS-->
//#define GND_P OUTPUT_B
#define VCC_P OUTPUT_D
//#define VCC2L_P OUTPUT_A
#define VCC2H_P OUTPUT_C

// <-- FUNCTION PROTOTYPES -->
void configPorts();                 //Configure I/O & ADC
void configOsc();                   //Configure Oscilators
void configTimers();                //Configure Timers
void configInterrups();             //Configure Interruptions
void setGlobalVars();               //Init Global Vars
void getMuxValues();                //Sets VCC values to multiplex
void getVccMuxH();                  //Sets VCC High part
void getVccMuxL();                  //Sets VCC Low part
void muxMatrix();                   //Multiplex the matrix
void VCC2L_P(unsigned char data);   //WRITE VVC2 HP
void GND_P(unsigned char data);     //WRITE GND PORT

// <-- GLOBAL VARIABLES -->
unsigned char gndMux;
unsigned char vccMuxH[8];
unsigned char vccMuxL[8];
int muxCounter;
int refreshDelay;
int1 isModFlag;
int recievedDataCounter;

unsigned int8 address;

unsigned char matrixStatus[16] = {
        0b10010101,     //Row 1
        0b10010001,     //Row 2
        0b11110101,     //Row 3
        0b10010100,     //Row 4
        0b10010101,     //Row 5
        0b00000000,     //Row 6
        0b00011100,     //Row 7
        0b00100010,     //Row 8
        0b00100010,     //Row 9
        0b00011100,     //Row 10
        0b00001000,     //Row 11
        0b01111111,     //Row 12
        0b00001000,     //Row 13
        0b00010100,     //Row 14
        0b00100010,     //Row 15
        0b01000001,     //Row 16
    };

// <-- INTERRUPTION SERVICE ROUTINES -->

#INT_SSP
void ssp_isr(){
    unsigned char incoming, state;
    //unsigned char dataw=0x60;
 
    state = i2c_isr_state();

    if(state <= 0x80){                                  //Master is sending data
       
        if(state == 0x80) 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
            //matrixStatus[address++] = incoming;
            address++;
            matrixStatus[address] = incoming;
            recievedDataCounter++;
            if(recievedDataCounter == 16){
                recievedDataCounter = 0;
                isModFlag = 1;
            }
        }
    }

    if(state >= 0x80){                      //Master is requesting data
        i2c_write(matrixStatus[address++]);
   }
    output_toggle(PIN_E0);
}

// <-- PROGRAM CODE -->

void configPorts(){
    setup_adc_ports(NO_ANALOGS);
    //set_tris_a(0x43); //*IOO OOII VCC2 Low part A2 - A5
    set_tris_a(0x40); //*IOO OOOO VCC2 Low part A2 - A5
    set_tris_b(0x03); // GND B0 - B7
    set_tris_d(0x00); // VCC D0 - D7
    set_tris_c(0x02); //OOOO OOIO VCC2 High part C4 - C7
    set_tris_e(0xFE); //**** *IIO E0 for status Led
   
    GND_P(0x00);
    VCC_P(0x00);
    VCC2H_P(0x00);
    VCC2L_P(0x00);
}

void setGlobalVars(){   
    muxCounter = 0;
    gndMux = 0b01111111;
    refreshDelay = 500;
    isModFlag = 1;
    recievedDataCounter = 0;
}

void main(){
    //configOsc();
    OSCCON=0xFF;
   
    configPorts();
   
    //configTimers();
   
    setGlobalVars();
   
    //configInterrups();
    enable_interrupts(GLOBAL);   
    enable_interrupts(INT_SSP); 
   
    while(TRUE){
        if(isModFlag) getMuxValues();
        muxMatrix();
        //printf("Hola");
    }
}



MASTER CODE:

Code:

#include <18f4550.h>

// <-- CONFIG BITS -->

#fuses INTHS        //Internal Oscillator, HS used by USB
#fuses PLL1         //No PLL PreScaler
#fuses CPUDIV1      //No System Clock Postscaler
#fuses USBDIV       //USB clock source comes from PLL divide by 2
#fuses VREGEN       //USB voltage regulator enabled
#fuses NOPROTECT    //Code not protected from reading
#fuses NOLVP        //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#fuses NOMCLR       //Master Clear pin used for I/O
#fuses NOWDT        //No Watch Dog Timer
#fuses NODEBUG      //No Debug mode for ICD

// <-- CONFIG MODULES -->
#use delay(clock=8000000)
#use i2c(MASTER, SDA=PIN_B0, SCL=PIN_B1, address=0x60)

// <-- ACCESIBLE BYTES -->
#BYTE OSCCON = 0xFD3
#BYTE PORTA = 0xF80
#BYTE PORTB = 0xF81
#BYTE PORTC = 0xF82
#BYTE INTCON = 0xFF2
#BYTE LATB = 0xF8A

// <-- CONSTANT FUNCTIONS-->

// <-- FUNCTION PROTOTYPES -->
void configPorts();         //Configure I/O & ADC
void configOsc();           //Configure Oscilators
void configTimers();        //Configure Timers
void configInterrups();     //Configure Interruptions
void setGlobalVars();       //Init Global Vars
void checkButtons();        //Check for button press
void updateMuxer();         //Update the Muxer matrix status
void sendData();            //Send muxer data
void gameTick();            //GameTick

// <-- UTILITIES PROTOTYPES -->
void write_b(int8 toWrite);

// <-- STRUCTURES -->

// <-- GLOBAL VARIABLES -->
unsigned char muxerAddress = 0xC0;
//unsigned char usbAddress = 0xC2;

int buttonCounter;
int muxerCounter;
int tickCounter;

unsigned char gameStatus[16] = {
    0x3C,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
    0x00,
};

// <-- PROGRAM CODE -->

void configPorts(){
    setup_adc_ports(NO_ANALOGS);    //All ports to digital
   
    set_tris_b(0xF0);               // PORT B IIII 0***
    set_tris_d(0x00);               // PORT D **** OOOO
   
    //LATB = 0x00;
    output_d(0x00);
    output_high(PIN_B3);
}

void setGlobalVars(){
    buttonCounter = 0;
    muxerCounter = 0;
    tickCounter = 0;
}

void updateMuxer(){
    if(muxerCounter > 0){
        muxerCounter--;
    } else {
        muxerCounter = 5;
        sendData();
    }
}

void sendData(){
    i2c_start();
    i2c_write(muxerAddress);
    for(int i = 0; i < sizeof(gameStatus); i++){
        i2c_write(gameStatus[i]);
    }
    i2c_stop();
}

void main(){
    //configOsc();
    OSCCON=0xFF;
   
    configPorts();
   
    //configTimers();
   
    setGlobalVars();
   
    //configInterrups();
    while(TRUE){
        checkButtons();
        gameTick();
        updateMuxer();
        delay_ms(10);
    }
}



Hope I've explained well my problem. As i said, I'm pretty sure my problem is I don't understand well what the protocol is doing and what values are address and incoming taking.

I removed most the irrelevant code for the problem, i may upload the complete file if needed.

Thanks in advance!
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Wed Apr 20, 2016 2:59 am     Reply with quote

In I2C, there are two addresses.

The device address, and
the register address in the device.
The device address also has a 'flag' as the low bit, saying to read or write.

So the sequence to write a set of bytes to a device is:

Start
Send device 'write' address
Send register address in device to write
Write data
Stop

The sequence to read a set of bytes is:

Start
Send device 'write' address
Send register address to read from
Restart
Send device 'read' address (write address+1)
Read bytes (NACK the last byte)
Stop

So you are 90% there.
For your 'write' code:
Code:

void sendData()
{
    i2c_start();
    i2c_write(muxerAddress);
    i2c_write(0); //assuming you want to start at register 0
    for(int i = 0; i < sizeof(gameStatus); i++)
    {
        i2c_write(gameStatus[i]);
    }
    i2c_stop();
}

To 'read' the block back:

Code:

void getData()
{
    i2c_start();
    i2c_write(muxerAddress);
    i2c_write(0); //assuming you want to start at register 0
    i2c_start(); //the restart
    i2c_write(muxerAddress + 1);
    //Now in read mode at register 0
    for(int i = 0; i < sizeof(gameStatus); i++)
    {
        if (i==(sizeof(gameStatus)-1))
            gameStatus[i]=i2c_read(0); //NACK the last byte
        else
            gameStatus[i]=i2c_read();
    }
    i2c_stop();
}


There are ways of slightly short-cutting parts of the transaction (you could assume that all transactions begin at register 0), but in general I2C prefers/requires the transactions to be handled fully. The PIC hardware in particular differs between chips on how abbreviated transactions are done, so it is better to use the standard form.
Particularly sending the NACK on the last read keeps the hardware synced up better.

For error checking, look at PCM_programmers I2C scanner program in the code library. Note how this works, even when testing addresses with no device, by checking the bit returned when the device address is written. Do the same in your master code, and you can tell if the device is acknowledging correctly before trying to write/read data.
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