whaleygeek / pyenergenie

A python interface to the Energenie line of products
MIT License
82 stars 51 forks source link

SPI Hardware driver - spidev #120

Open whaleygeek opened 4 years ago

whaleygeek commented 4 years ago

I have been experimenting with the spidev device driver tonight on a pi, with good results.

https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md

I might make the SPI auto detect and use an spidev instance, and fall back to softspi if it can't find it.

This will increase turnaround performance in challenging use cases like eTRV.

wget https://raw.githubusercontent.com/raspberrypi/linux/rpi-3.10.y/Documentation/spi/spidev_test.c

pi@raspberrypi:~ $ while [ 1 ]; do ./spidev_test -D /dev/spidev0.0 -s 20000000; done
Achronite commented 4 years ago

Sounds good David, I'm definitely interested in this :)

Achronite commented 1 year ago

Hi David - I've been having a look at this. I've hit an issue I wondered if you could help with?

I successfully managed to swap out the software driver for the bcm2835 driver, but there was an issue in that it needed root to execute. So I am now trying to use spidev (C kernel library version). The issue I have is that I can't read the register values, I always seem to be getting 0 back... Is there anything special I need to do between Tx of the address and the Rx of the response?

bcm2835 version (working):

uint8_t HRF_readreg(uint8_t addr)
{
    uint8_t buf[2];
    buf[0] = addr;
    bcm2835_spi_transfern((char*)buf, 2);
    return buf[1];
}

spidev version:

uint8_t HRF_readreg(uint8_t addr)
{
    int status = 0;
    struct spi_ioc_transfer spi_msg;
    memset(&spi_msg, 0, sizeof(spi_msg));

    uint8_t txbuf[2];
    uint8_t rxbuf[2] = {1,1};

    // Populate register to read in tx
    txbuf[0] = addr;
    txbuf[1] = 0;

    // Setup transfer.
    spi_msg.tx_buf      = (__u64)txbuf;
    spi_msg.rx_buf      = (__u64)rxbuf;
    spi_msg.len         = 2;
    spi_msg.delay_usecs = 0;
    spi_msg.speed_hz    = 8000000;
    spi_msg.bits_per_word = 8;
    spi_msg.cs_change   = 0;

    // Perform transfer.
    printf("0 tx=%d,%d rx=%d,%d\n",txbuf[0],txbuf[1],rxbuf[0],rxbuf[1]);

    // transmit the register to read - this returns length of transmit!
    status = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi_msg);

    printf("1 status=%d tx=%d,%d rx=%d,%d\n",status,txbuf[0],txbuf[1],rxbuf[0],rxbuf[1]]);
    fflush(stdout);
...

Called with HRF_readreg(HRF_ADDR_VERSION); (radio version) it just returns 0 instead of 36. output:

0 tx=16,0 rx=1,1
1 status=2 tx=16,0 rx=0,0
whaleygeek commented 1 year ago

Hmm, nothing obvious wrong.

Is spifd set correct, what does SPI_IOC_MESSAGE(1) bind to, is that correct?

Max clock of rfm69 is 10MHz, that should be fine

Have you pulsed reset to get the radio into the correct initial state?

Is the polarity of CS correct?

Is the SPI mode PHA and POL correct?

This might also help... https://github.com/OnionIoT/python-spidev

Do you need cs_change 1 or are you doing cs external to send?

Do you have a scope of logic analyser to get a trace of the transaction?

Achronite commented 1 year ago

So on initial inspection it looks like CS is set to CS_HIGH at the moment, looking at the HopeRF specs, NSS (aka CS) should be set LOW at start and HIGH at the end. Now the weird thing is, that I'm not setting CS_HIGH by the spi mode command:

       uint8_t spiMode = SPI_MODE_0;
        printf("Setting spiMode=%d\n",spiMode);
        ret = ioctl(spi_fd, SPI_IOC_RD_MODE, &spiMode);
        printf("Interim spiMode=%d\n",spi_fd,spiMode);

        if (ioctl(spi_fd, SPI_IOC_WR_MODE, &spiMode) == 0){
            // Get SPI mode
            ret = ioctl(spi_fd, SPI_IOC_RD_MODE, &spiMode);
            printf("Read ret=%d,spiMode=%d\n",ret,spiMode);
            fflush(stdout);

Outputs:

Setting spiMode=0
Interim spiMode=19
Read ret=0,spiMode=4

I believe 4 means (see: https://github.com/spotify/linux/blob/master/include/linux/spi/spidev.h): CPHA (0x01)=0, CPOL (0x02)=0, CS_HIGH (0x04)=1

Can you confirm that CS should be set this LOW? If so, I Might have to get the linux kernel guys involved here!

Achronite commented 1 year ago

Follow-up: I've fixed it by switching to /dev/spidev0.1 (from /dev/spidev0.0) although it seems to be working the opposite way to expected to me.