Interfejs EEPROM z mikrokontrolerem PIC – oparty na I2C

Interfejs EEPROM z mikrokontrolerem PIC – oparty na I2C

W artykule widzimy interfejs EEPROM z mikrokontrolerami PIC za pomocą protokołu I2C. W prawdziwym świecie mikrokontrolery mają ograniczoną ilość pamięci. Czasami ograniczona ilość pamięci staje się dużym problemem i blokuje rozwój projektu.

Każdy mikrokontroler zapewnia elastyczność podłączenia zewnętrznej pamięci do przechowywania wymaganych danych. Dane te mogą być przechowywane w pamięci za pomocą I2C, SPI lub innego protokołu komunikacyjnego.

Tutaj wyjaśniam przykład, w którym będę przechowywać pojedynczy bajt w EEPROM-ie, a następnie odczytam zapisany bajt z EEPROM-u. W celu weryfikacji porównam odczytane bajty z bajtami, które zostały zapisane, i przełączę diodę LED, jeśli oba bajty są równe.

Co to jest EEPROM?

EEPROM jest pamięcią nieulotną, co oznacza, że ​​może przechowywać dane na stałe bez żadnego zasilania. EEPROM to skrót od Electricly Erasable Programmable Read-Only Memory. Możemy usunąć dane EEPROM za pomocą gumki UV.

EEPROM jest wolniejszy niż pamięć flash. W pamięci flash możemy skasować cały sektor na raz, ale w przypadku EEPROM możemy skasować pojedynczy bajt na raz.
Niektóre mikrokontrolery ograniczają niewielką ilość EEPROM do przechowywania dodatkowych bajtów, które są wymagane w programie, na przykład nazwy użytkownika i hasła.

Jest wielu dostawców, którzy produkują różne rodzaje EEPROM-ów, ale w tym artykule omówię tylko 24lc64 (produkowane przez mikrochip). 24lc64 (EEPROM) jest podłączony do mikrokontrolera za pośrednictwem protokołu magistrali I2C.

Istnieje kilka ważnych cech 24lc64 (EEPROM)

  • Zorganizowane jako 8 bloków 8kbit (64 Kbit).
  • Interfejs dwuprzewodowy (I2C).
  • Bufor zapisu strony do 32 bajtów
  • Możemy na nim czytać/pisać około milion razy.
  • Może przechowywać dane przez ponad 200 lat.

Bajty kontrolne 24lc64 (EEPROM)

Przed przystąpieniem do programowania musimy pamiętać o adresowaniu 24lc64 (EEPROM). Zawiera bajt kontrolny, który jest wysyłany przez mastera w komunikacji I2C, po którym następuje warunek startu. Bajt kontrolny ogranicza kod kontrolny, wybór chipa i bity odczytu/zapisu.

Kod kontrolny dla 24lc64 to „1010” dla operacji odczytu i zapisu. Rozmiar wyboru chipa wynosi 3 bity (A2, A1, A0), co pozwala użytkownikowi podłączyć maksymalnie 8 urządzeń do magistrali I2c. Ostatni bit bajtu kontrolnego to bity odczytu/zapisu, ten bit to zero dla operacji zapisu i jeden dla operacji odczytu.

Podstawowe wymagania dotyczące połączenia EEPROM z PIC

Znajomość protokołu I2C

Jeśli chcesz połączyć EEPROM z mikrokontrolerem, musisz mieć dobrą znajomość protokołu I2C. Jeśli nie znasz protokołu I2C, nie musisz się martwić, jest to bardzo prosty protokół komunikacji szeregowej. Więc to moja rada, zanim przeczytasz dalszą część tego artykułu, zapoznaj się z protokołem I2C.

Mikrokontroler

W naszym przypadku mikrokontroler pracuje tutaj jako master i rozpoczyna komunikację w celu wykonania operacji odczytu i zapisu na EEPROM.

Pamięć EEPROM

