2

我已将 AS5048A 磁编码器连接到 ESP32 开发板(VSPI/SPI3 总线:CS=5,CLK=18,MISO=19,MOSI=23)。使用 ESP IDF 4.0.1 版。Arduino 库: https ://github.com/espressif/arduino-esp32/tree/idf-release/v4.0

我测试了两个版本的 SPI 传输:a)有b)没有Arduino ESP-IDF 库

Arduino 版本通常在不同的速度下都能很好地工作。纯 IDF 很难正确接收数据。奇怪的是,纯 IDF 版本设法在 SPI 模式 3(Arduino 与模式 1 下工作)下发送 0x0000 和 0xFFFF(具有奇偶校验和 r/w 的读取角度命令),但其他命令失败。0x0000(空命令)也会返回一些垃圾(0x6000 或类似的东西,而不是 0x00000)。所以纯 IDF 版本可以使用 SPI 模式 3 正确读取角度(?!),但其他任何操作都失败(我想这是一些意外,因为读取角度命令是 0xFFFF)。

我尝试了每种模式和很多计时配置。试图查看 Arduino 库 SPI 内部,它使用与 IDF 本身不同的 SPI 方法。

任何帮助/想法?下面是代码。

Arduino 实现非常简单:

this->settings = SPISettings(3000000, MSBFIRST, SPI_MODE1);
pinMode(this->_cs, OUTPUT);
SPI.begin();

SPI.beginTransaction(this->settings);

digitalWrite(this->_cs, LOW);
uint16_t response = SPI.transfer16(command);
digitalWrite(this->_cs, HIGH);

纯 IDF 如下:

spi_host_device_t spi_host = SPI3_HOST;
spi_device_handle_t spi;   
esp_err_t ret;

spi_bus_config_t buscfg;
memset(&buscfg, 0, sizeof(buscfg));
buscfg.mosi_io_num = pinMOSI;
buscfg.miso_io_num = pinMISO;
buscfg.sclk_io_num = pinCLK;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
buscfg.max_transfer_sz = 1;
buscfg.flags = SPICOMMON_BUSFLAG_MASTER ; /*| SPICOMMON_BUSFLAG_IOMUX_PINS ;*/
buscfg.intr_flags = 0;

spi_device_interface_config_t devcfg;
memset(&devcfg, 0, sizeof(devcfg));
devcfg.command_bits = 0;
devcfg.address_bits = 0;
devcfg.dummy_bits = 0;
devcfg.mode = (uint8_t) 1; //SPI_MODE;
devcfg.duty_cycle_pos = 0;
devcfg.cs_ena_pretrans = 0;
devcfg.cs_ena_posttrans = (uint8_t) 0; //CS_ENA_POSTTRANS;
devcfg.clock_speed_hz = 300 * 1000; //SPI_HZ;
devcfg.input_delay_ns = 50; // INPUT_DELAY_NS;
devcfg.spics_io_num = pinCS;
devcfg.flags = SPI_DEVICE_NO_DUMMY ;
devcfg.queue_size = 1;
devcfg.pre_cb = 0;
devcfg.post_cb = 0;

ret = spi_bus_initialize(spi_host, &buscfg, 0); // No DMA 
ESP_ERROR_CHECK(ret);
ret =  spi_bus_add_device(spi_host, &devcfg, &spi);
ESP_ERROR_CHECK(ret);

ESP_LOGI(TAG, "AS5048 device initialized on SPI with MISO=%d, MOSI=%d, CLK=%d, CS=%d, HZ=%d", pinMISO, pinMOSI, pinCLK, pinCS, SPI_HZ);

并传输(cmd 是 uint16_t,带有奇偶校验和 r/w 位):

esp_err_t ret;
spi_transaction_t t;

spi_device_acquire_bus(spi, portMAX_DELAY);

memset(&t, 0, sizeof(t));       
t.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA  ; 
*((uint16_t*)t.tx_data) = SPI_SWAP_DATA_TX(cmd, 16);
t.length = 16;                   

//ret =  spi_device_transmit(spi, &t);
ret =  spi_device_polling_transmit(spi, &t);
if(ret != ESP_OK)               
    ESP_LOGE(TAG, "spi_device_transmit() failed");

spi_device_release_bus(spi);
uint16_t response = SPI_SWAP_DATA_RX(*((uint16_t*)t.rx_data), 16);   
4

1 回答 1

1

我刚刚拉出组件上的数据表并注意到它指出 SPI WireMode 为 1。

还有一张表,其中提到了我在此处包含的其他一些项目。指定为 350ns ,Time between CSn falling edge and CLK rising edge最小串行时钟周期为 100ns,因此如果以 10MHz 运行,则最多需要等待 4 个时钟。我认为这不是您正在运行的速度的问题,但想指出这一点。

您对 Arduino 的配置显示为 3MHz,CS 线在传输之前由软件驱动,这可能会处理您的设置时间,并且您的 ESP-IDF 代码让您运行模式 3,但在 300KHz 下,这应该会给您带来更多所需的 350ns 建立时间(文档显示 CS 驱动后 1 spi 时钟延迟,稍后会详细介绍)。模式 3 上的时钟与模式 1 相反,这意味着 SPI 将在数据线转换的同时尝试读取数据。我的猜测是,您遇到了一些逻辑竞赛,并且在某些情况下它正在“工作”,因为input_delay_ns参数可能会在样本之前数据状态发生变化的地方产生足够的延迟。

使用错误的 SPI 时序模式可能会让您发疯,因为事情几乎可以正常工作,而且您有时会得到正确的结果(此外,以过高的速度运行设备或总线布局也会给您带来同样的痛苦)。为了快速重新散列,模式是时钟极性和相位的矩阵。模式 1 和 3 极性相反,在上升沿或下降沿采样,当您使用错误的时序时,您可以在时钟改变状态的同时设置值。

我赞扬您使用 ESP-IDF 并远离 Arduino,即嵌入式世界的“Visual Basic”。如果您想尝试接近设备的 10MHz 最大速度,您还需要使用 SPI ctrl2 寄存器 (0x14)。根据文档,位 0-3(默认值为 1)指定被驱动的 CS 线与数据传输开始之间的延迟。

于 2020-09-20T16:17:35.930 回答