STM32 entry development: using IIC hardware timing to read and write AT24C08 (EEPROM)

1. Environmental introduction

Programming software: keil5

Operating system: win10

MCU model:  STM32F103ZET6

STM32 programming method: register development (convenient for program porting to other microcontrollers)

IIC bus:  STM32 itself supports IIC hardware timing. The previous article has introduced the use of IIC analog timing to read and write AT24C02. This article introduces the hardware IIC configuration method of STM32, and reads and writes AT24C08.

Article address: https://xiaolong.blog.csdn.net/article/details/117586108

The simulation sequence is more convenient to transplant to other single-chip microcomputers, with higher versatility, regardless of MCU; hardware sequence efficiency is higher, each MCU configuration method is different, and it depends on the support of the hardware itself.

Device model: using AT24C08 EEPROM memory chip

The complete project source code download address, download it to compile and run the test (including simulated IIC timing, STM32 hardware IIC timing to drive AT24C02 and AT24C08 respectively): https://download.csdn.net/download/xiaolong1126626497/19399945

Two, AT24C08 memory chip introduction

2.1 Introduction to chip features

AT24C08 is a serial CMOS type EEPROM memory chip. The AT24C0x series includes specific chip models such as AT24C01, AT24C02, AT24C04, AT24C08, and AT24C16 .

Their capacities are: 1K (128 x 8), 2K (256 x 8), 8K (1024 x 8), 16K (2048 x 8), where 8 means 8 bits (bit)

Their pin functions and packaging features are as follows:

Chip function description:

The AT24C08 series supports I2C, the bus data transfer protocol I2C. The bus protocol stipulates that any device that transfers data to the bus is used as a transmitter. Any device that receives data from the bus is a receiver; data transmission is controlled by the master device that generates the serial clock and all start and stop signals. Both the master device and the slave device can be used as transmitters or receivers, but the master device controls the mode of transmitting data (sending or receiving).

Introduction of chip characteristics:

1. Low voltage and standard voltage operation
          -2.7 (VCC=2.7V to 5.5V)
          -1.8 (VCC=1.8V to 5.5V)

2. Two-wire serial interface (SDA, SCL)

3. There is a write protection pin for hardware data protection

4. Self-timed write cycle (5 milliseconds to 10 milliseconds), because there is a page buffer inside, after writing data to AT24C0x, you need to wait for AT24C0x to write the buffer data to the internal EEPROM area.

5. Data can be stored for up to 100 years

6. 1 million erase and write cycles

7. The high data transfer rate is 400KHz, the low speed 100KHZ is compatible with IIC bus. 100 kHz (1.8V) and 400 kHz (2.7V, 5V)

8. 8-byte page write buffer
       The size of this buffer is related to the specific chip model: 8-byte pages (1K, 2K), 16-byte pages (4K, 8K, 16K)

2.2 Introduction of chip device address

Because the IIC protocol stipulates that each data transfer is transmitted in 8 bytes, AT24C08 is 1024 bytes, and the address selection is different from AT24C02;

The standard address bit of the IIC device is 7 bits. In the above figure, 1010 of AT24C08 is a fixed value inside the chip, A2 is a hardware pin, and the level is determined by the hardware; P1 and P0 are space storage block selections, each storage block size is 256 bytes, and the addressing range is 0~ 255, AT24C08 is equivalent to the structure of 4 AT24C02; the last bit is the read/write bit (1 is read, 0 is write), the read and write bits are not counted in the address bits, but according to the sequence of IIC, before operating the device , You need to send a 7-bit address first, and then send a 1-bit read and write bit to start the operation of the chip. We are writing the simulation timing in order to write the for loop uniformly and send it by byte, so generally the 7 address bits are combined with 1 read and write bits are put together and combined into 1 byte, which is convenient for byte-by-byte transmission of data.

The schematic diagram of the AT24C08 on the development board I am using now looks like this:

Then the standard device addresses of this AT24C08 are:

The first area: 0x50 (hexadecimal), the corresponding binary is: 1010000

The second area: 0x51 (hexadecimal), the corresponding binary is: 1010001

The third area: 0x52 (hexadecimal), the corresponding binary is: 1010010

The fourth area: 0x53 (hexadecimal), the corresponding binary is: 1010011

If the read and write bits are combined, the device address of the read permission:

The first area: 0xA1 (hexadecimal), the corresponding binary is: 10100001

The second area: 0xA3 (hexadecimal), the corresponding binary is: 10100011

