0

I am trying to communicate via SPI in Linux user space using the spidev driver. Physically, this SPI interface is a 4 bus (SCLK, CS, MOSI, MISO).

For this purpose I have attached to my dts the relevant node in the corresponding chip select. Once Linux boots I have the device available.

Using c++ and according to the spi-test example I have treated it as a character device, so I open it, require a full duplex transfer and close it (with the relevant calls to open, ioctl/spi_ioc_transfer and close primitives). The set bus configuration is 8 bits per word, at a maximum frequency of 1.29 MHz and in mode 0 (in other words, CPHA=CPOL=0).

Then, using the oscilloscope I see how my request is present in the MOSI, but I don't see any type of response in the MISO. What am I missing?

In case it helps, I see that the clock signal drops the level immediately after sending the data through the MOSI, so it makes sense that I am not getting any data in the MISO.

In addition, I have checked that the maximum frequency as well as all settings have been set correctly. I also have a baremetal application using the bus correctly, so any HW failure is ruled out.

Spi.hpp

#pragma once
#include <string>
#include <sys/ioctl.h>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include <linux/spi/spidev.h>
#include <drivers/common/literals.hpp>
#include <drivers/common/Utils.hpp>

namespace interfaces
{

namespace spi
{

namespace lnx
{

enum class Mode
{
    ZERO = SPI_MODE_0,
    ONE = SPI_MODE_1,
    TWO = SPI_MODE_2,
    THREE = SPI_MODE_3
};

enum class Bpw
{
    BITS_8 = 8,
    BITS_16 = 16
};

enum class State
{
   CLOSED,
   OPEN
};

class Spi
{
public:
    explicit Spi( const std::string& aDevice )
    {
        memset( &theFrame, 0, sizeof( struct spi_ioc_transfer ) );

        theFileDescriptor = -1;
        theState = State::CLOSED;

        theFrame[0].delay_usecs = 0;

        // Do keep CS activated
        theFrame[0].cs_change = 0;

        if( setDevice( aDevice ) != ErrorCode::OPERATION_SUCESS )
        {
            trace( DEBUG_GENERAL, "error setting device\r\n" );
        }

        if( setMode( Mode::ZERO ) != ErrorCode::OPERATION_SUCESS )
        {
            trace( DEBUG_GENERAL, "error setting mode\r\n" );
        }

        if( setBitsPerWord( Bpw::BITS_8 ) != ErrorCode::OPERATION_SUCESS )
        {
            trace( DEBUG_GENERAL, "error setting bpw\r\n" );
        }

        // Set the SPI device pre-scaler to divide by 128
        // (SPI_CLK_FREQ_HZ = 166 MHz) to transfer below 2MHz clock rate.
        if( setMaxSpeedHz( ( 166 / 128.0 ) * 1000000 ) != ErrorCode::OPERATION_SUCESS )
        {
            trace( DEBUG_GENERAL, "error setting maximum speed\r\n" );
        }
    }

    virtual ~Spi( void )
    {
        if( theState != State::CLOSED && theFileDescriptor != -1 )
        {
            if( close() != ErrorCode::OPERATION_SUCESS )
            {
                trace(
                    DEBUG_GENERAL,
                    "error closing device %s\r\n",
                    theDevice.c_str() );
            }
        }
    }

    Spi( const Spi& ) = delete;

    Spi& operator=( const Spi& ) = delete;

    inline std::string getDevice( void )
    {
        return this->theDevice;
    }

    inline ErrorCode setDevice( const std::string& aDevice )
    {
        ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;

        if( theState != State::CLOSED && theFileDescriptor != -1 )
        {
            if ( close() != ErrorCode::OPERATION_SUCESS )
            {
                goto exit;
            }
        }

        this->theDevice = aDevice;
        anErrorCode = open();

exit:
        return std::move( anErrorCode );
    }

