Proper technique needed to nest a setWatch() within a separate setWatch() #1032
Replies: 31 comments
-
Posted at 2019-08-02 by AkosLukacs Aside: You can pass in the CS pin to the send command like Also, the setWatch docs say you can ask for the state of another pin, so you don't have to read it (might matter, as you don't have to spend the time to read it):
I don't completely understand the snippets, for example, you don't call Why not just set up one setWatch once on the SCK pin, and ignore if the CS is not low? Ok, depends on how many other slaves are there, and how much CPU the watch uses... So I guess you could
|
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-02 by @allObjects Putting a watch on a (fast) clocked pin (such as SPI SCK) is the recipe for Espruino's interrupt / event queue / FIFO overflow... |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-02 by maze1980 After 10 clock cycles you would remove all watchers with |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-02 by Robin Fri 2019.08.20
It was extremely late as I posted the above, and not proud of it's brevity. Did so, as had I waited till morning for a more complete post, most that might respond would already be heading for bed in their timezone.
Yes that is it's intention as my note there indicates. There is no way to Ctrl+C out of this, forcing a WebIDE reboot at these speeds.
Thanks for that reminder. In this proof on concept phase, just trying to see if I can detect at least a few bits correctly.
Right now just proof of concept with two micros.
There are a bazillion pulses. I'm sure overflow would occur then. Also, since the master controls the clock, how does the slave know when to record which bit for which byte? I was relying on the rising edge detection of setWatch()
The two functions are effectively doing just that. It is difficult to comprehend what the actual code execution is going on, when in single step mode it appears to work, but at full speed, the wrong data is returned. Maybe the SCK pulse width is just too narrow for setWatch()? > 'recipe for Espruino's interrupt / event queue overflow' As there are only 32, I felt that starting after CS goes low, might not be an issue. But as the responses are all zero during this sampling, it is quite possible that, that is in fact what is going on. Any idea on how to check for IRQ issues? I'll continue to try slowing down the SPI bus, but don't know how slow below the default is reliable? |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-02 by maze1980
Get the current time, call the function, get the current time and calculate the duration. If it's slower than your clock there's a problem for sure. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-02 by Robin The Javascript getTime() function seems to be ~100 times slower than the pulse sampling e.lastTime using setWatch(), which seems to be able to send the correct time duration. I was really after is there a way to check the IRQ register for flag detail. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-02 by @gfwilliams You're almost certainly hitting timing issues because Espruino doesn't execute JS code in an interrupt. However there's a solution to this. Espruino's setWatch provides a https://www.espruino.com/Reference#l__global_setWatch
And in this particular case I wouldn't do a setWatch in a setWatch either - just leave them both running:
Because they're both running, events will be put in the queue in the correct order. Even though they are not processed immediately everything will still work great. HOWEVER this will only work for small packets of SPI data. Anything with >100 clock cycles will fill up the input FIFO before Espruino is able to process it. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-02 by Robin Fri 2019.08.02 Thank you @gordon for the explanation, as I'm sure that will help others also. I knew I was on the right thought/concept, just didn't have the know-how for coding the solution. Your snippet is concise and self explanatory. Will work on it/study this weekend. Any idea of the fastest pulse, shortest duration that might be acceptable for setWatch() to that 100 event limit? These are tid-bits that IMHO should be added to the docs. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by Robin Sun 2019.08.04
Only have a need for four bytes, around 32 edges, so shouldn't be an issue. @gordon, would it be possible to provide the option to allow slower than the SPI 100Kbit default rate? How difficult/time frame would that be to implement with your already over limit demands? fingers crossed From:
and
Made several attempts to slow SPI by dividing 100000 by 10's down as far as 5000, but the result is the same. The slowest clock pulse is around 3usec 333KHz or the same as the default for both hardware and software SPI. I ended up writing a Javascript pin toggler a slow SPI emulator that clocks out 1000 times slower, so that I could verify added code to the snippet you provided. Data is solid and the slave is able to absorb at that slow speed. The fastest that Javascript would eventually run is limited by how fast setInterval can be called, and that appears to be around 1.8msec so two pulses at 2msec 0.004 is 250Hz. So using Javascript, that is limited to around 1000 times not fast enough to approach the current SPI default setting implementation. The source I have cobbled together runs at 2 pulses 0.01 10msec or 100Hz When clocked out of the MDBT42Q using the only available software SPI at the default, which is the slowest SPI will clock data, pulses are counted but the bits are not as they should be. I'm going to play with the clock mode setting tomorrow, but even inverting the bits doesn't come close the the data sent.
Pin wiring
Found the send cmd bool jsspiSend() but don't know where to find object spi_sender
This proof of concept does indeed show the WiFi running as a slave, at 1000 times slower but only as true SPI appears to be too fast for setWatch() to monitor. Will mess with clock mode and attempting to speed up the Javascript. Cleaning up both code modules to upload shortly (late Monday). . . . |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by @gfwilliams Ok, so this is with MDBT42Q as the master? I don't believe the hardware on the MDBT42Q is capable of going below 125kHz, at least according to the datasheet. You could try On the STM32 the hardware should be capable of going lower though so if you used that as a master you'd be sorted. I'm afraid that right now software SPI doesn't implement a programmable baud rate - it's so rare that you wouldn't push SPI as fast as you can that it just wasn't worth it. You could try:
Which should implement software SPI in JS in a reasonably fast way - I just tried it and I'm getting around 2kHz ... but as I'd mentioned in other threads, if you want to do device->device comms, UART is so much easier and less trouble - and it uses half as many pins. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by Robin Mon 2019.08.05
Project goal to push EspruinoWiFi for end users. Current board layout already has SPI for a separate slave. The other micro, short on available pins, will act as a slave initially, will need to act as a master periodically. The above proof of concept works, but at much too slow a speed. Goal to stay between 100000-800000 bits/sec. Thank you for promptly responding @gordon. You have validated some additional questions I had.
My initial testing is showing this isn't the case. I'll re-check that, but the reason to use my initial observation to use the MDBT42Q as the master.
New learning there, but will look into. The real issue isn't necessarily the need to change the clock speed, but to understand why setWatch() becomes unreliable at top speed.
No need to panic, really not needed. Just needed a method to be able to get at the setWatch() issue. Can only go so fast using Javascript, and at lowest default SPI speed too fast for setWatch() to handle. One thought I had using software SPI, is whether the clock width is remaining stable. There is a bit of jitter using the logic analyzer, and wondered if that might be causing setWatch() to misbehave. The data stays in tact with the protocol analyzer, so I didn't originally think so. Don't have a fast enough scope to cross check. Working on the WiFi slave code cleanup right now. Only one line inside the snippet you provided, so I don't believe setWatch() execution is the cause. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by @gfwilliams
You're totally out of luck then - setWatch is not going to work. Maybe you'll get it working but it'll take ~32ms to process your 32 bits of data in JS - so I don't understand why you want a really high bitrate. Can you try this code:
Just tested this on an nRF52 (all running on the same device) and it's fine, even at the 'normal' speed. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by maze1980 If you want to use SPI I would suggest extending the Espruino SPI class to support slave mode. The Espruino Wifi chip itself supports SPI slave mode, the source code library for this chip includes SPI slave mode already, so it seems feasible. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by @gfwilliams
That would be the ideal, yes. I'm not sure Robin's up for that though since it requires a lot of hacking around in C. Ok, I just tried this: Espruino WiFi
MDBT42
Get them grounded, connect B3->D15 and B5->D14 and it works perfectly. So I'm not quite sure what's going on with your code. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by Robin Mon 2019.08.05
Yeah, and I have a soldered bread board to boot. More high freq caps needed? I know it's getting late for you guys, thanks for the samples for me to work on as I have the rest of the day. Here is a possible area of contention I'll look at:
When I use
Bit shifting is being performed. (Isn't the left shift done first, then the 'Or', then the assignment is performed?) As @AkosLukacs pointed out in #2 'and the state will be included as a 'data' field in the callback' > 'so I don't understand why you want a really high bitrate' Not absolutely needed. Have seen Arduino projects running that high though. (at least coded that way) Me thinks others blindly start coding without checking the data sheets, then like wildfire, everyone provides a quick knock off with all the original source, also without doing their homework. Then we all wonder why code examples are as they are.
A nice thought, but not much call for it and I'm sure it would be at the bottom of Gordon's overloaded list right now.
Although I've been poking around the source to attempt to get answers, I am absolutely sure I don't possess the necessary skills, and wouldn't within at minimum, a years time, even investing full-time, but the day job gets in the way of that. Also running short on years left and that item just isn't on my bucket list. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by Robin
I don't believe that is the case. The setWatch() is performing that task. Gordon's example #8 on which the last is based is fine. I'm with you Gordon.
See second logic analyzer image in #1 above. One second between bus usage, and only the sixteen clock pulses there during valid data. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by @gfwilliams Could try using the exact code I posted, with no modifications other than the pin names? It might help to ensure we're on the same page. Then the CS line could be added later.
Try it - boolean values get converted to 0/1. The code itself is a hack to try and get a little faster decoding of binary data - since at the end of the day this is what you wanted. I started off with |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by @gfwilliams
Yes, there's no CS. That'd have to be added as as you note the only way to do it would use up processing power even when CS wasn't asserted. And I agree this will fail when there is more than a little bit of traffic on the bus. This is more to humour @robin and prove that ... because right now since there's no support for hardware SPI or I2C slave, using Serial would be the sane option for transferring data between two Espruino devices. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by Robin Quick update as I realize it is late by you, . . . with more testing to do,
Okay humored! (sic US sp)
Took a bit to get up and running, determined PulseView software requires MISO to be included also, for the protocol decoder to work. With using just the code from #15 First thing I noticed is the active state on the pins is not the same. Clock phase. As pinMode() isn't used, I'm guessing looking at default and setup will uncover that. Added a setInterval() on send side MDBT42Q so I didn't have to keep pulsing that side, and kept the bus quiet for at least a full second. Without the CS detect, the WiFi slave side does decode as in #15 MDBT sends out 8usec clock ~100KHz Speculation here: Sanity Check Commented out strings, outside of functions are not included in the upload? (yes?) Inside the CS setWatch() during development, I placed console.log() statements to see what was going on. Had a hunch that the update to the WebIDE console window was getting bogged down, as it took a while to Ctrl+C out. I commented out ~three debug statements, and the console updated fine. HUNCH: Even though the console now appeared to be updating without noticeable delay, that in reality, the commented out lines were causing a delay in the setWatch() read perhaps? Off for coding CS inclusion and more testing . . . . |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by AkosLukacs
"it depends" :) Without minification, all comments and whitespace is included! |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by maze1980 console.log() takes a lot of time. If you call it once every 8 bit it's somewhat ok, but if you call it for every bit it'll slow down things significantly. But the read data should be still valid in my understanding. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by Robin
That is in fact what I am doing, an array and indexer, adding only one line of code to Gordon's #8. On CD low set array index to zero, inc when clocking in bits with second setWatch(). I print to the console on/after raised CS detection. And now the really bizarre . . . . One can not make this stuff up!! Using Gordon's #15 and adding a setInterval() as explained in #21 I sent a bit stream at a one second interval that has an easy to detect bit inversions in the stream:
Endless stream of these repeating pairs: (as expected)
Walked away for a half hour to run an errand. Upon return, without any intervention by me, the WebIDE is now reporting instead: Endless stream of these repeating pairs: (incorrect)
Quickly checked PulseView and it is reporting 0xD6 0x37 (as it should) Rechecked with new runs two more times!! Master is sending correctly as PulseView reveals this to be true. The slave is suspect as the data is now not being decoded correctly, and mirrors what I observed over the weekend with different #8 code snippet and setWatch.
It is this inconsistency that is driving me nuts and making coding this a chore. A one second pause to 200usec of data duty cycle shouldn't have an effect. Could setWatch() have an internal counter that eventually rolls over in the middle of parsing bits. Say at count 65530 the seventh bit inclusion resets and then starts at zero rather than continuing for the seventh and eighth bits? |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by maze1980 Speculation: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-05 by Robin
Ahhhh, . . . Yes thank you maze1980, any chance you have found it?
Var d is global in line 3 That is the entire code file This is what Gordon's #8 is doing. Will attempt to merge #15 into #8 and try with the global array I originally had. Not sure which of the two is more important to try first. Code cleanup for upload probably will take as much time as merging those two. Maybe I'll have time for both . . . . |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-06 by @gfwilliams
@maze1980 sounds spot on here. Unfortunately it won't get it right 100% of the time especially with the odd interrupt conflict, and it looks like a bit got lost. Using cs in that way would help to always 're-frame' the data, and then you could use a CRC or similar to detect the rare transmit errors. RE: ... but most likely the buffer got full and caused the event queue to get full - but that should have flagged up an interpreter error. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-06 by Robin Tue 2019.08.06 What is the command or method to reveal if any setIntervals are running in memory, despite the attempt to stop them with clearInterval? I remember seeing this in a post a while back, but Google isn't helping me out here. Update: There are several conflicting issues I have uncovered. Now I have a repeatable bad read data situation, bits stream is there, detection start is off caused by issue above, I'm pretty sure I'll be able to solve this. Code and docs to follow when done. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-06 by maze1980 Just copying the code together I'd say it should look like this:
|
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-06 by Robin Thanks @maze1980, having another view is helpful. With the comment "I'd say it should look like" sounds like it hasn't been tested, which is okay don't get me wrong, any and all contributions are helping. That code block is pretty much what is being done and implemented right now based on Gordon's #8 snippets. As I pointed out in #28 from an observation you pointed out earlier in this thread, setTimeout and setInterval 'actual' execution time is getting in the way of the setWatch() read. Too much to detail right now, but on the horizon. Don't want #28 to get buried here with other updates: Fr #28 |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-06 by maze1980
|
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-06 by Robin Thank you for the quick response @maze1980, it is the latter I was after, and confirms my hunch. How did you locate that tid-bit? Now knowing what the syntax is, submitting to Google now provides the original article from two months ago. Memory still in tact, as I knew I saw it in a recent post. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2019-08-02 by Robin
Thr 2019.08.01
Seeking technique suggestions here. Individually, manual toggle of lines allows correct result from one pass of setWatch().
Continuation of threads:
Goal: After CS Chip Select goes low, on rising edge of SCK clock line, read state of MOSI
Setup: Espruino MDBT42Q as master sending software SPI to Espruino WiFi as slave
Data sent inside a setInterval() creating a CS pulse Low of ~200msec surounding 4 x 8bits of ~300usec clocked at 3usec:
Limited code for brevity
Verified: Using logic analyzer and protocol decoder am able to see and verify CS, SCK, MOSI, MISO and data as placed on MOSI
Individually, the setWatch() definitions execute as expected.
The idea is to only allow the second setWatch() to run after tripped by the setWatch() monitoring CS
When I manually set the state of MOSI using
function fetchBit() reads and stores manually, correctly.
Suspicion: A timing issue, starting technique or a different means to trigger the second setWatch() is needed.
Beta Was this translation helpful? Give feedback.
All reactions