programming avr i2c interface _ embedded projects from around the web

11
Embedded projects from around the web MCU project everyday Home AVR Tutorials ARM Cortex Tutorials Contact Form Ads Programming AVR I2C interface January 8, 2012 | By admin In AVR Tutorial | I2C (also referred as IIC or TWI) is widely used interface in embedded applications. Two wire bus initially was used by Philips and become a standard among chip vendors. I2C bus consists of two lines called Serial Data Line (SDA) and Serial Clock Line (SCL). Communication is relatively fast and short distance mainly used to communicate between sensors, RTC, EEPROM, LCD. I2C protocol allows up to 128 devices connected to those two lines where each of them has unique address. Communication between devices is master and slave based. Master generates clock signal, initiates and terminates data transfer. From electrical point of view I2C devices use open drain (open collector) pins. In order to operate correctly SDA and SCL lines require pull up resistors. Typically 4.7kΩ resistors are used. Each communication is initiated by START signal and finished by STOP. These are always generated by master. START and STOP signals are generated by pulling SDA line low while SCL line is high. In other cases when data is transferred data line must be stable during clock high and can be changed when clock is low:

Upload: suhaila38

Post on 08-Nov-2014

101 views

Category:

Documents


6 download

DESCRIPTION

Atmega I2c

TRANSCRIPT

Page 1: Programming AVR I2C Interface _ Embedded Projects From Around the Web

Embedded projects from around the web

MCU project everyday

Home

AVR Tutorials

ARM Cortex Tutorials

Contact Form

Ads

Programming AVR I2C interface

January 8, 2012 | By admin In AVR Tutorial |

I2C (also referred as IIC or TWI) is widely used interface in embedded applications. Two wire bus initiallywas used by Philips and become a standard among chip vendors. I2C bus consists of two lines called Serial

Data Line (SDA) and Serial Clock Line (SCL). Communication is relatively fast and short distance mainly

used to communicate between sensors, RTC, EEPROM, LCD. I2C protocol allows up to 128 devices

connected to those two lines where each of them has unique address. Communication between devices ismaster and slave based. Master generates clock signal, initiates and terminates data transfer.

From electrical point of view I2C devices use open drain (open collector) pins. In order to operate correctly

SDA and SCL lines require pull up resistors. Typically 4.7kΩ resistors are used.

Each communication is initiated by START signal and finished by STOP. These are always generated by

master. START and STOP signals are generated by pulling SDA line low while SCL line is high. In other

cases when data is transferred data line must be stable during clock high and can be changed when clock is

low:

Page 2: Programming AVR I2C Interface _ Embedded Projects From Around the Web

Bus is considered to be busy between START and STOP signals. So if there are more than one master eachof them has to wait until bus is freed by current master with STOP signal.

I2C communication packet consists of several parts:

START signal;

Address packet – seven address bits lead by data direction bit (read or write) + acknowledge bit;

Data packet – eight data bits + acknowledge bit;

STOP signal.

Acknowledge bit is a ninth bit of every byte sent. Receiver always has to confirm successful receive with

ACK by pulling SDA low or in case receiver cannot accept data it will leave SDA high (NACK) so mastercould stop transmitting and do other scenario if needed.

I2C devices can work in four different modes:

1. Master Transmitter – initiates transfer sends data to slave device;

2. Master Receiver – initiates transfer reads data from slave device;3. Slave Transmitter – waits for master request and then sends data;

4. Slave Receiver – waits for master transmission and accepts data.

It is worth mention that I2C interface supports multi-master transmission. It doesn’t mean that all master areable to transmit data at same time. As matter of fact each master must wait for current transmission to be

finished and then can initiate transfer. It may be situation when multiple masters tries to initiate transfer. In thiscase so called arbitration happens where each transmitter check level of bus signal and compares it withexpected. If master loses arbitration it must leave bus immediately and/or switch to slave mode.

Bursting multiple bytes

I2C can send and receive multiple bytes inside single packet. This is handy for instance to write or read

memory. For instance we need to read 3 bytes from EEPROM memory address 0x0F. Say that EEPROMslave address is 0×1111000. This is how whole reading process would look:

Note that first master has to write in order to select initial memory address. Then send start signal again in

order to initiate master read mode and then after reading of all bytes is done free line by sending stop signal.

AVR I2C registers

AVR microcontroller is using TWI (Two Wire Interface) nomenclature when talking about I2C. So all