    inline ErrorCode setMode( const Mode& aMode )
    {
        ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;

        if( theState != State::CLOSED && theFileDescriptor != -1 )
        {
            if( ::ioctl(
                    theFileDescriptor,
                    SPI_IOC_WR_MODE32,
                    &aMode ) != -1 )
            {
                auto aResult = getMode();

                if( aResult.first == ErrorCode::OPERATION_SUCESS &&
                    aResult.second == aMode )
                {
                    anErrorCode = ErrorCode::OPERATION_SUCESS;

                    trace(
                        DEBUG_INFO,
                        "mode set to %d\r\n",
                        aMode );
                }
                else
                {
                    close();
                    trace(
                        DEBUG_GENERAL,
                        "mode incongruence\r\n" );
                }
            }
            else
            {
                close();
                trace(
                    DEBUG_GENERAL,
                    "failed setting mode\r\n" );
            }
        }

        return std::move( anErrorCode );
    }

    inline std::pair<ErrorCode, Mode> getMode( void )
    {
        Mode aMode;
        ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;

        if( theState != State::CLOSED && theFileDescriptor != -1 )
        {
            if( ::ioctl(
                    theFileDescriptor,
                    SPI_IOC_RD_MODE32,
                    &aMode ) != -1 )
            {
                anErrorCode = ErrorCode::OPERATION_SUCESS;
            }
            else
            {
                trace(
                    DEBUG_GENERAL,
                    "failed reading mode\r\n" );
            }
        }

        return std::make_pair( anErrorCode, aMode );
    }

    inline ErrorCode setBitsPerWord( const Bpw& aBitsPerWord )
    {
        ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;

        if( theState != State::CLOSED && theFileDescriptor != -1 )
        {
            if( ::ioctl(
                    theFileDescriptor,
                    SPI_IOC_WR_BITS_PER_WORD,
                    &aBitsPerWord ) != -1 )
            {
                auto aResult = getBitsPerWord();

                if( aResult.first == ErrorCode::OPERATION_SUCESS &&
                    aResult.second == aBitsPerWord )
                {
                    anErrorCode = ErrorCode::OPERATION_SUCESS;
                    theFrame[0].bits_per_word =
                        static_cast<uint8_t>( aBitsPerWord );

                    trace(
                        DEBUG_INFO,
                        "bpw set to %d\r\n",
                        aBitsPerWord );
                }
                else
                {
                    close();
                    trace(
                        DEBUG_GENERAL,
                        "bpw incongruence\r\n" );
                }
            }
            else
            {
                close();
                trace(
                    DEBUG_GENERAL,
                    "failed setting bpw\r\n" );
            }
        }

        return std::move( anErrorCode );
    }

    inline std::pair<ErrorCode, Bpw> getBitsPerWord( void )
    {
        uint8_t aBitsPerWord;
        ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;

        if( theState != State::CLOSED && theFileDescriptor != -1 )
        {
            if( ::ioctl(
                    theFileDescriptor,
                    SPI_IOC_RD_BITS_PER_WORD,
                    &aBitsPerWord ) != -1 )
            {
                anErrorCode = ErrorCode::OPERATION_SUCESS;
            }
            else
            {
                trace(
                    DEBUG_GENERAL,
                    "failed reading bpw\r\n" );
            }
        }

        return std::make_pair(
            anErrorCode,
            static_cast<Bpw>( aBitsPerWord ) );
    }

    inline ErrorCode setMaxSpeedHz( const uint32_t aSpeed )
    {
        ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;

        if( theState != State::CLOSED && theFileDescriptor != -1 )
        {
            if( ::ioctl(
                    theFileDescriptor,
                    SPI_IOC_WR_MAX_SPEED_HZ,
                    &aSpeed ) != -1 )
            {
                auto aResult = getMaxSpeedHz();

                if( aResult.first == ErrorCode::OPERATION_SUCESS &&
                    aResult.second == aSpeed )
                {
                    anErrorCode = ErrorCode::OPERATION_SUCESS;
                    theFrame[0].speed_hz = aSpeed;

                    trace(
                        DEBUG_INFO,
                        "maximum speed set to %d Hz\r\n",
                        aSpeed );
                }
                else
                {
                    close();
                    trace(
                        DEBUG_GENERAL,
                        "maximum speed incongruence\r\n" );
                }
            }
            else
            {
                close();
                trace(
                    DEBUG_GENERAL,
                    "failed setting maximum speed\r\n" );
            }
        }

        return std::move( anErrorCode );
    }

