-
Breaking up an SPI transfer - as far as I know - is possible - because the clock defines what is going on (and of course some other pins too). This is a main reason why clocked communication is so much more faster and more reliable than timed communication. For Espruino, the only penalty can be performance by going back and forth form JavaScript to firmware SPI.send implementation (as I read in Espruino doc).
WP and HOLD held high
WP and HOLD are hardwired to high.
CS NOT floating
I know that CS is NOT floating. Your implementation makes totally sense... but I assume that because of FRAM/MRAMs can handle much faster transfers, the timing can be a challenge... We know that in the past, the CS got raised to fast and communication did not complete - and this has been fixed. Furthermore, I do not know what Espruino - and the chip - 'really' do when changing state on the CS output pin with automatic pinMode() when applying set(), reset().
-
@Gordon, I kept the entry as short as possible in this thread and is new in 1v72. May be a '1v72 caveats' thread could hold on to things that can be handled in a 1..2 liner, similar to 'known issues' and change log.
How can I delete post? - or ask admin to delete it?
-
AT25 #MRAM #FRAM #1v72: add pinMode(pin,"output"); or external pull-up resistor on CS to guarantee read();
Update: The seemed to be a flashback bug... therefore: watch closely what the default flashing does.
-
Just tested with brand new 1v72... #FRAM / #MRAM with AT25 module is not working anymore, even when doing an extra read before the real read, I get all the times 0xFF... Definitely, at least a pull-up is required. Adding a pinMode(pin,"output"); get's it back to work. I assume the better performance in 1v72 - and the 18Mhz SCK (connected FRAM/MRAM can handle 40MHz) - provides not enough stability on the CS with having it floating...
-
CS / line as open drain with (external) pull-up
How about adding something to connect() that gives this option, like the string parameter for the pinMode()?
So far the issue was only on power cycle...
On the other hand, CS is essential, especially in a bus concept - where it is (conceptually) the only private line, a floating CS line calls for unpredictable effects. Therefore, pull-up - internal or external - is the minimum to be applied in any setup.
Also, a lot of devices observe the power and cs line cycle to get setup in a predictable state.
-
-
[1] write(add,data,num):
AT25.prototype.write= function(add,data,num) { var l=data.length,g; if(typeof data!="string"){data=E.toString(data);} var idx=0; while (idx < l) { this.spi.send(6,this.cspin); //WREN var i=this.pgsz?(this.pgsz-(add%this.pgsz)):l; var t=(this.cap>65536)?E.toString(2,add>>16,add>>8,add):E.toString(2,add>>8,add); t=t+data.substr(idx,i); this.spi.send(t,this.cspin); var et=getTime()+0.012; while (getTime() < et && this.pgsz) {g="";} idx+=i; add+=i; } return l;
[2] write(a,d) w/ address, data:
, write: function(a,d){ spi.send(0x06,cs); cs.reset(); spi.send([0x02,a>>8,a]); spi.send(d); cs.set(); return d.length; }
[1] is and has to be elaborate to adhere to the EEPROM's page size restrictions / buffering and page write timing. It uses the pgsz page size information as provided in the connect for catering as optimal as possible to FRAM/MRAM, which has none of the EEPROM limitations, not even even in regard of the virtual limit of rewrites. Addresses are also handled capacity dependent.
[2] is geared towards FRAM/MRM and can therefore be leaner.
Major difference - again - is the command w/ address and data send sequence: in one shot or two (per page for EEPROM and per write for FRAM/MRAM. Some juggling around could eventually increase performance a bit - I expect. 'Insiders' would be able to tell if something like this is more frugal in resource consumption (even though having an additional spi.send() invocation):
See 3 code variations as attachment of this post..
-
[1] read():
AT25.prototype.read= function(add,bytes) { if (add===undefined) { add=this.ca; } var t=new Uint8Array(bytes+(this.cap>65536?4:3)); var i=0; t[i++]=3; if(this.cap>65536){t[i++]=add>>16;} t[i++]=add>>8; t[i]=add; var ov=this.spi.send(t,this.cspin); this.ca=add+bytes; return new Uint8Array(ov.buffer,(this.cap>65536?4:3),bytes); }
[2] read(a,n) w/ adress and number of bytes:
, read: function(a,n){ cs.reset(); spi.send([0x03,a>>8,a]); return spi.send(new Uint8Array(n),cs); }
[1] enables an easy read next by allowing to call ~
.read(undefined,bytes);~ to get the next bytes following a previous read(s) that provided an initial address. Since AT25 is also geared towards large capacity EEPROM memory, it supports size dependent 2 and 3 bytes addressing. After reading the specified number of bytes, the next address to read from is kept for a simple read next bytes. @DrAzzy, what is the reason for creating Uint8Array for returning the value(s)? Is spi.send() not returning already an Uint8Array in ov variable? - And if it returns a string, it is about - with 1v72's flat strings and arrays - as efficient as Uint8Array in both cases, where enough memory is still available for a contiguous block to allocate and for the regular, chained storage block allocation. If the user does not like the result as a string, 'conversion-overlay' is easy (as shown), and could even be off different format.[2] having no state and being singleton, there is no support for read next. Since [2] is geared towards FRAM/MRAM - which are not as popular - and more pricy - and usually of smaller capacity, only two byte addressing is supported. Extension to size dependent number of address bytes is desirable, since recently larger chips showed up on the market and market presence is growing.
Most difference between [1] and [2] are in write sequence. Even though both suffer from having to crate a trow away buffer to keep the sck pin clocking, [1] includes the command w/ address AND 'clock bytes' in that buffer with an uses a single spi.send(), where as [2] sends command w/ address and data using separate *spi.send()*s.
*It is suggested to add to spi.send() the same parameter object option as spi.write() has, where a single byte and a count keep the SCK line clocking without sacrificing memory for throw away. The key part of a read would then look like this:
return spi.send({ data: 0x00, cnt: n }; // 0x00 or other suitable: 0xAA
The advantage of sending all at once is the use of integrated CS handling. [2] can use it only for the data and uses a separate reset() before sending command w/ address.
-
@DrAzzy, let's compare the pieces from [1]=your and [2]=my code.
setup [1]:
exports.connect = function(spi, pgsz, cap, cspin) { if (cap > 4096 || !cap || pgsz > cap || !cspin) { throw "Unsupported or invalid options"; } cspin.set(); //pull the CS line high. return new AT25(spi, pgsz, cap, cspin);
setup [2]:
var framspiModule = { connect: function(spi,cs,hd,wp) { pinMode(cs,"output"); cs.set(); if (hd) { pinMode(hd,"output"); hd.set(); } if (wp) { pinMode(wp,"output"); wp.set(); } var fram = { ... } return fram; }
The relevant difference is the CS setup: code[2] uses pinMode() to make sure that the pin is not automatically but dedicated configured before setting it high with *set(). I 'm not sure if EEPROM's need this pin for feedback for ready/not ready sate detection by the processor. I wouldn't have expected either to have to use the dedicated configuration, because the set() happens surely early enough before the first read() hits and it is set low with reset(). My defensive/conservative programming made me add it, and it obviously had spared me trouble.
runtime instance state/pattern[1]:
function AT25(spi, pgsz, cap, cspin) { this.spi = spi; this.cspin=cspin; this.pgsz=pgsz; this.cap=cap<<7; this.ca=0; } AT25.prototype.read = function(...){}; AT25.prototype.write = function(...){};
runtime instance state/pattern[2]:
var fram = { read: function(...){...}, write: function(){...} };
[1] allows multiple instances that keep (dynamic) state, where as [2] is just a singleton with the functions that hold (statically) on to the connect parms by closure. Most important, [1] keeps memory size information to prevent lock up in read-opnly mode, and - as a convenience - previously read address + 1 for read next n bytes (for details see read() implementation). [2] could be enhanced to support this as well by adding a variable in the closure scope. A better approach though is to choose the multi-instances pattern rather than the singleton. [2] could also be enhanced with size information (and check functions) , but it was intentionally kept a 'unintelligent', simple, low level device access API, because from the very beginning, it was never meant to be directly used by the application. A memory manager was intended to know about size and would provide safe access, and at the same time a much higher level API for the application. For resource constraint setups, the user should have options to go simpler and closer to the hardware, therefore [2] needs to be enhanced for justified standalone use.
-
@Gordon, I wonder why the read still has to create (temporary) twice buffer space / memory. new Uint8Array(... is the first to just keep the clock going for the spi.send, the second one is the var ov=... for receiving the bytes delivered by the sent read command. I elaborated on that in other threads, where I suggested to have to have same parm options for spi.send() as spi.write() has: optional object to provide just a (single byte) string and a repetition count. This would eliminate the creation of one buffer - the buffer to just keep the SCK line clocking. What is the reason that this parameter option of spi.write() is not available for spi.send()?
-
-
About the write(): shows the same behavior: first write() after power cycle fails... I other words, any first operation fails. A dummy read of one single byte gets the code over the bump.
fram.read(0,1); fram.write(0,"Thanks, Mr. DrAzzy!"); console.log(E.toString(fram.read(0,19)));
delivers:
1v72 Copyright 2014 G.Williams >echo(0); Thanks, Mr. DrAzzy! =undefined >
What is the 3rd parm num do for or in the write()?
-
read() works - with somehow a hick-up:
- powering board on
- connect
- upload with run with included reading of 256 bytes returned 256 0xFF
- executing same read from the console: delivers real data
subsequent uploads with runs delivers real data.
This is the console output:1v72 Copyright 2014 G.Williams >echo(0); new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]) =undefined >console.log(fram.read(0,256)); new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 32, 46, 46, 46, 105, 116, 32, 119, 111, 114, 107, 115, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 82, 65, 77, 64, 49, 50, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) =undefined > =undefined >reset(); =undefined _____ _ | __|___ ___ ___ _ _|_|___ ___ | __|_ -| . | _| | | | | . | |_____|___| _|_| |___|_|_|_|___| |_| http://espruino.com 1v72 Copyright 2014 G.Williams >echo(0); new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 32, 46, 46, 46, 105, 116, 32, 119, 111, 114, 107, 115, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 82, 65, 77, 64, 49, 50, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) =undefined >
data read as string:
>console.log(E.toString(fram.read(0,256))); ! ...it works!FRAM@128 =undefined >
That' stuff I wrote to the memory in post three (3) months ago...
This also proves that it is pretty resilient: I had the FRAM for a long time wired and did many other things... and countless power cycles, and more, and the stuff is still there.Have to find out if I can recreate the initial case,...
Yes, can be recreated: the first time after a power cycle reads just 0xFF...
...all the times. So it has to do with power up. May be I go too quickly after the FRAM...It is not a timing issue... it has to do with the first power cycle...
Definitly, it has to do with power cycle: reading just one byte upfront clears the issue and all subsequent reads read what they are supposed to.
1v72 Copyright 2014 G.Williams >echo(0); =undefined >console.log(fram.read(0,1)); new Uint8Array([255]) =undefined new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 32, 46, 46, 46, 105, 116, 32, 119, 111, 114, 107, 115, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 82, 65, 77, 64, 49, 50, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) =undefined >
Btw, my 256KBits / 32KBytes FRAM was $4 only... not that bad.
- powering board on
-
...Pico (that's more plug-sized) will get people's attention a bit more as an IO board.
It sure does, Espruino got me already there, but not only because of that.
How would I setup Espruino with a HTTP server and client and Browser and Apache or a like on my Laptop/Mac for duplex (not necessarily simultaneously) and have them tethered directly with USB?
Such a setup would also be beneficial for a Test environment (in the browser)... it would not have to poll anymore... it could be event driven...
-
Had some time to work on memory manger - solidifying code, adding regression, auto gc(). In the cross-implementation environment, I'm about ready for connecting it to the actual memory device API/module - recently published by @DrAzzy (also intended to be cross emulated in the code before moving the memory manager into Espruino context). The attached file has a memory manager configured for 128 byte with a set of tests that can be run as a whole or also step by step to see what's going on. Each step has a pre and post validation... and it was already very helpful in catching glitches on changes and extensions (ability to write/read besides a string also true, false, null, and undefined into the memory). Download the html file and run it by stepping through the tests... behaves like a slide show for geeks... ;)
-
-
Had some time to spend with new serial memory modules.
Loaded AT25 from git, placed it inline as below, and tried to run it (in 1v72). First, the editor complained about several things... mostly just missing semicolons (see screen shot attachment). To get the statement in the wait loop accepted by the IDE, I added a dmy variable.
// using AT25 (copy - inline) to write/read to/from 256KBit FRAM: // https://github.com/SpenceKonde/EspruinoDocs/blob/master/devices/AT25.js // (?)same as http://drazzy.com/e/espruino/etc/AT25_new.js(?) // using SPI2 and A2 for CS as used before and posted about experience // http://forum.espruino.com/comments/11937103/ var exports = {}; (function() { // AT25 module code exports.connect = function(spi, pgsz, cap, cspin) { if (cap > 4096 || !cap || pgsz > cap || !cspin) { throw "Unsupported or invalid options"; } return new AT25(spi, pgsz, cap, cspin); }; function AT25(spi, pgsz, cap, cspin) { this.spi = spi; this.cspin=cspin; this.pgsz=pgsz; this.cap=cap<<7; this.ca=0; } AT25.prototype.read= function(add,bytes) { if (add===undefined) { add=this.ca; } t=(this.cap>65536)?E.toString(3,add>>16&0xff,add>>8&0xff,add&0xff):E.toString(3,add>>8&0xff,add&0xff); var ov=this.spi.send(t,this.cspin); var o=new Uint8Array(ov.buffer,(this.cap>65536?4:3),bytes); this.ca=add+bytes; return o; }; AT25.prototype.write= function(add,data,num) { if(typeof data!="string"){data=E.toString(data);} var idx=0,dmy; while (idx < data.length) { this.spi.send(6,this.cspin); //WREN var i=this.pgsz?(this.pgsz-(add%this.pgsz)):data.length; // console.log(this.spi.send([5,0],this.cspin)); t=(this.cap>65536)?E.toString(2,add>>16&0xff,add>>8&0xff,add&0xff):E.toString(2,add>>8&0xff,add&0xff); t=t+data.substr(idx,i); this.spi.send(t,this.cspin); var et=getTime()+0.012; while (getTime() < et && this.pgsz) {dmy="";} idx+=i; add+=i; } return data.length; }; // /AT25 module code }()); SPI2.setup({sck:B13, miso:B14, mosi:B15, baud: 18000000}); var fram = exports.connect(SPI2,0,256,A2); console.log(fram.read(0,256));
The tries did not go that well. The console complained:
1v72 Copyright 2014 G.Williams >echo(0); undefined =undefined Uncaught Error: Field or method does not already exist, and can't create it on String at line 7 col 28 var o=new Uint8Array(ov.buffer,(this.cap>65536?4:3),byte... ^ in function "read" called from line 1 col 28 console.log(fram.read(0,256));
Furthermore, I wondered about several items:
- Why is t in the write() not declared?
- Does it matter - for ESPRUINO's JS interpreter implementation if declarations of variables are inside or outside a loop?
- The console.log() in write() must be a left over... :}
I'll look a bit more into the code...
- Why is t in the write() not declared?
-
-
You obviously can control what image is loaded... How about adding actionPoint of type image, which takes the expression result - literally or executed on Espruino - and loads the 'next' definition. Such an option of hard - or Espruino soft - controlled loading of definition enables flows through views (images)... More about along these lines in a later post.
In what language is this testing plug-in written? (I'd like the answer: JS ;-) - of course).
-
-
@JumJum, great intro - raising some questions:
a) Can I use this feature just for a graphical console (or monitor for Espruino)?
b) Where can I place code for doing stuff in the IDE to not overload the Espruino? -
-
-
@DrAzzy, looked at the module and wondered why for each byte the address has to be written - have to say that I have not studied the EEPROM api. For FRAM resetting the address for each byte is not required - see](http://forum.espruino.com/comments/11937103/). Are you referring to that when talking about? Yep, the page size of 0 takes brilliantly care 0f skipping the wait cycles.
connecting SO of FRAM to SI of display...
You just invented Super-Direct Memory Access! I did not think about that... for cases where the memory is equally or larger in size, this really works: preparing the next image/page in the memory and then update the display at once. For my case, the FRAM I have is a bit too small: 32,768 bytes FRAM vs. 172,800 byte display buffer (=230 x 320 pixel * 2bytes in ILI9341... and spending a lake of tears for 6 FRAMS and also giving up 6 CS lines, will become a sea of tears... nope. I need only a few KB to ship back and forth areas of the display. For that, I have to set two different addresses, and also connect SO of display to SI of FRAM. Super Direct DMA for save and restore... really cool idea.
What I already thought of - but not had the time this morning to write about - was to have a DMA Hub... inspired by the 0 and 1 to indicate source and destination in my envisioned A_to_B API. Since the process would anyway not be multi-threaded, I could just add any communicating device to a hub, and then trigger the transfers between the *from-to*s with device#X to device#Y. For streaming devices, the address would be -1 (MSB = 1) or something other detectable (with 2 byte address and MSBit of MSByte a control bit, 32KB is the max addressable... just what my FRAM gives me. Note that not all devices have a single dimension address, even though they may support continuous read and write across dimensions, such as the ILI9341 display controler. This display controller requires to provide row and column address, but does auto increment/reset columns and rows....
I personally like the integrated CS pin option - after I understood what it is for and how it works... that's why I used it for the last send... to get rid of an extra statement. What I miss(ed) is(was) to have the same control for the first spi.send... that's why I had to do it - hacky ;-) -manually. Therefore: what about an additional parm in spi.send() that would allow the CS to go low and be kept low... after all, it is a 'hard'-wired sequence of statements that begin a communication, have some 'inner'-segments, and close a communication. After all a connect, do some stuff, disconnect pattern.
I do not want to sound ungreatful... it's just the crave for to getting to the bottom of things... and surface back up with increased understanding and confidence... and accept the things that cannot be changed (reasonably). May be it is the ethnic related 'gesunde Unzufriedenheit' and the style of 'Das Bessere is der Feind des Guten' - literally: Better is the enemy of Good - ...only the best is good enough, just short of unhealthy perfection. In sports, it's called practice... - or what answer do you get from any athlete that already has done it yesterday, the day before, three days ago,... and want's to do it today? ...may be for the seventh day the athlete will take a break... ;-)