Page 3: Programming AVR I2C Interface _ Embedded Projects From Around the Web

registers are named as TWI.

First important register is bit rate register TWBR. It is used to scale down CPU frequency in to SCL.Additionally there are two bits (TWPS1 and TWPS2) in status register TWSR to prescale SCL frequency

with values 1, 4, 16 and 64. You can find formula in datasheet that is used to calculate SCL end frequency:

As usually there is control register TWCR which has a set of bits that are used to enable TWI interrupt, TWI

enable, Start, Stop.

Status register TWSR holds earlier mentioned prescaller bits but its main purpose to sense I2C bus statuswith TWS[7:3] bits. TWDR is data register which is used to hold next byte to transmit or received byte.

TWAR and TWARM register are used when AVR works as I2C slave.

Example of using I2C in AVR

As example we are going to interface old good 24C16 I2C EEPROM chip to Atmega328P.

As you can see connection is simple – only SDA and SCL lines has to be connected. For different EEPROM

capacities you may need to connect A0, A1 and A2 pins to GND or pull high in order to set device address.

24C16 doesn’t use these pins for addressing chip. We leave them open. For demonstration I am using

Arduino328P board which is used as general AVR test board.

Page 4: Programming AVR I2C Interface _ Embedded Projects From Around the Web

You can use any other AVR development board to test this example. If you have Arduino board laying

arround I suggest not to clear original bootloader by writing hex with some ISP adapter, but use built in

bootloader to upload hex. Download http://russemotto.com/xloader/ program that communicates tobootloader so as from Arduino IDE:

Hardware is really simple lets head to software writing.

For debugging purposes USART is used which was discussed in earlier tutorials. So we are gonna set it to

9600 baud and use as library usart.h.

In order to have nice control of I2C interface lets split whole process in to multiple functions. First of all we

need to initialize TWI (I2C):

12345

void TWIInit(void){ //set SCL to 400kHz TWSR = 0x00; TWBR = 0x0C;

Page 5: Programming AVR I2C Interface _ Embedded Projects From Around the Web

so we set bit rate register to 0x0C value which sets SCL to 400kHz. We don’t need any additionalprescallers so set TWSR to 0. And finally we simply enable TWI by setting TWEN bit to “1”.

Next we take care of TWIStart and TWIStop functions that generate start and stop signals.

For start we need to set TWSTA and for stop TWSTO bits along with TWINT and TWEN bits. After start

signal is sent we need to wait for status (until TWINT resets to zero).

Another function is TWIWrite:

it writes data byte to TWDR register which is shifted to SDA line. It is important to wait for transmission

complete within while loop. After which status can be read from status register TWSR.

Reading is done in similar way. I have wrote two functions where one transmits ACK signal after byte

transfer while another doesn’t:

And finally last function we gonna use is reading status:

678

//enable TWI TWCR = (1<<TWEN);}

12345678910

void TWIStart(void){ TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); while ((TWCR & (1<<TWINT)) == 0);}//send stop signalvoid TWIStop(void){ TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);}

123456

void TWIWrite(uint8_t u8data){ TWDR = u8data; TWCR = (1<<TWINT)|(1<<TWEN); while ((TWCR & (1<<TWINT)) == 0);}

12345678910111213

uint8_t TWIReadACK(void){ TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA); while ((TWCR & (1<<TWINT)) == 0); return TWDR;}//read byte with NACKuint8_t TWIReadNACK(void){ TWCR = (1<<TWINT)|(1<<TWEN); while ((TWCR & (1<<TWINT)) == 0); return TWDR;}

12