The third area: 0xA5 (hexadecimal), the corresponding binary is: 10100101

The fourth area: 0xA7 (hexadecimal), the corresponding binary is: 10100111

If the read and write bits are combined, the device address of the write permission:

The first area: 0xA0 (hexadecimal), the corresponding binary is: 10100000

The second area: 0xA2 (hexadecimal), the corresponding binary is: 10100010

The third area: 0xA4 (hexadecimal), the corresponding binary is: 10100100

The fourth area: 0xA6 (hexadecimal), the corresponding binary is: 10100110

2.3 The instruction flow (timing) of writing data by byte to AT24C08

explain in detail:

1. Send the start signal first

2. Sending device address (write permission)

3. Wait for AT24C08 response, low level is active

4. Send the storage address. There are 256 bytes in AT24C08. The addressing starts from 0 and the range is (0~255). Sending this memory address tells AT24C08 where to store the next data.

5. Waiting for AT24C08 response, low level is active

6. Send one byte of data, this data is the data you want to store in AT24C08.

7. Wait for AT24C08 response, low level is active

8. Send stop signal

2.3 The instruction flow of writing data by page to AT24C08 (timing)

explain in detail:

1. Send the start signal first

2. Sending device address (write permission)

3. Wait for AT24C08 response, low level is active

4. Send the storage address. There are 256 bytes in AT24C08. The addressing starts from 0 and the range is (0~255). Sending this memory address tells AT24C08 where to store the next data.

5. Waiting for AT24C08 response, low level is active

6. 8 bytes of data can be sent cyclically. These data are the data that you want to store in the AT24C08.

The page buffer of AT24C08 is 16 bytes, all the loops here can only send 16 bytes at most, and the more bytes sent will overwrite the previous ones.

Points to note: The addressing of this page buffer also starts from 0, for example: 0~15 counts as the 1st page, 16~32 counts as the 2nd page...and so on. If the starting address of writing data is now 3, then there are only 13 bytes left to write on this page; it does not mean that 16 bytes can be written in a loop from anywhere.

Detailed process: The for loop is generally used in this program to achieve

(1). Send byte 1

(2). Waiting for AT24C08 response, low level is active

(3). Send byte 2

(4). Wait for AT24C08 response, low level is active

.........

Up to 8 times.

7. Wait for AT24C08 response, low level is active

8. Send stop signal

2.4 Read arbitrary byte data from AT24C08 arbitrary address (timing)

AT24C08 supports current address reading, arbitrary address reading, the most commonly used is arbitrary address reading, because the address for reading data can be specified, which is more flexible. The above specified timing diagram is arbitrary address reading.

explain in detail:

1. Send the start signal first

2. Sending device address (write permission)

3. Wait for AT24C08 response, low level is active

4. Send the memory address. There are 2048 bytes in AT24C08. The addressing starts from 0 and the range is (0~1024). Sending this memory address tells AT24C08 that the data at that address should be returned to the MCU next.

5. Waiting for AT24C08 response, low level is active

6. Resend the start signal (switch read and write mode)

7. Sending device address (read permission)

8. Wait for AT24C08 response, low level is active

9. Cyclic read data: Receive data returned by AT24C08.

There is no byte limit for reading data, the first byte or the entire chip can be read continuously.

10. Send non-response (active high)

11. Send stop signal

Three, IIC bus introduction

2.1 Introduction to IIC bus

The I2C (Inter-Integrated Circuit) bus is a two-wire serial bus developed by PHILIPS, which is used to connect microcontrollers and their peripheral devices. It is a bus standard widely used in the field of microelectronics communication control. It has the advantages of fewer interface lines, simple control mode, small device package form, and higher communication speed.

The I2C protocol uses master/slave two-way communication. The device sending data to the bus is defined as a transmitter, and the device receiving data is defined as a receiver. Both the master device and the slave device can work in receiving and sending states.

The I2C bus transfers information between the devices connected to the bus through the serial data (SDA) line and the serial clock (SCL) line. Each device has a unique address identification, and can be used as a transmitter or receiver (determined by the function of the device).

I2C has four working modes:
       1. Host sending
       2. Host receiving
       3. Slave sending
       4. Slave receiving

The I2C bus uses only two wires: serial data SDA (Serial Data) and serial clock SCL (Serial Clock).

The bus must be controlled by the host (usually a microcontroller). The host generates a serial clock (SCL) to control the transmission direction of the bus, and generates start and stop conditions.

The data state on the SDA line can only be changed when SCL is low.

2.2 Device connection diagram on IIC bus

