Help with SPI reading/writing to as5048a

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Help with SPI reading/writing to as5048a

Postby sb_espressif » Thu Mar 14, 2024 3:07 am

Hi, this is my first rodeo with SPI communication; I am trying to interact with an as5048a, which is an angular magnetic encoder with 16-bit registers:
https://look.ams-osram.com/m/287d7ad97d ... 000298.pdf

I am using an esp32-s3 seeed module. My problem is that I cannot seem to read data successfully from the sensor, for reasons I don't understand - the result is always the same, for all registers (0b0110000000000000).

Some code:

I configure my SPI bus and device like so:

Code: Select all

   // -------------------------
    // Configure spi
    // Per these docs:
    // https://docs.espressif.com/projects/esp-idf/en/v5.2.1/esp32s3/api-reference/peripherals/spi_master.html

    int8_t error_status;

    // First, configure the bus
    spi_bus_config_t spi_bus_config = {
        .miso_io_num = PIN_NUM_MISO,
        .mosi_io_num = PIN_NUM_MOSI,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 16
    };

    // Now initialize the SPI bus
    error_status = spi_bus_initialize(SPI_HOST, &spi_bus_config, SPI_DMA_DISABLED);
    ESP_ERROR_CHECK(error_status);

    spi_device_interface_config_t sensor_config = {
        .clock_speed_hz = 300 * 1000, 
        .mode = 1,                              
        .spics_io_num = PIN_NUM_CS, 
        .queue_size = 1,                     
        .command_bits = 0,
        .address_bits = 0,
        .dummy_bits = 0,
        .input_delay_ns = 350
    };

    // Attach the sensor
    spi_device_handle_t sensor_handle;
    error_status = spi_bus_add_device(SPI_HOST, &sensor_config, &sensor_handle);
    ESP_ERROR_CHECK(error_status);
Then, I try to read from the device like so:

Code: Select all

int8_t AS5048A_read_register(AS5048A *sensor,
                                 uint16_t register_address,
                                 uint16_t *data)
{

    int8_t error_status;
    
    uint16_t command = (register_address & 0x3FFF);
    command |= AS5048A_READ_FLAG;
    command = AS5048A_calculate_even_parity(command);
    // to read the sensor, command at this point = 0xFFFF

    spi_transaction_t spi_transaction;
    memset(&spi_transaction, 0, sizeof(spi_transaction));
    spi_transaction.length = 16; 
    spi_transaction.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
    *((uint16_t*)spi_transaction.tx_data) = SPI_SWAP_DATA_TX(command, 16);    
    // this crazy line is because of
    // this note in the ESP SPI docs:
    // https://docs.espressif.com/projects/esp-idf/en/v5.2.1/esp32s3/api-reference/peripherals/spi_master.html#transactions-with-integers-other-than-uint8-t

    // We send one transaction, which is our READ comment. We don't care about the received part yet. 
    spi_device_acquire_bus(sensor->sensor_handle, portMAX_DELAY);
    error_status = spi_device_transmit(sensor->sensor_handle, &spi_transaction);
    spi_device_release_bus(sensor->sensor_handle);

    if (error_status != ESP_OK) {
        ESP_LOGE(TAG, "Error sending read command to register: %d", command);
        return -1; // Return -1 on failure
    }


    // We send another (arbitrary) command, because what we now are interested
    // in is the received data, which is the response to the above read command. 
    spi_device_acquire_bus(sensor->sensor_handle, portMAX_DELAY);
    error_status = spi_device_transmit(sensor->sensor_handle, &spi_transaction);
    spi_device_release_bus(sensor->sensor_handle);
    if (error_status != ESP_OK) {
        ESP_LOGE(TAG, "Error receiving read data from register.");
        return -1; // Return -1 on failure
    }

    
    uint16_t response = SPI_SWAP_DATA_RX(*((uint16_t*)spi_transaction.rx_data), 16); 
   // response is always 0110000000000000;
  
    return error_status; 
}
I'm trying a logic analyzer to confirm that signals are indeed being sent and look like what I expect, though I do have some questions. Like, the datasheet prescribes a 350ns min time between when CS gets pulled low and MOSI starts speaking, which I tried to configure in the sensor config but which doesn't seem to be reflected in my logic analyzer. And I'm unsure if that might cause the issue I'm seeing. But this is all new to me so I might just be doing something obviously wrong...

any advice?

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Re: Help with SPI reading/writing to as5048a

Postby sb_espressif » Sat Mar 16, 2024 7:18 pm

For any intrepid adventurers in the future who stumble upon this thread:

I eventually traced my issue above to a quirk of the as5048a, which is namely that the datasheet for this sensor prescribes a minimum 350ns delay between when cs is pulled low and when the first clock cycle is sent for an SPI transaction. Setting the cs pin in the device configuration part of things when setting up the SPI pipeline:

Code: Select all

    spi_device_interface_config_t sensor_config = {
        .clock_speed_hz = 300 * 1000, 
        .mode = 1,                              
        .spics_io_num = PIN_NUM_CS,  // < ---BAD NEWS BEARS
        .queue_size = 1,                     
        .command_bits = 0,
        .address_bits = 0,
        .dummy_bits = 0,
        .input_delay_ns = 350
    };
turned out to be, as you no doubt surmise from my comment above, bad news bears. This is because esp-idf sends both the cs signal and the first clock cycle at the same time, and the ".input_delay_ns" flag didn't do what I thought it did (which was to enforce this delay).

The way around it for me is to set .spics_io_num = -1, which wrests control from esp-idf to manage the timing of all this, and I manually pull cs low myself before issuing the spi_transmit command:

Code: Select all

    spi_device_acquire_bus(sensor->sensor_handle, portMAX_DELAY);
    gpio_set_level(sensor->cs_pin, 0);
    error_status = spi_device_polling_transmit(sensor->sensor_handle, &spi_transaction);
    gpio_set_level(sensor->cs_pin, 1);
    spi_device_release_bus(sensor->sensor_handle);
Inspecting this with a logic analyzer (buy a logic analyzer! They're like $15 on amazon! Super helpful!) shows the requisite delay happening, and voila, everything started working as expected.

User avatar
ok-home
Posts: 78
Joined: Sun May 02, 2021 7:23 pm
Location: Russia Novosibirsk
Contact:

Re: Help with SPI reading/writing to as5048a

Postby ok-home » Sun Mar 17, 2024 2:25 am

Hi.
You can try
spi_device_interface_config.cs_ena_pretrans
Amount of SPI bit-cycles the cs should be activated before the transmission (0-16).
https://docs.espressif.com/projects/esp ... _pretransE

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Re: Help with SPI reading/writing to as5048a

Postby sb_espressif » Sun Mar 17, 2024 2:46 am

It's the "this only works on half-duplex transactions" part of that particular parameter that dissuaded me from using it, as this sensor communicates using full duplex transactions.

Who is online

Users browsing this forum: No registered users and 82 guests