BCM2835 SPI communication - LSB first

1,065 views
Skip to first unread message

Damiano Benedetti

unread,
Apr 2, 2013, 7:00:27 AM4/2/13
to bcm...@googlegroups.com
Hi

I'm starting in this weeks using the Mike McCauley's library on Raspberry PI... what I want to do is interfacing the Ras-pi V2 to a playstation joypad controller (which communicates using SPI)... On the technical specification about this joypad is reported that the bit order on SPI is LSB first... Unfortunately, I saw in the "bcm2835_spi_setBitOrder" function that the bcm2835's only supported mode is MSB first... So I checked on the "BCM2835 Arm peripherals" datasheet and, as reported on page 23, also the LSB first mode seems to be available... 

What I'm asking now is:

- Am I misunderstanding the datasheet and for sure LSB first mode is not allowed?
- If I'm right and LSB first mode is available, is there an easy way to edit the code to allow this mode?

If I'm wrong and LSB first mode is not available, I can try modify the code with a "software mirroring function" before sending and receiving data. The question is: after editing the code, then I just need to re-build the library with make and make install command... Right? Do I need to change anything else?

One last question... if I install a new version of your code, then I don't need to remove the older ones (except the folders) because the new one overwrite the old one... right? 

Thank you for your help...

Best Regards

Damiano

Shane Guineau

unread,
Apr 2, 2013, 5:20:15 PM4/2/13
to bcm...@googlegroups.com
I haven't played with MSB/LSB, so I might be wrong, but it looks like you can configure SPI to use LSB by using the following line:

Damiano Benedetti

unread,
Apr 2, 2013, 7:17:13 PM4/2/13
to bcm...@googlegroups.com
Yeah I also was happy when I notice this... but if you open the bcm2835_spi_setBitOrder source you will find that this function is not doing anything for now... the comment report that SPI0 hardware is not able to work with LSB firs. 

Anyhow Today I was able to communicate with the joypad using a conversion table to mirror the bits before sending and after receiving them from hardware... for now I was able to do it successfully in my code; I'm also trying to do it in the BCM2835 source code but, for now, with no luck :-(

Hope that next day's I'll be more lucky and I'll tell here if my modification will give expected successfull results!

bye 

Damiano Benedetti

unread,
Apr 3, 2013, 6:36:47 PM4/3/13
to bcm...@googlegroups.com
Hi

If anybody cares, I wrote some code to add the missing part about the SPI - LSB first mode...

The working mode is pretty simple: If the LSB bit first mode is not supported by SPI0 hardware, than we can do it by software... mirroring all the byte's before sending it to the HW and before passing the data from HW to the software.

You enable this functionality using the already present (but empty) function bcm2835_spi_setBitOrder and passing to it the value BCM2835_SPI_BIT_ORDER_LSBFIRST


 You'll find the parts I changed and attached and you can find both the bcm2835.c and bcm2835.h with changes (you'll find the changes searching for my name... i put comments near each change ). 

To apply them, you just need to place the modified files in the .src folder in the Mike's bcm2835 folder... please backup the orignal files first! Than you need to re-build the library with the well explained procedure on Mike's page.

I hope that I'll be excused by Mike about my really messy code... it's the first time that I edit someone else's code and I hope that will be added and fixed in next release of the library.

If you need anything, just ask.

bye


=======================================================================================
Changed/added thing by Damiano Benedetti in BCM2835.c
=======================================================================================

//added by Damiano Benedetti
//enable or disable bit inversion in SPI
uint8_t spi_LSB_first = 0;

//added by Damiano Benedetti... here because i didn't know where to put it!
//declaration of a matrix of conversion to invert by sw the bit order
//each index of the array contains the mirrored value of its index number

uint8_t BitReverseTable[256] = 
{
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};

=======================================================================================

void bcm2835_spi_setBitOrder(uint8_t order)
{
    // BCM2835_SPI_BIT_ORDER_MSBFIRST is the only one suported by SPI0
    
    //Added by Damiano Benedetti
    //MSB first is probably the only hardware supported mode... a software
    //way to fix this is to invert the bit order before sending and receiveng data
    //the easiest way is to use an array with all the data already stored
    
    if (order == BCM2835_SPI_BIT_ORDER_MSBFIRST)
    {spi_LSB_first=0;}
    
    if (order == BCM2835_SPI_BIT_ORDER_LSBFIRST)
    {spi_LSB_first=1;}
    
}

=======================================================================================


// Writes (and reads) a single byte to SPI
// SW mirroring for LSB first inserted and tested
uint8_t bcm2835_spi_transfer(uint8_t value)
{
    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
    volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;

    // This is Polled transfer as per section 10.6.1
    // BUG ALERT: what happens if we get interupted in this section, and someone else
    // accesses a different peripheral? 
    // Clear TX and RX fifos
    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);

    // Set TA = 1
    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);

    // Maybe wait for TXD
    while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
delayMicroseconds(10);
    //added by Damiano Benedetti - software mirroring for SPI
    if(spi_LSB_first)
    {value = mirror_byte(value);}
    
    // Write to FIFO, no barrier
    bcm2835_peri_write_nb(fifo, value);

    // Wait for DONE to be set
    while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
delayMicroseconds(10);

    // Read any byte that was sent back by the slave while we sere sending to it
    uint32_t ret = bcm2835_peri_read_nb(fifo);

//added by Damiano Benedetti - software mirroring for SPI
if(spi_LSB_first)
    {ret = mirror_byte(ret);}
    
    // Set TA = 0, and also set the barrier
    bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);

    return ret;
}

=======================================================================================