The I2C bus is very simple in physical connection, consisting of SDA (serial data line) and SCL (serial clock line) and pull-up resistors. The communication principle is to generate the signals required by the I2C bus protocol for data transmission by controlling the high and low timing of the SCL and SDA lines. When the bus is in idle state, these two lines are generally pulled high by the pull-up resistor connected above, and maintain a high level.

The pull-up resistance range is 4.7K~100K.

2.3 I2C bus characteristics

Each device on the I2C bus can be used as a master device or a slave device, and each slave device corresponds to a unique address (which can be learned from the data manual of the I2C device). The master and slave devices use this address to determine which device to communicate with. In normal applications, we use the CPU module with I2C bus interface as the master device, and all other devices connected to the bus as slave devices.

1. The number of devices
      that can be attached to the bus The number of devices that can be attached to the I2C bus is limited by the bus's maximum capacitance of 400pF. If the devices of the same model are attached, it is also limited by the device address.
    Generally, the I2C device address is a 7-bit address (there are also 10 bits), and the address is divided into two parts: the chip curing address (which is grounded when the chip is produced, and which is connected to the power supply, which has been fixed), and the programmable address (leads out the IO port, which is determined by the hardware device) .
     For example: A certain device has a 7-bit address. The upper 4 bits of 10101 xxx are fixed at the factory, and the lower 3 bits can be determined by the designer.
At least 8 devices of this type can only be connected to an I2C bus.
If all 7-bit addresses can be programmed, then in theory 128 devices can be reached, but in practice, so many devices will not be mounted.

2. Bus speed transmission speed:
I2C bus data transmission rate can reach 100kbit/s in standard mode, 400kbit/s in fast mode, and 3.4Mbit/s in high-speed mode. The adjustment of the transmission rate is generally realized through the programmable clock of the I2C bus interface.

3. Bus data length
Two-way data transmission is carried out in bytes (8 bits) between the master device and the slave device on the I2C bus.

2.4 I2C bus protocol basic timing signal

Idle state: Both SCL and SDA remain high.

Start condition: When the bus is in the idle state, both SCL and SDA remain high. When SCL is high, the transition of SDA from high to low indicates that a starting condition is generated. After the initial condition is generated, the bus is in a busy state and is exclusively occupied by the master and slave devices of this data transfer, and other I2C devices cannot access the bus.

Stop condition: When SCL is high and SDA transitions from low to high, it means that a stop condition is generated.

Acknowledgement signal : the next clock signal after each byte transmission is completed. During the high level of SCL, when SDA is low, it means an acknowledgement signal.

Non-accepted signal: the next clock signal after the completion of each byte transmission. During the high level of SCL, when SDA is high, it means an acknowledge signal. The acknowledge signal or non-acknowledge signal is sent by the receiver, and the transmitter detects this signal (the transmitter, the receiver can be a slave device or a master device).

Note: The start and end signals are always generated by the master device.

2.5 Start signal and stop signal

The start signal is: when the clock line SCL is at a high level, the data line SDA changes from a high level to a low level. SCL=1; SDA=1; SDA=0;

The stop signal is: when the clock line SCL is at a low level, the data line SDA changes from a low level to a high level. SCL=1; SDA=0; SDA=1;

2.6 Response signal

The ninth bit of the data bit is an immediate response bit. The process of reading the response bit is the same as that of reading the data bit. Example: SCL=0;SCL=1;ACK=SDA; This ACK is the read response status.

2.7 Data bit transmission timing

Through the timing diagram, the data is stable when SCL is at a high level, and data is unstable when SCL is at a low level.

Then for writing a bit of data (STM32--->AT24C08): SCL=0; SDA=data; SCL=1;

Then for reading one bit of data (STM32<-----AT24C08): SCL=0;SCL=1;data=SDA;

2.8 Bus timing

Four, IIC bus timing code, AT24C08 read and write code

When debugging IIC simulation timing, you can buy a 24M USB logic analyzer on Taobao. If there is a problem with the timing, you can quickly find the problem after analyzing it with the logic analyzer.

4.1 iic.c This is the complete code of STM32 IIC hardware timing

