Full Version : Captain's I2C EEPROM Example & Schematic
avr >>TECHNICAL & HARDWARE >>Captain's I2C EEPROM Example & Schematic


AVR_Admin- 04-29-2006
Atmel ATmega (ATmega16) I2C EEPROM (E2PROM) Example Schematic and C program

This is an example for connecting a 64kbit serial EEPROM (i.e. 24C64) to an Atmel ATmega16 microcontroller over the I2C bus (I2C = TWI - two wire interface). It also includes a MAX232 serial port level converter for communication with i.e. a PC over the serial port (RS232).


The firmware:

Use avr-gcc or WinAVR to compile it and the Atmel ATmega16 Parallel Port Programmer to program the uC. "make" compiles the firmware, "make load" programs the firmware.

The firmware sends one character ("C") at startup and then returns any character sent via the serial port in the interrupt service routine (ISR). Furthermore it writes 2 bytes to the EEPROM, reads them back and sends them over the serial port.

Linux software (serterm2) for receiving data over the serial port can be found at the PIC - MMC (Multi Media Card) Flash Memory Extension page. Also on this page, the program "ser". It sends three characters ("ABC") via the PC serial port and tries to receive them. These three characters are returned by the MCU via the interrupt service routine.

Furthermore the firmware blinks a LED connected to pin 19 (similar to avrledtest.c at the Atmel ATmega16 Parallel Port Programmer page).

Link to Site: http://www.captain.at/electronic-atmega-eeprom.php

eeprom.c
CODE

/*********************************************
* Chip type           : ATmega16
* Clock frequency     : 2457600Hz
*********************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <inttypes.h>
#include <avr/iom16.h>

#include "e2prom.h"

#define F_OSC 2457600             /* oscillator-frequency in Hz */
#define UART_BAUD_RATE 9600
#define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)

void delay_ms(unsigned short ms) {
unsigned short outer1, outer2;
outer1 = 200;
while (outer1) {
 outer2 = 1000;
 while (outer2) {
  while ( ms ) ms--;
  outer2--;
 }
 outer1--;
}
}

void usart_putc(unsigned char c) {
  // wait until UDR ready
while(!(UCSRA & (1 << UDRE)));
UDR = c;    // send character
}

void uart_puts (char *s) {
//  loop until *s != NULL
while (*s) {
 usart_putc(*s);
 s++;
}
}

void init(void) {
// set baud rate
UBRRH = (uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)>>8);
UBRRL = (uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);
// Enable receiver and transmitter; enable RX interrupt
UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
//asynchronous 8N1
UCSRC = (1<<URSEL)|(3<<UCSZ0);
}

// INTERRUPT can be interrupted
// SIGNAL can't be interrupted
SIGNAL (SIG_UART_RECV) { // USART RX interrupt
unsigned char c;
c = UDR;
usart_putc(c);
}

int main(void) {
char buffer;
char buffer2;
init();
sei();

// send initial character
while(!(UCSRA & (1 << UDRE)));
UDR = 0x43; // "C"
while(!(UCSRA & (1 << UDRE)));
UDR = 0x0d;

eeprom_init();

// write first character to eeprom
// buffer = 0x21; // "!"
buffer = 0x23; // "#"
eeprom_write_byte(10, buffer);
delay_ms(500);

// write second character to e2prom
// buffer = 0x40; // "@"
buffer = 0x24; // "$"
eeprom_write_byte(11, buffer);
delay_ms(500);

// read first character from eeprom
eeprom_read_byte(10, &buffer2);
while(!(UCSRA & (1 << UDRE)));
UDR = buffer2;
while(!(UCSRA & (1 << UDRE)));
UDR = 0x0d;
 
// read second character from e2prom
eeprom_read_byte(11, &buffer2);
while(!(UCSRA & (1 << UDRE)));
UDR = buffer2;
while(!(UCSRA & (1 << UDRE)));
UDR = 0x0d;

// blink LED
// enable  PD5 as output
DDRD |= (1<<PD5);
while (1) {
 // PIN5 PORTD set -> LED on
 PORTD |= (1<<PD5);
 delay_ms(500);
 // PIN5 PORTD clear -> LED off
 PORTD &= ~(1<<PD5);
 delay_ms(500);
}
return 0;
}


e2prom.c

CODE

// EEPROM functions
// NOTE: These are the functions only for a 24C64!
// If you use a smaller/bigger E2PROM, the I2C communication works different!
// Check the datasheet of the EEPROM you'd like to use and modify the
// functions. i.e. smaller I2C EEPROM's already have the high-byte address
// information in the inital byte (which is 0xa0 or 0xa1 in this case).

#include "e2prom.h"

uint8_t twst;

void eeprom_init(void) {
 /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
#if defined(TWPS0)
 /* has prescaler (mega128 & newer) */
 TWSR = 0;
#endif
 TWBR = 6;
 
}