//function added by Damiano Benedetti for software mirroring
uint8_t mirror_byte (uint8_t data_to_be_mirrored)
{
uint8_t mirrored;
mirrored = BitReverseTable[data_to_be_mirrored];
return mirrored;

=======================================================================================

// Writes (and reads) an number of bytes to SPI
// SW mirroring for LSB first inserted but not tested
void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len)
{
    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
    volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;

    // This is Polled transfer as per section 10.6.1
    // BUG ALERT: what happens if we get interupted in this section, and someone else
    // accesses a different peripheral? 

    // Clear TX and RX fifos
    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);

    // Set TA = 1
    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);

    uint32_t i;
    for (i = 0; i < len; i++)
    {
// Maybe wait for TXD
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
   delayMicroseconds(10);

//added by Damiano Benedetti - software mirroring for SPI
    if(spi_LSB_first)
    {tbuf[i] = mirror_byte(tbuf[i]);}
    
// Write to FIFO, no barrier
bcm2835_peri_write_nb(fifo, tbuf[i]);

// Wait for RXD
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))
   delayMicroseconds(10);

// then read the data byte
rbuf[i] = bcm2835_peri_read_nb(fifo);
//added by Damiano Benedetti - software mirroring for SPI
    if(spi_LSB_first)
    {rbuf[i] = mirror_byte(rbuf[i]);}
    }
    
    // Wait for DONE to be set
    while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
delayMicroseconds(10);

    // Set TA = 0, and also set the barrier
    bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
}

=======================================================================================


// Writes an number of bytes to SPI
// SW mirroring for LSB first inserted but not tested
void bcm2835_spi_writenb(char* tbuf, uint32_t len)
{
    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
    volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;

    // This is Polled transfer as per section 10.6.1
    // BUG ALERT: what happens if we get interupted in this section, and someone else
    // accesses a different peripheral?

    // Clear TX and RX fifos
    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);

    // Set TA = 1
    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);

    uint32_t i;
for (i = 0; i < len; i++)
{
// Maybe wait for TXD
while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
;

//added by Damiano Benedetti - software mirroring for SPI
if(spi_LSB_first)
{tbuf[i] = mirror_byte(tbuf[i]);}
    
// Write to FIFO, no barrier
bcm2835_peri_write_nb(fifo, tbuf[i]);
}

    // Wait for DONE to be set
    while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
    ;

    // Set TA = 0, and also set the barrier
    bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
}

=======================================================================================
Changed/added thing by Damiano Benedetti in BCM2835.h
=======================================================================================

///Added by Damiano Benedetti: Mirrors the given byte. 
/// Receive the data to be mirrored data and return the mirrored data
extern uint8_t mirror_byte (uint8_t data_to_be_mirrored); 

=======================================================================================




bcm2835.c
bcm2835.h

Damiano Benedetti

unread,
Apr 21, 2013, 6:34:48 AM4/21/13
to bcm...@googlegroups.com
For convenience I've created to patch files... so there is no need to download all the files.

the patch are created from 1.25 version of the library.

bye


bcm2835.c.patch
bcm2835.h.patch

Christian Kevin Alvarado Rimas

unread,
Jun 9, 2018, 7:00:28 PM6/9/18
to bcm2835
Thank you for the patch, just as comment the version 1.55 (June, 2018) still has this issue, it doesnt matter if you write BCM2835_SPI_BIT_ORDER_LSBFIRST or BCM2835_SPI_BIT_ORDER_ MSBFIRST in bcm2835_spi_setBitOrder, it stays MSB First.

ckevar

unread,
Jun 9, 2018, 7:41:06 PM6/9/18
to bcm2835
Thank you for letting me know the version.

Mike McCauley

unread,
Jun 9, 2018, 8:22:38 PM6/9/18
to bcm...@googlegroups.com, Christian Kevin Alvarado Rimas
Thanks for pointing out this patch.

New version 1.56 has been uploaded that now supports
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST)
and mirrors bytes in software for SPI read and write operations.

Cheers.
--
Mike McCauley VK4AMM mi...@airspayce.com
Airspayce Pty Ltd 9 Bulbul Place Currumbin Waters QLD 4223 Australia
http://www.airspayce.com 5R3MRFM2+X6
Phone +61 7 5598-7474



Udi Fuchs

unread,
Jul 31, 2018, 11:25:41 PM7/31/18
to bcm2835
I am trying to use the AD/DA board in this link with an RPi3B:


The ADS1256 sample code (attached and can be found on that page) uses the bcm2835 library. It works with version 1.55. But with version 1.56 all I get from the SPI bus is zeros.

Let me know how can I help you debug this issue.

Udi


ads1256_test.c

Mike McCauley

unread,
Jul 31, 2018, 11:33:04 PM7/31/18
to bcm...@googlegroups.com
Hello,

In version 1.56,

Supports bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST), after which
SPI bytes are reversed on read or write.
Based on a suggestion by Damiano Benedetti.

So if your bit order is set incorrectly, it may not work as you expect.

The default is BCM2835_SPI_BIT_ORDER_MSBFIRST;

Cheers.

Udi Fuchs

unread,
Aug 2, 2018, 10:43:06 PM8/2/18
to bcm2835
Apparently the code I'm using was trying to apply LSB ordering even though the chip requires MSB. Because the bcm2835_spi_setBitOrder() function wasn't doing anything it worked. Once you enabled the this function the old code stopped working.

In some sense your change in version 1.56 broke old code. Considering that the old code was wrong, it is hard to blame you.

Udi

Mike McCauley

unread,
Aug 2, 2018, 10:49:19 PM8/2/18
to bcm...@googlegroups.com
Yes, I was very conscious of that but I think it was the right decision.
Reply all
Reply to author
Forward
0 new messages