本帖最后由 dirty 于 2024-5-12 12:50 编辑
本篇讲述I2C读写板载EEPROM存储器AT24C02C-SSHM-T。
一.硬件原理
AT24C02C为I2C接口,容量256Bytes.I2C引脚配置为:PH4--SCL,PB11--SDA.
图1:AT24C02C原理
引脚 功能
VCC、GND 电源
WP 写保护
SCL、SDA I2C接口
A0、A1、A2 I2C地址
二.代码
1.引脚、参数宏定义
#define I2C_SPEED 400000
#define I2C_PAGE_SIZE 8
#define I2CX I2C1
#define RCU_GPIO_I2C_SDA RCU_GPIOB
#define RCU_GPIO_I2C_SCL RCU_GPIOH
#define RCU_I2C RCU_I2C1
#define I2C_SCL_PORT GPIOH
#define I2C_SDA_PORT GPIOB
#define I2C_SCL_PIN GPIO_PIN_4
#define I2C_SDA_PIN GPIO_PIN_11
#define I2C_GPIO_AF GPIO_AF_4
2.io引脚初始化配置
void gpio_config(void)
{
/* enable I2C_SCL_PIN clock */
rcu_periph_clock_enable(RCU_GPIO_I2C_SCL);
/* enable I2C_SDA_PIN clock */
rcu_periph_clock_enable(RCU_GPIO_I2C_SDA);
/* enable I2C clock */
rcu_periph_clock_enable(RCU_I2C);
/* connect I2C_SCL_PIN to I2C_SCL */
gpio_af_set(I2C_SCL_PORT, I2C_GPIO_AF, I2C_SCL_PIN);
/* connect I2C_SDA_PIN to I2C_SDA */
gpio_af_set(I2C_SDA_PORT, I2C_GPIO_AF, I2C_SDA_PIN);
/* configure GPIO pins of I2C */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SDA_PIN);
}
3.i2c接口初始化配置
void i2c_config(void)
{
/* configure the I2C1 clock source selection */
rcu_i2c_clock_config(IDX_I2C1, RCU_I2CSRC_IRC64MDIV);
/* configure I2C timing */
i2c_timing_config(I2CX, 0x0, 0x6, 0);
i2c_master_clock_config(I2CX, 0x26, 0x73);
/* enable I2C */
i2c_enable(I2CX);
}
3.器件地址,数据大小、超时时长,起始页等定义
#define EEPROM_BLOCK0_ADDRESS 0xA0
#define BUFFER_SIZE 256
#define MAX_RELOAD_SIZE 255
#define I2C_TIME_OUT (uint32_t)(50000)
#define EEP_FIRST_PAGE 0x00
4.i2c读、写函数
void eeprom_page_write(uint8_t *p_buffer, uint8_t write_address, uint8_t number_of_byte)
{
i2c_process_enum state = I2C_START;
uint32_t timeout = 0;
uint8_t end_flag = 0;
while(!end_flag) {
switch(state) {
case I2C_START:
/* configure slave address */
i2c_master_addressing(I2CX, eeprom_address, I2C_MASTER_TRANSMIT);
/* configure number of bytes to be transferred */
i2c_transfer_byte_number_config(I2CX, number_of_byte + 1);
/* clear I2C_TDATA register */
I2C_STAT(I2CX) |= I2C_STAT_TBE;
/* enable I2C automatic end mode in master mode */
i2c_automatic_end_enable(I2CX);
/* i2c master sends start signal only when the bus is idle */
while(i2c_flag_get(I2CX, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
i2c_start_on_bus(I2CX);
timeout = 0;
state = I2C_SEND_ADDRESS;
} else {
/* timeout, bus reset */
i2c_bus_reset();
timeout = 0;
state = I2C_START;
printf("i2c bus is busy in page write!\n");
}
break;
case I2C_SEND_ADDRESS:
/* wait until the transmit data buffer is empty */
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
/* send the EEPROM's internal address to write to : only one byte address */
i2c_data_transmit(I2CX, write_address);
timeout = 0;
state = I2C_TRANSMIT_DATA;
} else {
timeout = 0;
state = I2C_START;
printf("i2c master sends address timeout in page write!\n");
}
break;
case I2C_TRANSMIT_DATA:
while(number_of_byte--) {
/* wait until TI bit is set */
while((!i2c_flag_get(I2CX, I2C_FLAG_TI)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
/* while there is data to be written */
i2c_data_transmit(I2CX, *p_buffer);
/* point to the next byte to be written */
p_buffer++;
timeout = 0;
state = I2C_STOP;
} else {
/* wait TI timeout */
timeout = 0;
state = I2C_START;
printf("i2c master sends data timeout in page write!\n");
return ;
}
}
break;
case I2C_STOP:
/* wait until the stop condition is finished */
while((!i2c_flag_get(I2CX, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
/* clear STPDET flag */
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
timeout = 0;
state = I2C_END;
end_flag = 1;
} else {
/* stop detect timeout */
timeout = 0;
state = I2C_START;
printf("i2c master sends stop signal timeout in page write!\n");
}
break;
default:
/* default status */
state = I2C_START;
end_flag = 1;
timeout = 0;
printf("i2c master sends start signal in page write!\n");
break;
}
}
}
/*!
\brief read data from the EEPROM
\param[in] p_buffer: pointer to the buffer that receives the data read from the EEPROM
\param[in] read_address: EEPROM's internal address to start reading from
\param[in] number_of_byte: number of bytes to reads from the EEPROM
\param[out] none
\retval none
*/
void eeprom_buffer_read(uint8_t *p_buffer, uint8_t read_address, uint16_t number_of_byte)
{
uint32_t nbytes_reload = 0;
i2c_process_enum state = I2C_START;
uint32_t timeout = 0;
uint8_t end_flag = 0;
uint8_t restart_flag = 0;
uint8_t first_reload_flag = 1;
uint8_t last_reload_flag = 0;
while(!end_flag) {
switch(state) {
case I2C_START:
if(0 == restart_flag) {
/* clear I2C_TDATA register */
I2C_STAT(I2CX) |= I2C_STAT_TBE;
/* configure slave address */
i2c_master_addressing(I2CX, eeprom_address, I2C_MASTER_TRANSMIT);
/* configure number of bytes to be transferred */
i2c_transfer_byte_number_config(I2CX, 1);
/* disable I2C automatic end mode in master mode */
i2c_automatic_end_disable(I2CX);
/* i2c master sends start signal only when the bus is idle */
while(i2c_flag_get(I2CX, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
i2c_start_on_bus(I2CX);
timeout = 0;
state = I2C_SEND_ADDRESS;
} else {
/* timeout, bus reset */
i2c_bus_reset();
timeout = 0;
state = I2C_START;
printf("i2c bus is busy in read!\n");
}
} else {
/* restart */
i2c_start_on_bus(I2CX);
restart_flag = 0;
state = I2C_TRANSMIT_DATA;
}
break;
case I2C_SEND_ADDRESS:
/* wait until the transmit data buffer is empty */
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
/* send the EEPROM's internal address to write to : only one byte address */
i2c_data_transmit(I2CX, read_address);
timeout = 0;
state = I2C_RESTART;
} else {
timeout = 0;
state = I2C_START;
printf("i2c master sends data timeout in read!\n");
}
break;
case I2C_RESTART:
/* wait until the transmit data buffer is empty */
while((!i2c_flag_get(I2CX, I2C_FLAG_TC)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
/* configure the EEPROM's internal address to write to : only one byte address */
i2c_master_addressing(I2CX, eeprom_address, I2C_MASTER_RECEIVE);
/* enable I2C reload mode */
i2c_reload_enable(I2CX);
/* configure number of bytes to be transferred */
timeout = 0;
state = I2C_RELOAD;
restart_flag = 1;
} else {
timeout = 0;
state = I2C_START;
printf("i2c master sends EEPROM's internal address timeout in read!\n");
}
break;
case I2C_RELOAD:
if(number_of_byte > MAX_RELOAD_SIZE) {
number_of_byte = number_of_byte - MAX_RELOAD_SIZE;
nbytes_reload = MAX_RELOAD_SIZE;
} else {
nbytes_reload = number_of_byte;
last_reload_flag = 1;
}
if(1 == first_reload_flag) {
/* configure number of bytes to be transferred */
i2c_transfer_byte_number_config(I2CX, nbytes_reload);
if(1 == last_reload_flag) {
last_reload_flag = 0;
/* disable I2C reload mode */
if(number_of_byte <= MAX_RELOAD_SIZE) {
i2c_reload_disable(I2CX);
/* enable I2C automatic end mode in master mode */
i2c_automatic_end_enable(I2CX);
}
}
first_reload_flag = 0;
state = I2C_START;
} else {
/* wait for TCR flag */
while((!i2c_flag_get(I2CX, I2C_FLAG_TCR)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
/* configure number of bytes to be transferred */
i2c_transfer_byte_number_config(I2CX, nbytes_reload);
/* disable I2C reload mode */
if(number_of_byte <= MAX_RELOAD_SIZE) {
i2c_reload_disable(I2CX);
/* enable I2C automatic end mode in master mode */
i2c_automatic_end_enable(I2CX);
}
timeout = 0;
state = I2C_TRANSMIT_DATA;
} else {
timeout = 0;
state = I2C_START;
printf("i2c master reload data timeout in read!\n");
}
}
break;
case I2C_TRANSMIT_DATA:
/* wait until TI bit is set */
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
while(nbytes_reload) {
delay_1ms(2);
/* wait until the RBNE bit is set and clear it */
if(i2c_flag_get(I2CX, I2C_FLAG_RBNE)) {
/* read a byte from the EEPROM */
*p_buffer = i2c_data_receive(I2CX);
/* point to the next location where the byte read will be saved */
p_buffer++;
/* decrement the read bytes counter */
nbytes_reload--;
}
}
timeout = 0;
/* check if the reload mode is enabled or not */
if(I2C_CTL1(I2CX) & I2C_CTL1_RELOAD) {
timeout = 0;
state = I2C_RELOAD;
} else {
timeout = 0;
state = I2C_STOP;
}
} else {
/* wait TI timeout */
timeout = 0;
state = I2C_START;
printf("i2c master read data timeout in read!\n");
}
break;
case I2C_STOP:
/* wait until the stop condition is finished */
while((!i2c_flag_get(I2CX, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT)) {
timeout++;
}
if(timeout < I2C_TIME_OUT) {
/* clear STPDET flag */
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
timeout = 0;
state = I2C_END;
end_flag = 1;
} else {
timeout = 0;
state = I2C_START;
printf("i2c master sends stop signal timeout in read!\n");
}
break;
default:
/* default status */
state = I2C_START;
end_flag = 1;
timeout = 0;
printf("i2c master sends start signal in read!\n");
break;
}
}
}
5.i2c读写数据测试。将256字节数据写入,然后读出并比较,根据结果做相应日志输出。
uint8_t i2c_24c02_test(void)
{
uint16_t i;
uint8_t i2c_buffer_write[BUFFER_SIZE];
uint8_t i2c_buffer_read[BUFFER_SIZE];
printf("\r\nAT24C02 writing...\r\n");
/* initialize i2c_buffer_write */
for(i = 0; i < BUFFER_SIZE; i++) {
i2c_buffer_write[i] = i;
printf("0x%02X ", i2c_buffer_write[i]);
if(15 == i % 16) {
printf("\r\n");
}
}
/* EEPROM data write */
eeprom_buffer_write(i2c_buffer_write, EEP_FIRST_PAGE, BUFFER_SIZE);
printf("AT24C02 reading...\r\n");
/* EEPROM data read */
eeprom_buffer_read(i2c_buffer_read, EEP_FIRST_PAGE, BUFFER_SIZE);
/* compare the read buffer and write buffer */
for(i = 0; i < BUFFER_SIZE; i++) {
if(i2c_buffer_read[i] != i2c_buffer_write[i]) {
printf("0x%02X ", i2c_buffer_read[i]);
printf("Err:data read and write aren't matching.\n\r");
return I2C_FAIL;
}
printf("0x%02X ", i2c_buffer_read[i]);
if(15 == i % 16) {
printf("\r\n");
}
}
printf("I2C-AT24C02 test passed!\n\r");
return I2C_OK;
}
三.编译烧录测验
编译烧录后日志如下:
图2:I2C读写EEPROM
上面可以看到,i2c读写EEPROM AT24C02获得成功。