Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP32C3 9bit-SPI Transmit Error (IDFGH-6865) #8487

Closed
liaozhelin opened this issue Mar 2, 2022 · 9 comments
Closed

ESP32C3 9bit-SPI Transmit Error (IDFGH-6865) #8487

liaozhelin opened this issue Mar 2, 2022 · 9 comments
Labels
Awaiting Response awaiting a response from the author Resolution: Done Issue is done internally Status: Done Issue is done internally

Comments

@liaozhelin
Copy link

Environment

  • Development Kit: [ESP32-C3 LUATOS/ESP32C3]
  • Module or chip used: [ESP32-C3]
  • IDF version :v4.4
  • Build System: idf.py
  • Compiler version :8.4.0
  • Operating System: Windows
  • (Windows only) environment type: PowerShell.
  • Using an IDE?: Yes Vscode
  • Power Supply: USB

Problem Description

I have a 3-wire TFT screen, his communication protocol is 9bit, the highest bit is cmd/dat control bit, and the remaining 8 bits are communication data.
Through the vscode-idf plug-in tool, the project was created through the template spi-master. According to the sending method of 9bit spi in the manual, the content after the spi initialization was deleted, and the following code was written

(After the "ESP_ERROR_CHECK(ret);" 441lines):

uint16_t data;
data = SPI_SWAP_DATA_TX(0x145, 9);
memset(&t, 0, sizeof(t));       
t.length = 9 ;                     
t.tx_buffer=&data;               
ret=spi_device_transmit(spi, &t);  
assert(ret==ESP_OK);            

It's works great. I use the Logic Analyzer to view the gpio statues:

image-20220302205138425

the date seems Ok,but when I change the DATE in SPI_SWAP_DATA_TX,to 0X045:

data = SPI_SWAP_DATA_TX(0x045, 9);
memset(&t, 0, sizeof(t));       
t.length = 9 ;                     
t.tx_buffer=&data;               
ret=spi_device_transmit(spi, &t);  
assert(ret==ESP_OK);            

The logic analyzer detected the following information:

image-20220302205442085

It can be seen that the highest bit (9bit) and the lowest bit (1bit) of the SPI output seem to be bound together, which is controlled by the ninth bit of DATE in SPI_SWAP_DATA_TX. I have tried many times with different data, and the result is the same , also checked the Mode and other settings, what could be the problem, thank you very much!

Debug Logs

none

Other items if possible

none

@espressif-bot espressif-bot added the Status: Opened Issue is new label Mar 2, 2022
@github-actions github-actions bot changed the title ESP32C3 9bit-SPI Transmit Error ESP32C3 9bit-SPI Transmit Error (IDFGH-6865) Mar 2, 2022
@liaozhelin liaozhelin reopened this Mar 2, 2022
@DavidJRobertson
Copy link

DavidJRobertson commented Mar 5, 2022

I have been trying to use 9-bit SPI transfers on ESP32-S3 (not C3 like londonlove) which appears to have a similar issue.

Using a buffer containing [0xff, 0xff] I tested sending various size transfers from 1 to 16 bits. Two cases fail: sending 1 bit results in a 0 bit being transmitted, and sending 9 bits results in the last bit being erroneously sent as a 0. On the original ESP32, this does not happen.

