|
|
View previous topic :: View next topic |
Author |
Message |
Distorzion
Joined: 19 Apr 2016 Posts: 1 Location: Mexico
|
[I2C] Help with multibyte read |
Posted: Tue Apr 19, 2016 4:43 pm |
|
|
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: 19520
|
|
Posted: Wed Apr 20, 2016 2:59 am |
|
|
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. |
|
|
|
|
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
|