Tachyon - Tips, Tricks, Techniques, Tutorials
Peter Jakacki
Posts: 10,193
Okay, now that I got that tongue twisting tripping tricky title out of the way what's this all about?
I figured I'd start a separate thread from the main Tachyon one and just "blog" about ways to get the most out of Tachyon with little tips and useful tricks, techniques that can speed up development, and tutorials on different aspects of software and hardware. To kick off here is the first one....
Interfacing to the SPI bus
SPI is perhaps the most basic serial interface there is as essentially you interface to a shift register which at a minimum requires a clock and a data signal. The idea is that to send data to a shift register we output the data a bit at a time and pulse the clock line, nice and simple. SPI simply allows for both directions with an output to the device (MOSI:Master Out Slave In) and an input to read data from the device (MISO:Master In Slave Out). Finally a chip select signal is used to indicate the start and end of a valid transfer.
Tachyon has SPI support at the bytecode level, it has fast single byte instructions for SPIRD and SPIWR as well as SPIWRB to write a byte or SPIWR16 to write 16-bits. So sending a single byte over SPI takes only 2.4us which includes making sure the chip enable is selected.
Before these instructions can be used we need to setup the masks which indicate which pins to use. In Tachyon these pins are combined and encoded as a single long but to make it easier to enter we can use the decimal byte method that is used for IP notation. For instance if we use P20 for the clock (SCK), P21 for MOSI, P22 for MISO, and P23 for chip enable (CE) we can specify the long as &23.22.21.20. Internally the long in hex will be $17161514 so the IP notation is certainly friendlier. Now use this pin id like this &23.22.21.20 SPIPINS
This is all that is required to setup a device and if you switch between devices a bit you could give them a name instead or if it's only the CE line that's different you could use a faster method of just setting the CE mask directly.
The SPI instructions are optimized for fast bit-bashed operation so they also do not push or pop the stack. If you want to read from the SPI bus you must supply a stack parameter to start off with. If you want to write then you must lastly also drop the result. This is not a waste as it first seems because if you want to read 32 bits together you simply 0 SPIRD SPIRD SPIRD SPIRD and it will accumulate 32-bits of data and conversely $12345678 SPIWR SPIWR SPIWR SPIWR DROP will transmit 32-bits starting from the most significant byte. In fact the result we dropped after the SPIWR was the same $12345678 that had been rotated left 32 times so we could send it many times without having to reload.
Now here is SPI being used to talk to the WIZnet W5500 Ethernet chip highlighting just two basic methods of reading and writing a byte at an address.
Capture of multiple 16-bit SPI writes to a W5500 chip.
Similarly to write to a much simpler device like the MAX7219 8-digit 7-segment LED display driver we need to send one byte as the address and another byte as the data and all it is is this:
Here ledce is simply a constant with the pin number of the chip enable whereas previously we had &WNCS OUTSET which was actually a mask constant needing OUTSET to take the pin high. This latter form is used if we want to trim and save every microsecond which is important for high volume high speed traffic talking to the WIZnet chip but not so important for the lazy LED display where we can use the perhaps friendlier form of <pin> HIGH. (Use SPICE instead)
Summary of definitions:
SPIRD ( xxxxxxxx -- xxxxxxnn ) Read a byte from the SPI bus and rotate in left into the current long (2.4us)
SPIWR ( nnxxxxxx -- xxxxxxnn ) Write the msb byte of the long to the SPI bus and rotate for the next most msb byte (2.4us)
SPIWRB ( xxxxxxnn -- xxxxxxnn ) Write the byte to the SPI bus and return with the same (2.6us)
SPIWR16 ( xxxxnnnn -- xxxxnnnn ) Write the word of the long to the SPI bus and return with the same (4.6us)
SPICE Release the chip enable (automatically enabled on any transfer)
More about the finer points of using SPI in another blog.
EDITED: Added SPICE
I figured I'd start a separate thread from the main Tachyon one and just "blog" about ways to get the most out of Tachyon with little tips and useful tricks, techniques that can speed up development, and tutorials on different aspects of software and hardware. To kick off here is the first one....
Interfacing to the SPI bus
SPI is perhaps the most basic serial interface there is as essentially you interface to a shift register which at a minimum requires a clock and a data signal. The idea is that to send data to a shift register we output the data a bit at a time and pulse the clock line, nice and simple. SPI simply allows for both directions with an output to the device (MOSI:Master Out Slave In) and an input to read data from the device (MISO:Master In Slave Out). Finally a chip select signal is used to indicate the start and end of a valid transfer.
Tachyon has SPI support at the bytecode level, it has fast single byte instructions for SPIRD and SPIWR as well as SPIWRB to write a byte or SPIWR16 to write 16-bits. So sending a single byte over SPI takes only 2.4us which includes making sure the chip enable is selected.
Before these instructions can be used we need to setup the masks which indicate which pins to use. In Tachyon these pins are combined and encoded as a single long but to make it easier to enter we can use the decimal byte method that is used for IP notation. For instance if we use P20 for the clock (SCK), P21 for MOSI, P22 for MISO, and P23 for chip enable (CE) we can specify the long as &23.22.21.20. Internally the long in hex will be $17161514 so the IP notation is certainly friendlier. Now use this pin id like this &23.22.21.20 SPIPINS
This is all that is required to setup a device and if you switch between devices a bit you could give them a name instead or if it's only the CE line that's different you could use a faster method of just setting the CE mask directly.
The SPI instructions are optimized for fast bit-bashed operation so they also do not push or pop the stack. If you want to read from the SPI bus you must supply a stack parameter to start off with. If you want to write then you must lastly also drop the result. This is not a waste as it first seems because if you want to read 32 bits together you simply 0 SPIRD SPIRD SPIRD SPIRD and it will accumulate 32-bits of data and conversely $12345678 SPIWR SPIWR SPIWR SPIWR DROP will transmit 32-bits starting from the most significant byte. In fact the result we dropped after the SPIWR was the same $12345678 that had been rotated left 32 times so we could send it many times without having to reload.
Now here is SPI being used to talk to the WIZnet W5500 Ethernet chip highlighting just two basic methods of reading and writing a byte at an address.
pub LC! ( byte wizadr -- ) SPIWR16 DROP --- write address WR_WCTRL --- control byte SPIWRB DROP --- write a single byte SPICE --- release chip select ; pub LC@ ( wizadr -- data ) SPIWR16 DROP --- send addr RD WCTRL --- control byte 0 SPIRD --- read back data SPICE --- release chip enable ;
Capture of multiple 16-bit SPI writes to a W5500 chip.
Similarly to write to a much simpler device like the MAX7219 8-digit 7-segment LED display driver we need to send one byte as the address and another byte as the data and all it is is this:
pub LED! ( data adr -- ) SPIWRB DROP SPIWRB DROP SPICE ;
Here ledce is simply a constant with the pin number of the chip enable whereas previously we had &WNCS OUTSET which was actually a mask constant needing OUTSET to take the pin high. This latter form is used if we want to trim and save every microsecond which is important for high volume high speed traffic talking to the WIZnet chip but not so important for the lazy LED display where we can use the perhaps friendlier form of <pin> HIGH. (Use SPICE instead)
Summary of definitions:
SPIRD ( xxxxxxxx -- xxxxxxnn ) Read a byte from the SPI bus and rotate in left into the current long (2.4us)
SPIWR ( nnxxxxxx -- xxxxxxnn ) Write the msb byte of the long to the SPI bus and rotate for the next most msb byte (2.4us)
SPIWRB ( xxxxxxnn -- xxxxxxnn ) Write the byte to the SPI bus and return with the same (2.6us)
SPIWR16 ( xxxxnnnn -- xxxxnnnn ) Write the word of the long to the SPI bus and return with the same (4.6us)
SPICE Release the chip enable (automatically enabled on any transfer)
More about the finer points of using SPI in another blog.
EDITED: Added SPICE
Comments
I started a collection of your/@Peter's code and documentation snippets from the original Tachyon thread
here, because inside the very long thread they are hard to find again.