Urządzenie przechowujące służy do przechowywania stałych danych, takich jak informacje o użytkowniku (nazwa użytkownika, hasło). Tutaj używam chipa 24lc64 do przechowywania danych.
Ma 8 bloków 8kbit (8*1024 bajtów przechowywania danych). Chip ma lokalizację pamięci, każda lokalizacja ma unikalny adres od (0 do 8191), a każda lokalizacja jest traktowana jako komórka.

Na przykład po zapisaniu danych „K” w lokalizacji 0x0001, jeśli odczytasz lokalizację 0x0001 otrzymasz „K”. Najważniejszą rzeczą jest zapamiętanie, że każda komórka 24lc64 może przechowywać 8 bitów danych (0 -255). Więc jeśli chcesz przechowywać większe dane, musisz użyć więcej niż jednej komórki.

C Program do współpracy EEPROM z mikrokontrolerami PIC:

W tym programie C uczymy się, jak zapisać bajt w zewnętrznej pamięci EEPROM (24lc64) za pomocą protokołu I2C i jak odczytać zapisany bajt z EEPROM za pomocą protokołu I2C. Tutaj piszę „Amlendrak” w pamięci zewnętrznej i odczytuję go z powrotem i wyświetlam na alfanumerycznym wyświetlaczu LCD 16×2.

/* Name     : main.c
*  Purpose  : Main file for internal EEPROM access code for PIC16F877.
*  Author   : Amlendra Kumar
*  Website  : https://aticleworld.com
*/
#include<htc.h>

// Configuration word for PIC16F877A
__CONFIG( FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF & BOREN_ON
          & LVP_OFF & CPD_OFF & WRT_OFF & DEBUG_OFF);



// Define CPU Frequency
// This must be defined, if __delay_ms() or
// __delay_us() functions are used in the code
#define _XTAL_FREQ   20000000


// Define Pins for LCD
#define LCD_E    			RB0     // Enable pin for LCD
#define LCD_RS	 			RB1     // RS pin for LCD
#define LCD_Data_Bus_D4		RB4		// Data bus bit 4
#define LCD_Data_Bus_D5		RB5		// Data bus bit 5
#define LCD_Data_Bus_D6		RB6		// Data bus bit 6
#define LCD_Data_Bus_D7		RB7		// Data bus bit 7

// Define Pins direction register
#define LCD_E_Dir     			TRISB0
#define LCD_RS_Dir   	 		TRISB1
#define LCD_Data_Bus_Dir_D4   	TRISB4
#define LCD_Data_Bus_Dir_D5     TRISB5
#define LCD_Data_Bus_Dir_D6  	TRISB6
#define LCD_Data_Bus_Dir_D7 	TRISB7

// Constants
#define E_Delay       500


// Function Declarations
void WriteCommandToLCD(unsigned char);
void WriteDataToLCD(char);
void InitLCD(void);
void WriteStringToLCD(const char*);
void ClearLCDScreen(void);


// Define i2c pins
#define SDA			RC4				// Data pin for i2c
#define SCK			RC3				// Clock pin for i2c
#define SDA_DIR		TRISC4			// Data pin direction
#define SCK_DIR		TRISC3			// Clock pin direction

// Define i2c speed
#define I2C_SPEED	100				// kbps

//Function Declarations
void InitI2C(void);
void I2C_Start(void);
void I2C_ReStart(void);
void I2C_Stop(void);
void I2C_Send_ACK(void);
void I2C_Send_NACK(void);
bit  I2C_Write_Byte(unsigned char);
unsigned char I2C_Read_Byte(void);



// Define 24LC64 i2c device address
#define Device_Address_24LC64_EEPROM	0xA0


// Function Declarations
void Write_Byte_To_24LC64_EEPROM(unsigned int, unsigned char);
unsigned char Read_Byte_From_24LC64_EEPROM(unsigned int);
void Write_Page_To_24LC64_EEPROM(unsigned int, unsigned char*, unsigned char);
void Read_Bytes_From_24LC64_EEPROM(unsigned int, unsigned char*, unsigned int);



int main(void)
{
    unsigned char RxByte = 0;
    unsigned char TxArray[9] = "Amlendra";
    unsigned char RxArray[9] = { 0 };

    // Initialize LCD
    InitLCD();

    // Initialize i2c module
    InitI2C();

    // Write 'd' at 0x0001 address in EEPROM
    Write_Byte_To_24LC64_EEPROM(0x0001, 'K');
    // Read from 0x0001 address	from EEPROM
    RxByte = Read_Byte_From_24LC64_EEPROM(0x0001);

    // Write 9 bytes of TxArray starting from 0x0020 address in EEPROM
    Write_Page_To_24LC64_EEPROM(0x0020, TxArray, 9);
    // Read 9 bytes starting from 0x0020 address in EEPROM, save these bytes into RxArray
    Read_Bytes_From_24LC64_EEPROM(0x0020, RxArray, 9);

    // Display received char array on LCD display
    WriteStringToLCD(RxArray);

    // Display received char on LCD display
    WriteDataToLCD(RxByte);

    while(1)
    {
    }
    return 0;
}



//Function related to LCD

void ToggleEpinOfLCD(void)
{
    LCD_E = 1;                // Give a pulse on E pin
    __delay_us(E_Delay);      // so that LCD can latch the
    LCD_E = 0;                // data from data bus
    __delay_us(E_Delay);
}


void WriteCommandToLCD(unsigned char Command)
{
    LCD_RS = 0;				  // It is a command

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= (Command&0xF0);  // Write Upper nibble of data
    ToggleEpinOfLCD();		  // Give pulse on E pin

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= ((Command<<4)&0xF0); // Write Lower nibble of data
    ToggleEpinOfLCD();		  // Give pulse on E pin
}


void WriteDataToLCD(char LCDChar)
{
    LCD_RS = 1;				  // It is data

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= (LCDChar&0xF0);  // Write Upper nibble of data
    ToggleEpinOfLCD();		  // Give pulse on E pin

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= ((LCDChar<<4)&0xF0); // Write Lower nibble of data
    ToggleEpinOfLCD();		  // Give pulse on E pin
}


void InitLCD(void)
{
    // Firstly make all pins output
    LCD_E  		 		 = 0;   // E  = 0
    LCD_RS  	 		 = 0;   // RS = 0
    LCD_Data_Bus_D4		 = 0;  	// Data bus = 0
    LCD_Data_Bus_D5		 = 0;  	// Data bus = 0
    LCD_Data_Bus_D6		 = 0;  	// Data bus = 0
    LCD_Data_Bus_D7		 = 0;  	// Data bus = 0
    LCD_E_Dir    		 = 0;   // Make Output
    LCD_RS_Dir    	 	 = 0;   // Make Output
    LCD_Data_Bus_Dir_D4  = 0;   // Make Output
    LCD_Data_Bus_Dir_D5  = 0;   // Make Output
    LCD_Data_Bus_Dir_D6  = 0;   // Make Output
    LCD_Data_Bus_Dir_D7  = 0;   // Make Output

    ///////////////// Reset process from datasheet //////////////
    __delay_ms(40);

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= 0x30;			  // Write 0x3 value on data bus
    ToggleEpinOfLCD();		  // Give pulse on E pin

    __delay_ms(6);

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= 0x30;			  // Write 0x3 value on data bus
    ToggleEpinOfLCD();		  // Give pulse on E pin

    __delay_us(300);

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= 0x30;			  // Write 0x3 value on data bus
    ToggleEpinOfLCD();		  // Give pulse on E pin

    __delay_ms(2);

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= 0x20;			  // Write 0x2 value on data bus
    ToggleEpinOfLCD();		  // Give pulse on E pin

    __delay_ms(2);
    /////////////// Reset Process End ////////////////
    WriteCommandToLCD(0x28);    //function set
    WriteCommandToLCD(0x0c);    //display on,cursor off,blink off
    WriteCommandToLCD(0x01);    //clear display
    WriteCommandToLCD(0x06);    //entry mode, set increment
}


void WriteStringToLCD(const char *s)
{
    while(*s)
    {
        WriteDataToLCD(*s++);   // print first character on LCD
    }
}


void ClearLCDScreen(void)       // Clear the Screen and return cursor to zero position
{
    WriteCommandToLCD(0x01);    // Clear the screen
    __delay_ms(2);              // Delay for cursor to return at zero position
}


//Function related to I2C

// Function Purpose: Configure I2C module
void InitI2C(void)
{
    SDA_DIR = 1;		// Make SDA and
    SCK_DIR = 1;		// SCK pins input

    SSPADD  = ((_XTAL_FREQ/4000)/I2C_SPEED) - 1;
    SSPSTAT = 0x80;		// Slew Rate control is disabled
    SSPCON  = 0x28;		// Select and enable I2C in master mode
}


// Function Purpose: I2C_Start sends start bit sequence
void I2C_Start(void)
{
    SEN = 1;			// Send start bit
    while(!SSPIF);		// Wait for it to complete
    SSPIF = 0;			// Clear the flag bit
}


// Function Purpose: I2C_ReStart sends start bit sequence
void I2C_ReStart(void)
{
    RSEN = 1;			// Send Restart bit
    while(!SSPIF);		// Wait for it to complete
    SSPIF = 0;			// Clear the flag bit
}


//Function : I2C_Stop sends stop bit sequence
void I2C_Stop(void)
{
    PEN = 1;			// Send stop bit
    while(!SSPIF);		// Wait for it to complete
    SSPIF = 0;			// Clear the flag bit
}



//Function : I2C_Send_ACK sends ACK bit sequence
void I2C_Send_ACK(void)
{
    ACKDT = 0;			// 0 means ACK
    ACKEN = 1;			// Send ACKDT value
    while(!SSPIF);		// Wait for it to complete
    SSPIF = 0;			// Clear the flag bit
}


//Function : I2C_Send_NACK sends NACK bit sequence
void I2C_Send_NACK(void)
{
    ACKDT = 1;			// 1 means NACK
    ACKEN = 1;			// Send ACKDT value
    while(!SSPIF);		// Wait for it to complete
    SSPIF = 0;			// Clear the flag bit
}


// Function Purpose: I2C_Write_Byte transfers one byte
bit I2C_Write_Byte(unsigned char Byte)
{
    SSPBUF = Byte;		// Send Byte value
    while(!SSPIF);		// Wait for it to complete
    SSPIF = 0;			// Clear the flag bit

    return ACKSTAT;		// Return ACK/NACK from slave
}


// Function Purpose: I2C_Read_Byte reads one byte
unsigned char I2C_Read_Byte(void)
{
    RCEN = 1;			// Enable reception of 8 bits
    while(!SSPIF);		// Wait for it to complete
    SSPIF = 0;			// Clear the flag bit

    return SSPBUF;		// Return received byte
}


//Function related to eeprom

// Function Purpose: Write_Byte_To_24LC64_EEPROM writes a single byte on given address
// Address can have any value fromm 0 to 0x1FFF, and DataByte can have a value of 0 to 0xFF.
void Write_Byte_To_24LC64_EEPROM(unsigned int Address, unsigned char DataByte)
{
    I2C_Start();										// Start i2c communication

    // Send i2c address of 24LC64 with write command
    while(I2C_Write_Byte(Device_Address_24LC64_EEPROM + 0) == 1)// Wait until device is free
    {
        I2C_ReStart();
    }

    I2C_Write_Byte(Address>>8);							// Write Address upper byte
    I2C_Write_Byte((unsigned char)Address);				// Write Address lower byte
    I2C_Write_Byte(DataByte);							// Write data byte
    I2C_Stop();											// Stop i2c communication
}



// Function Purpose: Read_Byte_From_24LC64_EEPROM reads a single byte from given address
// Address can have any value fromm 0 to 0x1FFF.
unsigned char Read_Byte_From_24LC64_EEPROM(unsigned int Address)
{
    unsigned char Byte = 0;								// Variable to store Received byte

    I2C_Start();										// Start i2c communication

    // Send i2c address of 24LC64 with write command
    while(I2C_Write_Byte(Device_Address_24LC64_EEPROM + 0) == 1)// Wait until device is free
    {
        I2C_ReStart();
    }

    I2C_Write_Byte(Address>>8);							// Write Address upper byte
    I2C_Write_Byte((unsigned char)Address);				// Write Address lower byte
    I2C_ReStart();										// Restart i2c

    // Send i2c address of 24LC64 EEPROM with read command
    I2C_Write_Byte(Device_Address_24LC64_EEPROM + 1);

    Byte = I2C_Read_Byte();								// Read byte from EEPROM

    I2C_Send_NACK();									// Give NACK to stop reading
    I2C_Stop();											// Stop i2c communication

    return Byte;				// Return the byte received from 24LC64 EEPROM
}



