MCU EEPROM single byte read and write operation timing

Publisher:collectorsLatest update time:2017-11-16 Source: eefocusKeywords:MCU Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

EEPROM write data process

The first step is the I2C start signal, followed by the first byte, which is the I2C device address we talked about earlier, and selecting the "write" operation in the read and write direction.

The second step is to send the storage address of the data. 24C02 has a total of 256 bytes of storage space, with addresses from 0x00 to 0xFF. We write the address where we want to store the data.

The third step is to send the first byte and the second byte of the data to be stored... Note that during the data writing process, each byte of the EEPROM will respond with an "acknowledge bit 0" to tell us that the EEPROM data is written successfully. If there is no acknowledge bit, it means that the writing is unsuccessful.

During the data writing process, each time a byte is successfully written, the address of the EEPROM storage space will automatically increase by 1. When it reaches 0xFF, if another byte is written, the address will overflow and become 0x00 again.

EEPROM read data flow

The first step is to send the I2C start signal, followed by the first byte, which is the I2C device address we mentioned earlier, and select the "write" operation in the read and write direction. Some students may be surprised at this point. Why do we have to choose "write" in the direction when we are clearly reading data? As I said just now, 24C02 has a total of 256 addresses. We choose the write operation to write the storage address of the data to be read first, telling the EEPROM which address we want to read the data. This is like when we make a phone call, we first dial the switchboard number (EEPROM device address), and then continue to dial the extension number (data address). When dialing the extension number, the host is still the sender, and the direction is still "write".

The second step is to send the address of the data to be read. Note that it is the address, not the data in the EEPROM, to inform the EEPROM which extension information I want.

The third step is to resend the I2C start signal and device address, and select "read" operation in the direction bit.

In these three steps, each byte is actually being "written", so each byte of EEPROM will respond with an "acknowledge bit 0".

The fourth step is to read the data sent back from the device. After reading a byte, if you want to continue reading the next byte, send an "acknowledgement bit ACK (0)". If you do not want to read anymore, tell the EEPROM that you do not want the data and do not send any more data, then send a "non-acknowledgement bit NAK (1)".

The same as the write operation rules, each time we read a byte, the address will automatically increase by 1. If we want to continue reading, we give the EEPROM an ACK (0) low level, and then continue to give the SCL complete timing, and the EEPROM will continue to send out data. If we don't want to read anymore, we want to tell the EEPROM that we don't want data, then we can directly give a NAK (1) high level. You must understand this place logically thoroughly, and you can't simply rely on rote memorization. You must understand it clearly. Here are a few key points: A. In this example, the microcontroller is the host and 24C02 is the slave; B. Whether reading or writing, SCL is always controlled by the host; C. When writing, the response signal is given by the slave, indicating whether the slave has received the data correctly; D. When reading, the response signal is given by the host, indicating whether to continue reading.

Next, we will write a program to read a data at address 0x02 of EEPROM. No matter what the data is before, we will add 1 to the read data and write it to address 0x02 of EEPROM. In addition, we will create a file for the I2C program, write an I2C.c program file, and form another program module. You can also see that the programs in the Lcd1602.c file are the same for these consecutive programs. In the future, we can directly use them when writing 1602 display programs, which greatly improves the convenience of program transplantation.

/******************************I2C.c file program source code******************************/
#include 
#include 
#define I2CDelay() {_nop_();_nop_();_nop_();_nop_();}
sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;
/* Generate bus start signal */
void I2CStart(){
    I2C_SDA = 1; //First make sure SDA and SCL are both high level
    I2C_SCL = 1;
    I2CDelay();
    I2C_SDA = 0; // Pull SDA low first
    I2CDelay();
    I2C_SCL = 0; //Pull SCL low again
}
/* Generate bus stop signal */
void I2CStop(){
    I2C_SCL = 0; //First make sure SDA and SCL are both low level
    I2C_SDA = 0;
    I2CDelay();
    I2C_SCL = 1; //Pull SCL high first
    I2CDelay();
    I2C_SDA = 1; //Pull SDA high again
    I2CDelay();
}
/* I2C bus write operation, dat-byte to be written, return value-slave response bit value */
bit I2CWrite(unsigned char dat){
    bit ack; //Used to temporarily store the value of the response bit
    unsigned char mask; //Mask variable used to detect a certain bit value in a byte

    for (mask=0x80; mask!=0; mask>>=1){ //From high to low
        if ((mask&dat) == 0){ //The value of this bit is output to SDA
            I2C_SDA = 0;
        }else{
            I2C_SDA = 1;
        }
        I2CDelay();
        I2C_SCL = 1; //Pull SCL high
        I2CDelay();
        I2C_SCL = 0; //Pull SCL low again to complete a bit cycle
    }

    I2C_SDA = 1; //After sending 8 bits of data, the host releases SDA to detect the slave's response
    I2CDelay();
    I2C_SCL = 1; //Pull SCL high
    ack = I2C_SDA; //Read the SDA value at this time, which is the response value of the slave
    I2CDelay();
    I2C_SCL = 0; //Pull SCL low again to complete the response bit and hold the bus
    // The response value is inverted to conform to the usual logic:
    //0 = does not exist or is busy or write failed, 1 = exists and is idle or write succeeded
    return (~ack);
}
/* I2C bus read operation, and send a non-response signal, return value - the byte read */
unsigned char I2CReadNAK(){
    unsigned char mask;
    unsigned char dat;

    I2C_SDA = 1; //First make sure the host releases SDA
    for (mask=0x80; mask!=0; mask>>=1){ //From high to low
        I2CDelay();
        I2C_SCL = 1; //Pull SCL high
        if(I2C_SDA == 0){ //Read the value of SDA
            dat &= ~mask; //When it is 0, the corresponding bit in dat is cleared
        }else{
            dat |= mask; //When it is 1, the corresponding position in dat is 1
        }
        I2CDelay();
        I2C_SCL = 0; //Pull SCL low again to allow the slave to send out the next bit
    }
    I2C_SDA = 1; //After sending 8 bits of data, pull up SDA to send a non-response signal
    I2CDelay();
    I2C_SCL = 1; //Pull SCL high
    I2CDelay();
    I2C_SCL = 0; //Pull SCL low again to complete the non-acknowledge bit and hold the bus
    return dat;
}

/* I2C bus read operation, send response signal, return value - read byte */
unsigned char I2CReadACK(){
    unsigned char mask;
    unsigned char dat;

    I2C_SDA = 1; //First make sure the host releases SDA
    for (mask=0x80; mask!=0; mask>>=1){ //From high to low
        I2CDelay();
        I2C_SCL = 1; //Pull SCL high
        if(I2C_SDA == 0){ //Read the value of SDA
            dat &= ~mask; //When it is 0, the corresponding bit in dat is cleared
        }else{
            dat |= mask; //When it is 1, the corresponding position in dat is 1
        }
        I2CDelay();
        I2C_SCL = 0; //Pull SCL low again to allow the slave to send out the next bit
    }
    I2C_SDA = 0; //After sending 8 bits of data, pull SDA low and send a response signal
    I2CDelay();
    I2C_SCL = 1; //Pull SCL high
    I2CDelay();
    I2C_SCL = 0; //Pull SCL low again to complete the response bit and hold the bus
    return dat;
}

The I2C.c file provides all the low-level operation functions of the I2C bus, including start, stop, byte write, byte read + response, and byte read + non-response.

/***************************Lcd1602.c file program source code********************************/
#include 

#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;

