我已经将磁编码器连接到 ESP32 开发板AS5048A(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(null 命令(返回一些垃圾(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与奇偶校验和R/W位uint16_t(:
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);
我刚刚调出组件上的数据表,并注意到它指出SPI WireMode为1。
还有一个表格提到了我在这里包含的其他一些项目。Time between CSn falling edge and CLK rising edge
指定为 350ns,最小串行时钟周期为 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,擺脫了嵌入式世界的「Visual Basic」Arduino。如果要尝试接近器件的10MHz最大速度,则还需要使用SPI ctrl2寄存器(0x14(。根据文档,位0-3(默认值为1(指定被驱动CS线和数据传输开始之间的延迟。