📄 i2c_slave.c
字号:
#include <htc.h>
__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & UNPROTECT
& UNPROTECT & BORDIS & IESODIS & FCMDIS);
// SSPSTAT byte is: SMP (7) CKE (6) D/A (5) P (4) S (3) R/W (2) UA (1) BF (0)
#define STATUS_BUFFER_READ_ADD 0x0D // D/A = 0; S = 1; R/W = 1; BF = 1;
#define STATUS_BUFFER_READ_BYTE 0x2C // D/A = 1; S = 1; R/W = 1; BF = 0;
#define STATUS_BUFFER_WRITE_ADD 0x09 // D/A = 0; S = 1; R/W = 0; BF = 1;
#define STATUS_BUFFER_WRITE_BYTE 0x29 // D/A = 1; S = 1; R/W = 0; BF = 1;
#define STATUS_BUFFER_NACK 0x28 // D/A = 0; S = 1; R/W = 1; BF = 0;
volatile char flag, data;
main(void)
{
char c;
int i;
PORTB = 0;
PORTC = 0;
PIR1 = 0;
ANSEL = 0;
ANSELH = 0;
ADON = 0;
// Set RB4 & 6 as inputs
TRISB = 0x50;
TRISC = 0;
for (i=0; i<32000; i++) {} // A short delay
SSPADD = 0x20;
SSPMSK = 0xFF;
// Set SSPEN bit of SSPCON (bit 5)
// Set lower four bits to I2C Slave mode (0110)
SSPCON = 0x26;
SSPSTAT &= ( ~ 0x1 ); // clear BF bit
PIE1 |= 0x08; // enable SSPIE
INTCON |= 0xC0; // enable PEIE and GIE
flag = 0;
// Enable CKP bit
SSPCON |= 0x10;
for (;;) {
c = PORTC;
if ( flag )
c |= 0x3;
else
c |= 0x1;
PORTC = c;
}
}
static void interrupt isr(void) {
char CurrentStatus, dummy;
// SSPIF
if ( PIR1 & 0x08 ) {
PIR1 &= ( ~ 0x08 ) ;
flag = 1;
CurrentStatus = SSPSTAT & 0b00101101;
switch (CurrentStatus) {
case STATUS_BUFFER_WRITE_ADD:
dummy = SSPBUF; // dummy read
break;
case STATUS_BUFFER_WRITE_BYTE:
data = SSPBUF; // read in real data
break;
case STATUS_BUFFER_READ_ADD:
SSPBUF = 0xAB;
SSPCON |= 0x10; // set CKP bit
break;
case STATUS_BUFFER_READ_BYTE:
SSPBUF = 0x55;
SSPCON |= 0x10; // set CKP bit
break;
case STATUS_BUFFER_NACK:
break;
default:
break;
}
}
}
It's probably fortunate that you don't get an interrupt, because the first one, with all those horrible returns will stop any further interrupts because the GIE is not cleared by a return! So the first thing to say is GET RID OF those returns in the interrupt service routine! The compiler will add the needed retfie instruction at the end of the ISR (which will re-enable global interrupts).
The second suggestion is to use this form to do so:
if()
{}
else if()
{}
else if()
{}
else
{}
I also notice that you seem to do the same action (data = SSPBUF;) for each option except the last, so why not do the exception test first, and do the else afterwards. Less code, more speed. And you're using an int for a flag??? - a bit would do, and save more code and RAM!
Quote:
--------------------------------------------------------------------------------
Code:
--------------------------------------------------------------------------------
int c, flag;main(void){ flag = 0; for (;;) { if ( flag ) c = 0x3; else c = 0x1;
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
At this point the compiler is perfectly entitled to eliminate the if and set c to 1. You have said (implicitly) to the compiler "flag will not be changed behind your back" (because the C standard assumes a single thread of execution), and then you test this unchanging value. To tell the compiler "flag will be changed behind your back", you must define it thus:
Code:
--------------------------------------------------------------------------------
volatile bit flag;
--------------------------------------------------------------------------------
(I've made it a bit, and could have used char for slightly larger code, because the PIC can read at most 1 byte at a time from RAM, so might be interrupted between reading the high and low bytes of any variable larger than a char. Since you're only using one bit of that there wouldn't be any real problem, but why not use the most appropriate type? If you were really using a larger-than-char value, then any access to it outside the ISR should be wrapped with di()/ei() - if not, then either your main code or your interrupt routine (or both) could use corrupt values.)
If you were to use data outside the ISR, it too would need to be volatile; if you don't use it outside the ISR then you might as well make it local to that function.
**********************************************
Program to write a byte to I2C bus,
using interrupts.
INPUTS:
RB4 - SDA
RB6 - SCL
OUTPUTS:
RC0-1 = LEDs (lights when SSPIF interrupts)
********************************************* */
#include <htc.h>
__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & UNPROTECT
& UNPROTECT & BORDIS & IESODIS & FCMDIS);
// SSPSTAT byte is: SMP (7) CKE (6) D/A (5) P (4) S (3) R/W (2) UA (1) BF (0)
#define STATUS_BUFFER_READ_ADD 0x0D // D/A = 0; S = 1; R/W = 1; BF = 1;
#define STATUS_BUFFER_READ_BYTE 0x2C // D/A = 1; S = 1; R/W = 1; BF = 0;
#define STATUS_BUFFER_WRITE_ADD 0x09 // D/A = 0; S = 1; R/W = 0; BF = 1;
#define STATUS_BUFFER_WRITE_BYTE 0x29 // D/A = 1; S = 1; R/W = 0; BF = 1;
#define STATUS_BUFFER_NACK 0x28 // D/A = 0; S = 1; R/W = 1; BF = 0;
volatile char flag, data;
main(void)
{
char c;
int i;
PORTB = 0;
PORTC = 0;
PIR1 = 0;
ANSEL = 0;
ANSELH = 0;
ADON = 0;
// Set RB4 & 6 as inputs
TRISB = 0x50;
TRISC = 0;
for (i=0; i<32000; i++) {} // A short delay
SSPADD = 0x20;
SSPMSK = 0xFF;
// Set SSPEN bit of SSPCON (bit 5)
// Set lower four bits to I2C Slave mode (0110)
SSPCON = 0x26;
SSPSTAT &= ( ~ 0x1 ); // clear BF bit
PIE1 |= 0x08; // enable SSPIE
INTCON |= 0xC0; // enable PEIE and GIE
flag = 0;
// Enable CKP bit
SSPCON |= 0x10;
for (;;) {
c = PORTC;
if ( flag )
c |= 0x3;
else
c |= 0x1;
PORTC = c;
}
}
static void interrupt isr(void) {
char CurrentStatus, dummy;
// SSPIF
if ( PIR1 & 0x08 ) {
PIR1 &= ( ~ 0x08 ) ;
flag = 1;
CurrentStatus = SSPSTAT & 0b00101101;
switch (CurrentStatus) {
case STATUS_BUFFER_WRITE_ADD:
dummy = SSPBUF; // dummy read
break;
case STATUS_BUFFER_WRITE_BYTE:
data = SSPBUF; // read in real data
break;
case STATUS_BUFFER_READ_ADD:
SSPBUF = 0xAB;
SSPCON |= 0x10; // set CKP bit
break;
case STATUS_BUFFER_READ_BYTE:
SSPBUF = 0x55;
SSPCON |= 0x10; // set CKP bit
break;
case STATUS_BUFFER_NACK:
break;
default:
break;
}
}
}
It's probably fortunate that you don't get an interrupt, because the first one, with all those horrible returns will stop any further interrupts because the GIE is not cleared by a return! So the first thing to say is GET RID OF those returns in the interrupt service routine! The compiler will add the needed retfie instruction at the end of the ISR (which will re-enable global interrupts).
The second suggestion is to use this form to do so:
if()
{}
else if()
{}
else if()
{}
else
{}
I also notice that you seem to do the same action (data = SSPBUF;) for each option except the last, so why not do the exception test first, and do the else afterwards. Less code, more speed. And you're using an int for a flag??? - a bit would do, and save more code and RAM!
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -