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******************************/ #includeextern 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.
Previous article:MCU I2C addressing mode
Next article:MCU EEPROM multi-byte read and write operation timing
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- From probes to power supplies, Tektronix is leading the way in comprehensive innovation in power electronics testing
- Sn-doped CuO nanostructure-based ethanol gas sensor for real-time drunk driving detection in vehicles
- Design considerations for automotive battery wiring harness
- Do you know all the various motors commonly used in automotive electronics?
- What are the functions of the Internet of Vehicles? What are the uses and benefits of the Internet of Vehicles?
- Power Inverter - A critical safety system for electric vehicles
- Analysis of the information security mechanism of AUTOSAR, the automotive embedded software framework
- Brief Analysis of Automotive Ethernet Test Content and Test Methods
- How haptic technology can enhance driving safety
- Let’s talk about the “Three Musketeers” of radar in autonomous driving
- Happy New Year!
- General Grounding Guidelines for Most Mixed-Signal Devices
- If the database is deleted, it must be run away! What if AI deletes the database?
- Low power MCU design concept
- DSK6713 big endian mode
- [AT-START-F425 Review] +RT-Thread ported to AT32F425
- Make a beautiful mini clock based on ESP8266
- How many PWM waves can the MSP430F149 microcontroller output?
- CircuitPython 7.3.0 released
- R8-5 Attribute Introduction