Synchronous Serial IO Serial Peripheral Interface (SPI) Three wire interface: SCK (clock), SDO (serial data out), SDI (Serial Data Out) • Send a separate clock line with data – SPI (serial peripheral interface) protocol – I2C (or I2C) protocol SDO, SDI is uni-directional, but communication can occur in both directions at same time (full-duplex). • 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) From: The Quintessential PIC Microcontroller, Sid Katzen V 0.1 1 V 0.1 2 I2C (Inter-Integrated-Circuit) Bus Multi-Receiver I2C is a two wire serial interface. Each peripheral must have a SELECT line that is asserted that indicates this serial communication is for them. 16F873 Vdd 10K Microchip 24LC515 SCL SCL A2 Vdd A1 10K SDA SDA SCL SCL – clock line SDA – data (bidirectional) V 0.1 3 I2C Features A0 V 0.1 4 Example: I2C Serial EEPROM • Multiple receivers do not require separate select lines as in SPI Will use the Microchip 24LC515 Serial EEPROM to discuss I2C operation. – 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 The 24LC515 is a 64K x 8 memory. This would require 16 address lines, and 8 data lines if a parallel interface was used, which would exceed the number of available IO pins our PIC16873!!! • SDA (data line) is bidirectional, communication is half duplex • SDA, SCLK are open-drain, require external pullups Putting a serial interface on a memory device lowers the required pin count. Reduces speed since data has to be sent serially, but now possible to add significant external storage to a low pincount micro controller. – Allows multiple bus masters (will discuss this more later). V 0.1 A2 A1 SDA From: The Quintessential PIC Microcontroller, Sid Katzen A0 5 V 0.1 6 1 IDLE: SCL, SDA high. I2C Device Addressing Each I2C device has either a 7-bit or 10-bit device address. I2C Transmission We will use an I2C EEPROM and an I2C DAC (Digital-toAnalog Converter, MAX517) in lab. Both of these devices have a 7-bit address. Upper four bits are assigned by device manufacturer and are hardcoded in the device. Lower three bits are used in different ways by manufacturer. LC515 control byte (contains slave address): Microchip 24LC515 R/W = 1 SCL A2 7654 3 2 1 0 for read, 0 1 0 1 0 B0 A1 A0 R/W for write. A1 ‘B0’ is block select (upper/lower 32K). A1, A0 SDA A0 are chip selects, four devices on one bus. V 0.1 7 START: high to low transition on SDA while SCL high. Acknowledgement Valid data: While clock is high, data valid and stable. V 0.1 Data changes while clock is low. STOP: low to high transition on SDA while SCL high. 8 Byte Write Operation 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. • Byte Write: used to write one byte – Send Control Byte, High address byte, low address byte, data. – After data is sent, takes 5 ms for write to complete – SLOOOOOWWWWWW.... Most common cause of ACK error – incorrect device address. ‘0’ indicates write mode. V 0.1 9 V 0.1 Page Write Operation ‘X’ because block select bit chooses high or low 32K. 10 Speed Comparison • Send a block of 64 bytes, then perform write • Assume a 400 Khz I2C bus, 2.5 us clock period (2.5 e-6) • Random write: – Send starting address, followed by 64 bytes – After 64 bytes sent, wait 5 ms for write to complete – Much faster than individual writes – 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!!! Address must be on a page boundary. For page size = 64 = 0x40, starting address must be a multiple of 64. V 0.1 11 V 0.1 12 2 Checking for end-ofwrite Read Operation: Current Address • An internal counter is used to keep track of last address used • A current address read uses this address, only sends the command byte Timing on write is guaranteed to finish after 5 ms. But can end sooner; to determine if write finished use polling method. – Internal counter incremented after read operation ‘1’ indicates read operation No ACK means device is still busy with last write. V 0.1 13 Random Read Operation • 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. Current address read V 0.1 15 PIC 16 I2C Registers V 0.1 16 I2C on the PIC16 • Synchronous Serial Port on PIC implements I2C • Registers are: • Will always use master mode on the PIC16 – SSPCON – control register - we will always set this to 0x28 which enables I2C MASTER mode. – SSPCON1 – control register - used to initiate a START/STOP conditions, indicates if ACK has been received – SSPSTAT – status register – check this to see if byte finished transmitting, or byte has been received – SSPBUF – read/write to this register for data transfer – SSPADD – used to control clock rate V 0.1 14 Sequential Read • Must first set the internal address counter by starting a write operation, but aborting by not sending data byte Aborted random write (address only, no data) V 0.1 ‘no ack’ because slave is driving data back to master. 17 – This means that the PIC will always initiate all I2C bus activity • To set I2C clock rate, write 8-bit value to the SPADD 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 wors V 0.1 18 3 i2cmsu.c Subroutines Lab #8: Read/Write to Serial EEPROM • 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 • Lab #8 has you read/write to a Serial EEPROM via the I2C bus • The files i2cmsu.h, i2cmsu.c define interface subroutines for the I2C bus – This file also has subroutines for random read/write, block read/write for the serial eeprom • The file memblk.c tests uses the subroutines to read/write data to the serial EEPROM. V 0.1 19 V 0.1 Random Read: memread(cmd,addr) memread(cmd,addr) cmd is unsigned char that has EEprom i2c device address i2c_Writeto (write_cmd) i2c_PutByte (address_hibyte) Does a random read of EEPROM /* random read */ unsigned char mem_read(unsigned char cmd,int addr) { unsigned char hbyte, lbyte, val; addr is unsigned char is memory address within EEprom i2c_PutByte (address_lobyte) i2c_Stop() 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); Set internal address counter. i2c_Writeto (read_cmd) Read byte from current address i2c_GetByte () 20 } i2c_Stop() V 0.1 i2c_Writeto (write_cmd) 21 V 0.1 Page (Block) Write Page Write i2c_PutByte (address_hibyte) /* block write */ void block_mem_write(unsigned char cmd,int addr, char *buf) unsigned char hbyte, lbyte, val; set starting address. i2c_PutByte (address_lobyte) Send 64 bytes. i = 64? //DelayMs(5); } i2c_Stop() V 0.1 { 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(); i = 0; i2c_PutByte (); i++ 22 23 ms. // no need to delay, will not be back here for 5 ms FastPutByte does not check for idle condition before sending data. V 0.1 24 4 i2c_Writeto (write_cmd) Block Read in C Block Read i2c_PutByte (address_hibyte) set starting address. i2c_PutByte (address_lobyte) i2c_Stop() Send read command i2c_Writeto (read_cmd) Use sequential read to get 64 bytes. i2c_GetByte (); i++ i = 64? no i2c_doAck(); Sequential read can read up to 32K, block size of 64 bytes was arbitrary 25 yes i2c_Stop() V 0.1 /* 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.1 EEPROM Test: memblk.c EEPROM Test: memblk.c memblk.c tests serial EEPROM by doing block read/write to EEPROM. printf("Enter 'w'(write), or 'r' (read): "); pcrlf (); inchar=getch(); Read 64 bytes from if (inchar == 'w') { address 0, store in buffer. // see previous slide }else if (inchar == 'r') { printf("###Reading first 64 memory locations, via a block read"); pcrlf (); block_mem_read(ROM,0,buf); for (i=0;i<64;i++) { printf("Read %d, val: %x (%c) \n",i,buf[i], buf[i]); pcrlf (); } pcrlf (); Print the 64 bytes read printf("###Mem Read complete"); from EEPROM, hopefully pcrlf (); } they will match what was printf("Enter 'w'(write), or 'r' (read): "); pcrlf (); From main(), ask user for inchar=getch(); if (inchar == 'w') { write or read test. pcrlf (); printf("Capturing next 64 bytes for write to memory"); pcrlf (); Get 64 bytes from serial for (i=0;i<64;i++) { buf[i] = getch(); port, save in buffer. } printf("Block write to memory"); Print bytes before write to pcrlf (); EEPROM. for (i=0;i<64;i++) { printf("Write at %d, val: %x (%c) \n",i,buf[i], buf[i]); pcrlf (); } Write bytes to EEPROM block_mem_write(ROM,0,buf); at address 0. printf("Mem Write complete"); pcrlf (); } elsif... V 0.1 written! 27 V 0.1 i2c_idle() unsigned char byte1; unsigned char byte2; 28 i2c_Start()/i2c_Stop() i2c_Start(){ i2c_idle(); /* initiate start, SEN=1 */ bitset(SSPCON2,0); /* wait until start finished */ while (bittst(SSPCON2,0)); R/W bit, when ‘0’ then transmit is not in progress i2c_idle() { 26 begin START wait for end } do { byte1 = SSPSTAT & 0x04; byte2 = SSPCON2 & 0x1F; }while (byte1 | byte2); return; } // gte R/W bit. Check if lower 5 bits are all ‘0’ indicating that Start, Stop, Acknowledge sequences are all idle. V 0.1 29 i2c_Stop() { i2c_idle(); /* initiate stop, PEN=1 */ bitset(SSPCON2,2); /* wait until stop finished */ while (bittst(SSPCON2,2)); } V 0.1 begin STOP wait for end 30 5 i2c_PutByte() i2c_WriteTo i2c_PutByte(unsigned char byte) { i2c_FastPutByte deletes this call. i2c_idle(); i2c_WriteTo(unsigned char addr) { /* first, send start */ i2c_Start(); SSPBUF = addr; /* write data */ SSPBUF holds outgoing data /* write data */ SSPBUF = byte; /* wait until finished */ while(bittst(SSPSTAT,2)); /* wait until finished */ Cleared when transmit finished. /* wait for acknowledge */ while(bittst(SSPCON2,6)); while(bittst(SSPCON2,6)); Cleared when ACK received. V 0.1 31 Enable receive bitset(SSPCON2,3); /* wait until finished */ while(bittst(SSPCON2,3)); while (!(bittst(SSPSTAT,0))); Will be cleared when finished. Receive buffer should be full Interrupt Service Subroutine 1 Active buffer? 64 bytes? Save char in buff1 no 64 bytes? yes yes Write_flag = 1 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. 33 Two Buffers Char arrival triggers interrupt no Interrupt service routine stores bytes in a buffer. Get data V 0.1 Save char in buff0 Capture 64 bytes from serial port to buffer Page Write of 64 bytes /* read data */ } Write_flag = 1 exit interrupt service At least one of the 64 byte buffers MUST be in bank1, not enough room for both in bank0. V 0.1 32 Lab 9: I2C & Serial EEPROM unsigned char byte; 0 V 0.1 Goal: Capture streaming data from serial port, store to serial EEPROM unsigned char i2c_GetByte() { return(byte); Cleared when ACK received. } i2c_GetByte() byte = SSPBUF; Cleared when transmit finished. while(bittst(SSPSTAT,2)); /* wait for acknowledge */ } i2c_idle(); /* initiate read event */ SSPBUF holds outgoing data 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. 35 V 0.1 34 Streaming Write Loop: main() Streaming Write Loop 0 Write_flag? Wait for interrupt service routine to fill a buffer. 1 0 Active buffer? 1 Active buffer = 1 Active buffer = 0 Page write buff0 Page write buff1 Write_flag = 0 addr = addr + 64 V 0.1 36 6 What do you have know? • Serial Peripheral Interface (SPI) – how is this different from I2C? • I2C Protocol – What is start, stop, ack conditions, purpose of each – How is data sent, received – Device addressing • Serial EEPROM – Sequential, Random Read operations – Random, Block Write operations V 0.1 37 7