ESP32C3 SPI 的使用
0.ESP32C3 SPI 接口介绍
ESP32-C3 内部集成了 3 个 SPI 外设,SPI0、SPI1、SPI2。其中SPI0和SPI1两个控制器共享相同的 SPI 总线信号,由一个仲裁器来确定SPI0和SPI1哪个可以访问总线,SPI0 和SPI1 只可以配置成SPI 存储器模式。SPI2 是一个通用 SPI 控制器。具有独立的同名信号总线。该总线有 6 条 CS 线来驱动多达 6 个 SPI 从机。
1.ESP32C3 SPI2 设置
ESP32C3有三个SPI外设,其中只有SPI2是通用的SPI控制器(SPI0和SPI1只能做存储器且共享总线,这里就就不介绍了)。
ESP32C3的SPI配置大体上可以分为两个部分:1.SPI2外设的设置(包括SPI总线速度、SPI模式等)。2.SPI2总线的设置(其实就是设置SPI2控制器和哪些GPIO相连)
ESP32C3 内部存在着IO MUX 和GPIO交换矩阵用来灵活的配置外设的GPIO,可以将其内部结构简单理解为下图。

我们可以通过GPIO交换矩阵将任意GPIO和SPI2控制器相连。当信号频率较高时,可以设置IO MUX 绕过GPIO交换矩阵获得更好的高频数字特性。由于绕过了GPIO交换矩阵SPI2控制器便只能和规定GPIO连接。下图为官方手册中给出的SPI2控制器对应的IO MUX管脚。

由上图不难看出,SPI2控制器工作在FSPIQ模式的IO MUX管脚对应的是GPIO2,FSPIHD对应的是GPIO4 .......
但是SPI2工作在其他模式时SPI2控制器对应的IO MUX管脚是什么呢?我在手册上找到了下图。
结合两张图可以看出,当SPI2控制器工作在全双工SPI模式时MOSI对应GPIO7 MISO对应GPIO2 CS对应GPIO10 CLK对应GPIO6
需要注意的是,全双工SPI信号的CS可以对应FSPI信号的FSPICS0~FSPICS5中的任意一个,但是只有FSPICS0有对应的IO MUX管脚(说人话就是只有FSPICS0可以绕过GPIO交换矩阵)

2.ESP32C3 SPI2 设置代码介绍
2.1SPI总线初始化
以下代码配置SPI2工作在master模式作为说明,首先定义了buscfg结构体,在buscfg结构体中,描述了SPI2外设和外部GPIO口的映射关系,不需要使用的信号使用-1标记。最后使用spi_bus_initialize()对SPI2总线初始化,其实总初始化的过程可以理解为对IO MUX 和GPIO交换矩阵的设置过程。
最后需要注意的两点:
- SPI的
CS不在此处设置,我们将在下文中设置。 buscfg结构体中存在着uint32_t flags成员变量,我们可以使用flags进行一些特殊的配置,比如设置GPIO和SPI外设之间强制使用GPIO交换矩阵等等。希望大家在使用时留意一下,在下面的例子中并没有对flags做什么设置,默认情况下spi_bus_initialize()检查buscfg中描述的GPIO。如果为IO MUX功能引脚,会自动设置绕过GPIO交换矩阵。
spi_bus_config_t buscfg={
.miso_io_num=PIN_NUM_MISO, //MISO
.mosi_io_num=PIN_NUM_MOSI, //MOSI
.sclk_io_num=PIN_NUM_CLK, //CLK
.quadwp_io_num=-1, //不使用
.quadhd_io_num=-1, //不使用
.max_transfer_sz=320 //最大传送数据长度
};
spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO); //初始化SPI总线2.2 SPI2外设初始化
void lcd_spi_pre_transfer_callback(spi_transaction_t *t)
{
int dc=(int)t->user;
gpio_set_level(PIN_NUM_DC, dc);
}
spi_device_interface_config_t devcfg={
.clock_speed_hz=50*1000*1000, //时钟速度
.mode=3, //SPI 工作模式
.spics_io_num=-1, //CS
.queue_size=6, //传送队列深度
.pre_cb=lcd_spi_pre_transfer_callback, //SPI发送数据前执行的回调函数,一般用于SPI屏幕的D/C信号切换
};
static spi_device_handle_t spi;
spi_bus_add_device(SPI2_HOST, &devcfg, &spi);2.3 SPI2发送数据
根据博主不负责任的研究,spi_device_queue_trans()在发送数据前会申请一块与trans.tx_buffer一样大的内存,将trans.tx_buffer中的数据拷贝到新申请的内存后,通过DMA发送数据,此处应在注意内存的消耗问题。
static spi_transaction_t trans;
trans.tx_buffer=(void *)(data);
trans.length=len;
trans.user=(void*)1; //此处的user会被lcd_spi_pre_transfer_callback()回调函数的形参spi_transaction_t *t 携带
spi_device_queue_trans(spi, &trans, portMAX_DELAY);就先写这么多吧!!
以上
小尾巴,SPI的 slave模式的配置和master模式大体相同,只是使用的是spi_slave_initialize()初始化总线和SPI外设,嗯大家自己研究下吧。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
非常感谢博主的精彩讲解,正好在找ESP32-C3的SPI引脚复用的相关资料