A little bit [off topic]
I've just learnied a few interesting things about eeproms while talking to some (way more) experienced friend than me.
2 links of interest, at least to me:
Back to [on topic]. Sorry
A little bit [off topic]
I've just learnied a few interesting things about eeproms while talking to some (way more) experienced friend than me.
2 links of interest, at least to me:
Back to [on topic]. Sorry
So, after the above code succeeded, I tried the following one:
#include <Wire.h>
#define SDA_PIN 4
#define SCL_PIN 5
const int I2C_SLAVE_ADDR7 = 0x57;
bool ok = true;
char myData[2] = {0x23, 0x24};
void setup(){
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
}
void loop(){
if(ok){
Wire.beginTransmission(I2C_SLAVE_ADDR7);
Wire.write(0x2f);
Wire.write(myData);
Wire.endTransmission();
delay(5);
/* ---- Read from EEPROM ---- */
Wire.beginTransmission(I2C_SLAVE_ADDR7);
Wire.write(0x2f);
Wire.endTransmission();
Wire.requestFrom(I2C_SLAVE_ADDR7, sizeof(myData));
Serial.print("Byte read: ");
Serial.print(Wire.read(), HEX);
Serial.print(" ");
Serial.println(Wire.read(), HEX);
ok = false;
}
}
I can only see the following in the serial monitor:
��l ��n�lByte read: 23 FF
So, what is wrong? Do I need to provide the "next" memory address within the block adress to read? Isn't there an internal pointer that increments itself after a successeful read or something like that?
//Wire.write(myData);
Wire.write(myData,sizeof(myData))
When you are writing a block of data you need to specify the length.
You also need to pay attention to your page boundaries as you can't block write across them.
Your starting memory address 2F is the last address in a page of 16 and your data will wrap to an address at the start of the block ( 0x20) rather than be written to 0x30.
Ok, I think I got it. Tomorrow I'll try to fix the thing!
Yet yesterday I tried to do a visual map of how the memory blocks, pages and addresses are organized.
Because I was a bit confused when you said that 0x2f was at the end of a 16 byte page. I was not sure how you knew that.
So, I came up with this thing. Is this kind of accurate?
I did 2 lines per page to keep the map smaller in width. But is this accurate? Only like this I can find 0x2f at the end of a page.
So, in this case, I do have to actually send another Wire.write()
command with the next address to write in?
See post #13.
Yes, its an accurate representation of the pages within a memory block.
So, in this case, I do have to actually send another
Wire.write()
command with the next address to write in?
Yes. You need to move the pointer across the page boundary.
The process for writing larger amounts of data (like your long character string) involves knowing the the starting address and number of bytes you need to write.
You figure out the number of bytes available on the starting page, write them, move to the starting byte of the next page. Since you are now at the start of a page you can write a full page of bytes. You do this until you get to the point where the remaining unwritten bytes will only take up a partial page and you write them.
Hobbytronix has a series of tutorials on external i2c eeproms and here's where they discuss page writing and how to do it.
https://www.hobbytronics.co.uk/eeprom-page-write
Some of the Arduino libraries for working with external i2c eeproms deal with this page restriction. You can look at them for models of how to do the page work, or you can make life more simple by just using them.
https://github.com/JChristensen/JC_EEPROM/blob/master/src/JC_EEPROM.cpp
I'm trying to write my own version of the code in that hobbytronics.co.uk example. However, I need to clear a question I have.
The example code from the above link doesn't take into account any other blocks of the device other than the one 0x50, right? So, we can only write up to the available space in block 0x50, yes?
The example code from the above link doesn't take into account any other blocks of the device other than the one 0x50, right? So, we can only write up to the available space in block 0x50, yes?
Yes. The tutorial is written for the larger 24LC256 eeprom which has a two byte storage cell address and only one device address.
You will need to manage block boundaries as well as page boundaries if your data requires you to write across both.
I had to stop for a few days with this code, but I'm slowly getting back to it.
I have one question.
To start a page write, we always need to use Wire.beginTransmission(0x..)
and after each page, we need to use Wire.enTransmission()
so that the buffer is actually written on the given address, right?
So the question is when I want to start to write in the next page, I need to use Wire.beginTransmission(last_address + 16)
and then Wire.endTransmission()
again, right?
In short, we need to begin and end transmission after each page write, yes?
Editied;
Also, another question:
If I understood correctly, the procedure in a page write, is to write the target address, then write one byte, then increment the target address by one byte, then write the next byte until we write 16 bytes, then we end transmission to get those 16 bytes actually written onto the eeprom. Is this correct?
Yes.
the procedure in a page write, is to write the target address, then write one byte, then increment the target address by one byte, then write the next byte until we write 16 bytes
This is not correct.
Internal to the wire library there is a buffer which holds the data to be sent with the end transmission statement. There is no incrementing of a storage address when filling that buffer. The data buffer can be filled with individual Wire.write(byte) statements or with as a group with Wire.write( multiByteData, numberOfBytes);
then we end transmission to get those 16 bytes actually written
You are correct in that the page of bytes is actually written with the end transmission statement.
Ok, but that still leaves a question (or yet another question) unanswered.
Please confirm (or deny) the following procedure for a page write.
1 - Wire.begin(deviceAddress)
2 - Wire.write(targetAddress)
3 - Wire.write(data, 16 bytes)
4 - Wire.endTransmission()
(question starts here)
5 - Repeat 2, 3 and 4 until and of string
or
5 - Repeat 1, 2, 3 and 4 until end of string
And then, if the last write operation is less than 16 bytes, can I write 16 bytes anyway (and have garbage? after the end of the string) or I need to write the specific amount of remaining bytes?
That is my understanding. But your syntax for step #1 is not correct
//1 - Wire.begin(deviceAddress)
1 - Wire.beginTransmission(deviceAddress)
And then, if the last write operation is less than 16 bytes, can I write 16 bytes anyway (and have garbage? after the end of the string) or I need to write the specific amount of remaining bytes?
I think you can write the full page if you want and have garbage, but none of the libraries I am familiar with do that. Because you know how long the string or long array you are writing is, there's no real penalty of writing a partial page at the end as your code should be calculating remaining unwritten bytes anyway in order to know whether to write another page.
Ok, I'm again back to this.
So, when we write a set of 16 bytes, I'm not sure about something.
For instance, let's say I have a 101 bytes. And the target address is 0x75.
First write will be 11 bytes.
Last write will be 10 bytes.
I'll need 7 pages to write these 101 bytes, where 1st and last pages are not 100% filled.
So, when I write a specific number of bytes, I also need to keep track of the index in the array of chars where the last write was?
I'm not sure I explained myself clearly. I'll try with an example.
Let's say I write the 1st 11 bytes. So, next time I write, I need to start at index 12 of the array and write 12 + 16 bytes. After this write, I'll be at array index 28 to start writing the next 16 bytes of the array. Then, I'll have to be at array index 44 (28 + 16) and so on.
Am I correct?
I don't think so. With array and address indexes which start at 0 you can sometimes get confused.
Let's say I write the 1st 11 bytes. So, next time I write, I need to start at index 12
The first 11 bytes will start at index 0 and end at index 10. The 12th byte will be at index 11.
It would probably help you to be doing some of this experimentally. If you write and read back, you can see if your ideas and calculations are correct. You can't hurt anything.
I know. I'm just ignoring it for simplicity. But I got the point.
This is what is guiding me at the moment:
I'm rather looking for the reasoning, to see if it is correct.
I'm trying to write a 101 bytes array of chars, starting at address 0x75.
I'm trying to compile the code I have right now but I'm getting errors I have no idea how to fix.
I'm calling the very normal function Wire.write(data, bytes)
and I'm getting errors like thiese (among others):
I'm calling the very normal function
Wire.write(data, bytes)
and I'm getting errors like these
Do not post images of code or monitor output. It's hard to read. Just copy and post the errors as text.
Please post the complete code throwing those errors.
Your char data[ ] may need to be defined as bytes or cast to bytes with a byte*.
Hi.
I just went back to this. I changed a few things in the code and the only thing missing, might be the way the reading and the writting processes are done. I still didn't get it right because examples from github are not even close to the scenario I have and documentation is lacking or even missing. What I can find is docs on the functions of the library and not actually an explanation on how the library works in a intuitive way.
For instance, I don't understand the difference between the following 2 functions:
Wire.begin();
Wire.beginTransmission();
From the docs it says it accepts as parameter, the 7-bit slave address, however, if I also give it the SDA, SCL and the device address as parameters, compilation doesn't complain about anything!
Wire.begin(SDA_PIN, SCL_PIN, I2C_SLAVE_ADDR7);
So is it supposed to accept more parameters or not?
Example from github also sends pins SDA and SCL as parameters.
Information across multiple supposedely official resources are contradictory, misleading or missing, I guess
Then, if it doesn't accept any other parameters, and if we need to change the pins for SDA and SCL lines,how do we do it?
Late yesterday I was able to use my readEEPROM()
function without crashing my ESP01 (it was rebooting over an over again before), but te only thing the function shows in serial port are -1-1-1-1-1-1-1-1...
.
So, not sure yet if the problem is yet the reaEEPROM()
or the writeEEPROM()
.
This is my readEEPROM()
function for now:
// Read bytes from eeprom device
void readEEPROM(uint16_t eepromaddr, int datalen){
char* data;
Wire.beginTransmission(I2C_SLAVE_ADDR7);
Wire.write(eepromaddr);
Wire.endTransmission();
Wire.requestFrom(I2C_SLAVE_ADDR7, datalen - 1);
for(uint8_t i = 0; i < datalen - 1; i++)
Serial.print(Wire.read());
}
My writeEEPROM()
is a long one with tons of comments, but the critical parts are the starting and ending of communication with te bus. That's where I need to make sure things are correct:
void writeEEPROM(byte blkaddr, byte intaddr, char* data){
uint8_t msglen = 0, bytes_left_in_page = 0, page_number = 1;
uint8_t data_fit = 0, avail_eemem = 0, last_write_size = 0;
uint8_t remaining_pages = 0, pages_needed = 0;
uint8_t next_addr_to_write = 0;
// Compute the length of the string
do{
msglen++;
}while(data[msglen]);
Serial.print("Mesage length: ");
Serial.println(msglen);
// For the given string in line 22, this should return 102. 101 + '\0'
// Given the address to write to (which is 0x75 as an example),
// we need to know in which page the target address is located in
// terms of pages, starting at page [0]
page_number = intaddr / PAGE_BUFFER;
// page_number = 7, ignoring the decimal part of the result, means we
// still have some bytes available in this page (step below follow).
// We need to know how much space is available in the current page
// from step above where we ignored the decimal part of the result
bytes_left_in_page = PAGE_BUFFER - (intaddr % PAGE_BUFFER);
// bytes_left_in_page = 11. This will be used to write byte-by-byte
//until the end of the page, then we can write entire pages at once.
// For 24LC16B, there are more x pages available for write
// including the page where the target address actually is
remaining_pages = NUM_OF_PAGES - page_number - 1;
// remaining_pages = 8
// Check how much memory there is available, starting from the
// target address
avail_eemem = MAX_MEMORY
- ( remaining_pages * PAGE_BUFFER - bytes_left_in_page );
// avail_eemem = 139
// Check if data fits the eeprom block memory capacity, starting from
// target address - [1] doesn't fit, [0] it fits
data_fit = msglen > avail_eemem ? 1 : 0;
// data_fit = 1
// Calculate size of last write
last_write_size = (msglen - bytes_left_in_page) % PAGE_BUFFER;
// last_write_size = 10
// Check how many pages we need to write the string
pages_needed = msglen % PAGE_BUFFER ? (msglen / PAGE_BUFFER + 1)
: msglen / PAGE_BUFFER;
// pages_needed = 7
// Check if data fits eeprom block memory
// then check if target address is at the beginning or at
// the end of a page. If it is, we write 16 bytes at once
// and then, we keep writting the remaining string
// always 16 bytes at a time, until last 16 whole bytes,
// then we just write the remaining final bytes of the array
if( !data_fit )
if ( !bytes_left_in_page || !(bytes_left_in_page % PAGE_BUFFER)){
// If we enter here, is because we are at the very end of a
// page or at the beginning of a page so, we can write whole pages at once
for(uint8_t i = 0; i < pages_needed; i++){
Wire.write(intaddr + i * PAGE_BUFFER);
Wire.write(&data[intaddr + i * PAGE_BUFFER], PAGE_BUFFER);
Wire.endTransmission();
delay(5); // delay for page write
if(i < pages_needed)
// if there are still pages to write, start transmission again,
// otherwise, don't
Wire.beginTransmission(blkaddr + i * PAGE_BUFFER);
}
}else{
// We are not at the beginning or end of any page
// Fill the remaining bytes of the current page
Wire.write(intaddr);
Wire.write(data, bytes_left_in_page);
Wire.endTransmission();
delay(5); // delay for page write
next_addr_to_write = intaddr + bytes_left_in_page;
// Fill next (complete) pages with 16 bytes
Wire.beginTransmission(blkaddr);
for(uint8_t i = 0; i < pages_needed - 2; i++){
Wire.write(next_addr_to_write + i * PAGE_BUFFER);
Wire.write(&data[(bytes_left_in_page + i * PAGE_BUFFER) + 1],
PAGE_BUFFER);
Wire.endTransmission();
delay(5); // delay for page write
if(i < pages_needed - 2)
// if there are still pages to write, start transmission again,
// otherwise, don't
Wire.beginTransmission(blkaddr);
}
// Last write
Wire.beginTransmission(blkaddr);
Wire.write((page_number + remaining_pages - 2) * PAGE_BUFFER);
Wire.write(&data[msglen - last_write_size], last_write_size + 1);
Wire.endTransmission();
delay(5); // delay for page write
}
else{
// Turn on a red led because data is larger than eeprom memory
}
}