lancaster-university / microbit-dal

http://lancaster-university.github.io/microbit-docs
Other
256 stars 130 forks source link

Accessing SPI functions in Makecode #464

Closed MTKilpatrick closed 4 years ago

MTKilpatrick commented 4 years ago

The SPI device is not initialised as part of the uBit object and therefore it is not accessible to C code written in the Makecode environment - it is only available through the Javascript/Typescript functions. In JS the compile of a tight FOR loop such as the below, the compile is not very efficient:

            for (let i = 0; i < bytearray.length(); i++) {
                pins.spiWrite(bytearray[i])
            }

Such code results in dead time of nearly 300 ARM processor clock cycles (16MHz) between each successive byte being writen to the SPI - or a data rate of less than a third of the theoretical maximum at 1MHz SPI clock speed, if the receiving device is capable of receiving one bit per clock cycle non-stop for the entire buffer.

If this cannot be improved upon by writing C code within a Makecode extension, what other routes exist such that I can reduce the dead time? Can I use the mbed.com tools to create an ASM package that can be used within a Makecode extension, accessing the native SPI functions with less overhead.

I have not used mbed or any such tools before. Therefore I would be grateful for any communication that would help - if indeed what I want to achieve is possible?

mmoskal commented 4 years ago

Unfortunately, it seems the fastest version of this code using the mbed driver will only get you 1/2 of the transfer rate (gotta love mbed!). I'm adding pins.spiTransfer() function to microbit - use that and if we happen to improve that in future you may get performance bump, but don't count on it...

MTKilpatrick commented 4 years ago

Thank you for the reply. Does pins.spiTransfer()take a buffer as its argument? The biggest problem is that of requiring a call to pins.spiWrite()for every byte within a FOR loop.

A rate of 1/2 would be better than the current rate of 1/3,5 approx, as seen using JS. I've managed to do in C within my Makecode extension, using SPI spi(mbit_p15, mbit_p14, mbit_p13); to set it up and thenthe C equivalent spi.Write(byte);to send bytes. A tight FOR loop in C is 15% faster than in JS, so most of the overheads are still in the call tospiWrite().

mmoskal commented 4 years ago

Yes, it takes a buffer (two buffers actually, the second one for the response, but you can skip either of them). It actually just calls mbed's SPI::write in a loop.