ESP32: spi_master: check_trans_valid(801) при использовании SPI

Я использую следующий код для связи с дисплеем:

void Display_HandleDC(spi_transaction_t* p_Transaction)
{
    gpio_set_level((gpio_num_t)LCD_DC, (uint32_t)p_Transaction->user);
}

static void Display_SendCommand(const uint8_t Command)
{
    spi_transaction_t t;

    memset(&t, 0, sizeof(t));

    t.length = 8;
    t.tx_buffer = &Command;
    t.user=(void*)0;

    assert(spi_device_polling_transmit(_SPI, &t) == ESP_OK);
}

static void Display_SendData(const uint8_t* p_Data, uint32_t Length)
{
    spi_transaction_t t;

    if(Length == 0)
    {
        return;
    }

    memset(&t, 0, sizeof(t));

    t.length = Length * 8;
    t.tx_buffer = p_Data;
    t.user=(void*)1;

    assert(spi_device_polling_transmit(_SPI, &t) == ESP_OK);
}

static uint32_t Display_ReadID(void)
{
    spi_transaction_t t;

    Display_SendCommand(0x04);

    memset(&t, 0, sizeof(t));
    t.length = 4 * 8;
    t.flags = SPI_TRANS_USE_RXDATA;
    t.user = (void*)1;

    assert(spi_device_polling_transmit(_SPI, &t) == ESP_OK);

    return *(uint32_t*)t.rx_data;
}

static void Display_Write(int ypos, uint16_t *linedata)
{
    static spi_transaction_t trans[6];

    for(uint8_t x=0; x<6; x++)
    {
        memset(&trans[x], 0, sizeof(spi_transaction_t));
        if ((x&1)==0) {
            //Even transfers are commands
            trans[x].length=8;
            trans[x].user=(void*)0;
        } else {
            //Odd transfers are data
            trans[x].length=8*4;
            trans[x].user=(void*)1;
        }

        trans[x].flags = SPI_TRANS_USE_TXDATA;
    }

    trans[0].tx_data[0] = 0x2A;
    trans[1].tx_data[0] = 0;
    trans[1].tx_data[1] = 0;
    trans[1].tx_data[2] = 320 >> 8;
    trans[1].tx_data[3] = 320 & 0xFF;

    trans[2].tx_data[0] = 0x2B;
    trans[3].tx_data[0] = ypos >> 8;
    trans[3].tx_data[1] = ypos & 0xFF;
    trans[3].tx_data[2] = (ypos + 1) >> 8;
    trans[3].tx_data[3] = (ypos + 1) & 0xFF;

    // Write the data to memory
    trans[4].tx_data[0] = 0x2C;
    trans[5].tx_buffer = linedata;
    trans[5].length = 320 * sizeof(uint16_t) * 8;
    trans[5].flags = 0x00;

    for(uint8_t x = 0; x<6; x++)
    {
        assert(spi_device_queue_trans(_SPI, &trans[x], portMAX_DELAY) == ESP_OK);
    }
}

static void Display_Busy(void)
{
    spi_transaction_t* rtrans;
    for(uint8_t x=0; x<6; x++)
    {
        assert(spi_device_get_trans_result(_SPI, &rtrans, portMAX_DELAY) == ESP_OK);
    }
}

esp_err_t Display_Init(void)
{
    static uint16_t* Line;

    gpio_set_direction((gpio_num_t)LCD_DC, GPIO_MODE_OUTPUT);
    gpio_set_direction((gpio_num_t)LDC_RESET, GPIO_MODE_OUTPUT);
    gpio_set_direction((gpio_num_t)LCD_BACKLIGHT, GPIO_MODE_OUTPUT);

    if((spi_bus_initialize(HSPI_HOST, &_BusCfg, 0) != ESP_OK) || (spi_bus_add_device(HSPI_HOST, &_DevDcfg, &_SPI) != ESP_OK))
    {
        return ESP_FAIL;
    }

    Display_Reset();
    ESP_LOGI(TAG, "ID: %08X", Display_ReadID());

    uint32_t Commands = sizeof(_Init_Cmd_List) / sizeof(_Init_Cmd_List[0]);
    ESP_LOGI(TAG, "Loading initialization commands: %u", Commands);

    for(uint32_t i = 0x00; i < Commands; i++)
    {
        ESP_LOGI(TAG, "     Command %u / %u", i + 1, Commands);
        Display_SendCommand(_Init_Cmd_List[i].Command);
        Display_SendData(_Init_Cmd_List[i].Data, _Init_Cmd_List[i].Length & 0x1F);
        if(_Init_Cmd_List[i].Length & 0x80)
        {
            vTaskDelay(100 / portTICK_RATE_MS);
        }
    }

    Line = (uint16_t*)heap_caps_malloc(320 * sizeof(uint16_t), MALLOC_CAP_DMA);
    if(Line == NULL)
    {
        return ESP_FAIL;
    }

    for(uint32_t i = 0x00; i < 320; i++)
    {
        Line[i] = 0xF8;
    }

    Display_Write(0, Line);
    Display_Busy();
    free(Line);

    return ESP_OK;
}