image
(captured using esp32-s3 with code at https://gist.github.com/DavidJRobertson/0e33a51ddb2462d7c48bdc5f14a355db)

I have tried using other data as well, and it seems that for 9-bit transfers the last bit is always sent as 0 no matter what.

I am also using IDF v4.4

@DavidJRobertson
Copy link

With some further testing of the issue on ESP32-S3, the last bit is always sent as zero for any transaction with 8n+1 bits. There's something special happening when the last buffer byte only has one bit used.

Workaround is to detect the 8n+1 bits case and split it into two transactions (8n-1 and 2 bits respectively). Here is an example of the workaround: https://gist.github.com/DavidJRobertson/3d077b55dce744c4c87ae6c49d0fc2cb

The workaround can't fix the case of 1 bit transactions, but these are not of much practical use anyway.

@ginkgm
Copy link
Collaborator

ginkgm commented Mar 9, 2022

Hi @londonlove , @DavidJRobertson ,
Thanks for reporting this issue. We'll check if it's an SW or HW issue..

@liaozhelin
Copy link
Author

Hi @londonlove , @DavidJRobertson , Thanks for reporting this issue. We'll check if it's an SW or HW issue..

Thanks,I haved change the IDF branch to v4.3,and the problem is also exist,Waiting for your response

@Maksons
Copy link

Maksons commented Jun 1, 2022

Small question: is SPI host with DMA enabled or disabled?

@ginkgm
Copy link
Collaborator

ginkgm commented Jul 5, 2022

Hi @liaozhelin ,

We confirmed this is a limitation in the HW. The architecture of HW is based on bytes, not for bit-based transactions, regardless of whether DMA is used or not. Though it happens to support all other cases except 8n + 1 bits on ESP32-C3..

You may workaround this issue with the command/addr phases.

We may improve this in future chips, but maybe not possible on ESP32-C3.

@espressif-bot espressif-bot added Status: In Progress Work is in progress Awaiting Response awaiting a response from the author and removed Status: Opened Issue is new labels Jul 5, 2022
@DavidJRobertson
Copy link

Could the software workaround be included in the IDF? This would at least give API compatibility between ESP32 and ESP32-C3/S3

@ginkgm
Copy link
Collaborator

ginkgm commented Jul 23, 2022

Hi @DavidJRobertson ,

The workaround would take some execution time, space, and cannot fix the issue perfectly (can fix tx only, cannot use with the DMA mode). Finally the user still need to know this limitation, and is suggested to avoid this use cases by making a descriptor that meets HW limitation.

Due to the space usage (the driver doesn't keep any internal buffer to avoid data copying, temporary buffer should be given by the user), if there should be a layer for compatibility, it should be a helper function above the driver, instead of integrated into the driver core. Please see and try the code below:

//User only writes to trans. Other fields to be used by the helper
typedef struct {
union {
    spi_transaction_t trans;
    spi_transaction_ext_t trans_ext;
};
uint8_t temp_buf[64];
} spi_transaction_arb_ext_t;

// Only support TX MSB first, with no command, address, dummy bits, shorter than 64 bytes, no DMA enabled.
esp_err_t spi_device_queue_trans_arb_len(spi_device_handle_t handle, spi_transaction_arb_ext_t *trans_desc, TickType_t ticks_to_wait) {
   if (trans_desc->trans.rx_buffer) {
        if (trans_desc->trans.length % 8 != 0) {
             return ESP_ERR_NOT_SUPPORTED;
        } else {
            return spi_device_queue_trans(trans);
        }
    } else if (trans_desc->trans.tx_buffer) {
        if (trans_desc->trans.length > 64 * 8) {
            return ESP_ERR_NOT_SUPPORTED;
        }
    }

   if (trans_desc->trans.rx_buffer) {
        return spi_device_queue_trans(handle, trans_desc, ticks_to_wait);
    } else if (trans_desc->trans.tx_buffer) {
        if (trans_desc->trans.length % 8 == 0) {
            return spi_device_queue_trans(handle, trans_desc, ticks_to_wait);
        }
    }
 
    int bit_remain = trans_desc->trans.length % 8;
    trans_desc->trans.cmd =  ((uint8_t*)trans_desc->trans.tx_buffer)[0] >> (8-bit_remain);
    trans_desc->trans.flags |= SPI_TRANS_VARIABLE_CMD_ADR;

    trans_desc->trans_ext.command_bits = bit_remain;
    trans_desc->trans_ext.address_bits = 0;
    trans_desc->trans_ext.dummy_bits = 0;

    for (int i = 0; i < trans_desc->trans.length/8; i++) {
        trans->temp_buf[i] = ((uint8_t*)trans_desc->trans.tx_buffer)[i] << bit_remain | ((uint8_t*)trans_desc->trans.tx_buffer)[i+1] >> (8-bit_remain);
    }    
    
    return spi_device_queue_trans(handle, trans_desc, ticks_to_wait);
}

@ginkgm
Copy link
Collaborator

ginkgm commented Aug 22, 2022

Issue closed, feel free to re-open it if you find more problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Response awaiting a response from the author Resolution: Done Issue is done internally Status: Done Issue is done internally
Projects
None yet
Development

No branches or pull requests

5 participants