DS24B33/DS2431 eeprom #882
Replies: 36 comments
-
Posted at 2015-01-14 by DrAzzy Cool, thanks - that gives me an idea how to talk to it. OW is so weird. I intend to write a module for the DS2431/33/28EC20 (the 20kbit one) this weekend, since I'm already in the process of overhauling the AT24 and AT25 modules for general streamlining and to support a wider range of parts. I can get sizes up to 2mbit supported pretty easy on AT24 (bringing it to 32-2048 kbit, with a second module for the smaller sizes being easy), and I think I can get support for all sizes on AT25 (no lower bound). Adding support for FRAM should be trivial. I'm honestly inclined to get rid of separate documentation for the AT24,AT25,DSxx, and instead replace it with one big "eeprom.md" which would describe the common interface provided by all the eeprom modules, list and link to said modules, and list considerations specific to each type of EEPROM. What do you think? |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by @gfwilliams Well, I'd implement a separate module for OneWire vs I2C devices at least (and even AT24/25 if you find the protocol is different). As we're after saving as much RAM as possible we don't want to be including code that's never used. I'd have a very basic
That will then automatically link in all the pages for the EEPROMs. By the way, Let me know if I can help at all... Can we try and simplify the API a bit and stick with reading Strings or Arrays (or Uint8Array via |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by DrAzzy Sounds good, I'll do that. I expect there will be 4 modules: OneWire (name TBD) I think the logic to support FRAM will be trivial and tiny - if it's not, I'll split it out into other modules. I ordered a couple of FRAM chips - I realized I actually have a use case where not caring about write cycles makes my life much easier. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by @gfwilliams Great, thanks! I thought that might be the case - just wanted to check :) So the lower-size AT24s use a significantly different protocol? It's going to be really handy - I did something recently on a Teensy and I'd forgotten just how handy the EEPROM was. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by @allObjects
Exactly what made me go for FRAM vs EEPROM... ;-) - recalling the challenges when building hardware where EPROMs needed those even for reading (...with then 'fast' micros: 4MHz Z80). What are the thoughts regarding the module name and the setup. I could imagine something more on the lines of external memory or just memory - MEMORY, **XMEMORY **, which includes the memory management logic, and it is connected by passing a connected driver for the specific memory attachment. The memory attachment could be something like OWMsomething, SPIMsomething, I2CMsomething, or even P(arallel)(4..8)Msomething. The something could then be generic or the whole driver name is device specific. Regarding the API for the MEMORY or XMEMORY, @gordon's suggestion works for me - or the memory manager API as suggested in response to @Gordon's post - because it is the bare necessity. For performing operations though - such as in graphics where I have the need - the memory manager may be a bit far away from the actual memory access code. What I experienced in writing the the suggested memory management code is the need for 1 byte, 2 bytes, and n byte reads and writes, where 1 byte and 2 bytes operation are more for the int type data and n bytes are string type data. With write() and read() begin the string type - with or without count for the read, I would like to have also write1(), write2(), read1(), and read2() (or alike). Those would then accept any data and take the LSByte(s) from numerics (ints) and first Byte(s) from any other reasonably suitable data type, and would return int. Furthermore, with smart arranged data in the memory, a first read with giving address could be followed by subsequent read that does not need passing the address again: read() with no argument. Furthermore, the API with writing without passing size(limit) and reading with size for bare metal operations looks to me as inconsistent. A write without limit is quite dangerous... I agree that matter on fact/nature of being on a micro everything is dangerous, that was the reason that my memory manager goes with the write() without size/limit for a dynamically managed memory space with garbage collection. I adds more complexity, sure, but it is a more robust/safe place. The passed address is therefore not the actual data area address but the address of the pointer to the data area. Adding an optional size argument in the bare metal write would solve the 'issue'. Even with the bare metal operations hidden behind/in a memory manager, the shining through of some of the operations - such as the read/write of 1 or 2 bytes and related data typing is very useful for application code. In the memory manager I used the notion of datatype. Currently I'm only supporting (assuming) string data, because that's what I needed (and took the extra app work to convert any other data types first into and afterwards from strings). The most other data type is key-value pair - with key a string, and value what ever will be supported. Beyond the string and int data type though, the bare metal should - and obviously does already - not care about. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by @gfwilliams
This is exactly what I hope to avoid - it is basically all duplicated code, when a single If you want these functions, maybe add another module that implements them on top of the basic read/write functions.
It's not really - just saving space. If you're doing |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by @allObjects See the point to avoid... and also the point that a wrapping layer can take care of the read/write 1, 2, or n. Regarding a robust/safe API: with the paradigm all power to the app, just taking the passed in data and write works. I'd like to prefer though having the support of an (optional) safe guard, which makes above paradigm and the bare metal version even more applicable... ...without any memory manager. Even though there is just a pointer passed, the pointer does not point to bare date (data bytes). The pointer points to an object which includes meta data... such as the length of the array/string... that's the reason that I actually never much liked C in an (business) application context, where tracking byte counts should not really be part of the problem to be solved. Saving space is a noble thing. Making the app waste it before every write to be safe not so much. Please no offense taken here. Are you considering of implementing the module natively? ...then saving space has a totally different ranking... ;) |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by @gfwilliams I think we're probably misunderstanding each other... These modules will be (and are) written as JavaScript, so when I say |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by DrAzzy Yeah - read2(adr) makes no sense to me, when it could be read(adr,2) - only then you can read as many as you want. Currently my modules have these functions: My thinking for the overhaul is: Remove readc(), and instead allow read(,bytes) to have same functionality (should be easy). Combine writeb() and writes() and detect the type of the data, and react appropriately. I think this will also shrink code size. That leaves reads() and writel() reads() is necessary to read long strings, because you need to read those piecewise and convert to string, and combine those, since you don't have the memory to read it in all at once as a simple array. I'm still thinking about what the right way to handle this is. writel() handles long writes (ie, ones longer than a page). I think I can probably make writel smart enough to determine when it needs to do this. I'm also toying with the idea of a couple of helper functions that you could add if you need, like easy ones for converting to signed/unsigned 16 bit numbers and stuff, whether in the modules, or in a second module (that would add methods to the first, like the font modules do). Re: small AT24's: Ones with 12-19 bits of address space are addressed using 2 bytes of data and up to 3 bits of the device address. Ones with 11 or less bits of address space are addressed using 1 byte of data and up to 3 bits of the device address. I hadn't given much thought to how to do this before, but there may be an easy way of doing it that I hadn't thought of before. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by DrAzzy Thoughts on how much sanity checking to provide? Catching people who call the functions with bad arguments makes things more user friendly, but it also makes the module bigger. Edit: AT24 is down to read() write() and writel()... You know how sometimes you look back at your old work and you're like, "man, was I that dumb back then?" Definitely did some things the hard way. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by JumJum Could we have commented errorchecking ? |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by DrAzzy You mean have the errors be turned on/off in the IDE? I cannot describe how much I would like to have some sort of preprocessor in the IDE that would let you do #define and #ifdef , and have that handled by the webide before it sends the code. Then you could just #define debug, and send the code and get error checking. But that's an IDE feature that doesn't exist (yet), so it's outside the scope of this topic. I've got AT24 converted to just read(add,bytes,asStr) and write(add,data), and cut 500 bytes from pre-minified code size, will try it out later tonight and see if it works correctly. If it does, converting AT25 should be a cinch. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by @allObjects I like the discussion... sort of... ?-) We all come from different requirement angles. Requirements we came across while trying a thing the best we know and understand - biased or not biased by past experience or immediacy of need. The angles are from usage, implementation options, feasibility, cost, and visionary point of views... Therefore, I don't want to get carried away (anymore) and look at at all the options. So far, I can do with the bare metal API what I want to achieve (in, for example, the memory manager), since it accepts arrays as well. To have some minimal support for int data type was just an idea. I do not have much knowledge what a function definition costs, and therefore - even if an additional layer or incrementally addable extensions may cost more with little extensions and for sure alway more in cycles, overall it may come in more beneficial. Extending incrementally - like Smalltalk could do add methods to given classes - can be done with additional require(s) - especially with the option of the 1v72+ of returning plain objects versus the 1v7 export functions only pattern, a great extension request @gordon added instantly. I do not know the cost, but for sure he would have objected if too costly. My time with Espruino changed my thinking about resourcefulness, even though I was already always challenging requirements with frugalness. On a related matter - but not related to passing optionally a length - I have the following question: Regarding space, what is the price of a variable definition var v = and its meta data for hosting any data type (I mean instance of class) vs. var v = {k:"", v:f} or var v = {k:i, v:f}. with i and f defined (upfront as global) variables referencing an int and function, respectively? (I know that variable name lengths matter, but for the calculation example I need only to know the minimum). - May be I can reliably find out myself with process.memory()... Can I? I was thinking to uses a new data type which is a reference and retrieves the value from (external) memory on access. I did not wanted to make it an additional data type of the language yet... but was kind of thinking of it... I'm sure you are getting where I'm heading to or for. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-15 by DrAzzy mmm, yeah - I see your point on 16 bit integers, since they're such a common use case, there's a need to have code to do that readily available. Thinking about where to put it. What we're mostly concerned about, I think, is the memory footprint of the modules, and helping users stay out of jail, as you say - particularly since one of the use-cases for an eeprom is storing code that you're out of RAM for. I think process.memory() works for seeing how much memory something takes up. It wouldn't really have much point if it didn't. There's also E.getSizeOf(), which i think now returns accurate data (at one point modules confused it). |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-16 by DrAzzy The helper functions to encode and decode 32-bit signed ints are easy (i picked 32-bit because that's what espruino uses for ints). My vote is that if you want that, you do: rom.write(0,rom.nToS(1231864)); Those two functions cost 16 jsvars (hand minified cause I hit the cap) combined - this probably is okay? New AT24 is working now. Next is AT25. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-18 by @allObjects Looks great... and takes care of the page stuff transparently. I know the usual way is to export only the connect(). The connect hides the new. With 1v72 you could return the Exposing the class itself allows to add/modify the prototype by additional, There are other helpful effects coming along: returning the class allows to name it what ever the user wants it to have... to overcome the typical name space issue in JavaScript (taken care of in requre.js/AMD module technique for the browser). Loading a bare metal OWmem and extending it with what ever space allows and benefits the application the most. I could even see your 'room for improvement' methods using this technique: in bare metal empty defined but already called, and allowed to be overridden on demand. Other methods could be any data conversion types / helper methods to be added Dynamic extensions should work with minification. I do though not know about how it works with save(). |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-19 by @gfwilliams Looks great, Shouldn't Can we change the name to something like What do you think about:
Not sure what you think of @allObjects suggestion about connect? I guess you could do something like:
So we keep the 'connect' form which is used all over, but if you don't call |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-19 by DrAzzy Re: i==3 Re: allObjects' comment about connect - Hmm. Not sure what to do here. Am I understanding correctly that the issue is that if you don't export the whole class, then you can't add things to the prototype? And thus, if you had multiple eeproms and wanted to add somethign to the prototypes (like the helper functions I mentioned above for converting numbers to bytes or w/e). What are the disadvantages of exporting the class? DS2xxx? Done. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-19 by @gfwilliams
Ahh, right! It still makes me cringe a bit :) Maybe just add a comment as to why it's like that? If the Does the delay after writing seem to be a common thing? If so it might be worth adding a 'finished' callback to the API?
Well, you can, it's just ever so slightly more difficult:
Personally I'm not sure it's really worth the extra effort and memory usage to make it a tiny bit easier for the few people who would want to extend the basic class. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-19 by DrAzzy All the eeproms have a delay after writing. I do not know how I would go about making the write process not block while writing several pages, though, since you have to keep writing, then waiting, writing, then waiting. It seems like a nightmare to try to do it with callbacks. Any way I can think of comes with a monstrous overhead. yeah, good call on commenting about the i==3. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-19 by @allObjects
With binding the number of arguments to a function is creating a dependency that may come back biting. For sure it is an efficient way to manage the power. If the device has a NOP after the command, this could be used as an escape by artificially extending the command. What ever other four-byte command could then just add the NOP to escape. An alternative to that could be an additional parameter when writing a four byte command, but with the frequency the command is called, it creates overhead. Since until now - and this is a while - it is the only four byte command and thus could be considered rare, I though would stick with the i == 3. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-19 by @allObjects
What are the possible (bad) side effects when having first to connect to get to the prototype via a - throw-away - instance? In a language where classes are 1st class objects, I just would add connect() as a class (~static) method, which creates the object, issues the connect, and returns the object. In JavaScript this would be adding the connect as a 'strange' method to the constructor (or class) function. Would that be safe/robust enough in regard to side effects? If we want to stick with creating a throw-away through connect() to access the prototype, passing no parms to the connect could be the way out of the dilemma. A connect with no passed parms could return the instance right after the new - without following through to the end. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-19 by @gfwilliams
Who said it was throw-away?
I think that's fine, and it has the nice side-effect that we don't have to re-write any of the existing modules. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-19 by @allObjects For keeping consistency it has to be a throw-away, because quite many connects actually do something and would create an object in a state one is most likely not interested in. If otherwise, the modification can just be sticked to the instance - as per the beauty of JavaScript. Therefore, I came up with the idea of passing no parms which makes it comparable with the default constructor in Java and it can be assured that nothing unwanted happens. Going that route is the easiest for keeping the require().connect() pattern vs. returning class. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-20 by DrAzzy I just realized a really simple way to do conversions to arrays of bytes for saving...
And so on... |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-20 by @gfwilliams @drazzy yes, and you can actually do it on an existing buffer, so for instance:
I guess you could do:
And another nice one - although you don't use sToA:
|
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-20 by DrAzzy Woah, ya, I forgot about .set() |
Beta Was this translation helpful? Give feedback.
-
Posted at 2016-07-29 by Cale Does the DSxxx modules still work? I can not seem to make it work with ds2431 |
Beta Was this translation helpful? Give feedback.
-
Posted at 2016-07-31 by Cale N/M got it. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2016-08-30 by maxogden I couldn't get either the DS2xxx module or the code examples in this thread to work for writing to a DS2431, so I ended up porting some Java code I found to JavaScript and writing worked https://gist.github.com/maxogden/c0b33da285e0fe809a8aa7009bc510f7 // Used with GeekHeart https://www.tindie.com/products/MakersBox/geekheart/
var ow = new OneWire(4) // D2 on NodeMCU
var code = ow.search()[0] // will be empty array if it failed to find your DS2431
// write a Uint8Array of arbitrary length 8 bytes at a time with a 100ms delay (add callback if you need it)
function write (data) {
var buf = new Uint8Array(13)
var row = 0
function next () {
var pos = row * 8;
for (var i = 0; i < 8; i++) {
buf[i] = data[pos + i]
}
write8(pos, buf);
if (pos + 8 >= data.length) return console.log('done');
row++;
if (row >= 16) return console.log('done'); // EEPROM has 16 rows of 8 bytes
setTimeout(next, 100);
}
next()
}
// write 8 bytes to addr offset
// example write8(0, new Uint8Array([1,1,1,1,1,1,1,1)
function write8 (addr, data) {
console.log(addr, data)
var i
ow.reset();
ow.select(code);
ow.write(0x0F, 1); // Write ScratchPad
ow.write(addr, 1);
ow.write(0x00, 1);
for ( i = 0; i < 8; i++)
ow.write(data[i], 1);
ow.reset();
ow.select(code);
ow.write(0xAA); // Read Scratchpad
for ( i = 0; i < 13; i++)
data[i] = ow.read();
ow.reset();
ow.select(code);
ow.write(0x55, 1); // Copy ScratchPad
ow.write(data[0], 1);
ow.write(data[1], 1); // Send TA1 TA2 and ES for copy authorization
ow.write(data[2], 1);
}
// read num bytes from addr offset
// example read(0, 8)
function read(addr, num) {
ow.reset();
ow.select(code);
ow.write(0xF0); // read
ow.write(addr&0xFF);
ow.write(addr>>16);
var buf = new Uint8Array(num)
for (var i = 0; i < num; i++) buf[i] = ow.read()
return buf
}
// same as read() but decodes into string
function readString (addr, num) {
var buf = read(addr, num)
var str = ''
for (var i = 0; i < buf.length; i++) {
str += String.fromCharCode(buf[i])
}
return str
}
// write a long string starting at addr 0
function writeString (str) {
var data = new Uint8Array(str.split('').map(function (c){ return c.charCodeAt(0) }))
write(data)
} |
Beta Was this translation helpful? Give feedback.
-
Posted at 2015-01-14 by @gfwilliams
Hi, I know @drazzy was going to look at this, but the bits arrived and I couldn't resist.
Note: you need a 0.2-2.2k pullup resistor - so pretty hefty (not like the ~10k you can get away with on the DS18B20).
I'll stick it in a module soon.
Beta Was this translation helpful? Give feedback.
All reactions