И этот код приводит к следующему сообщению об ошибке:

E (1833) spi_master: check_trans_valid(801): txdata transfer > host maximum
assertion "spi_device_queue_trans(_SPI, &trans[x], portMAX_DELAY) == ESP_OK" failed: file "src/Peripherals/Display/display.cpp", line 225, function: void Display_Write(int, uint16_t*)
abort() was called at PC 0x400e47a4 on core 1

ELF file SHA256: bc86d952445723b2

Backtrace: 0x4008e8c0:0x3ffb4b30 0x4008ecb9:0x3ffb4b50 0x400e47a4:0x3ffb4b70 0x400d170d:0x3ffb4ba0 0x400d1873:0x3ffb4bc0 0x400d13ea:0x3ffb4bf0 0x400d194b:0x3ffb4c20 0x40088601:0x3ffb4c40
  #0  0x4008e8c0:0x3ffb4b30 in invoke_abort at .platformio\packages\[email protected]\components\esp32/panic.c:155
  #1  0x4008ecb9:0x3ffb4b50 in abort at .platformio\packages\[email protected]\components\esp32/panic.c:172
  #2  0x400e47a4:0x3ffb4b70 in __assert_func at /builds/idf/crosstool-NG/.build/HOST-i686-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdlib/assert.c:62 (discriminator 8)  
  #3  0x400d170d:0x3ffb4ba0 in Display_Write(int, unsigned short*) at src/Peripherals/Display/display.cpp:225 (discriminator 1)
  #4  0x400d1873:0x3ffb4bc0 in Display_Init() at src/Peripherals/Display/display.cpp:288
  #5  0x400d13ea:0x3ffb4bf0 in setup() at src/Application/application.cpp:21
  #6  0x400d194b:0x3ffb4c20 in applicationTask(void*) at src/main.cpp:41
  #7  0x40088601:0x3ffb4c40 in vPortTaskWrapper at .platformio\packages\[email protected]\components\freertos/port.c:143

Но ошибка не возникает, когда я меняю канал DMA с 0 на 1:

if((spi_bus_initialize(HSPI_HOST, &_BusCfg, 1) != ESP_OK) || (spi_bus_add_device(HSPI_HOST, &_DevDcfg, &_SPI) != ESP_OK))
{
    return ESP_FAIL;
}

В чем разница между обоими каналами и почему работает только 1 канал?

, 👍0


1 ответ


Лучший ответ:

2

Ваш код инициализирует шину SPI, вызывая функцию spi_bus_initialize(HSPI_HOST, &_BusCfg, 0). Последний аргумент этой функции (int dma_chan) определяет используемый канал DMA, который может быть 0, 1 или 2.

Выбор канала DMA "0" на самом деле не будет выбирать канал DMA вообще (что объясняется в документации Espressif):

  • dma_chan: Либо канал 1, либо 2, либо 0 в том случае, когда DMA не требуется. Выбор канала DMA для шины SPI позволяет передавать данные по шине с размерами, ограниченными только объемом внутренней памяти. Выбор no DMA channel (путем передачи значения 0) ограничивает количество передаваемых байтов максимум 32 байтами.

Причина, по которой вы получаете ошибку, когда ваш ESP32 пытается выполнить передачу SPI, когда канал DMA не выбран (опция 0), заключается в том, что передача, по-видимому, превышает 32 байта.

Возникает ошибка "E (1833) spi_master: check_trans_valid(801): txdata transfer > host maximum", которая отражает это.

Причина, по которой канал DMA 1 работает правильно, заключается в том, что это фактический канал DMA, и он не имеет ограничений на количество передаваемых байтов (ограничивается только объемом внутренней памяти).

,