Jump to content
ELFORUM - Forumul Electronistilor
Sign in to follow this  
Zerro

Problema I2C Master/Slave - multibyte

Recommended Posts

Am un mic proiect cu o comunicatie I2C intre 2 pic-uri 16F877 (master - slave). Problema care o am apare la citirea din slave a ceea ce am trimis in prealabil. Cand masterul trimite catre slave datele se primesc ok, insa... cand vreau sa citesc aceste date in master imi ajung eronate - eronate la modul urmator:

- daca valoarea trimisa in prealabil are bitii 6 si 7 00xxxxxx sau 11xxxxxx atunci valoarea e citita corect de master

- daca valoarea trimisa in realabil are bitii 6 si 7 01xxxxxx sau 10xxxxxx atunci valoarea e citita eronat de master sau probabil trimisa eronat de slave catre master ....

 

Folosesc PCWH Compiler V3.245 iar pentru simulari folosesc Proteus 6.7 SP3 (proiectul a fost realizat si hardware si aceiasi problema o are - deci nu e de la simulator).

 

Codul pentru master este:

 

#include <16F877.h>#define FREQUENCY 20000000#define I2C_SDA  PIN_C4#define I2C_SCL  PIN_C3#define SLAVE_ADDR 0x40	// Even number, address to be sent to slave pic,						// must match. Note read address is slave address + 1.#fuses HS,NOWDT,PROTECT,NOLVP,NOPUT,BROWNOUT#use delay(clock=FREQUENCY)#use fast_io(C)#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)#use i2c(master, sda=I2C_SDA, scl=I2C_SCL, FORCE_HW, SLOW)#include <math.h>byte com,calculeaza;char x[12];short int status=0;void trimite_primeste_I2C(unsigned int8 comanda, unsigned int8 valoare1, unsigned int8 valoare2, unsigned int8 valoare3, unsigned int8 valoare4);void primeste_I2C();#int_rdavoid rda(){	x[0]=getc();	delay_us(10);	if(x[0]=='x')	{		com=1;		disable_interrupts(INT_RDA);		calculeaza=0;	}}void inttobin (int intval){	short int x0,x1,x2,x3,x4,x5,x6,x7;	byte byteval;	byteval=intval;	x0=bit_test(byteval,0);	x1=bit_test(byteval,1);	x2=bit_test(byteval,2);	x3=bit_test(byteval,3);	x4=bit_test(byteval,4);	x5=bit_test(byteval,5);	x6=bit_test(byteval,6);	x7=bit_test(byteval,7);	printf("\n\rValoare= %u --> Binar: %u%u%u%u%u%u%u%u",intval,x7,x6,x5,x4,x3,x2,x1,x0);}void main(){   //set_tris_c(0b11011000);	//initializare variabile	output_float(I2C_SDA);    output_float(I2C_SCL); 	calculeaza=0;	com=0;	printf("\n\rStart...");	enable_interrupts(INT_RDA);	enable_interrupts(GLOBAL);	//Se verifica daca exista modulul de relee de frecventa atasat	i2c_start();	status=i2c_write(SLAVE_ADDR);	i2c_stop();	if(status==1)		printf("\n\rModulul Relee de Frecventa - OFF.");	if(status==0)		printf("\n\rModulul Relee de Frecventa - ON.");	delay_ms(100);	if(status==0)	{		trimite_primeste_i2c(127,128,129,130,131);		//trimite_primeste_I2C(64,65,128,129,255);		//primeste_i2c();	}	while(TRUE)	{		///////////////////////////////////////////////////////////////////////		//modulul de verificare a transmisiei de date prin RS232		///////////////////////////////////////////////////////////////////////		if(com==1)		{			printf("Comunicatie ON.");	      	while(com==1)	      	{			     	gets(x);			     	delay_us(10);		         	if(x[0]=='s' && x[1]=='t' && x[2]=='o' && x[3]=='p')		         	{			         	printf("Comunicatie OFF.");			            enable_interrupts(INT_RDA);		         	}		         	if(x[0]=='s' && x[1]=='t' && x[2]=='a' && x[3]=='r' && x[4]=='t')		         	{			         	trimite_primeste_I2C(254,64,127,128,255);				}			}		}		/////////////////////////////////////////////////////////////////////////////////////////////////////////////		//modulul de calculare a turatiei in functie de timpul unei ture (numarul de dinti ai unei ture)		/////////////////////////////////////////////////////////////////////////////////////////////////////////////		if(calculeaza==1)		{			calculeaza=0;		}	}}void trimite_primeste_I2C(unsigned int8 comanda, unsigned int8 valoare1, unsigned int8 valoare2, unsigned int8 valoare3, unsigned int8 valoare4){	unsigned int8 rxbuf[5];		// Buffer for data received back from the slave pic	int8 i=0;	// Clear the Master receive buffer	for(i=0;i<5;i++)		rxbuf[i] = 0;	inttobin(comanda);	inttobin(valoare1);	inttobin(valoare2);	inttobin(valoare3);	inttobin(valoare4);	// Send some numbers to the slave pic buffer. Slave clears it's buffer	// each time round when address match occurs.	// Small delays are to allow time for slave to get to and from	// interrupt handler.	i2c_start();	i2c_write(SLAVE_ADDR);		// Must match address of slave pic	delay_us(10);	i2c_write(comanda);			//Trimite comanda	delay_us(10);	i2c_write(valoare1);	delay_us(10);	i2c_write(valoare2);	delay_us(10);	i2c_write(valoare3);	delay_us(10);	i2c_write(valoare4);	delay_us(10);	//i2c_stop();	//delay_ms(5);	// Now read back the numbers without releasing i2c bus,	// could do a STOP here and a new START.	i2c_start();				// A restart	i2c_write(SLAVE_ADDR + 1);	// Slave read address	for(i=0; i<4; i++)	{		//output_high(I2C_SDA);		//output_low(I2C_SCL);		rxbuf[i] = i2c_read();	// Note that Slave may be delaying response								// by holding SCL low. i2c_read() monitors								// SCL and takes care of this.	}	rxbuf[4] = i2c_read(0);	// Last one nack (high), resets slave i2c								// logic. Very important, i2c bus will hang								// otherwise.	i2c_stop();					// Release the i2c bus	for(i=0; i<=4; i++)	{		inttobin(rxbuf[i]);	}}void primeste_I2C(){	unsigned int8 rxbuf[5];		// Buffer for data received back from the slave pic	int8 i=0;	// Clear the Master receive buffer	for(i=0;i<5;i++)		rxbuf[i] = 0;		// Now read back the numbers without releasing i2c bus,	// could do a STOP here and a new START.	i2c_start();				// A restart	i2c_write(SLAVE_ADDR + 1);	// Slave read address	for(i=0; i<4; i++)	{		//output_high(I2C_SDA);		//output_low(I2C_SCL);		rxbuf[i] = i2c_read();	// Note that Slave may be delaying response								// by holding SCL low. i2c_read() monitors								// SCL and takes care of this.	}	rxbuf[4] = i2c_read(0);	// Last one nack (high), resets slave i2c								// logic. Very important, i2c bus will hang								// otherwise.	i2c_stop();					// Release the i2c bus	for(i=0; i<=4; i++)	{		inttobin(rxbuf[i]);	}}

