From 7036ca076ae2da712914c04409872eab2aadfbdb Mon Sep 17 00:00:00 2001 From: Robin Krens Date: Mon, 12 Aug 2019 21:39:32 +0800 Subject: [PATCH] pretty print EEPROM --- drivers/at24c.c | 528 +++++++++++++++++++++++------------------------- include/drivers/at24c.h | 18 +- main.c | 4 +- 3 files changed, 272 insertions(+), 278 deletions(-) diff --git a/drivers/at24c.c b/drivers/at24c.c index 449c606..7bf900a 100644 --- a/drivers/at24c.c +++ b/drivers/at24c.c @@ -1,10 +1,23 @@ /* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL * * $LOG$ - * 2019/7/25 - ROBIN KRENS + * 2019/8/11 - ROBIN KRENS * Initial version * * $DESCRIPTION$ + * Driver for AT24C256 EEPROM for STM32 based boards. Communication protocol is I2C + * Maximum write in limited to 64 bytes. Reading bytes is unlimited, but memory should + * be allocated accordingly. + * + * I2C protocol : (W)rite or (R)ead + * MCU is in master mode: either as transmitter or receiver + * | P | DEVICEADDR + R/W bit | ACK | ADDR_PART1 | ACK | ADDR_PART2 | (d+a*n) | (N)ACK | S + * Sending data: wait for ACK from EEPROM + * Receiving data: send (n)ACK to EEPROM after receiving data + * + * STM32F1 microcontrollers do not provide the ability to pull-up SDA and SCL lines. Their + * GPIOs must be configured as open-drain. So, you have to add two additional resistors to + * pull-up I2C lines. Something between 4K and 10K is a proven value. * * */ @@ -17,102 +30,45 @@ #include #include -#include #include #include #define TIMEOUT 5000 +#define READ_CMD 0xA1 +#define WRITE_CMD 0xA0 +#define PAGE 64 /* Bytes that can be written continiously */ +#define BUFFER 64 /* Reading buffer */ -#define READ_CMD 0xA1 -#define WRITE_CMD 0xA0 -#define PAGE 64 /* Bytes that can be written continiously */ -#define BUFFER 4 /* */ - -/* STM32F1 microcontrollers do not provide the ability to pull-up SDA and SCL lines. Their -GPIOs must be configured as open-drain. So, you have to add two additional resistors to -pull-up I2C lines. Something between 4K and 10K is a proven value. -*/ static char eeprombuf[BUFFER]; -void * cap_handler() { - - printf("a!"); -} - -void at24c_init() { +void eeprom_at24c_init() { - /* Program the peripheral input clock in I2C_CR2 Register - * in order to generate correct timings. Configure the clock control registers CCR - Configure the rise time register TRIS Program the I2C_CR1 register to enable the peripheral Enable GPIOB6 and B7*/ + /* Program the peripheral input clock generate correct timings. + * Configure the clock control registers CCR + * Configure the rise time register TRIS + * Program the I2C_CR1 register to enable the peripheral + * Enable GPIOB6 and B7*/ - rsetbit(RCC_APB1ENR, 21); - rsetbit(RCC_APB2ENR, 3); - rwrite(GPIOB_CRL, 0xEE444444); + rsetbit(RCC_APB1ENR, 21); // enable GPIOB + rsetbit(RCC_APB2ENR, 3); // enable I2C + rwrite(GPIOB_CRL, 0xEE444444); // open-drain rsetbitsfrom(I2C_CR2, 0, 0x2); // 2 MHz rwrite(I2C_TRISE, 0x3); // MAX = 1000ns, TPCLK1 = 500ns (+1) - rwrite(I2C_CCR, 0x000A); // standard mode, output 100 kHz (100hz* / perip) - rsetbit(I2C_CR1, 10); // send ack if Master receives data + rwrite(I2C_CCR, 0x000A); // standard mode rsetbit(I2C_CR2, 10); // buffer interrupt rsetbit(I2C_CR1, 0); // enable } -static void start_condition() { - rsetbit(I2C_CR1, 8); //start -} - -static void stop_condition() { - rsetbit(I2C_CR1, 9); //stop -} - -static int ack_recv() { - int cnt = 0; - while(!(*I2C_SR1 & 0x2)) { - cnt++; - if (cnt > TIMEOUT) - return 0; - } - - int a = *I2C_SR2; - return 1; -} - -static int buf_empty() { - int cnt = 0; - while(!(*I2C_SR1 & 0x80)) { - cnt++; - if (cnt > TIMEOUT) - return 0; - } - return 1; -} - -// TODO: interrupt base, so it doesn't block -static void data_recv() { - while(!(*I2C_SR1 & 0x40)) { - } -} - -static void late_recv() { - while(!(*I2C_SR1 & 0x4)) { - } -} - -// TODO: polling -static int delay() { - - int a = 0; - for (int i = 0; i < 0xFFFF; i++) - a++; -} - +/* Writes data to the EEPROM starting at address addr. Size can not + * be more than one PAGE (64 bytes) */ int eeprom_write(uint16_t addr, char * data, size_t size) { if(size > PAGE) { - printf("Maximum writable page size: %d\n", PAGE); + printf("Error: Maximum writable page size: %d\n", PAGE); return -1; } @@ -120,256 +76,280 @@ int eeprom_write(uint16_t addr, char * data, size_t size) { start_condition(); rwrite(I2C_DR, WRITE_CMD); - if(!ack_recv()) { - printf("Can't reach device"); + if(!ack_recv()) return -1; - } + rwrite(I2C_DR, hi_lo[0]); // higher part of address - if(!buf_empty()) { - printf("Can't address location"); + if(!buf_empty()) return -1; - } + rwrite(I2C_DR,hi_lo[1]); // lower part of address - if(!buf_empty()) { - printf("Can't address location"); + if(!buf_empty()) return -1; - } + for (int i = 0; i < size; i++) { rwrite(I2C_DR, *data++); - if(!buf_empty()) { - printf("Write error"); + if(!buf_empty()) return -1; - } + } stop_condition(); + delay(); // wait for write action to finish + return 0; +} + +/* Erase (write all 0xFF) data on EEPROM + * Maximum hardware allowed sequential writes of 64 bytes */ +int eeprom_erase() { + + uint16_t cur_addr = 0x0000; + + for (int i = 0; i < 512; i++) { + + printf("Writing at address: %#x (64 bytes) ", cur_addr); + uint8_t hi_lo[] = { (uint8_t)(cur_addr >> 8), (uint8_t)cur_addr }; + + start_condition(); + rwrite(I2C_DR, WRITE_CMD); + if(!ack_recv()) + return -1; + + + rwrite(I2C_DR, hi_lo[0]); // higher part of address + if(!buf_empty()) + return -1; + + rwrite(I2C_DR,hi_lo[1]); // lower part of address + if(!buf_empty()) + return -1; + + + for (int i = 0; i < PAGE; i++) { + rwrite(I2C_DR, 0xFF); // write all ones + if(!buf_empty()) + return -1; + + } + stop_condition(); + printf("[COMPLETE]\n"); + cur_addr += 0x40; // 64 bytes; next PAGE + delay(); // wait for write to finish + } return 0; } -/* Random access read based on dummy write */ -int eeprom_read(uint16_t addr, int num) { - printf("ENTERING"); +/* Random access read of num bytes + * Initialize dummy write first to set correct address location + * The read function differentiate between num = 1 num = 2 and num > 3 + * Data is saved to rvalues */ +int eeprom_read(uint16_t addr, int num, char * rvalues) { + uint8_t hi_lo[] = { (uint8_t)(addr >> 8), (uint8_t)addr }; /* Dummy write to set address */ start_condition(); rwrite(I2C_DR, WRITE_CMD); - if(!ack_recv()) { - printf("Can't reach device"); + if(!ack_recv()) return -1; - } rwrite(I2C_DR, hi_lo[0]); // higher part of address - if(!buf_empty()) { - printf("Can't address location"); + if(!buf_empty()) return -1; - } + rwrite(I2C_DR,hi_lo[1]); // lower part of address - if(!buf_empty()) { - printf("Can't address location"); + if(!buf_empty()) return -1; - } + stop_condition(); - delay(); // NEEDED? + delay(); // wait form EEPROM + + switch(num) { + case 1: + start_condition(); // restart condition + rwrite(I2C_DR, READ_CMD); // read? to address CMD + if(!ack_recv()) + return -1; + stop_condition(); + + if(!data_recv()) + return -1; + + rvalues[0] = (char) *I2C_DR; + rvalues[1] = '\0'; + break; + case 2: + + rsetbit(I2C_CR1, 10); // set ACK + rsetbit(I2C_CR1, 11); // set POS + start_condition(); // restart condition + rwrite(I2C_DR, READ_CMD); // read to address CMD + if(!ack_recv()) + return -1; + rclrbit(I2C_CR1, 10); // clear ACK + if(!late_recv()) + return -1; + + stop_condition(); + + rvalues[0] = (char) *I2C_DR; + rvalues[1] = (char) *I2C_DR; + rvalues[2] = '\0'; + break; + + default: + rsetbit(I2C_CR1, 10); // set ACK + start_condition(); // restart condition + rwrite(I2C_DR, READ_CMD); // read to address CMD + if(!ack_recv()) + return -1; + + for(int i = 0; i < num-3; i++) { + if(!data_recv()) + return -1; + rvalues[i] = (char) *I2C_DR; + } + + if(!late_recv()) + return -1; + + rclrbit(I2C_CR1, 10); + rvalues[num-3] = *I2C_DR; + stop_condition(); + rvalues[num-2] = *I2C_DR; + if(!data_recv()) + return -1; + + rvalues[num-1] = *I2C_DR; + rvalues[num] = '\0'; + } + return 0; - if (num == 1) { - start_condition(); // restart condition - rwrite(I2C_DR, READ_CMD); // read? to address CMD - if(!ack_recv()) { - printf("Can't initiate read"); - return -1; - } - stop_condition(); - data_recv(); - char c = (char) *I2C_DR; - printf("DATA: %c\n", c); - } +} - else if (num == 2) { - rsetbit(I2C_CR1, 10); // set ACK - rsetbit(I2C_CR1, 11); // set POS - start_condition(); // restart condition - rwrite(I2C_DR, READ_CMD); // read to address CMD - if(!ack_recv()) { - printf("Can't initiate read"); - return -1; - } - rclrbit(I2C_CR1, 10); // clear ACK - late_recv(); - stop_condition(); - char c = (char) *I2C_DR; - char c2 = (char) *I2C_DR; - printf("DATA: %c,%c\n", c, c2); - } +/* Dump all data on EEPROM to std out */ +int eeprom_dump() { - else if (num > 2) { - rsetbit(I2C_CR1, 10); // set ACK - start_condition(); // restart condition - rwrite(I2C_DR, READ_CMD); // read to address CMD - if(!ack_recv()) { - printf("Can't initiate read"); + uint16_t curr_addr = 0x0000; + + for (int i = 0; i < 512; i++) { + + if(eeprom_read(curr_addr, 64, eeprombuf) == -1) { + printf("Error: Can't (continue) dump"); return -1; - } - for(int i = 0; i < num-3; i++) { - data_recv(); - eeprombuf[i] = (char) *I2C_DR; } - late_recv(); - rclrbit(I2C_CR1, 10); - eeprombuf[num-3] = *I2C_DR; - stop_condition(); - eeprombuf[num-2] = *I2C_DR; - data_recv(); - eeprombuf[num-1] = *I2C_DR; - eeprombuf[num] = '\0'; - printf("DATA: %s\n", eeprombuf); + printf("%#x:\n", curr_addr); + for (int i = 0; i < strlen(eeprombuf); i++) { + printf("%x ", eeprombuf[i]); + if (((i % 16) == 0) && (i != 0)) + printf("\n"); + } + printf("\n"); + curr_addr += 0x40; // 64 bytes } + return 0; +} +/* HELPER SUBROUTINES */ -//W rsetbit(I2C_CR1, 10); // send ack if Master receives data -//W// data_recv(); -//W// char c = *I2C_DR; -//W// printf("%d:%p\n", addr, c); -//W for (int i = 0; i < BUFFER ; i++ ) { -//W data_recv(); -//W buf[i] = (char) *I2C_DR; -//W } -//W -//W printf("%p, %p, %p, %p\n", *I2C_SR1, *I2C_SR2, *I2C_DR, *I2C_CR1); -// printf("DATA: %s\n", buf); - +static void start_condition() { + rsetbit(I2C_CR1, 8); //start bit } -void at24c_run() { - -// char * gd = "abcd"; -// eeprom_write(0x0000, gd, strlen(gd)); +static void stop_condition() { + rsetbit(I2C_CR1, 9); //stop bit +} -// delay(); -// char * global_data = "abcdefghijklmnop"; +/* Initial ACK received after address lookup + * read registers clear ADDR bit */ +static int ack_recv() { + int cnt = 0; + while(!(*I2C_SR1 & 0x2)) { + cnt++; + if (cnt > TIMEOUT) { + printf("Error: Can't reach device\n"); + return 0; + } + } + int a = *I2C_SR2; + return 1; +} -// eeprom_write(0x0080, global_data, strlen(global_data)); - -// delay(); - -// for (int i = 0; i < 0xFFFF; i++) { -// eeprom_read(i); -// delay(); -// } - - //delay(); - -// delay(); -// eeprom_read(0x0000, 1); -// delay(); -// eeprom_read(0x0000, 2); - delay(); - eeprom_read(0x0000, 4); - delay(); - -//W uint32_t statusr; -//W -//W start_condition(); -//W rwrite(I2C_DR, WRITE_CMD); // write to address CMD -//W if(!ack_recv()) -//W cputs("CAN'T REACH DEVICE"); -//W -//W rwrite(I2C_DR, 0x00); -//W if(!buf_empty()) -//W cputs("FAIL"); -//W rwrite(I2C_DR, 0x03); -//W if(!buf_empty()) -//W cputs("FAIL"); -//W //rwrite(I2C_DR, 0x61); -//W //if(!buf_empty()) -//W // cputs("FAIL"); -//W -//W //statusr = *I2C_SR1; -//W //statusr = *I2C_SR2; -//W stop_condition(); - -// start_condition(); -// rwrite(I2C_DR, 0xA0); // dummy write -// if(!ack_recv()) -// cputs("CAN'T REACH DEVICE"); -// -// rwrite(I2C_DR, 0x00); -// if(!buf_empty()) -// cputs("FAIL"); -// rwrite(I2C_DR, 0x00); -// if(!buf_empty()) -// cputs("FAIL"); -// -//W delay(); -//W -//W start_condition(); // restart condition -//W rwrite(I2C_DR, 0xA1); // read? to address CMD -//W if(!ack_recv()) -//W cputs("COULDN'T START READ CMD"); -//W -//W -//W data_recv(); -//W //cputs("NO RESPONSE"); -//W -//W char a = (char) *I2C_DR; -//W printf("DATA %c\n", a); -//W -//W stop_condition(); - //delay(); - - //start_condition(); - //statusr = *I2C_SR1; // clear start_signal - //regw_u32(I2C_DR, 0xC1, 0, OWRITE); - //if(!ack_recv()) - // cputs("TIMEOUT2!"); - //statusr = *I2C_SR1; - //statusr = *I2C_SR2; - //regw_u32(I2C_DR, 0x7D, 0, OWRITE); - //if(!buf_empty()) - // cputs("TIMEOUT3!"); - //stop_condition(); - -/* delay(); +/* Check send buffer + * Note: BLOCKING function */ +static int buf_empty() { + int cnt = 0; + while(!(*I2C_SR1 & 0x80)) { + cnt++; + if (cnt > TIMEOUT) { + printf("Error: Can't send data\n"); + return 0; + } + } + return 1; +} - start_condition(); - statusr = *I2C_SR1; - regw_u32(I2C_DR, DISPLAY_ON, 0, OWRITE); - if(!ack_recv()) - cputs("TIMEOUT4!"); - stop_condition(); */ +/* Check data receive buffer + * Note: BLOCKING function */ +static int data_recv() { + int cnt = 0; + while(!(*I2C_SR1 & 0x40)) { + cnt++; + if (cnt > TIMEOUT) { + printf("Error: Timeout receiving data\n"); + return 0; + } + } + return 1; +} +/* Similar as above, waits for two packages to be received + * one in DR and one in the shadow register */ +static int late_recv() { + int cnt = 0; + while(!(*I2C_SR1 & 0x4)) { + cnt++; + if (cnt > TIMEOUT) { + printf("Error: Timeout receiving data\n"); + return 0; + } + } + return 1; - /* regw_u32(I2C_CR1, 0x1, 8, SETBIT); //start - uint32_t read_status = *I2C_SR1; - regw_u32(I2C_DR, 0x40, 0, OWRITE); // write to address CMD - read_status = *I2C_SR1; - read_status = *I2C_SR2; - regw_u32(I2C_CR1, 0x1, 9, SETBIT); //stop - read_status = *I2C_SR1; +} - regw_u32(I2C_CR1, 0x1, 8, SETBIT); //start - read_status = *I2C_SR1; - regw_u32(I2C_DR, 0xC1, 0, OWRITE); // segment address - read_status = *I2C_SR1; - read_status = *I2C_SR2; - regw_u32(I2C_DR, 0x7D, 0, OWRITE); // write a six +/* Write delay form EEPROM chip */ +static int delay() { + int a = 0; + for (int i = 0; i < 0xFFFF; i++) + a++; +} - regw_u32(I2C_CR1, 0x1, 9, SETBIT); //stop - read_status = *I2C_SR1; - regw_u32(I2C_CR1, 0x1, 8, SETBIT); //start - read_status = *I2C_SR1; +int eeprom_test() { + char * gd = "Testing the EEPROM chip AT24C256 write and read function"; + eeprom_write(0x1000, gd, strlen(gd)); + + uint16_t curr_addr = 0x1000; - regw_u32(I2C_DR, DISPLAY_ON, 0, OWRITE); - read_status = *I2C_SR1; - regw_u32(I2C_CR1, 0x1, 9, SETBIT); //stop */ + for (int i = 0; i < 4; i++) { + if(eeprom_read(curr_addr, 16, eeprombuf) == -1) { + printf("Can't (continue) dump"); + return -1; + } + printf("%#x: ", curr_addr); + for (int i = 0; i < strlen(eeprombuf); i++) { + printf("%x ", eeprombuf[i]); + } + printf("\n"); + curr_addr += 0x10; // 16 bytes + } } - diff --git a/include/drivers/at24c.h b/include/drivers/at24c.h index ef1e09c..c5e6026 100644 --- a/include/drivers/at24c.h +++ b/include/drivers/at24c.h @@ -1,7 +1,21 @@ #ifndef __AT24C_H #define __AT24C_H -extern void at24c_init(); -extern void at24c_run(); +/* HELPER SUBROUTINES DECLARATIONS */ +static void start_condition(); +static void stop_condition(); +static int ack_recv(); +static int buf_empty(); +static int data_recv(); +static int late_recv(); +static int delay(); + +extern void eeprom_at24c_init(); +extern int eeprom_write(uint16_t addr, char * data, size_t size); +extern int eeprom_erase(); +extern int eeprom_read(uint16_t addr, int num, char * rvalues); +extern int eeprom_dump(); + +extern int eeprom_test(); #endif diff --git a/main.c b/main.c index 0390040..51c2b10 100644 --- a/main.c +++ b/main.c @@ -67,8 +67,8 @@ void main() // run(); led_init(); - at24c_init(); - at24c_run(); + eeprom_at24c_init(); + eeprom_test(); // rtc_init(); // tm1637_init(); -- 2.7.4