I 2 C
I 2 C
Encode a clock with data so that clock be extracted or data has guaranteed transition density with receiver clock via Phase-Locked-Loop (PLL)
IEEE Firewire (clock encoded in data) USB (data has guaranteed transition density)
V 0.3
V 0.3
CKE configuration bit allows either falling or rising edge of clock to be used, while CKP selects clock polarity.
V 0.3
SPM bit selects whether data is sampled in middle of clock period or at end of clock period.
Between the CKP, CKE, SPM bits there is a lot of flexibility in how data is clocked in. Can make the SPI protocol work with just about any serial device.
V 0.3 4
Multiple SPI peripherals each require a separate chip select line via parallel port line. We will concentrate on the I2C serial bus as it does not require use of chip selects.
V 0.3 5
SDA
SDA
A0
SCL
A2 A1
A0
6
What is a bus??
V 0.3
V 0.3
I2C Features
Multiple receivers do not require separate select lines as in SPI
At start of each I2C transaction a 7-bit device address is sent Each device listens if device address matches internal address, then device responds
SDA (data line) is bidirectional, communication is half duplex SDA, SCLK are open-drain, require external pullups
Allows multiple bus masters (will discuss this more later).
V 0.3 9
Multiple bytes sent in a transaction; every 8 bits has a 9th bit that is an acknowledge.
V 0.3 11
V 0.3
12
I2C Transmission
V 0.3
Acknowledgement
ACK sent by slave after every 8-bits received. Master releases line (stops driving), samples line on next clock. Slave MUST pull line low. If Master does not detect ACK, then sets error bit. If Slave does not pull line low, the pullup resistors will pull the line low. Most common cause of ACK error incorrect device address.
V 0.3
16
Address should be on a page boundary. For page size = 64 = 0x40, starting address should be a multiple of 64.
V 0.3 18
Speed Comparison
Assume a 400 Khz I2C bus, 2.5 us clock period (2.5 e-6) Random write:
9 bit transmission = 2.5 us * 9 = 22.5 us 5 ms + 22.5 us* 4 (control,addhi,addlo,data) =5.09 ms For 64 bytes = 325 ms approximately, not counting software overhead.
Page Write
67 bytes total (control, addhi, addlo, data) 5 ms + 67 * 22.5 us = 6.5 ms!!!
V 0.3
19
V 0.3
20
Sequential Read
Like a current address read, but after Slave sends data byte, Master sends ACK instead of STOP
Slave then sends next byte Can do this from 0x0000h to 0x7FFF (lower 32K block). When 0x7FFF is reached, address rolls over to 0x0000 Upper block goes from 0x8000 to 0xFFFF; at 0xFFFF address rolls over to 0x8000 Internal address counter is only 15 bits wide.
V 0.3
23
To set I2C clock rate, write 8-bit value to the SSPADD register
Clock rate = Fosc/(4 *(SSPADD+1))
I2C standard defines 100 KHz and 400 KHz but in reality just about any clock rate from DC to 400 KHz works
Clock Rate formula in SSPCON1 description, page 136 of datasheet (page 138 PDF page), section 15-4 of datasheet
V 0.3 25
V 0.3
26
i2cmsu.c Subroutines
i2c_idle() wait for idle condition on I2C bus i2c_Start() send a START and wait for START end i2c_Stop() send a STOP and wait for STOP end i2c_doAck() do an ACK cycle i2c_doNak() do a NACK cycle i2c_WriteTo(address) do a i2c_Start(), then send address to I2C bus. i2c_PutByte(byte) write byte to I2C, wait for finish, then get an ACK i2c_GetByte() get a byte from I2C bus
V 0.3 27
V 0.3
28
i2c_PutByte (address_hibyte)
i2c_PutByte (address_lobyte)
i2c_Stop()
i2c_Writeto (read_cmd) i2c_GetByte () i2c_Stop()
V 0.3
memread(cmd,addr)
/* random read */ unsigned char mem_read(unsigned char cmd,int addr) { unsigned char hbyte, lbyte, val;
if (addr & 0x8000) { // if MSB set , set block select bit cmd = cmd | 0x08; } hbyte = (addr >> 8) & 0x7F; // high address byte lbyte = (addr) & 0xFF; // low address byte i2c_WriteTo(cmd); // send write cmd, do this to set address counter i2c_PutByte(hbyte); // send high address byte i2c_PutByte(lbyte); // send low address byte i2c_Stop(); // send stop cmd = cmd | 0x1; // set read bit i2c_WriteTo(cmd); // send read cmd, address set by previous cmd val = i2c_GetByte(); // read data i2c_Stop(); // send stop return(val); }
V 0.3 30
i2c_Writeto (write_cmd)
i2c_PutByte (address_hibyte)
Page Write
set starting address.
i2c_PutByte (address_lobyte)
i = 0; i2c_PutByte (); i++ i = 64? i2c_Stop()
V 0.3 31
Send 64 bytes.
hbyte = (addr >> 8) & 0x7F; // high address byte lbyte = (addr) & 0xFF; // low address byte i2c_WriteTo(cmd); // send write cmd i2c_PutByte(hbyte); // send high address byte i2c_PutByte(lbyte); // send low address byte for (k=0;k<64;k++) { Uncomment if //i2c_PutByte(buf[k]); // send data i2c_FastPutByte(buf[k]); // send data subroutine called } more often than 5 i2c_Stop();
//DelayMs(5);
ms.
FastPutByte does not check for idle condition before sending data.
V 0.3 32
i2c_Writeto (write_cmd)
i2c_PutByte (address_hibyte) i2c_PutByte (address_lobyte) i2c_Stop() i2c_Writeto (read_cmd)
Block Read
set starting address. Send read command Use sequential read to get 64 bytes. Sequential read can read up to 32K, block size of 64 bytes was arbitrary 33
i = 64?
yes
no
i2c_doAck();
i2c_Stop()
V 0.3
/* block read */ void block_mem_read(unsigned char cmd,int addr,char *buf) { unsigned char hbyte; unsigned char lbyte; unsigned char k; if (addr & 0x8000) { // if MSB set , set block select bit cmd = cmd | 0x08; } hbyte = (addr >> 8) & 0x7F; // high address byte lbyte = (addr) & 0xFF; // low address byte i2c_WriteTo(cmd); // send write cmd, do this to set address counter i2c_PutByte(hbyte); // send high address byte i2c_PutByte(lbyte); // send low address byte i2c_Stop(); // send stop cmd = cmd | 0x1; // set read bit i2c_WriteTo(cmd); for (k=0;k<64;k++){ buf[k] = i2c_GetByte(); // read data if (k== 63) i2c_Stop(); // last byte, so send stop else i2c_doAck(); // do ack if not at end } }
V 0.3 34
Block Read in C
written!
V 0.3
36
i2c_idle()
i2c_idle() { unsigned char byte1; unsigned char byte2; do { byte1 = SSPSTAT & 0x04; byte2 = SSPCON2 & 0x1F; }while (byte1 | byte2); asm(clrwdt);
Check if lower 5 bits are all 0 indicating that Start, Stop, Acknowledge sequences are all idle.
V 0.3 37
i2c_Start()/i2c_Stop()
i2c_Start(){ i2c_idle(); /* initiate start, SEN=1 */ bitset(SSPCON2,0); /* wait until start finished */ while (bittst(SSPCON2,0)); asm(clrwdt); } i2c_Stop() { i2c_idle(); /* initiate stop, PEN=1 */ bitset(SSPCON2,2); /* wait until stop finished */ while (bittst(SSPCON2,2)); asm(clrwdt); } V 0.3
begin START
begin STOP
i2c_PutByte()
i2c_PutByte(unsigned char byte) { i2c_idle();
i2c_FastPutByte deletes this call. SSPBUF holds outgoing data Cleared when transmit finished. Cleared when ACK received.
/* write data */ SSPBUF = byte; /* wait until finished */ while(bittst(SSPSTAT,2)); asm(clrwdt); /* wait for acknowledge */ while(bittst(SSPCON2,6)); asm(clrwdt); }
If device does not respond with ACK bit, then Watchdog timer will expire.
V 0.3 39
i2c_WriteTo
i2c_WriteTo(unsigned char addr) { /* first, send start */ i2c_Start(); SSPBUF = addr; /* write data */ /* wait until finished */ while(bittst(SSPSTAT,2)); asm(clrwdt); /* wait for acknowledge */ while(bittst(SSPCON2,6)); asm(clrwdt);
SSPBUF holds outgoing data Cleared when transmit finished. Cleared when ACK received.
If device does not respond with ACK bit, then Watchdog timer will expire.
V 0.3 40
i2c_GetByte()
unsigned char i2c_GetByte() { unsigned char byte;
Enable receive
while(bittst(SSPCON2,3));
while (!(bittst(SSPSTAT,0))); byte = SSPBUF; return(byte); } /* read data */
Get data
V 0.3 41
i2c_GetByte() gets reads one byte i2c_WriteTo() initiates a start condition first, then sends one byte.
i2c_WriteTo() used to send the first byte of a transaction i2c_PutByte or i2c_FastPutByte used to send remaining bytes of transaction.
V 0.3 42
An I2C Transaction
An I2C transaction is an exchange of data between the PIC and an external device over the I2C bus. All transactions use the calls: i2c_Writeto() Start Transaction i2c_Start() combines these Send device i2c_PutByte() two actions address Read/Write Data
End Transaction
i2c_Stop()
V 0.3
Problem: While writing bytes to serial EEPROM, more bytes are arriving!!! Solution: Use two buffers! Second buffer captures data while first buffer data written to EEPROM.
V 0.3 44
main()
ISR
EEPROM
When main() sees that buffer is full, tells ISR to swap buffers, then empties the full buffer. Empty buffer by writing contents to EEPROM, no longer need data after written to EEPROM. V 0.3
Buffer A
Interrupt Service Routine (ISR) fills data buffer, sets flag when data buffer is full. ISR is invoked for each new input character.
45
Two Buffers
ISR Flowchart
0
Active buffer?
While writing data to EEPROM from one buffer, use other buffer to receive incoming data. ISR sets write_flag to indicate to main() that 64 bytes has been received, and that block_mem_write() should be called. active_buffer flag set by main() to indicate what buffer the ISR uses for byte storage.
46
64 bytes?
64 bytes?
yes
Write_flag = 1
yes
Write_flag = 1
At least one of the 64 byte buffers MUST be in bank1, not enough room for both in bank0. V 0.3
main() flowchart
0
Active buffer = 1
Page write buff0
Active buffer = 0
Page write buff1
V 0.3
47
Serial EEPROM
Sequential, Random Read operations Random, Block Write operations
V 0.3
48