Codul pentru Slave este:

#include <16F877.h>#define FREQUENCY 20010000#define I2C_SDA  PIN_C4#define I2C_SCL  PIN_C3#define SLAVE_ADDR 0x40	// Even number, address to be sent to slave pic,						// must match. Note read address is slave address + 1.#fuses HS,WDT,PROTECT,NOLVP,NOPUT,BROWNOUT#use delay(clock=FREQUENCY)#use fast_io(c)#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)#use i2c(slave, sda=I2C_SDA, scl=I2C_SCL,address=SLAVE_ADDR, FORCE_HW, SLOW)#include <math.h>// Registers and bits associated with i2c Sync Serial Port#byte SSPADD = 0x93  // Slave address held here#byte SSPCON1= 0x14  // SSP control reg 1#byte SSPBUF = 0x13  // Buffer for SSP data#byte SSPSTAT= 0x94  // SSP status reg// SSPSTAT bits#bit BF = SSPSTAT.0  // Buffer full status bit. Read-only bit.                     // Receive mode 1 = SSPBUF full.                     // Tx mode 1 = data transmit in progress, SSPBUF full.                     // 0 = complete, SSPBUF empty.                     // (does not include ack and stop bits in either case)#bit UA = SSPSTAT.1  // User needs to update addr. in SSPADD, 10 bit mode only.#bit R_W = SSPSTAT.2 // Holds read/write status from last address match, valid                     // to next start,stop, or not /ack bit.                     // Slave mode 1 = read, Master mode transmit in progress.#bit START=SSPSTAT.3 // Start bit (S) has been detected last#bit STOP= SSPSTAT.4 // Stop bit (P) has been detected last#bit D_A = SSPSTAT.5 // Data/addr bit. 1 = last byte received or transmitted                     // was data.#bit CKE = SSPSTAT.6 // 0 = input levels are i2c specs, 1 = SMB specs.#bit SMP = SSPSTAT.7 // Slew rate control. 1 = disabled for 100kHz or 1MHz modes// SSPCON bits                                   Slave mode 7 bits#bit SSPM0 = SSPCON1.0  // SSP mode bits                 0#bit SSPM1 = SSPCON1.1  // "                             1#bit SSPM2 = SSPCON1.2  // "                             1#bit SSPM3 = SSPCON1.3  // "                             0#bit CKP   = SSPCON1.4  // Clock, 1 = enable clock, 0 = clock stretch#bit SSPEN = SSPCON1.5  // SSP enable bit#bit SSPOV = SSPCON1.6  // Receive overflow indicator bit. In receive mode, a                        // byte is received while the SSPBUF is holding                        // previous byte. Must clear in software.#bit WCOL  = SSPCON1.7  // Write collision detect bit. In slave mode, 1 means                        // SSBUF written while transmitting previous word.                        // Must clear in software.#byte TRISC = 0x87#byte PIR1 = 0x0c#bit SSPIF = PIR1.3     // SSP interrupt flag bit#define BUF_LEN 5unsigned int8 buf[BUF_LEN];         // Buffer for data to be received or transmittedunsigned int8 buf2;         // Buffer for data to be received or transmittedint8 buf_index;// Possible i2c states, SSPSTAT for the four relevant bits#define STATE1 0x09  // D/A=0,S=1,R/W=0,BF=1. Master tx, slave addr#define STATE2 0x29  // D/A=1,S=1,R/W=0,BF=1. Master tx, data#define STATE3 0x0C  // D/A=0,S=1,R/W=1,BF=0. Master rx, slave addr+1#define STATE4 0x2C  // D/A=1,S=1,R/W=1,BF=0. Master rx, data with ack#define STATE5 0x28  // D/A=1,S=1,R/W=0,BF=0. Master rx, data with nack (high)#define STATE6 0x30  // D/A=1char x[16];short int tempverifica=0;short int com, calculeaza;void Comunicatie();void inttobin (int intval);#int_rdavoid rda(){	x[0]=getc();	delay_us(10);	if(x[0]=='z')	{		com=1;		disable_interrupts(INT_RDA);		calculeaza=0;	}}// i2c interrupt handler#int_SSPvoid SSP_isr(){	int8 state=0,incoming=0,TEMPSSPSTAT=0;	// Loop while flag set	while(SSPIF==1)	{		TEMPSSPSTAT=SSPSTAT;		//state=i2c_isr_state();		// Keep only bits D/A, S, R/W, BF		switch (TEMPSSPSTAT & 0x2d)		{		case(STATE1):		// Master write operation, address byte in SSPBUF.			// Clear the data buffer. Keep Master waiting by clock stretching			// for this to finish before it sends data byte(s). Otherwise			// SSPBUF will be full and an acknowledge will not be generated			// by the slave i2c hardware.			CKP = 0;       // Hold SCL low			for (buf_index = 0; buf_index < BUF_LEN; buf_index++)			{				buf[buf_index] = 0;			}			buf_index = 0; // Reset the data buffer index			buf[buf_index] = SSPBUF;   // Dummy read to clear BF (read only bit)         //buf[buf_index] = i2c_read();			SSPOV = 0;	// Clear receive overflow flag, could be set from last							// time because after nack, SSPBUF was still full.			CKP = 1;       // Allow master to continue by re-enabling SCL			break;		case(STATE2):		// Master write operation, data byte in SSPBUF		// If previous byte in SSPBUF not read, NACK sent back to master		// Get data byte, also clears BF			CKP = 0;       // Make Master wait by holding SCL low			buf[buf_index] = SSPBUF;   // Move received byte to array         //buf[buf_index] = i2c_read();			SSPOV = 0;     // Same comment as for State1			buf_index++;			if (buf_index >= BUF_LEN) buf_index = 0;   // Wrap around			CKP = 1;       // Allow master to continue by re-enabling SCL			break;		case(STATE3):		// Master has begun new read operation by initiating a START or RESTART		// then sending Slave address (read) byte, now in SSPBUF.		// Looks like clock enable bit CKP cleared on interrupt, so must		// set it again to allow Master to clock data byte out.			CKP = 0;                // Make Master wait by holding SCL low			////////////////////////////////////////			// Aici poti sa verifici ce ai primit //			////////////////////////////////////////			for (buf_index = 0; buf_index < BUF_LEN; buf_index++)			{				inttobin(buf[buf_index]);			}			buf_index = 0;          // Reset buffer index			while(BF)			{				//Asteapta ca BF sa fie 0			}			do			{				WCOL=0;         	//i2c_write(buf[buf_index]);				SSPBUF = buf[buf_index];// Load 1st byte from data buffer			}			while(WCOL);			buf_index++;			CKP = 1;                // Enable SCL for Master to shift byte out			break;		case(STATE4):		// Master read operation, last byte was data, SSPBUF empty.		// Move next byte to SSPBUF and SSPSR.		// Same comment for CKP bit as in STATE3.			CKP = 0;                // Make Master wait by holding SCL low			while(BF)			{				//Asteapta ca BF sa fie 0			}         do			{				WCOL=0;         	//i2c_write(buf[buf_index]);				SSPBUF = buf[buf_index];// Get next byte from data buffer			}			while(WCOL);			buf_index++;			if (buf_index >= BUF_LEN) buf_index = 0;   // Wrap around			CKP = 1;                // Enable SCL for Master to shift byte out			break;		case(STATE5):		// A nack (high) was received from Master in response to data		// byte received from Slave (last byte). Slave i2c logic is reset,		// and waits for next Master operation.			SSPEN=0;			SSPEN=1;			break;		default:		// Error, trap here. Watchdog will reset pic (must be enabled, and		// watchdog timer cleared in other loops).			printf("\n\rEroare: 0x%X",SSPSTAT);			while(1);			break;		}/*		if (state == 0x80)		{			////////////////////////////////////////			// Aici poti sa verifici ce ai primit //			////////////////////////////////////////			for (buf_index = 0; buf_index < BUF_LEN; buf_index++)			{				inttobin(buf[buf_index]);			}			buf_index = 0;          // Reset buffer index						printf("\n\r Trimit: %X",buf[buf_index]);						while(BF)			{				//Asteapta ca BF sa fie 0			}         do			{				WCOL=0;         	i2c_write(buf[buf_index]);				//SSPBUF = buf[buf_index];// Get next byte from data buffer			}			while(WCOL);			buf_index++;			CKP=1;		}		if (state > 0x80)		{			printf("\n\r Trimit: %X",buf[buf_index]);						while(BF)			{				//Asteapta ca BF sa fie 0			}         do			{				WCOL=0;         	i2c_write(buf[buf_index]);				//SSPBUF = buf[buf_index];// Get next byte from data buffer			}			while(WCOL);			buf_index++;			CKP=1;		}*/		SSPIF = 0;	}}void init_i2c(){	TRISC |= 0x18;           		// Set SCL and SDA as inputs and PIN_C6 & PIN_C7 as input	SSPCON1 = 0x36;         		// i2c slave mode, 7-bit address, enable SSP	SSPADD = SLAVE_ADDR;    	// Set slave address	SMP=1; 						//setarea pentru SlowSpeed	//SMP=0; 						//setarea pentru HiSpeed}void Comunicatie(){	printf("Comunicatie ON.");    	while(com==1)    	{	   	gets(x);	   	delay_us(10);       	if(x[0]=='s' && x[1]=='t' && x[2]=='o' && x[3]=='p')       	{	       	printf("Comunicatie OFF.");	       	delay_ms(500);			printf("Resetare PIC.");			delay_ms(500);			reset_cpu();       	}	}}void inttobin (int intval){	short int x0,x1,x2,x3,x4,x5,x6,x7;	byte byteval;	byteval=intval;	x0=bit_test(byteval,0);	x1=bit_test(byteval,1);	x2=bit_test(byteval,2);	x3=bit_test(byteval,3);	x4=bit_test(byteval,4);	x5=bit_test(byteval,5);	x6=bit_test(byteval,6);	x7=bit_test(byteval,7);	printf("\n\rValoare= %u --> Binar: %u%u%u%u%u%u%u%u",intval,x7,x6,x5,x4,x3,x2,x1,x0);}void main(){	//init_i2c();    // Set i2c slave mode, slave address, enable SSP	setup_wdt(WDT_2304MS);   //set_tris_c(0b11011000);	output_float(I2C_SDA);    output_float(I2C_SCL); 	//initializare variabile	com=0;	calculeaza=0;	enable_interrupts(INT_SSP);	enable_interrupts(INT_RDA);	enable_interrupts(GLOBAL);	while(TRUE)	{		if(tempverifica==1)		{			tempverifica=0;			for (buf_index = 0; buf_index < BUF_LEN; buf_index++)			{				inttobin(buf[buf_index]);			}			buf_index = 0;		}		if(com==1)		{			Comunicatie();		}		if(calculeaza==1)		{		}		restart_wdt();    	}}