/* Wait for LCD to be ready */
void LcdWaitReady(){
    unsigned char sta;
    LCD1602_DB = 0xFF;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do {
        LCD1602_E = 1;
        sta = LCD1602_DB; //Read status word
        LCD1602_E = 0;
    }while (sta & 0x80); //bit7 is equal to 1, indicating that the LCD is busy, repeat the test until it is equal to 0
}
/* Write a one-byte command to the LCD1602, cmd-the command value to be written */
void LcdWriteCmd(unsigned char cmd){
    LcdWaitReady();
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DB = cmd;
    LCD1602_E = 1;
    LCD1602_E = 0;
}
/* Write one byte of data to LCD1602, dat-data value to be written */
void LcdWriteDat(unsigned char dat){
    LcdWaitReady();
    LCD1602_RS = 1;
    LCD1602_RW = 0;
    LCD1602_DB = dat;
    LCD1602_E = 1;
    LCD1602_E = 0;
}
/* Set the display RAM start address, that is, the cursor position, (x, y) - corresponding to the character coordinates on the screen */
void LcdSetCursor(unsigned char x, unsigned char y){
    unsigned char addr;

    if (y == 0){ //Calculate the display RAM address based on the input screen coordinates
        addr = 0x00 + x; //The first line of characters starts at 0x00
    }else{
        addr = 0x40 + x; //The second line of characters starts at 0x40
    }
    LcdWriteCmd(addr | 0x80); //Set RAM address
}
/* Display the string on the LCD, (x,y)-corresponds to the starting coordinates on the screen, str-string pointer */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str){
    LcdSetCursor(x, y); //Set the starting address
    while (*str != '\0'){ //Continuously write string data until the end character is detected
        LcdWriteDat(*str++);
    }
}
/* Initialize 1602 LCD */
void InitLcd1602(){
    LcdWriteCmd(0x38); //16*2 display, 5*7 dot matrix, 8-bit data interface
    LcdWriteCmd(0x0C); //Display on, cursor off
    LcdWriteCmd(0x06); //The text remains unchanged, the address is automatically increased by 1
    LcdWriteCmd(0x01); //Clear screen
}
/********************************main.c file program source code******************************/
#include 
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
unsigned char E2ReadByte(unsigned char addr);
void E2WriteByte(unsigned char addr, unsigned char dat);

void main(){
    unsigned char dat;
    unsigned char str[10];

    InitLcd1602(); //Initialize LCD
    dat = E2ReadByte(0x02); //Read a byte at the specified address
    str[0] = (dat/100) + '0'; //Convert to decimal string format
    str[1] = (dat/10%10) + '0';
    str[2] = (dat%10) + '0';
    str[3] = '\0';

    LcdShowStr(0, 0, str); //Display on LCD
    dat++; //add 1 to its value
    E2WriteByte(0x02, dat); //Write back to the corresponding address
    while (1);
}

/* Read a byte in EEPROM, addr-byte address */
unsigned char E2ReadByte(unsigned char addr){
    unsigned char dat;

    I2CStart();
    I2CWrite(0x50<<1); //Address the device, then write operation
    I2CWrite(addr); //Write storage address
    I2CStart(); //Send a repeated start signal
    I2CWrite((0x50<<1)|0x01); //Address the device, then read the operation
    dat = I2CReadNAK(); //Read one byte of data
    I2CStop();
    return dat;
}
/* Write a byte to EEPROM, addr-byte address */
void E2WriteByte(unsigned char addr, unsigned char dat){
    I2CStart();
    I2CWrite(0x50<<1); //Address the device, then write operation
    I2CWrite(addr); //Write storage address
    I2CWrite(dat); //Write a byte of data
    I2CStop();
}

With the students' current foundation, it should not be difficult to analyze this program independently. If you encounter any statement you don't understand, you can ask others or search for it in time to understand the problem to be solved. After you copy this program and compile it, you will find that the Keil software prompts a warning: *** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS. This warning means that there are variables or functions in the code that have not been called, that is, the I2CReadACK() function in the I2C.c file is not used in this example.

Please observe this program carefully. When we read EEPROM, we only read one byte and then tell EEPROM that we don't need to read data anymore. After reading, we send a "NAK" directly. Therefore, we only call the I2CReadNAK() function, but not the I2CReadACK() function. We may need to read several bytes in succession in the future, so this function is written in the I2C.c file. It is necessary as part of the I2C function module to facilitate the porting of this file to other programs. Therefore, we don't need to care about this warning here.


Keywords:MCU Reference address:MCU EEPROM single byte read and write operation timing

Previous article:MCU I2C addressing mode
Next article:MCU EEPROM multi-byte read and write operation timing

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号