int eeprom_read_byte(uint16_t eeaddr, char *buf)
{
 uint8_t n = 0;

restart:
 if (n++ >= MAX_TRIES)
   return -1;

begin:
// send start cond.
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if ( (TW_STATUS != TW_REP_START) && (TW_STATUS != TW_START)) {
 return -1;
}

// send 0xa0
// 0xa0 = 1010 000 0
// 4 bits:   <a..device-indentifier>
// 3 bits:   <device-address set with chip pins>
// last bit: <0..write>
TWDR = 0xa0;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_SLA_NACK) goto restart;
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if (TW_STATUS != TW_MT_SLA_ACK) goto error;

// send low 8 bits of eeaddr
TWDR = eeaddr;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_DATA_NACK) goto restart;
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if (TW_STATUS != TW_MT_DATA_ACK) goto error;

// send high 8 bits of eeaddr
TWDR = eeaddr << 8;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_DATA_NACK) goto restart;
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if (TW_STATUS != TW_MT_DATA_ACK) goto error;

// send start cond.
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if ( (TW_STATUS != TW_REP_START) && (TW_STATUS != TW_START)) {
 return -1;
}

// send 0xa1
// 0xa0 = 1010 000 1
// 4 bits:   <a..device-indentifier>
// 3 bits:   <device-address set with chip pins>
// last bit: <1..read>
TWDR = 0xa1;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MR_SLA_NACK) goto quit;
if (TW_STATUS == TW_MR_ARB_LOST) goto begin;
if (TW_STATUS != TW_MR_SLA_ACK) goto error;

// start read transmission
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));

switch ((twst = TW_STATUS)) {
 case TW_MR_DATA_NACK:
  // FALLTHROUGH
 case TW_MR_DATA_ACK:
  *buf = TWDR;
  break;
 default:
  goto error;
}

quit:
//stop condition
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
return 1;

error:
//stop condition
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
return -1;
}

int eeprom_write_byte(uint16_t eeaddr, char buf) {
 uint8_t n = 0;

restart:
 if (n++ >= MAX_TRIES)
   return -1;
begin:

// start cond.
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if ( (TW_STATUS != TW_REP_START) && (TW_STATUS != TW_START)) {
 return -1;
}

// send 0xa0
// 0xa0 = 1010 000 0
// 4 bits:   <a..device-indentifier>
// 3 bits:   <device-address set with chip pins>
// last bit: <0..write>
  TWDR = 0xa0;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_SLA_NACK) goto restart;
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if (TW_STATUS != TW_MT_SLA_ACK) goto error;

// send low 8 bits of eeaddr
TWDR = eeaddr;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_DATA_NACK) goto quit;
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if (TW_STATUS != TW_MT_DATA_ACK) goto error;

// send high 8 bits of eeaddr
TWDR = eeaddr << 8;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS == TW_MT_DATA_NACK) goto quit;
if (TW_STATUS == TW_MT_ARB_LOST) goto begin;
if (TW_STATUS != TW_MT_DATA_ACK) goto error;

// put byte into data register and start transmission
TWDR = buf;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
if (TW_STATUS != TW_MT_DATA_ACK) goto error;

quit:
// send stop condition
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
return 1;

error:
// send stop condition
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
return -1;
}

e2prom.h

#include <compat/twi.h>

#define MAX_TRIES 100

int eeprom_read_byte(uint16_t eeaddr, char *buf);
int eeprom_write_byte(uint16_t eeaddr, char buf);
void eeprom_init(void);


Makefile:

CODE

MCU=atmega16
CC=avr-gcc
OBJCOPY=avr-objcopy
# optimize for size:
CFLAGS=-g -mmcu=$(MCU) -Wall -Wstrict-prototypes -Os -mcall-prologues
#-------------------
all: e2prom.o eeprom.hex
#-------------------
eeprom.hex : eeprom.out
$(OBJCOPY) -R .eeprom -O ihex eeprom.out eeprom.hex
eeprom.out : eeprom.o
$(CC) $(CFLAGS) -o eeprom.out -Wl,-Map,eeprom.map eeprom.o e2prom.o
eeprom.o : eeprom.c
$(CC) $(CFLAGS) -Os -c eeprom.c
e2prom.o : e2prom.c
$(CC) $(CFLAGS) -Os -c e2prom.c


# you need to erase first before loading the program.
# load (program) the software into the eeprom:
load: eeprom.hex
uisp -dlpt=/dev/parport0 --erase  -dprog=dapa -dpart=ATmega16
uisp -dlpt=/dev/parport0 --upload if=eeprom.hex -dprog=dapa -dpart=ATmega16 -v=3 --hash=32
#-------------------
clean:
rm -f *.o *.map *.out
#-------------------



Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.