RPI4 spigen - achieving higher throughput - mcp3912

Hello,

OS: FreeBSD:13.2-Release
Driver: spigen-rpi4

The issue: With 5MHz SPI clock the communication RPI<->mcp3912 is established smooth. After increasing the speed to 10MHz, data is very random/incorrect.
The SPI devices supports SPI clock to 20MHz.

Verification scenario is very short:
  1. store the value on a specific register
  2. read register and compare with the value
  3. values are not equal
I would like to now, if my SPI configuration and SPI transfer code might do some mess. On can find the code at the bottom.

Would be great to focus only onto a SW/FW site. - of course any HW specific thoughts are nice to read as well.

MCP insides:
  • Each register is 24 bit
  • Communication start with initial byte, where one determine address, register and write/read operation

Many thanks for any advice!

Best regards,


Code I use with some readability trunks:
C:
typedef union TB {
  uint32_t msg;
  uint8_t msg_u8[sizeof(uint32_t)];
} TB;

void mcp_spi_init(int32_t speed) {
  ASSERT_COND(speed > 0);
  atexit(close_descriptors);

  spifd0 = open(mcp_spidev_0, O_RDWR);
  ASSERT_IO(spifd0);
  ASSERT(ioctl(spifd0, SPIGENIOC_SET_SPI_MODE, &spimode));
  ASSERT(ioctl(spifd0, SPIGENIOC_SET_CLOCK_SPEED, &spispeed));
  is_mode32 = false;
}

void mcp_spi_write(uint8_t dev_mask, uint8_t mcp_reg, uint32_t cmd) {
  ASSERT_COND(!(cmd & 0xff000000));
  TB tb;
  tb.msg = bswap32(cmd);
  uint8_t mcp_addr = (MCP_DEVADDR_DEFAULT << MCP_DEVADDR__O) |
                     (mcp_reg << MCP_REGADDR__O) | MCP_WRITE;
                   
  struct spigen_transfer mcp_trn;
  mcp_trn.st_command.iov_base = &mcp_addr;
  mcp_trn.st_command.iov_len = 1;

  if (is_mode32) {
    mcp_trn.st_data.iov_base = tb.msg_u8;
    mcp_trn.st_data.iov_len = sizeof(uint32_t);
  } else {
    mcp_trn.st_data.iov_base = tb.msg_u8 + 1;
    mcp_trn.st_data.iov_len = sizeof(uint32_t) - 1;
  }

   ASSERT(ioctl(spifd0, SPIGENIOC_TRANSFER, &mcp_trn));
}

uint32_t mcp_spi_read(uint8_t mcp_reg) {
  TB tb;
  tb.msg = 0;
  uint8_t mcp_addr = (MCP_DEVADDR_DEFAULT << MCP_DEVADDR__O) |
                     (mcp_reg << MCP_REGADDR__O) | MCP_READ;

  struct spigen_transfer mcp_trn;
  mcp_trn.st_command.iov_base = &mcp_addr;
  mcp_trn.st_command.iov_len = 1;

  if (is_mode32) {
    mcp_trn.st_data.iov_base = tb.msg_u8;
    mcp_trn.st_data.iov_len = sizeof(uint32_t);
  } else {
    mcp_trn.st_data.iov_base = tb.msg_u8 + 1;
    mcp_trn.st_data.iov_len = sizeof(uint32_t) - 1;
  }

  ASSERT(ioctl(spifd0, SPIGENIOC_TRANSFER, &mcp_trn));
  return bswap32(tb.msg);
}
 
Back
Top