allanw wrote:Hi guys, I'm doing a week-long hackathon type event for my school which is making a propeller clock out of RGB LED's being driven by a 12-bit color resolution shift register. I've been trying to figure out how to modify the SPI library to send out data at 32-bits in SPI mode 0 at around 16MHz, and I've looked at a lot of threads but found no definitive answer. We've been using an ARM Cortex as the MCU but have an XMOS X1-A in case we need more processing horsepower. Anyway, we only have 24 hours until the demo and I'd like to be able to quickly switch over to the XMOS.
Supposedly this is the code for 8-bit SPI. I'm not sure I understand why sclk is being written to twice. I'll modify the ports to be 32-bit buffered and get rid of the bitshift, but how do I change the sclk statements? Should I just make them 0xAAAAAAAA?
Code: Select all
void spi_out_byte(unsigned char data)
{
// MSb-first bit order - SPI standard
unsigned x = bitrev(data) << 24;
spi_mosi <: x;
spi_sclk <: 0xAA;
spi_sclk <: 0xAA;
sync(spi_sclk);
spi_miso :> void;
}
Thanks!
I'm not sure about that direction of the bit shift for the 8-bit case you show: it looks wrong to me. Here's a snippet from an XMOS library:
Code: Select all
void spi_out_byte(spi_master_interface &i, unsigned char data)
{
// MSb-first bit order - SPI standard
unsigned x = bitrev(data) >> 24;
i.mosi <: x;
i.sclk <: 0xAA;
i.sclk <: 0xAA;
sync(i.sclk);
i.miso :> void;
}
The ports shift out LSB first. The bitrev is to reverse this so that the MSB is shifted out first as per the SPI protocol. Anyway we can ignore the shift for the 32-bit case.
Try
Code: Select all
void spi_out_byte(spi_master_interface &i, unsigned int data)
{
// MSb-first bit order - SPI standard
unsigned x = bitrev(data);
i.mosi <: x;
i.sclk <: 0xAAAAAAAA; // send 16 clocks. Each 0xA is 0b1010 i.e. 2 clocks
i.sclk <: 0xAAAAAAAA; // send 16 clocks
sync(i.sclk);
i.miso :> void;
}
This example used a struct to encapsulate the ports. Make ports 32-bit buffered.
As per my comments above, the clock is written twice as you need 32 rising clock edges.
You will need to configure the ports to be clocked off a different clock block if you want it to run slower than the default 50MHz that this will run the clocks at (100MHz ref clock clocking out 0xAAAAAAAA) when running from the default system reference clock of 100MHz.
Try this first. For completeness, I include some code cut from the xcore github spi project.
See
https://github.com/xcore/sc_spi
Unfortunately this example uses some inline embedded assembler to perform some trickery on shifting the 1st bit when using modes 0 or 2 for outputting bytes, which is difficult to understand for your average user! The reason for this is not clear to me yet.
Code: Select all
static inline void spi_master_out_byte_internal(spi_master_interface &spi_if, unsigned char data)
{
// MSb-first bit order - SPI standard
unsigned x = bitrev(data) >> 24;
#if (SPI_MASTER_MODE == 0 || SPI_MASTER_MODE == 2) // modes where CPHA == 0
// handle first bit
asm("setc res[%0], 8" :: "r"(spi_if.mosi)); // reset port
spi_if.mosi <: x; // output first bit
asm("setc res[%0], 8" :: "r"(spi_if.mosi)); // reset port
asm("setc res[%0], 0x200f" :: "r"(spi_if.mosi)); // set to buffering
asm("settw res[%0], %1" :: "r"(spi_if.mosi), "r"(32)); // set transfer width to 32
stop_clock(spi_if.blk2);
configure_clock_src(spi_if.blk2, spi_if.sclk);
configure_out_port(spi_if.mosi, spi_if.blk2, x);
start_clock(spi_if.blk2);
// output remaining data
spi_if.mosi <: (x >> 1);
#else
spi_if.mosi <: x;
#endif
spi_if.sclk <: sclk_val;
spi_if.sclk <: sclk_val;
sync(spi_if.sclk);
spi_if.miso :> void;
}
Hope this helps
Regards,
Max.