dupa cum se poate observa din codul pentru slave am incercat mai multe variatiuni (si varianta cu verificarea lui SSPSTAT si varianta ci functiile CCS i2c_isr_state() .... cea de-a doua varianta facat si mai multe probleme m-am rezumat mai mult la prima ....)

 

Ca un exemplu de ceea ce trimit cu masterul ... primesc in slave si ceea ce citesc dupa aia cu masterul sunt exemplele de mai jos:

 

Masterul transmite:

Valoare= 63 --> Binar: 00111111Valoare= 73 --> Binar: 01001001Valoare= 133 --> Binar: 10000101Valoare= 193 --> Binar: 11000001Valoare= 200 --> Binar: 11001000

Slave-ul primeste:

Valoare= 63 --> Binar: 00111111Valoare= 73 --> Binar: 01001001Valoare= 133 --> Binar: 10000101Valoare= 193 --> Binar: 11000001Valoare= 200 --> Binar: 11001000

Iar Masterul citeste din slave:

Valoare= 63 --> Binar: 00111111Valoare= 201 --> Binar: 11001001Valoare= 5 --> Binar: 00000101Valoare= 193 --> Binar: 11000001Valoare= 200 --> Binar: 11001000

Cam asta e problema mea .... apreciez daca cineva imi poate spune unde anume gresesc ... ce anume nu e in regula ....

 

Aaaaaaaa ... si am mai observat o chestie .... dupa ce masterul trimite cate un pachet de 8 biti si primeste ACK-ul=0 de la slave in pauza pana la urmatoarea trimitere de date SDA-ul imi sta in HI ... chestia asta se schimba doar cand masterul citeste date din slave, dupa ce trimite adresa de citire a datelor din slave SDA=ul e HI insa dupa ce incepe sa ceara date de citire SDA-ul e LOW .... poate aici undeva e anomalia ... nici nu stiu ce sa mai zic .... poate cineva imi poate descretii si mie mintea si asa obosita de atatea incercari .... multumesc!

Share this post


Link to post
Share on other sites

S-a rezolvat problema ... faceam simularile in Proteus .... Proteus are o mica problema la simularea I2C in Slave mode ... pus tot montajul pe o placa de incercari e totul ok ... programul functioneaza ....

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.