    inline std::pair<ErrorCode, uint32_t> getMaxSpeedHz( void )
    {
        uint32_t aSpeed;
        ErrorCode anErrorCode = ErrorCode::CANT_CONFIGURE_INTERFACE;

        if( theState != State::CLOSED && theFileDescriptor != -1 )
        {
            if( ::ioctl(
                    theFileDescriptor,
                    SPI_IOC_RD_MAX_SPEED_HZ,
                    &aSpeed ) != -1 )
            {
                anErrorCode = ErrorCode::OPERATION_SUCESS;
            }
            else
            {
                trace(
                    DEBUG_GENERAL,
                    "failed setting maximum speed\r\n" );
            }
        }

        return std::make_pair( anErrorCode, aSpeed );
    }

    std::pair<ErrorCode, std::vector<uint8_t>> transfer(
        const std::vector<uint8_t>& aBuffer,
        uint32_t aLenght );

    std::pair<ErrorCode, std::vector<uint8_t>>
        read( uint32_t aLenght );

    ErrorCode write( const std::vector<uint8_t>& aBuffer );

private:
    ErrorCode open( void );

    ErrorCode close( void );

    int32_t theFileDescriptor;

    std::string theDevice;

    struct spi_ioc_transfer theFrame[1];

    State theState;
};

} // end namespace lnx

} // end namespace spi

} // end namespace interfaces

Spi.cpp

#include <interfaces/lnx/Spi.hpp>

namespace interfaces
{

namespace spi
{

namespace lnx
{

ErrorCode Spi::open( void )
{
    ErrorCode anErrorCode = ErrorCode::CANT_OPEN_DEV;

    if( !theDevice.empty() )
    {
        theFileDescriptor = ::open(
            theDevice.c_str(), O_SYNC | O_RDWR );

        if( theFileDescriptor != -1 )
        {
            anErrorCode = ErrorCode::OPERATION_SUCESS;
            theState = State::OPEN;

            trace(
                DEBUG_INFO,
                "device %s opened\r\n",
                theDevice.c_str() );
        }
        else
        {
            trace(
                DEBUG_GENERAL,
                "device %s could not be opened\r\n",
                theDevice.c_str() );
        }
    }

    return std::move( anErrorCode );
}

ErrorCode Spi::close( void )
{
    ErrorCode anErrorCode = ErrorCode::CANT_CLOSE_DEV;

    if( theState != State::CLOSED && theFileDescriptor != -1 )
    {
        if( ::close( theFileDescriptor ) == 0 )
        {
            anErrorCode = ErrorCode::OPERATION_SUCESS;
            theFileDescriptor = -1;
            theState = State::CLOSED;

            trace(
                DEBUG_INFO,
                "device %s closed\r\n",
                theDevice.c_str() );
        }
    }

    return std::move( anErrorCode );
}

std::pair<ErrorCode, std::vector<uint8_t>> Spi::transfer(
    const std::vector<uint8_t>& aRequest, uint32_t aLenght )
{
    std::vector<uint8_t> aBuffer( aLenght, 0xFF );
    ErrorCode anErrorCode = ErrorCode::CANT_READ_FROM_INTERFACE;

    if( aRequest.empty() || aLenght <= 0 )
    {
        trace(
            DEBUG_GENERAL,
            "provided arguments not complains the conditions\r\n" );

        goto exit;
    }

    if( theState != State::CLOSED && theFileDescriptor != -1 )
    {
        theFrame[0].tx_buf = reinterpret_cast<uintptr_t>( aRequest.data() );
        theFrame[0].rx_buf = reinterpret_cast<uintptr_t>( &aBuffer[0] );

        // Length of the command to write/read
        theFrame[0].len = aLenght;

        if( ::ioctl(
                theFileDescriptor,
                SPI_IOC_MESSAGE( 1 ),
                &theFrame ) < 1 )
        {
            trace(
                DEBUG_GENERAL,
                "failed doing full duplex transfer\r\n" );
        }
        else
        {
            anErrorCode = ErrorCode::OPERATION_SUCESS;
            vprint( aBuffer.data(), aBuffer.size() );
        }
    }

exit:
    return std::make_pair( anErrorCode, aBuffer );
}

std::pair<ErrorCode, std::vector<uint8_t>>
    Spi::read( uint32_t aLenght )
{
    std::vector<uint8_t> aBuffer( aLenght, 0xFF );
    ErrorCode anErrorCode = ErrorCode::CANT_READ_FROM_INTERFACE;

    if( aLenght <= 0 )
    {
        trace(
            DEBUG_GENERAL,
            "provided arguments not complains the conditions\r\n" );

        goto exit;
    }

    if( theState != State::CLOSED && theFileDescriptor != -1 )
    {
        theFrame[0].tx_buf = static_cast<uintptr_t>( NULL );
        theFrame[0].rx_buf = reinterpret_cast<uintptr_t>( &aBuffer[0] );
        theFrame[0].len = aLenght;

        if( ::ioctl(
                theFileDescriptor,
                SPI_IOC_MESSAGE( 1 ),
                &theFrame ) < 1 )
        {
            trace(
                DEBUG_GENERAL,
                "failed receiving message\r\n" );
        }
        else
        {
            anErrorCode = ErrorCode::OPERATION_SUCESS;
            vprint( aBuffer.data(), aBuffer.size() );
        }
    }

exit:
    return std::make_pair( anErrorCode, aBuffer );
}

ErrorCode Spi::write( const std::vector<uint8_t>& aBuffer )
{
    ErrorCode anErrorCode = ErrorCode::CANT_WRITE_TO_INTERFACE;

    if( aBuffer.empty() )
    {
        trace(
            DEBUG_GENERAL,
            "provided arguments not complains the conditions\r\n" );

        goto exit;
    }

    if( theState != State::CLOSED && theFileDescriptor != -1 )
    {
        theFrame[0].tx_buf = reinterpret_cast<uintptr_t>( aBuffer.data() );
        theFrame[0].rx_buf = static_cast<uintptr_t>( NULL );
        theFrame[0].len = aBuffer.size();

        if( ::ioctl(
                theFileDescriptor,
                SPI_IOC_MESSAGE( 1 ),
                &theFrame ) < 1 )
        {
            trace(
                DEBUG_GENERAL,
                "failed sending message\r\n" );
        }
        else
        {
            anErrorCode = ErrorCode::OPERATION_SUCESS;
            vprint( aBuffer.data(), aBuffer.size() );
        }
    }

exit:
    return std::move( anErrorCode );
}

} // end namespace lnx

} // end namespace spi

} // end namespace interfaces