uint8_t TWIGetStatus(void){

Page 6: Programming AVR I2C Interface _ Embedded Projects From Around the Web

We need to read upper five bits from TWSR register so we simply mask out three lower bits. As we will see

reading status messages is essential part in detecting failures in I2C communication.

After we set up TWI functions we can use them to communicate with 24C16 EEPROM chip. This chip

contains 2048 bytes of EEPROM memory in order to address all bytes 11 byte addressing is used. 24Cxx

chips have four high bit fixed ID which his 0b1010 lower three bits are used for addressing chip memory.This way we avoid sending two bytes for memory addressing memory. But instead we need to split 11 bits in

to fit three high bits that goes to device ID 1, 2, 3 bit locations while rest byte is sent next as normal address

selection.

Having this in mind we can implement EEPROM byte write function:

As you can see after each TWI command we check status. Status codes can be found on AVR datasheet. In

case of communication failure we return ERROR. Using status codes may help to track bugs in program or

detect hardware failures.

34567

uint8_t status; //mask status status = TWSR & 0xF8; return status;}

1234567891011121314151617181920

uint8_t EEWriteByte(uint16_t u16addr, uint8_t u8data){ TWIStart(); if (TWIGetStatus() != 0x08) return ERROR; //select devise and send A2 A1 A0 address bits TWIWrite((EEDEVADR)|(uint8_t)((u16addr & 0x0700)>>7)); if (TWIGetStatus() != 0x18) return ERROR; //send the rest of address TWIWrite((uint8_t)(u16addr)); if (TWIGetStatus() != 0x28) return ERROR; //write byte to eeprom TWIWrite(u8data); if (TWIGetStatus() != 0x28) return ERROR; TWIStop(); return SUCCESS;}

Page 7: Programming AVR I2C Interface _ Embedded Projects From Around the Web

Before writing byte to memory we first start I2C communication then we write device device address

combined with three high memory address bits. Lowest device bit is “0” for write.

Next byte we send is 8 lower memory address bits and then finally if we get ACK by checking status (0×18)

we send data byte. Lastly we end communication by sending Stop signal.

Reading requires a bit more code:

Because first we need to select memory address by writing device ID and rest of memory address as write

command. Then after this we repeat START signal and then we send device address with read command

(last bit set to “1”). If read status is OK we can store received data in to variable. For single byte we don’t

need to send ACK signal just STOP.

Similarly EEPROM page write and read are implemented. 24C16 is divided in to 128 pages of 16 bytes .

Each page start address is located in high 7 bits of address. When writing page be sure to start from first byte

of page because if page address reaches its end address rols-over and writing starts from beginning of page.

This way you can overwrite existing data. I’m just giving my way of page write and read implementation:

12345678910111213141516171819202122232425262728

uint8_t EEReadByte(uint16_t u16addr, uint8_t *u8data){ //uint8_t databyte; TWIStart(); if (TWIGetStatus() != 0x08) return ERROR; //select devise and send A2 A1 A0 address bits TWIWrite((EEDEVADR)|((uint8_t)((u16addr & 0x0700)>>7))); if (TWIGetStatus() != 0x18) return ERROR; //send the rest of address TWIWrite((uint8_t)(u16addr)); if (TWIGetStatus() != 0x28) return ERROR; //send start TWIStart(); if (TWIGetStatus() != 0x10) return ERROR; //select devise and send read bit TWIWrite((EEDEVADR)|((uint8_t)((u16addr & 0x0700)>>7))|1); if (TWIGetStatus() != 0x40) return ERROR; *u8data = TWIReadNACK(); if (TWIGetStatus() != 0x58) return ERROR; TWIStop(); return SUCCESS;}

12345678

uint8_t EEWritePage(uint8_t page, uint8_t *u8data){ //calculate page address uint8_t u8paddr = 0; uint8_t i; u8paddr = page<<4; TWIStart(); if (TWIGetStatus() != 0x08)

Page 8: Programming AVR I2C Interface _ Embedded Projects From Around the Web

910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364

return ERROR; //select page start address and send A2 A1 A0 bits send write command TWIWrite(((EEDEVADR)|(u8paddr>>3))&(~1)); if (TWIGetStatus() != 0x18) return ERROR; //send the rest of address TWIWrite((u8paddr<<4)); if (TWIGetStatus() != 0x28) return ERROR; //write page to eeprom for (i=0; i<16; i++) { TWIWrite(*u8data++); if (TWIGetStatus() != 0x28) return ERROR; } TWIStop(); return SUCCESS;}uint8_t EEReadPage(uint8_t page, uint8_t *u8data){ //calculate page address uint8_t u8paddr = 0; uint8_t i; u8paddr = page<<4; TWIStart(); if (TWIGetStatus() != 0x08) return ERROR; //select page start address and send A2 A1 A0 bits send write command TWIWrite(((EEDEVADR)|(u8paddr>>3))&(~1)); if (TWIGetStatus() != 0x18) return ERROR; //send the rest of address TWIWrite((u8paddr<<4)); if (TWIGetStatus() != 0x28) return ERROR; //send start TWIStart(); if (TWIGetStatus() != 0x10) return ERROR; //select devise and send read bit TWIWrite(((EEDEVADR)|(u8paddr>>3))|1); if (TWIGetStatus() != 0x40) return ERROR; for (i=0; i<15; i++) { *u8data++ = TWIReadACK(); if (TWIGetStatus() != 0x50) return ERROR; } *u8data = TWIReadNACK(); if (TWIGetStatus() != 0x58) return ERROR; TWIStop(); return SUCCESS;}

Page 9: Programming AVR I2C Interface _ Embedded Projects From Around the Web

As you can see when receiving multiple bytes ACK must e generated after each reception. Juster after final

byte ACK is not needed.

In main program you can see EEPROM testing routines that shows everything is working correctly. Test

routine checks single byte write to custom address location and then reading. In terminal screen you can viewif written and read results are same. Also a page write test is done. It writes 16 bytes of information to page 5

and then reads them to different buffer. Then write and read buffers are compared and if both are equal – a

success message is displayed in terminal screen. Main program:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647

#include <stdio.h>#include <avr/io.h>#include <avr/pgmspace.h>#include "usart.h"#include "ee24c16.h"//set stream pointerFILE usart0_str = FDEV_SETUP_STREAM(USART0SendByte, USART0ReceiveByte, _FDEV_SETUP_RW);int main(void){ uint8_t u8ebyte; uint8_t u8erbyte; uint16_t u16eaddress = 0x07F0; uint8_t page = 5; uint8_t i; uint8_t eereadpage[16]; uint8_t eewritepage[16] = { 10, 44, 255, 46, 80, 87, 43, 130, 210, 23, 1, 58, 46, 150, 12, 46 };//Initialize USART0USART0Init();//TWIInit();//assign our stream to standard I/O streamsstdin=stdout=&usart0_str;printf("\nWrite byte %#04x to eeprom address %#04x", 0x58, u16eaddress);if (EEWriteByte(u16eaddress, 0x58) != ERROR){ printf_P(PSTR("\nRead byte From eeprom")); if (EEReadByte(u16eaddress, &u8ebyte) != ERROR) { printf("\n*%#04x = %#04x", u16eaddress, u8ebyte); } else printf_P(PSTR("\nStatus fail!")); } else printf_P(PSTR("\nStatus fail!")); printf_P(PSTR("\nWriting 16 bytes to page 5 "));if(EEWritePage(page, eewritepage) != ERROR){ printf_P(PSTR("\nReading 16 bytes from page 5 ")); if (EEReadPage(page, eereadpage) != ERROR) { //compare send and read buffers for (i=0; i<16; i++) { if (eereadpage[i] != eewritepage[i]) {

Page 10: Programming AVR I2C Interface _ Embedded Projects From Around the Web

You can play around by sending data bytes to custom address locations from terminal screen and test various

data memory locations.

48495051525354555657585960616263646566676869707172737475767778798081

break; } else continue; } if (i==16) printf_P(PSTR("\nPage write and read success!")); else printf_P(PSTR("\nPage write and read fail!")); } else printf_P(PSTR("\nStatus fail!")); }else printf_P(PSTR("\nStatus fail!")); printf_P(PSTR("\nContinue testing EEPROM from terminal!")); while(1) { printf("\nEnter EEPROM address to write (MAX = %u): ", EEMAXADDR); scanf("%u",&u16eaddress); printf("Enter data to write to EEPROM at address %u: ", u16eaddress); scanf("%u",&u8ebyte); printf_P(PSTR("\nWriting...")); EEWriteByte(u16eaddress, u8ebyte); printf_P(PSTR("\nTesting...")); if (EEReadByte(u16eaddress, &u8erbyte) !=ERROR) { if (u8ebyte==u8erbyte) printf_P(PSTR("\nSuccess!")); else printf_P(PSTR("\nFail!")); } else printf_P(PSTR("\nStatus fail!")); //TODO:: Please write your application code }}

Page 11: Programming AVR I2C Interface _ Embedded Projects From Around the Web

Share this:

AVR Studio 5 project files for download [I2CEE.zip]. Have fun!

You may also like -

AVR thermometer uses thermocouple

Usually we see digital temperature sensors in microcontroller projects. They are easy to use as

they connect directly to MCU using one of popular ...

Tiny-USB - cheap and easy USB for small projects

Small microcontrollers like Attiny45 don't have USB interface. And actually they aren't meant

for such connectivity. But if you find it necessary to have ...

Bus pirate – hacker's must have tool

Actually you don't have to be a real hacker to use this tool. Anyone involved in digital

electronics may find this to be real ...

Adding Nokia 1100 LCD to your microcontroller project