|
|
View previous topic :: View next topic |
Author |
Message |
valemike Guest
|
working i2c example... |
Posted: Thu Jun 10, 2004 9:31 am |
|
|
I too was not satisfied with CCS's Slave examples. So I made my own, based on the assembly examples in Microchip's APP Note AN734 (AN735?)
When looking at the code below, you'll find that I sprinkled way too many "delay_ms()" statements. This was just to let the other PIC catch up, but i'm sure not all of the delay_ms() statements are necessary, and probably the values in them are too excessive.
The master code is straightforward, and I use CCS's libraries...
Code: |
#include <18F452.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use i2c(master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)
//#use i2c(master,sda=PIN_C4, scl=PIN_C3)
#define SLAVE_ADDRESS 0x02
void main(void)
{
unsigned char rx_byte;
while (1)
{
printf ("Start i2c Master logger.\r\n");
i2c_start();
delay_ms(50);
i2c_write(SLAVE_ADDRESS); /* Device Address */
delay_ms(50);
i2c_write(0x05);
delay_ms(50);
// i2c_write(12);
delay_ms(100);
i2c_start(); // restart condition
i2c_write(SLAVE_ADDRESS + 1);
rx_byte = i2c_read(0);
printf ("rx_byte = 0x%2.2X\r\n", rx_byte);
i2c_stop();
printf ("Ending i2c Master Logger.\r\n");
}
}
|
Here is the slave code. Cut and paste it, and compile. If you're using a different processor, then you need to change the #byte statements to match the particular PIC you're using.
Code: |
#define PIC18F452
#include <18F452.h>
#fuses HS,NOWDT,NOPROTECT
#use delay(clock=4000000)
unsigned char read_i2c(void);
void i2c_interrupt_handler(void);
void i2c_initialize(void);
void i2c_error(void);
void write_i2c(unsigned char transmit_byte);
#INT_SSP
void ssp_interupt ()
{
i2c_interrupt_handler();
}
/* These #byte's are for PIC18F452 ONLY!! */
/* Change it per chip */
#byte PIC_SSPBUF=0xFC9
#byte PIC_SSPADD=0xFC8
#byte PIC_SSPSTAT=0xFC7
#byte PIC_SSPCON1=0xFC6
#byte PIC_SSPCON2=0xFC5
#byte PIC_SSPBUF=0x13
#byte PIC_SSPADD=0x93
#byte PIC_SSPSTAT=0x94
#byte PIC_SSPCON1=0x14
/* Bit defines */
#define PIC_SSPSTAT_BIT_SMP 0x80
#define PIC_SSPSTAT_BIT_CKE 0x40
#define PIC_SSPSTAT_BIT_DA 0x20
#define PIC_SSPSTAT_BIT_P 0x10
#define PIC_SSPSTAT_BIT_S 0x08
#define PIC_SSPSTAT_BIT_RW 0x04
#define PIC_SSPSTAT_BIT_UA 0x02
#define PIC_SSPSTAT_BIT_BF 0x01
#define PIC_SSPCON1_BIT_WCOL 0x80
#define PIC_SSPCON1_BIT_SSPOV 0x40
#define PIC_SSPCON1_BIT_SSPEN 0x20
#define PIC_SSPCON1_BIT_CKP 0x10
#define PIC_SSPCON1_BIT_SSPM3 0x08
#define PIC_SSPCON1_BIT_SSPM2 0x04
#define PIC_SSPCON1_BIT_SSPM1 0x02
#define PIC_SSPCON1_BIT_SSPM0 0x01
#define PIC_SSPCON2_BIT_GCEN 0x80
#define PIC_SSPCON2_BIT_ACKSTAT 0x40
#define PIC_SSPCON2_BIT_ACKDT 0x20
#define PIC_SSPCON2_BIT_ACKEN 0x10
#define PIC_SSPCON2_BIT_RCEN 0x08
#define PIC_SSPCON2_BIT_PEN 0x04
#define PIC_SSPCON2_BIT_RSEN 0x02
#define PIC_SSPCON2_BIT_SEN 0x01
#define RX_BUF_LEN 32
#define NODE_ADDR 0x02 /* I2C address of the slave node */
unsigned char slave_buffer[RX_BUF_LEN];
int buffer_index;
int comms_error;
int debug_state;
void i2c_initialize(void)
{
/* Set up SSP module for 7-bit */
PIC_SSPCON1 = 0x36; /* 0011 0101 */
PIC_SSPADD = NODE_ADDR; /* Set the slave's address */
PIC_SSPSTAT = 0x00; /* Clear the SSPSTAT register. */
enable_interrupts(INT_SSP); /* Enable MSSP interrupts. */
}
void i2c_interrupt_handler(void)
{
unsigned char i2c_mask = 0x2D; /* 0010 1101 */
unsigned char temp_sspstat;
unsigned char this_byte;
unsigned char tx_byte;
int x;
/* Mask out the unnecessary bits */
temp_sspstat = PIC_SSPSTAT & i2c_mask;
switch (temp_sspstat)
{
/* Write operation, last byte was an address, buffer is full */
case 0x09: /* 0000 1001 */
/* Clear the receive buffer */
for (x=0; x<RX_BUF_LEN; x++)
{
slave_buffer[x] = 0x00;
}
buffer_index = 0; /* Clear the buffer index */
this_byte = read_i2c(); /* Do a dummy read of PIC_SSPBUF */
debug_state = 1;
break;
/* Write operation, last byte was data, buffer is full */
case 0x29: /* 0010 1001 */
/* Point to the buffer */
this_byte = read_i2c(); /* Get the byte from the SSP */
slave_buffer[buffer_index] = this_byte; /* Put it into the buffer */
buffer_index++; /* Increment the buffer pointer */
/* Get the current buffer index */
/* Subtract the buffer length */
/* Has the index exceeded the buffer length? */
if (buffer_index >= RX_BUF_LEN)
{
buffer_index = 0; /* Yes, clear the buffer index. */
}
debug_state = 2;
break;
/* Read operation; last byte was an address, buffer is empty */
case 0x0C: /* 0000 1100 */
buffer_index = 0; /* Clear the buffer index */
/* Point to the buffer */
tx_byte = slave_buffer[buffer_index]; /* Get byte from the buffer */
write_i2c(tx_byte); /* Write the byte to PIC_SSPBUF */
buffer_index++; /* increment the buffer index */
debug_state = 3;
break;
/* Read operation; last byte was data, buffer is empty */
case 0x2C: /* 0010 1100 */
/* Get the current buffer index */
/* Subtract the buffer length */
/* Has the index exceeded the buffer length? */
if (buffer_index >= RX_BUF_LEN)
{
buffer_index = 0; /* Yes, clear the buffer index */
}
/* Point to the buffer */
/* Get the byte */
tx_byte = slave_buffer[buffer_index];
write_i2c(tx_byte); /* Write to PIC_SSPBUF */
buffer_index++; /* increment the buffer index */
debug_state = 4;
break;
/* A NACK was received when transmitting data back from the master. */
/* Slave logic is reset in this case. R_W=0, D_A=1, and BF=0. */
/* If we don't stop in this state, then something is wrong!! */
case 0x28: /* 0010 1000 */
debug_state = 5;
break;
/* Something went wrong!! */
default:
i2c_error();
break;
}
}
void i2c_error(void)
{
comms_error = 1;
printf ("I2C ERROR!\r\n");
}
void write_i2c(unsigned char transmit_byte)
{
unsigned char temp;
unsigned char write_collision = 1;
while (PIC_SSPSTAT & PIC_SSPSTAT_BIT_BF) /* Is BF bit set in PIC_SSPSTAT? */
{
/* If yes, then keep waiting */
}
while (write_collision)
{
/* If not, then do the i2c_write. */
PIC_SSPCON1 &= ~PIC_SSPCON1_BIT_WCOL; /* Clear the WCOL flag */
PIC_SSPBUF = transmit_byte;
/* Was there a write collision? */
if (PIC_SSPCON1 & PIC_SSPCON1_BIT_WCOL)
{
/* Yes there was a write collision. */
write_collision = 1;
}
else
{
/* NO, there was no write collision. */
/* The transmission was successful */
write_collision = 0;
}
}
PIC_SSPCON1 |= PIC_SSPCON1_BIT_CKP; /* Release the clock. */
}
/* This function returns the byte in SSPBUF */
unsigned char read_i2c(void)
{
return PIC_SSPBUF;
}
void main(void)
{
debug_state = 0;
i2c_initialize();
enable_interrupts(GLOBAL);
// printf ("Starting I2C Slave Logger.\r\n");
while (1)
{
if (debug_state)
{
// printf ("Debug State = %d\r\n", debug_state);
debug_state = 0;
}
}
}
|
|
|
|
falleaf
Joined: 23 May 2004 Posts: 48
|
|
Posted: Thu Jun 10, 2004 11:33 am |
|
|
Oh,thanks indeed. It's that what I need.
Thanks muchie. If I have some question on this code, may I ask you for more information?
Thanks. |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1933 Location: Norman, OK
|
|
Posted: Thu Jun 10, 2004 11:37 am |
|
|
With all the noise he is making about this I would suspect he is trying to get us to do a school project or something similar for him.
With the fact of his requestes do not make sense and how insistant he is to get specifically the I2C drivers. In that the posts in the board are apparently not good enough for him, I would suspect he wants us to hand him a working solution to his problem with us doing all the work for him.
I may be wrong but I bet not! |
|
|
falleaf
Joined: 23 May 2004 Posts: 48
|
|
Posted: Thu Jun 10, 2004 11:10 pm |
|
|
hallo,
Yeah, this is a school project. However, don't bet on this, because you will lose. . My project is on motion control.
I'm trying with my project on C. And if you pay a visit to electrotech, you will see I helped students there. That is, I would like to ask, and will help if I can.
I need to have to comparation between the C code >> view as ASM with the ASM writen myself. However, it's difficult to compare the code if I cannot write as I need. Don't know anything about C for PIC.
My undertaking is not writing the communication, but understanding C.
By the way, I2C communication in ASM published everywhere. If you need it to include to C, I think no problems.
That is my question about.
Dont feel so bad to meeh.
And my question will not as how to write the code, but it should be. As writing on ASM, I need to do this this this... so how should we do it on C... blah blah..
I asked about PWM, but no ans !!
As generating C code to change PWM. We only use set_pwm_duty function. But in ASM, as I change PWM duty, I have to wait until the timer overflow. So will the C code do this? The problem is that, if the pin is HIGH, and we change the duty, it still get HIGH at the next period, and we get wrong at the first period as changing duty. What is the way C code do?
I would like to know about such problems on C, to understand and make a C program better. And You will see that, C is not always good as ASM. Some init function, I have to use ASM for example. Now, I did some simple function and understand how to fix C code and ASM. With simple function, and repeated unexpectedly, I change to use ASM.
Sorry, my English is not good, so I have a long explanation. But that is what I'm doing. |
|
|
falleaf
Joined: 23 May 2004 Posts: 48
|
|
Posted: Fri Jun 11, 2004 1:07 am |
|
|
Example codes also here:
Source: http://www.walking-productions.com/school/adtech/
Master
Code: |
/* Standard Include for 16F877 Chip */
#include <16F877.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Delay for 4 mhz crystal */
#use delay (clock=4000000)
/* Setup RS232 */
#use rs232(baud=9600, xmit=PIN_D4,rcv=PIN_D5, INVERT)
/* Setup I2C */
#use I2C(MASTER, sda=PIN_C4, scl=PIN_C3, SLOW)
main()
{
int8 i2c_command = 66;
while (true)
{
delay_ms(1000);
printf("Outputting: %c", i2c_command);
/* Master */
i2c_start(); // Start condition
i2c_write(0xa0); // Device address
i2c_write(i2c_command); // Write Command
i2c_stop(); // Stop condition
}
} |
Slave
Code: | /* Standard Include for 16F877 Chip */
#include <16F877.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT
/* Delay for 4 mhz crystal */
#use delay (clock=4000000)
/* Setup RS232 */
#use rs232(baud=9600, xmit=PIN_D4,rcv=PIN_D5, INVERT)
/* Setup I2C */
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3, address=0xa0, SLOW, FORCE_HW)
#INT_SSP
void ssp_interupt ()
{
byte incoming;
incoming = i2c_read();
printf("\n\rRead byte 1: %x\n\r", incoming);
incoming = i2c_read();
printf("Read byte 2: %x\n\r\n\r", incoming);
}
main()
{
delay_ms(1000);
printf("Running");
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
while (true)
{
}
} |
|
|
|
valemike Guest
|
|
Posted: Fri Jun 11, 2004 8:17 am |
|
|
Not quite. It looks like the slave can only handle incoming bytes, but it doesn't handle when the master is requesting data. At one time, I too thought it was easy to just do like the example.
However, the "state machine" in the APP note AN735/AN734 is in fact the proper way to handle the i2c interrupts on the slave side. It's not really a state machine though as far as the source code is concerned, since I don't have to worry about previous states. I guess the PIC's SSP does it for us. You just have to worry about the current contents of SSPSTAT with each interrupt.
-Mike |
|
|
falleaf
Joined: 23 May 2004 Posts: 48
|
|
Posted: Sun Jun 13, 2004 5:43 am |
|
|
Yes, I used AN735 ASM code, and it worked very good. So the problem is that I don't know exactly how the C code work!!!. I'm trying to understand it.
Thanks muchie. |
|
|
ben500 Guest
|
Acknowledgment bit |
Posted: Mon Jun 14, 2004 1:09 pm |
|
|
Hi,
I'm trying to make communicate a PIC16F819 (master) with a PIC16F876 (slave) by I2C. I've tried now a lot of different example codes, but it is always the same error which occured: the slave does not send any acknowledgement! According to the datasheets (16f87x, table 9-2), I think I am in the fifth case where the BF bit is cleared and the SSPOV bit is set. My problem is that I don't know how to avoid it. Perhaps someone could help me, it is near to break my nerves...
Thanks you,
~Ben |
|
|
Guest
|
|
Posted: Thu Jun 17, 2004 3:28 am |
|
|
Code: | #byte PIC_SSPBUF=0xFC9
#byte PIC_SSPADD=0xFC8
#byte PIC_SSPSTAT=0xFC7
#byte PIC_SSPCON1=0xFC6
#byte PIC_SSPCON2=0xFC5
#byte PIC_SSPBUF=0x13
#byte PIC_SSPADD=0x93
#byte PIC_SSPSTAT=0x94
#byte PIC_SSPCON1=0x14 |
You redefine here, and the 4 below lines is use for 16Fxxx PIC only, does it mean in C that the program can choose one of these address? Or you write as an example for who want to use 16F PICs?
What is the different between hardware I2C and software?
As using #uses I2C from CCS C, what will it generate? |
|
|
falleaf
Joined: 23 May 2004 Posts: 48
|
|
Posted: Thu Jun 17, 2004 3:31 am |
|
|
Sorry, it's meeh, I forgot to login |
|
|
valemike1 Guest
|
oops |
Posted: Fri Jun 18, 2004 11:17 am |
|
|
Code: |
#byte PIC_SSPBUF=0xFC9
#byte PIC_SSPADD=0xFC8
#byte PIC_SSPSTAT=0xFC7
#byte PIC_SSPCON1=0xFC6
#byte PIC_SSPCON2=0xFC5
#byte PIC_SSPBUF=0x13
#byte PIC_SSPADD=0x93
#byte PIC_SSPSTAT=0x94
#byte PIC_SSPCON1=0x14
|
When I cut and pasted the code, I dont know why but I took out the #if 0...#endif statements.
Delete one of the groups, since you don't want to redefine twice. The second set 0x13,0x93,0x94,0x14 is for the PIC16F876, 873, etc. The numbers with the 0xFc9, etc. is for the PIC18F252/452/etc.
-Mike
p.s. Software i2c is bitbanged and you just toggle these pins high and low like any general purpose i/o pin. Hardware i2c actually uses the built-in MSSP/SSP module. If you must use i2c, then go use a PIC with the hardware i2c mssp/ssp module. That way you won't burn processor time bitbanging. |
|
|
falleaf
Joined: 23 May 2004 Posts: 48
|
|
Posted: Sat Jun 19, 2004 3:15 am |
|
|
Yes, I'm using 16F876A and 16F877A. I'm using 16F876A as a slave and 877 as the master.
Everything seems oki.
I found that CCS C is really like normal C, and the function is clear. But I still cannot read the ASM code generated from CCS C.
With your code, I save it as a file i2c_slave_mike.c and include to my program, then use it.
Code: | #byte PIC_SSPBUF=0x13
#byte PIC_SSPADD=0x93
#byte PIC_SSPSTAT=0x94
#byte PIC_SSPCON1=0x14
#byte PIC_SSPCON2=0x91 |
And I see that there is some #define *BIT* you didn't use in the code. Will it takes the memories a compiling the code? Because as defining, we can only define it as a number. And will the C takes some memories to save those number?
By the way, what is the difference between using your code on 16f876 and #uses I2C of CCS C?
Because I see that we can force it to hardware mode?
I would like to understand more about function i2c_start();
As it generate start condition, will it reset the buffer_index? Because if I would like to use 8 first bytes to save data from master, and 8 low bytes to save transmit bytes. I will have to set buffer length as 8 or 16 bytes?
If this is writen identically as AN734, I can use 16 bytes. Because the master will do as below: (I don't write in CCS C code, because I'm not sure of it).
function TX_RX ()
{
start condition
send the address of slave in write mode
for i from 1 to 8
write (data[i]);
start condition
send the address of slave in read mode
for i from 9 to 16
data[i] = read_i2c;
}
In slave chip, I have 16 bytes buffer. In write condition of master, the slave will receive 8 byte and save to buffer[1..8]. After that, the read condition, the index still at 9. Then the slave will send buffer[9..16].
May we do this with your code?
Then each sampling time I only have to call function TX_RX. I can send and receive all what I need.
I see that you put buffer_index as a global variable, so that it can do this. In ASM, I can easily know exactly register address, so I don't care muchie as writing in ASM, but C for PIC confuse meeh
And pls explain me about #uses I2C in CCS C? which is smaller code between yours and force hardware CCS C? I'm using 16F876a.
Thanks muchie. |
|
|
falleaf
Joined: 23 May 2004 Posts: 48
|
|
Posted: Sun Jun 20, 2004 11:57 am |
|
|
Mike,
I've tested with your code, the tx from master to slave is oki, but as rx, it wrong.
In your code, I've tested:
master:
....
i2c_write(0x05);
i2c_write(0x06);
i2c_write(0x07);
i2c_start(); //restart
i2c_write(add +1); // normally in asm I used to set this bit0
byte1 = i2c_read();
byte2 = i2c_read();
...
but rx way is not good, and I receive wrong result.
Pls explain me about this. |
|
|
valemike Guest
|
|
Posted: Mon Jun 21, 2004 3:28 pm |
|
|
For receiving, you will need to determine what you want to put in the outgoing bytes. The global array I provided is just an example taken from the application note. However, it is pretty much useless by itself.
So, you said that you tried it and it works fine for master sending bytes to the slave, and you confirmed that the slave received the bytes? But when the master tries to read from the slave, then you get errors?
I'll try to answer your questions later this evening when I go home.
-Mike |
|
|
Guest
|
Re: Acknowledgment bit |
Posted: Mon Jun 21, 2004 3:33 pm |
|
|
ben500 wrote: | Hi,
I'm trying to make communicate a PIC16F819 (master) with a PIC16F876 (slave) by I2C. I've tried now a lot of different example codes, but it is always the same error which occured: the slave does not send any acknowledgement! According to the datasheets (16f87x, table 9-2), I think I am in the fifth case where the BF bit is cleared and the SSPOV bit is set. My problem is that I don't know how to avoid it. Perhaps someone could help me, it is near to break my nerves...
Thanks you,
~Ben |
When using the CCS-supplied libraries for the MASTER, my AN735-based Slave gets NACK states (case 5?) all the time, but it doesn't seem to hurt anything. |
|
|
|
|
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
|