We have upgraded the community system as part of the upgrade a password reset is required for all users before login in.

spidev fails

  • Hello Everyone,

    I am trying to run SPI half-duplex for my gateway(LORA) and when I run (Spi_test) utility of gateway it shows some random data read and when I connect it to USB analyzer it works as expected.it shows all data read on MOSI is correct. but in software it is not as expected. and when I run same SPI utility with Raspberry pi it works perfectly.
    kindly help me.


  • administrators

    @rahul2413 You'll need to double-check that the program is actually using half-duplex SPI. Some programs have a full duplex vs half duplex switch.

    Another option to explore:
    Since LoRa radios have a pretty low datarate, you can also get away with using a software-based SPI bus. More info in this FAQ post.

  • I double checked it. its half duplex communication and same happens with python spidev .when I try to read single register value using xfer3 function it is reading 0x00 and it should be 0x12 but when i run it on raspberry pi using same function it gives the result and when i checked it on usb analyser with onion it shows that it reads 0x12.but not in same index but in [index-1] so when i try to read previous index using xfer3 it reads correctly.

    spi.max_speed_hz = 1000000
    spi.mode = 0b00
    spi.open(0, 1)
    (its five byte transmit)
    readVals = spi.xfer3([0x00,(0x00 | ((addr >>8) & 0x7F)),((addr >> 0) &0xFF),0x00,0x00],2) when i try to read using this it gives 0x00. and in rpi it gives result as expected.

    (its four byte transmit)
    readVals = spi.xfer3([0x00,(0x00 | ((addr >>8) & 0x7F)),((addr >> 0) &0xFF),0x00],2)
    this gives result 0x12.

    but according to datasheet of lora IC five byte transmit is the way its given..

  • administrators

    @rahul2413 said in spidev fails:

    but not in same index but in [index-1] so when i try to read previous index using xfer3 it reads correctly.

    Sorry, I'm a little confused. Does this resolve your issue?

  • no its not solved I am just reading the single register value of lora chip using python spidev by modified some bytes but I have to run this application on C language that is not working because full stack of lora gateway is available on C language so that's why I want run it. i want to know that if there is some problem in spidev.

  • I'm curious: How did you check that it is using half-duplex?
    Full-duplex on anything with a Mediatek-7688 MCU won't work due to a silicon bug.
    You can simulate full duplex by using two SPI transactions, a write, followed by a read, without dropping the CS line inbetween.

    I use SPI from C++ on My Oemags S2+ without any problems. (In half-duplex!)

  • @SpiderKenny

    I am sharing code can you tell me whats wrong with it. this similar code works with raspberry pi.

    #define READ_ACCESS 0x00
    #define WRITE_ACCESS 0x80
    #define LGW_SPI_MUX_TARGET_SX1302 0x0000
    #define SX1302_REG_COMMON 0x5600
    my spi is spidev 0.1

    //Function to Read Register for chip version...

    x = lgw_spi_r(LGW_COM_SPI,LGW_SPI_MUX_TARGET_SX1302, SX1302_REG_COMMON + 6, &data);
    if (x != 0) {
        printf("ERROR (%d): failed to read register\n", __LINE__);
    printf("SX1302 version: 0x%02X\n", data);

    int lgw_spi_r(void *com_target, uint8_t spi_mux_target, uint16_t address, uint8_t *data) {
    int spi_device;
    uint8_t out_buf[5];
    uint8_t command_size;
    uint8_t in_buf[ARRAY_SIZE(out_buf)];
    struct spi_ioc_transfer k;
    int a;

    /* check input variables */
    spi_device = *(int *)com_target; /* must check that com_target is not null beforehand*/
    /* prepare frame to be sent */
    out_buf[0] = spi_mux_target;   //spi_mux_target 0x00
    out_buf[1] = READ_ACCESS | ((address >> 8) & 0x7F);    //addrress=0x5600 //READ_ACCESS = 0x00
    out_buf[2] =  ((address >> 0) & 0xFF);
    out_buf[3] = 0x00;
    out_buf[4] = 0x00;
    command_size = 5;
    /* I/O transaction */
    memset(&k, 0, sizeof(k)); /* clear k */
    k.tx_buf = (unsigned long) out_buf;
    k.rx_buf = (unsigned long) in_buf;
    k.len = command_size;
    k.cs_change = 0;
    a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k);
    /* determine return code */
    if (a != (int)k.len) {
        return LGW_SPI_ERROR;
    } else {
        DEBUG_MSG("Note: SPI read success\n");
        *data = in_buf[command_size - 1];
        return LGW_SPI_SUCCESS;


    the output is "SX1302 version: 0x00" output should be 0x12
    I am also sharing datasheet information

    IScreenshot (9).png

    it will work on onion omega 2s+ or not

  • administrators

    @rahul2413 said in spidev fails:

    I am just reading the single register value of lora chip using python spidev by modified some bytes

    Since the python spidev module correctly reads the register, we can conclude the Omega2 is able to communicate with your lora radio module and there is no show-stopping issue in spidev.

    If the python spidev half-duplex transmission worked, and the C program doesn't work, I think it's safe to assume the C program is using full-duplex.

    Again, my recommendations are:

    1. Look at the C code to see if there's a flag or switch for full duplex vs half duplex
    2. Consider using a software-based SPI bus to communicate with the lora module. More info in this FAQ post.

    One more thing:

    @rahul2413 said in spidev fails:

    I am sharing code can you tell me whats wrong with it.

    This is a very large ask for a community forum. In my experience, the best way to resolve and debug technical issues on a forum is through collaboration rather than throwing the whole problem at another user. Please keep that in mind ๐Ÿ™‚

  • sorry for this

    but in python spidev its not working as it should be i have to change sending bytes .but in raspberry pi its working without any change. and according to documents of lora chip its half-duplex.

  • @rahul2413 Hello, Did you ever get the spidev to work, Its not working for me, all i need to do is read just ONE BYTE from the spi and i cant get that to work.

    Does anyone have C code that works with spidev for omega onion? Im using the code at this link below and it doesnt work.


    Im puzzled as to why the SPI that has been around for decades is such a mystery to use. I thought a SPI would be a no brainer, but i been at it for a week and cant get it to work.

    Using a SPI on arduino for example is just one line of code, why is it so complicated on onion? Cant they make a simple API like spi_transfer(....), i dont really understand all the mystery about it.

  • @nsmith access to SPI via spidev works fine for me in various setups, with one important exception: full duplex mode in the MT7688 chip is known to have a (unfixable, hardware) bug which affects the first byte received, making it unreliable.

    As SPI is always duplex on the wire, you need to send (clock) as many bits as you want to receive. The concept of half vs full duplex only differentiates SPI transfers where the bits coming in while sending data are considered return data, vs those where these bits are irrelevant fillers just to get the clock going.

    So reading ONE BYTE from the SPI actually means sending one dummy byte and recording the bits that come back while sending the dummy byte. Due to the MT7688 hardware bug this is exactly what does not work - the byte you get back will not be correct ๐Ÿ˜ž

    In some cases, you can work around the problem. Depending on your SPI slave it might be possible to just make a 2 byte transfer to have the slave repeat the data, and then just use the second byte returned.

    What chip are you trying to read from?

    For example I had to use a trick for the MCP3002 A/D converter, which usually works with a 2-byte full duplex transfer. Fortunately, the MCP3002 waits for a start bit in the incoming data before it starts responding, so in that case I could just use a 3-byte transfer with a delayed start bit to make sure no relevant data is in the first byte returned.

    Apart from this HW problem, the standard linux SPI_IOC_MESSAGE ioctl on /dev/spidevX.Y works fine for me. I use it from C++ via a wrapper function spidev_write_read(), see lines 211 ff in spi.cpp of p44utils.

    Hope this helps!

  • administrators

    ๐Ÿ‘ @luz

    @nsmith in addition to @luz's advice, you can try using the python-spidev module to get an initial sanity read/write test working quickly.
    Then you'll have a working example to base your own C code on.

  • @luz Because of the SPI BUG in the Onion, it seems like i cant use my A/D converter chip. Here is the data sheet of the one im using.


    Im using the 8 bit version of that chip.

    This A/D converter assumes a properly working SPI because according to "Auto Power Down" section on page 20 of that data sheet linked above, the A/D chip uses the CS and the SPI clock to time when to wake up, start, and stop conversions, and when to power down itself. It wakes up on CS going low, and starts conversion at 4th clock pulse.

    After 16th clock pulse it automatically powers down after transmitting the last bit of the A/D reading. So I dont know how it can work with the SPI bug, even if i try sending multiple bytes, it will power off after 16 pulses no matter what, unless i send another CS', then the same thing will just happen again.

    So now because of the SPI HW bug, I have to throw away my PCB that cost me hundreds of dollars and weeks to design, and start all over to make a new board for a different A/D converter, possibly the A/D your using.

    I cant use the bit bang SPI because i need to do 3 conversions every 500us (plus run some other code too).

    So great!

  • @nsmith from the ADS7868 datasheet you linked, in particular Figure 1 on page 9, it seems to me that you'll need to read more than one byte, anyway.

    From the first byte, the first 4 bits are just fillers. The actual result starts with the 5th bit of the first byte and ends with the 4th bit of the second byte (8bits total).

    As the HW bug in the MT7688 apparently affects only the second bit in the transfer, I first thought you could get back a usable result when you ignore the first 4 bits.

    However: what very much confuses me right now is that the spi-mt7621.c source code I linked above seems to completely reject any SPI_IOC_MESSAGE transfer trying to do full duplex.

    I can't understand how my MCP3002 C++ code could have possibly worked with this driver, but I know for certain it did with an Omega2 (that hardware is still in use). Maybe it was using an different version of spi-mt7621.c without that check (but I could not find it right now).

    Whatever - bottom line for ADS7868 and MT7688 (funny numbering similarity...) seems to be: impossible with spidev ๐Ÿ˜ข

    I cant use the bit bang SPI because i need to do 3 conversions every 500us (plus run some other code too).

    Now that could have been a problem even without the MT7688 SPI bug, because you don't have exclusive access to SPI - the system flash is connected to it as well and whenever any part of the system needs to access the flash, SPI will be in use for hard to predict intervals.

    But maybe you should reconsider bit bang SPI, which has a (fixed) ~1Mhz clock and full duplex working well. Your 16bits would take ~16ยตS, maybe leaving enough time for your other code.

  • @luz Awesome post, thank you very much for all your help. I think I will use the bitbang method since I didnt do the calculation that it would take only 16us to read one of the A/D converters, so it seems i have plenty of time.

    I think the HW SPI is only used during boot up to read the OpenWRT kernel code from the flash into RAM, after that i believe its not used at all, otherwise the SPI would be un-usable. I really hope this is true becuase I'm already lacking 4 GPIO pins, if I have to give up more pins to a bitbanged SPI then my project will be over.

    Thanks again!

  • @nsmith said in spidev fails:

    I think the HW SPI is only used during boot up to read the OpenWRT kernel code from the flash into RAM, after that i believe its not used at all, otherwise the SPI would be un-usable. I really hope this is true becuase I'm already lacking 4 GPIO pins, if I have to give up more pins to a bitbanged SPI then my project will be over.

    ๐Ÿ˜ž hopefully not, but:

    The SPI is used for loading the bootloader from flash (without any driver, this part is hard-wired in the MT7688), then the bootloader (u-boot) will load the Linux kernel from flash using its own flash driver.

    But from then onwards the linux kernel will still access the flash whenever it needs to read or write to a file that is not on the ramdisk (which is - pretty everything except from /tmp and /var). Of course, there is some caching, so not every tiny file access will always access flash, but still in many cases.

    Depending on what your overall application does, there might be very little file accesses, but avoiding them entirely (from linux services running in the background, responding to network, keeping WiFi connections, serving web requests or ssh etc.) would mean you need to strip down OpenWrt severely, essentially killing all services except for your own app, or transplant everything to the ramdisk. Maybe possible, but far from a no-brainer.
    (Btw: sysupgrade needs to do the ramdisk transplant thing when it replaces the firmware, so one could glean some ideas from there - complicated beast though, too.)

    Unfortunately, it seems to me not even the "switch everything to SW-SPI" trick you posted can help here, because after that you'll still be sharing the SPI bus with the flash chip, only that flash accesses now take 40 times more time ๐Ÿ˜ข

    Maybe, when you run out of GPIOs, a I/O extender chip like MCP23S17 might be far easier than too much struggle trying to re-use those HW SPI pins in any way. These are good for half-duplex SPI devices that need high bandwidth but no real time bus availability, such as a LCD display (and the internal flash), but for nothing else.

Log in to reply

Looks like your connection to Community was lost, please wait while we try to reconnect.