// Function Purpose: Write_Page_To_24LC64_EEPROM writes a page on given address
// Address can have value 0, 32, 64, .... only and pData is pointer to the array
// containing NoOfBytes bytes in it. NoOfBytes can have a value from 1 to 32 only.
void Write_Page_To_24LC64_EEPROM(unsigned int Address,unsigned char* pData,unsigned char NoOfBytes)
{
    unsigned int i;

    I2C_Start();										// Start i2c communication

    // Send i2c address of 24LC64 with write command
    while(I2C_Write_Byte(Device_Address_24LC64_EEPROM + 0) == 1)// Wait until device is free
    {
        I2C_ReStart();
    }

    I2C_Write_Byte(Address>>8);							// Write Address upper byte
    I2C_Write_Byte((unsigned char)Address);				// Write Address lower byte

    for(i=0; i<NoOfBytes; i++)							// Write NoOfBytes
        I2C_Write_Byte(pData[i]);						// Write data byte

    I2C_Stop();											// Stop i2c communication
}




// Function Purpose: Read_Bytes_From_24LC64_EEPROM reads a NoOfBytes bytes from given starting address.
// Address can have any value fromm 0 to 0x1FFF. Also, NoOfBytes can have any value 0 to 0x1FFF.
// Read bytes are returned in pData array.
void Read_Bytes_From_24LC64_EEPROM(unsigned int Address, unsigned char* pData, unsigned int NoOfBytes)
{
    unsigned int i;

    I2C_Start();										// Start i2c communication

    // Send i2c address of 24LC64 with write command
    while(I2C_Write_Byte(Device_Address_24LC64_EEPROM + 0) == 1)// Wait until device is free
    {
        I2C_ReStart();
    }

    I2C_Write_Byte(Address>>8);							// Write Address upper byte
    I2C_Write_Byte((unsigned char)Address);				// Write Address lower byte
    I2C_ReStart();										// Restart i2c

    // Send i2c address of 24LC64 EEPROM with read command
    I2C_Write_Byte(Device_Address_24LC64_EEPROM + 1);

    pData[0] = I2C_Read_Byte();							// Read First byte from EEPROM

    for(i=1; i<NoOfBytes; i++)							// Read NoOfBytes
    {
        I2C_Send_ACK();					// Give Ack to slave to start receiving next byte
        pData[i] = I2C_Read_Byte();		// Read next byte from EEPROM
    }

    I2C_Send_NACK();									// Give NACK to stop reading
    I2C_Stop();											// Stop i2c communication
}

Symulacja Proteusa:

Polecany post:

  • Interfejs RTC DS1307 z mikrokontrolerem PIC.
  • Wyświetlaj niestandardowe znaki na wyświetlaczu LCD za pomocą mikrokontrolera PIC.
  • Program migania diody w c dla 8051.
  • Interfejs przełącznika i diody led za pomocą 8051
  • Interfejs przekaźnika z mikrokontrolerem 8051
  • Przenoszenie wyświetlania wiadomości na wyświetlaczu LCD za pomocą 8051
  • Kod c 4-bitowego trybu LCD dla 8051.
  • Utwórz niestandardowe znaki LCD dla alfanumerycznego wyświetlacza LCD 16×2
  • Interfejs klawiatury z 8051
  • Elektroniczny zamek cyfrowy za pomocą 8051
  • Interfejs EEPROM z mikrokontrolerami 8051 za pomocą I2C
  • Osadzone pytania do rozmowy kwalifikacyjnej.
  • Schemat pinów i opis pinów mikrokontrolera 8051.
  • Potrafi protokołować pytania do rozmowy kwalifikacyjnej.
  • Architektura 8051.