myboard.dts

...
&spi0 {
   num-cs = <2>;
   spi-cpol = <0>;
   spi-cpha = <0>;
   status = "okay";

   spidev@0 {
      compatible = "xlnx,spidev";
      reg = <0>;
      spi-max-frequency = <1296875>;
   };
};

...

I use the transfer function to achieve my purpose over lnx-xlnx 4.14

Lundin
  • 195,001
  • 40
  • 254
  • 396
afinfante
  • 61
  • 1
  • 7
  • Please edit your question to add your code. – pmacfarlane Feb 09 '23 at 15:21
  • @pmacfarlane done, sorry – afinfante Feb 09 '23 at 15:34
  • I guess what we really need to see is _your_ code, that calls this library. – pmacfarlane Feb 09 '23 at 15:58
  • my code is this one, the other one is simply the object instantiation – afinfante Feb 09 '23 at 16:04
  • The slave has to answer me to such a request (and it is proven to do so) ```c std::vector aBuffer {0xAA, 0xBB, 0xCC, 0xDD}; auto aChannel = std::make_unique( "/dev/spidev1.0" ) aChannel->transfer( aBuffer, aBuffer.size() ); ``` – afinfante Feb 09 '23 at 16:10
  • 1
    After deepen the `spidev` driver documentation I have found the solution by my own means, the problem lies and can be solved completely remarking that the transfer is required to be full duplex, so [here](https://github.com/torvalds/linux/blob/master/tools/spi/spidev_fdx.c) you can find the illumination. – afinfante Feb 10 '23 at 09:14

0 Answers0