/*函数功能: 初始化IIC总线硬件连接: 				SCL---PB6				SDA---PB7*/void IIC_Init(void){	 /*1. 时钟配置*/	 RCC->APB2ENR|=1<<3; //PB  	 /*2. GPIO口模式配置*/	 GPIOB->CRL&=0x00FFFFFF;	 GPIOB->CRL|=0xFF000000; //复用开漏输出	 GPIOB->ODR|=0x3<<6;			 /*3. GPIO口时钟配置(顺序不能错)*/	 RCC->APB1ENR|=1<<21; //I2C1时钟	 RCC->APB1RSTR|=1<<21; //开启复位时钟	 RCC->APB1RSTR&=~(1<<21);//关闭复位时钟		 /*4. 配置IIC的核心寄存器*/	 I2C1->CR2=0x24<<0;  //配置主机频率为36MHZ	 I2C1->CCR|=0x2D<<0; //配置主机频率是400KHZ	 I2C1->CR1|=1<<0;    //开启IIC模块		/*	CCR=主机时钟频率/2/IIC总线的频率	45=36MHZ/2/400KHZ  ---0x2D	*/}  /*函数功能: 发送起始信号当时钟线为高电平的时候,数据线由高电平变为低电平的过程*/void IIC_SendStart(void){	I2C1->CR1|=1<<8; //产生起始信号	while(!(I2C1->SR1&1<<0)){} //等待起始信号完成	I2C1->SR1=0; //清除状态位} /*函数功能: 停止信号当时钟线为高电平的时候,数据线由低电平变为高电平的过程*/void IIC_SendStop(void){		I2C1->CR1|=1<<9;} /*函数功能: 发送地址数据*/void IIC_SendAddr(u8 addr){	u32 s1,s2;	I2C1->DR=addr; //发送数据	while(1)	{			s1=I2C1->SR1;			s2=I2C1->SR2;		  if(s1&1<<1) //判断地址有没有发送成功			{					break;			}	}} /*函数功能: 发送数据*/void IIC_SendOneByte(u8 addr){	u32 s1,s2;	I2C1->DR=addr; //发送数据	while(1)	{			s1=I2C1->SR1;			s2=I2C1->SR2;			if(s1&1<<2) //判断数据有没有发送成功			{					break;			}	}} /*函数功能: 接收一个字节数据*/u8 IIC_RecvOneByte(void){	u8 data=0;	I2C1->CR1|=1<<10; //使能应答	while(!(I2C1->SR1&1<<6)){} //等待数据	data=I2C1->DR;	I2C1->CR1&=~(1<<10); //关闭应答使能	return data;}

4.2 AT24C08.c This is the complete read and write code of AT24C08

*函数功能: 写一个字节函数参数:	u8 addr 数据的位置(0~1023)	u8 data 数据范围(0~255)*/void AT24C08_WriteOneByte(u16 addr,u8 data){    u8 read_device_addr=AT24C08_READ_ADDR;	u8 write_device_addr=AT24C08_WRITE_ADDR;    if(addr<256*1) //第一个块    {        write_device_addr|=0x0<<1;        read_device_addr|=0x0<<1;    }    else if(addr<256*2) //第二个块    {        write_device_addr|=0x1<<1;        read_device_addr|=0x1<<1;    }    else if(addr<256*3) //第三个块    {        write_device_addr|=0x2<<1;        read_device_addr|=0x2<<1;    }    else if(addr<256*4) //第四个块    {        write_device_addr|=0x3<<1;        read_device_addr|=0x3<<1;    }    addr=addr%256; //得到地址范围         IIC_SendStart();//起始信号    IIC_SendAddr(write_device_addr);//发送设备地址    IIC_SendOneByte(addr); //数据存放的地址    IIC_SendOneByte(data); //发送将要存放的数据    IIC_SendStop(); //停止信号    DelayMs(10); //等待写}  /*函数功能: 读一个字节函数参数:	u8 addr 数据的位置(0~1023)返回值: 读到的数据*/u8 AT24C08_ReadOneByte(u16 addr){    u8 data=0;    u8 read_device_addr=AT24C08_READ_ADDR;	u8 write_device_addr=AT24C08_WRITE_ADDR;    if(addr<256*1) //第一个块    {        write_device_addr|=0x0<<1;        read_device_addr|=0x0<<1;    }    else if(addr<256*2) //第二个块    {        write_device_addr|=0x1<<1;        read_device_addr|=0x1<<1;    }    else if(addr<256*3) //第三个块    {        write_device_addr|=0x2<<1;        read_device_addr|=0x2<<1;    }    else if(addr<256*4) //第四个块    {        write_device_addr|=0x3<<1;        read_device_addr|=0x3<<1;    }    addr=addr%256; //得到地址范围     IIC_SendStart();//起始信号    IIC_SendAddr(write_device_addr);//发送设备地址    IIC_SendOneByte(addr); //将要读取数据的地址    IIC_SendStart();//起始信号    IIC_SendAddr(read_device_addr);//发送设备地址    data=IIC_RecvOneByte();//读取数据    IIC_SendStop(); //停止信号    return data;}  /*函数功能: 从指定位置读取指定长度的数据函数参数:	u16 addr 数据的位置(0~1023)	u16 len 读取的长度	u8 *buffer 存放读取的数据返回值: 读到的数据*/void AT24C08_ReadByte(u16 addr,u16 len,u8 *buffer){	 u16 i=0;	IIC_SendStart();//起始信号	IIC_SendAddr(AT24C08_WRITE_ADDR);//发送设备地址	IIC_SendOneByte(addr); //将要读取数据的地址		IIC_SendStart();//起始信号	IIC_SendAddr(AT24C08_READ_ADDR);//发送设备地址		for(i=0;i<len;i++)	{			buffer[i]=IIC_RecvOneByte();//读取数据	}	IIC_SendStop(); //停止信号}  /*函数功能: AT24C08页写函数函数参数:	u16 addr 写入的位置(0~1023)	u8 len 写入的长度(每页16字节)	u8 *buffer 存放读取的数据*/void AT24C08_PageWrite(u16 addr,u16 len,u8 *buffer){		u16 i=0;		IIC_SendStart();//起始信号		IIC_SendAddr(AT24C08_WRITE_ADDR);//发送设备地址		IIC_SendOneByte(addr); //数据存放的地址		for(i=0;i<len;i++)		{			IIC_SendOneByte(buffer[i]); //发送将要存放的数据		}		IIC_SendStop(); //停止信号		DelayMs(10); //等待写}  /*函数功能: 从指定位置写入指定长度的数据函数参数:	u16 addr 数据的位置(0~1023)	u16 len 写入的长度	u8 *buffer 存放即将写入的数据返回值: 读到的数据*/void AT24C08_WriteByte(u16 addr,u16 len,u8 *buffer){		u8 page_byte=16-addr%16; //得到当前页剩余的字节数量		if(page_byte>len) //判断当前页剩余的字节空间是否够写		{				page_byte=len; //表示一次性可以写完		}		while(1)		{				AT24C08_PageWrite(addr,page_byte,buffer); //写一页				if(page_byte==len)break; //写完了				buffer+=page_byte; //指针偏移				addr+=page_byte;//地址偏移				len-=page_byte;//得到剩余没有写完的长度				if(len>16)page_byte=16;				else page_byte=len; //一次可以写完		}}

4.3 main.c This is the AT24C08 test code

#include "stm32f10x.h"#include "beep.h"#include "delay.h"#include "led.h"#include "key.h"#include "sys.h"#include "usart.h"#include <string.h>#include <stdio.h>#include "exti.h"#include "timer.h"#include "rtc.h"#include "adc.h"#include "ds18b20.h"#include "ble.h"#include "esp8266.h"#include "wdg.h"#include "oled.h"#include "rfid_rc522.h"#include "infrared.h"#include "iic.h"#include "at24c08.h" u8 buff_tx[50]="1234567890";u8 buff_rx[50];u8 data=88;u8 data2;int main(){       u8 key;    LED_Init();    KEY_Init();    BEEP_Init();    TIM1_Init(72,20000); //辅助串口1接收,超时时间为20ms    USART_X_Init(USART1,72,115200);     IIC_Init(); //IIC总线初始化    printf("usart1 ok\n");     while(1)    {        key=KEY_Scanf();        if(key)        {            //AT24C08_WriteByte(100,50,buff_tx);            //AT24C08_ReadByte(100,50,buff_rx);            //printf("buff_rx=%s\n",buff_rx);                //测试第0块//            data=AT24C08_ReadOneByte(0);//            AT24C08_WriteOneByte(0,data+1);//            printf("data=%d\n",data);               //测试第1块//            data=AT24C08_ReadOneByte(300);//            AT24C08_WriteOneByte(300,data+1);//            printf("data=%d\n",data);               //测试第2块//            data=AT24C08_ReadOneByte(600);//            AT24C08_WriteOneByte(600,data+1);//            printf("data=%d\n",data);               //测试第3块             data=AT24C08_ReadOneByte(900);             AT24C08_WriteOneByte(900,data+1);             printf("data=%d\n",data);        }    }}

Serial port debugging assistant source download address: https://blog.csdn.net/xiaolong1126626497/article/details/116040983