ESPIDF-通讯外设 SPI SPI资源 ESP32集成了4个SPI外设。
SPI0和SPI1通过总线仲裁器共享一条信号总线,用于在模组内部访问FLASH(SoC FLASH),不对用户开放。
SPI2和SPI3是通用SPI控制器,有时也被称为HSPI和VSPI。它们拥有独立的信号总线,每条总线都有三条片选(CS)信号,也就是说每个控制器都能驱动最多3个SPI从器件。这两个SPI控制器对用户开放。
SPI类型 esp32的SPI支持三线SPI、四线标准SPI、Dual SPI和Quad SPI等工作模式。
四线标准SPI 四线标准SPI由SCK、MOSI、MISO、CS四根线组成。四线标准SPI是全双工的通讯。
名称
功能
SCLK
时钟线,决定着通讯的速度
MISO
主输入从输出。主机输入,从机输出
MOSI
主输出从输入。主机输入,从机输入
CS
片选线。当片选线被拉低总线有效,可以开始通讯
三线SPI 三线 SPI 把 MISO 和 MOSI 总线进行了合并。同一时间只能进行单方向的读或者写。是半双工的通讯。
Dual SPI Dual SPI是四线半双工的SPI通讯。Dual SPI就是让MISO和MOSI同时进行发送或者接收的工作。因此通讯速度会得到极大的提高。此时MISO和MOSI总线名称就变成了IO0和IO1。
Quad SPI Quad SPI是六线半双工的SPI通讯。除了SCK和CS总线以外,增加了IO0、IO1、IO2、IO3四条总线,这四条总线能同时进行并行的读写,比Dual SPI通讯速度相比,又得到了极大的提高。有时候IO2和IO3引脚与WP和HD引脚共用。WD是写保护,HD是状态保持。
SPI配置 总线初始化结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 spi_bus_config_t spiCfg; //总线配置结构体 spiCfg.miso_io_num = GPIO_NUM_19; //gpio12->miso spiCfg.mosi_io_num = GPIO_NUM_23; //gpio13->mosi spiCfg.sclk_io_num = GPIO_NUM_18; //gpio14-> sclk spiCfg.quadhd_io_num = -1; // HD引脚不设置,这个引脚配置Quad SPI的时候才有用 spiCfg.quadwp_io_num = -1; // WP引脚不设置,这个引脚配置Quad SPI的时候才有用 spiCfg.max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE; //设置传输数据的最大值。非DMA最大64bytes,DMA最大4096bytes //spiCfg.intr_flags = 0; //这个用于设置SPI通讯中相关的中断函数的中断优先级,0是默认。 //这组中断函数包括SPI通讯前中断和SPI通讯后中断两个函数。 spiCfg.flags = SPICOMMON_BUSFLAG_MASTER; //这个用于设置初始化的时候要检测哪些选项。比如这里设置的是spi初始化为主机模式是否成功。 //检测结果通过spi_bus_initialize函数的 //返回值进行返回。如果初始化为主机模式成功,就会返回esp_ok
总线初始化 1 2 3 4 5 6 7 8 9 esp_err_t spi_init_info = spi_bus_initialize(SPI3_HOST, &spiCfg, SPI_DMA_DISABLED); if(spi_init_info != ESP_OK) { printf("spi initialize failed!\n"); } else { printf("spi initialize successed!\n"); }
设备初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 spi_device_interface_config_t spiDevCfg={ .clock_speed_hz = 1000 * 1000, .mode = 0, .spics_io_num = -1, .queue_size = 6, }; esp_err_t spi_dev_info = spi_bus_add_device(SPI3_HOST, &spiDevCfg, &spi3Handle); if(spi_dev_info != ESP_OK) { printf("device config error\n"); } else { printf("device config success\n"); }
发送数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 esp_err_t spiWriteData(uint8_t *data, uint8_t len) { esp_err_t ret; spi_transaction_t temp; if (len==0) return 0; memset(&temp, 0, sizeof(temp)); gpio_set_level(SPI3_CS, 0); temp.length = len * 8; temp.tx_buffer = data; temp.user = (void*)1; ret=spi_device_polling_transmit(spi3Handle, &temp); gpio_set_level(SPI3_CS, 1); return ret; }
IIC IIC资源 IIC 是一种串行同步半双工通信协议,总线上可以同时挂载多个主机和从机。IIC 总线由串行数据线 (SDA) 和串行时钟线 (SCL) 线构成。这些线都需要上拉电阻。 IIC 具有简单且制造成本低廉等优点,主要用于低速外围设备的短距离通信(一英尺以内)。 ESP32 有 2 个 IIC 控制器(也称为端口),负责处理在 IIC 总线上的通信。每个控制器都可以设置为主机或从机。
IIC配置驱动 建立 IIC 通信第一步是配置驱动程序,这需要设置 i2c_config_t
结构中的几个参数:
设置 IIC 工作模式 - 从 i2c_mode_t
中选择主机模式或从机模式
设置 通信管脚
指定 SDA 和 SCL 信号使用的 GPIO 管脚
是否启用 ESP32 的内部上拉电阻
(仅限主机模式)设置 I2C 时钟速度
(仅限从机模式)设置以下内容:
然后,初始化给定 IIC 端口的配置,请使用端口号和 i2c_config_t
作为函数调用参数来调用 i2c_param_config()
函数。
SCL 的时钟频率会被上拉电阻和线上电容(或是从机电容)一起影响。因此,用户需要自己选择合适的上拉电阻去保证 SCL 时钟频率是准确的。尽管 I2C 协议推荐上拉电阻值为 1 K 欧姆到 10 K 欧姆,但是需要根据不同的频率需要选择不同的上拉电阻。 通常来说,所选择的频率越高,需要的上拉电阻越小(但是不要小于 1 K 欧姆)。这是因为高电阻会减小电流,这会延长上升时间从而使频率变慢。通常我们推荐的上拉阻值范围为 2 K 欧姆到 5 K 欧姆,但是用户可能也需要根据他们的实际情况做出一些调整。
IIC主机配置 初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void iicMasterInit() { i2c_config_t iicMasterConfig = { .mode = I2C_MODE_MASTER, .sda_io_num = (gpio_num_t)IIC_SDA, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_io_num = (gpio_num_t)IIC_SCL, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = 100000, .clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL, }; i2c_param_config(I2C_NUM_0, &iicMasterConfig); esp_err_t iicInitInfo = i2c_driver_install(I2C_NUM_0, iicMasterConfig.mode, 0, 0, 0); if(iicInitInfo != ESP_OK) { printf("iic master initialize error\n"); } else { printf("iic master initialize success\n"); } }
ESP32 的内部上拉电阻范围为几万欧姆,因此在大多数情况下,它们本身不足以用作 I2C 上拉电阻。建议用户使用阻值在 I2C 总线协议规范规定范围内的上拉电阻。计算阻值的具体方法,可参考 TI 应用说明
IIC写数据 1 2 3 4 5 6 7 8 9 10 11 12 esp_err_t iicMasterWriteData(uint8_t addr, uint8_t *writeBuffer, uint8_t bufferLength) { esp_err_t ret; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, addr, true); // 启用ACK i2c_master_write(cmd, writeBuffer, bufferLength, true); i2c_master_stop(cmd); ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(500)); // 发送数据 i2c_cmd_link_delete(cmd); return ret; }
IIC读数据 1 2 3 4 5 6 7 8 9 10 11 12 esp_err_t iicMasterReadData(uint8_t addr, uint8_t *readBUffer, uint8_t bufferLength) { esp_err_t ret; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, addr, true); // 启用ACK i2c_master_read(cmd, readBUffer, bufferLength, I2C_MASTER_LAST_NACK); // I2C_MASTER_LAST_NACK i2c_master_stop(cmd); ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, pdMS_TO_TICKS(500)); // 发送数据 i2c_cmd_link_delete(cmd); return ret; }