ESP32外设学习部分--SPI篇

SPI学习

前言

我个人以为开始学习一个新的单片机最好的方法就是先把他各个外设给跑一遍,整体了解一下他的功能,由此记录一下我学习ESP32外设的过程,防止以后忘记。

SPI 配置步骤

SPI总线初始化

    spi_bus_config_t buscfg = {.miso_io_num = PIN_NUM_MISO,.mosi_io_num = PIN_NUM_MOSI,.sclk_io_num = PIN_NUM_CLK,.quadwp_io_num = -1,.quadhd_io_num = -1,.max_transfer_sz = PARALLEL_LINES * 320 * 2 + 8};

点击进入可以查看结构体中的内容如下

typedef struct {union {int mosi_io_num;    ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.int data0_io_num;   ///< GPIO pin for spi data0 signal in quad/octal mode, or -1 if not used.};union {int miso_io_num;    ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.int data1_io_num;   ///< GPIO pin for spi data1 signal in quad/octal mode, or -1 if not used.};int sclk_io_num;      ///< GPIO pin for SPI Clock signal, or -1 if not used.union {int quadwp_io_num;  ///< GPIO pin for WP (Write Protect) signal, or -1 if not used.int data2_io_num;   ///< GPIO pin for spi data2 signal in quad/octal mode, or -1 if not used.};union {int quadhd_io_num;  ///< GPIO pin for HD (Hold) signal, or -1 if not used.int data3_io_num;   ///< GPIO pin for spi data3 signal in quad/octal mode, or -1 if not used.};int data4_io_num;     ///< GPIO pin for spi data4 signal in octal mode, or -1 if not used.int data5_io_num;     ///< GPIO pin for spi data5 signal in octal mode, or -1 if not used.int data6_io_num;     ///< GPIO pin for spi data6 signal in octal mode, or -1 if not used.int data7_io_num;     ///< GPIO pin for spi data7 signal in octal mode, or -1 if not used.int max_transfer_sz;  ///< Maximum transfer size, in bytes. Defaults to 4092 if 0 when DMA enabled, or to `SOC_SPI_MAXIMUM_BUFFER_SIZE` if DMA is disabled.uint32_t flags;       ///< Abilities of bus to be checked by the driver. Or-ed value of ``SPICOMMON_BUSFLAG_*`` flags.esp_intr_cpu_affinity_t  isr_cpu_id;    ///< Select cpu core to register SPI ISR.int intr_flags;       /**< Interrupt flag for the bus to set the priority, and IRAM attribute, see*  ``esp_intr_alloc.h``. Note that the EDGE, INTRDISABLED attribute are ignored*  by the driver. Note that if ESP_INTR_FLAG_IRAM is set, ALL the callbacks of*  the driver, and their callee functions, should be put in the IRAM.*/} spi_bus_config_t;

可以看到上面可以配置的内容非常多,可以配置三线四线SPI,但是我们本次主要学习两线的就可以了,用不到的管脚直接配置-1就OK了。

添加SPI设备

    spi_device_interface_config_t devcfg = {#ifdef CONFIG_LCD_OVERCLOCK.clock_speed_hz = 26 * 1000 * 1000,     //Clock out at 26 MHz#else.clock_speed_hz = 10 * 1000 * 1000,     //Clock out at 10 MHz#endif.mode = 0,                              //SPI mode 0.spics_io_num = PIN_NUM_CS,             //CS pin.queue_size = 7,                        //We want to be able to queue 7 transactions at a time.pre_cb = lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line};

然后配置spi_device_interface_config_t 这个结构体,老规矩继续看一下这个结构体中的内容

typedef struct {uint8_t command_bits;           ///< Default amount of bits in command phase (0-16), used when ``SPI_TRANS_VARIABLE_CMD`` is not used, otherwise ignored.uint8_t address_bits;           ///< Default amount of bits in address phase (0-64), used when ``SPI_TRANS_VARIABLE_ADDR`` is not used, otherwise ignored.uint8_t dummy_bits;             ///< Amount of dummy bits to insert between address and data phaseuint8_t mode;                   /**< SPI mode, representing a pair of (CPOL, CPHA) configuration:- 0: (0, 0)- 1: (0, 1)- 2: (1, 0)- 3: (1, 1)*/spi_clock_source_t clock_source;///< Select SPI clock source, `SPI_CLK_SRC_DEFAULT` by default.uint16_t duty_cycle_pos;        ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128.uint16_t cs_ena_pretrans;       ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions.uint8_t cs_ena_posttrans;       ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16)int clock_speed_hz;             ///< SPI clock speed in Hz. Derived from `clock_source`.int input_delay_ns;             /**< Maximum data valid time of slave. The time required between SCLK and MISOvalid, including the possible clock delay from slave to master. The driver uses this value to give an extradelay before the MISO is ready on the line. Leave at 0 unless you know you need a delay. For better timingperformance at high frequency (over 8MHz), it's suggest to have the right value.*/int spics_io_num;               ///< CS GPIO pin for this device, or -1 if not useduint32_t flags;                 ///< Bitwise OR of SPI_DEVICE_* flagsint queue_size;                 ///< Transaction queue size. This sets how many transactions can be 'in the air' (queued using spi_device_queue_trans but not yet finished using spi_device_get_trans_result) at the same timetransaction_cb_t pre_cb;   /**< Callback to be called before a transmission is started.**  This callback is called within interrupt*  context should be in IRAM for best*  performance, see "Transferring Speed"*  section in the SPI Master documentation for*  full details. If not, the callback may crash*  during flash operation when the driver is*  initialized with ESP_INTR_FLAG_IRAM.*/transaction_cb_t post_cb;  /**< Callback to be called after a transmission has completed.**  This callback is called within interrupt*  context should be in IRAM for best*  performance, see "Transferring Speed"*  section in the SPI Master documentation for*  full details. If not, the callback may crash*  during flash operation when the driver is*  initialized with ESP_INTR_FLAG_IRAM.*/} spi_device_interface_config_t;

可以看到这个结构体里面的参数也是比较多的,但是我们主要关注的其实就是clock_speed_hz,mode,spics_io_num,这几个参数,这几个也是我们在STM32上最熟悉的;当然如果需要驱动LCD我们可能还需要控制一个DC脚,这时候也可以关注一下pre_cb这个参数,这个是在启用SPI传输前的回调,我们可以用它来控制下个发的是command还是data。

初始化总线

    //Initialize the SPI busret = spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);

用上面的函数就可以初始化我们配置好的SPI总线。

入参的选择

typedef enum {SPI_DMA_DISABLED = 0,     ///< Do not enable DMA for SPI#if CONFIG_IDF_TARGET_ESP32SPI_DMA_CH1      = 1,     ///< Enable DMA, select DMA Channel 1SPI_DMA_CH2      = 2,     ///< Enable DMA, select DMA Channel 2#endifSPI_DMA_CH_AUTO  = 3,     ///< Enable DMA, channel is automatically selected by driver} spi_common_dma_t;

前两个其实都不用讲,都是我们配置好的。最后一个要看下如果选择了DMA通道,发送的数据就要需要是被存储在DMA可以访问的内存中,不然容易出现问题。
添加设备

    //Attach the LCD to the SPI busret = spi_bus_add_device(SPI2_HOST, &devcfg, &spi);

用上面的函数就可以添加我们的SPI设备了

数据发送

数据发送的时候我们可以使用两个函数来发送我们的数据

spi_device_queue_trans 函数和spi_device_polling_transmit函数的区别

spi_device_queue_transspi_device_polling_transmit 都是ESP32 SPI主机驱动程序中用于发送SPI事务的函数,但它们在传输方式和使用场景上有所不同:

esp_err_t SPI_MASTER_ATTR spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait)esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_transmit(spi_device_handle_t handle, spi_transaction_t* trans_desc)
  1. spi_device_queue_trans

    • 这个函数用于将SPI事务添加到中断传输队列中。
    • 调用这个函数后,当前线程可以继续执行其他任务,而SPI传输将在后台通过中断服务程序异步处理。
    • 它允许多个事务排队,适合于需要连续发送多个SPI事务的场景,或者当需要在传输之间插入其他代码时。
    • 事务完成后,需要使用spi_device_get_trans_result函数来获取事务的结果。
    • 这种方式适用于非阻塞传输,可以提高CPU的利用率。
  2. spi_device_polling_transmit

    • 这个函数用于发送轮询模式下的SPI事务,调用后会等待事务完成并返回结果。
    • 在轮询模式下,CPU会一直等待直到SPI事务完成,期间不能执行其他任务,这可能会导致CPU资源的浪费。
    • 它适合于对实时性要求较高的场合,或者当需要在事务之间精确控制时序时。
    • 如果需要在传输中间插入其他代码,可以使用spi_device_polling_startspi_device_polling_end两个函数来实现。

总结来说,spi_device_queue_trans提供了一种非阻塞的、异步的SPI传输方式,适合于多任务环境和需要高CPU利用率的场景;而spi_device_polling_transmit提供了一种同步的、阻塞的传输方式,适合于对时序要求严格的场合。开发者可以根据具体的应用需求选择合适的函数。

struct spi_transaction_t {uint32_t flags;                 ///< Bitwise OR of SPI_TRANS_* flagsuint16_t cmd;                   /**< Command data, of which the length is set in the ``command_bits`` of spi_device_interface_config_t.**  <b>NOTE: this field, used to be "command" in ESP-IDF 2.1 and before, is re-written to be used in a new way in ESP-IDF 3.0.</b>**  Example: write 0x0123 and command_bits=12 to send command 0x12, 0x3_ (in previous version, you may have to write 0x3_12).*/uint64_t addr;                  /**< Address data, of which the length is set in the ``address_bits`` of spi_device_interface_config_t.**  <b>NOTE: this field, used to be "address" in ESP-IDF 2.1 and before, is re-written to be used in a new way in ESP-IDF3.0.</b>**  Example: write 0x123400 and address_bits=24 to send address of 0x12, 0x34, 0x00 (in previous version, you may have to write 0x12340000).*/size_t length;                  ///< Total data length, in bitssize_t rxlength;                ///< Total data length received, should be not greater than ``length`` in full-duplex mode (0 defaults this to the value of ``length``).void *user;                     ///< User-defined variable. Can be used to store eg transaction ID.union {const void *tx_buffer;      ///< Pointer to transmit buffer, or NULL for no MOSI phaseuint8_t tx_data[4];         ///< If SPI_TRANS_USE_TXDATA is set, data set here is sent directly from this variable.};union {void *rx_buffer;            ///< Pointer to receive buffer, or NULL for no MISO phase. Written by 4 bytes-unit if DMA is used.uint8_t rx_data[4];         ///< If SPI_TRANS_USE_RXDATA is set, data is received directly to this variable};} ;        //the rx data should start from a 32-bit aligned address to get around dma issue.

同样的使用上面两个函数我们都需要传递这个结构体来指示一些东西

flags:SPI 事务标志位,可以使用 SPI_TRANS_* 宏定义进行设置。这些标志位用于控制事务的行为,例如是否使用 DMA、是否在事务结束后保持 CS 信号等

length:本次发送的总数据长度,以位为单位。SPI 最多一次可传输 64 字节(65536 位)数据,如果需要传输更多数据,建议使用 DMA

user:用户定义的变量,可以用于存储事务 ID 等信息

tx_buffer:发送数据缓冲区指针,如果不需要发送数据,则设置为 NULL

rx_buffer:接收数据缓冲区指针,如果不需要接收数据,则设置为 NULL,在 DMA 使用时,每次读取 4 字节

最终代码


void lcd_spi_pre_transfer_callback(spi_transaction_t *t){int dc=(int)t->user;gpio_set_level(PIN_NUM_DC, dc);//  printf("DC get 1\n");}void send_non_blocking(spi_device_handle_t spi,uint8_t *data,uint16_t len){esp_err_t ret;static spi_transaction_t trans;memset(&trans, 0, sizeof(trans));       //Zero out the transactiontrans.length=8*len;trans.flags = 0; //undo SPI_TRANS_USE_TXDATA flagtrans.tx_buffer = data;ret=spi_device_queue_trans(spi, &trans, portMAX_DELAY);}spi_device_handle_t spi;uint8_t spi_send[5] DMA_ATTR = {0x23,0x12,0x37,0x44,0x55};void tx_spi_init(void){esp_err_t ret;spi_bus_config_t buscfg = {.miso_io_num = PIN_NUM_MISO,.mosi_io_num = PIN_NUM_MOSI,.sclk_io_num = PIN_NUM_CLK,.quadwp_io_num = -1,.quadhd_io_num = -1,.max_transfer_sz = 16 * 320 * 2 + 8};spi_device_interface_config_t devcfg = {#ifdef CONFIG_LCD_OVERCLOCK.clock_speed_hz = 26 * 1000 * 1000,     //Clock out at 26 MHz#else.clock_speed_hz = 1 * 1000 * 1000,     //Clock out at 10 MHz#endif.mode = 3,                              //SPI mode 0.spics_io_num = PIN_NUM_CS,             //CS pin.queue_size = 7,                        //We want to be able to queue 7 transactions at a time.pre_cb = lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line};//Initialize the SPI busret = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);ESP_ERROR_CHECK(ret);//Attach the LCD to the SPI busret = spi_bus_add_device(SPI2_HOST, &devcfg, &spi);ESP_ERROR_CHECK(ret);}void spi_test(void)
{send_non_blocking(spi,spi_send,5);
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/63988.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

禅道Bug的一次迁移

一、场景 平时工作记录在公司禅道上的问题想备份一份到本地&#xff0c;但是又没有公司禅道的数据库信息&#xff0c;有时候出测试报告想批量调整数据方便截图很困难&#xff0c;同时也为了学习禅道数据流转过程&#xff0c;所以有了把缺陷保存到本地一份的想法。 实际上禅道支…

Redis - 消息队列 Stream

一、概述 消息队列 定义 消息队列模型&#xff1a;一种分布式系统中的消息传递方案&#xff0c;由消息队列、生产者和消费者组成消息队列&#xff1a;负责存储和管理消息的中间件&#xff0c;也称为消息代理&#xff08;Message Broker&#xff09;生产者&#xff1a;负责 产…

C语言数组和字符串笔记

C语言数组和字符串笔记 1. 数组及其相关概念 1.1 为什么需要使用数组&#xff1f; 数组是一个有序的、类型相同的数据集合。这些数据被称为数组的元素。每个数组都有一个名字&#xff0c;数组名代表数组的起始地址。数组的元素通过索引或下标访问&#xff0c;索引从0开始。 …

双目摄像头标定方法

打开matlab 找到这个标定 将双目左右目拍的图像上传&#xff08;左右目最好不少于20张&#xff09; 等待即可 此时已经完成标定&#xff0c;左下角为反投影误差&#xff0c;右边为外参可视化 把这些误差大的删除即可。 点击导出 此时回到主页面&#xff0c;即可看到成功导出 Ca…

数据结构开始——时间复杂度和空间复杂度知识点笔记总结

好了&#xff0c;经过了漫长的时间学习c语言语法知识&#xff0c;现在我们到了数据结构的学习。 首先&#xff0c;我们得思考一下 什么是数据结构&#xff1f; 数据结构(Data Structure)是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素…

什么是MMD Maximum Mean Discrepancy 最大均值差异?

9多次在迁移学习看到了&#xff0c;居然还是Bernhard Schlkopf大佬的论文&#xff0c;仔细看看。 一.什么是MMD&#xff1f; 1. MMD要做什么&#xff1f; 判断两个样本&#xff08;族&#xff09;是不是来自于同一分布 2.怎么做&#xff1f;&#xff08;直观上&#xff09;…

电梯内电动车识别数据集,可准确识别电梯内是否有电动车 支持YOLO,COCO,VOC三种格式的标注 7111张图片

电梯内电动车识别数据集&#xff0c;可识别电梯内是否有电动车 支持YOLO&#xff0c;COCO&#xff0c;VOC三种格式的标注 7111张图片 7111总图像数 数据集分割 训练组 74&#xff05; 5291图片 有效集 16% 1168图片 测试集 9&#xff05; 652…

Collection接口

目录 一. Collection基本介绍 二. Collection中的方法及其使用 1. 添加元素 (1) 添加单个元素 (2) 添加另一集合中的所有元素 2. 删除元素 (1) 删除单个元素 (2) 删除某个集合中包含在其他集合中的元素 (3) 保留两个集合中的交集部分, 删除其他元素. 3. 遍历元素 (1) …

Mybatis Plus 3.0 快速入门

1、简介 MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 2、创建并初始化数据库 2.1、创建数据库 mybatis_plus 2.2、创建 User 表 其表结构如下: idnameageemail1Jone18test1@baomidou.com2Jack…

Verilog实现图像处理的行缓存Line Buffer

在图像处理中&#xff0c;难免会遇到对图像进行卷积或者模板的局部处理&#xff0c;例如ISP中的一些算法&#xff0c;很大部分都需要一个窗口&#xff0c;在实时视频处理中&#xff0c;可以利用行缓存Line buffer可以暂存几行数据&#xff0c;然后同时输出每行中的对应列的像素…

【银河麒麟高级服务器操作系统】有关dd及cp测试差异的现象分析详解

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn dd现象 使用银河麒麟高级服务器操作系统执行两次…

ORACLE逗号分隔的字符串字段,关联表查询

使用场景如下&#xff1a; oracle12 以前的写法&#xff1a; selectt.pro_ids,wm_concat(t1.name) pro_names from info t,product t1 where instr(,||t.pro_ids|| ,,,|| t1.id|| ,) > 0 group by pro_ids oracle12 以后的写法&#xff1a; selectt.pro_ids,listagg(DIS…

记录2024-leetcode-字符串DP

10. 正则表达式匹配 - 力扣&#xff08;LeetCode&#xff09;

微信开发者工具(小程序)的版本管理,Git Push 和 Pull

微信开发者工具&#xff08;小程序&#xff09;的版本管理&#xff0c;Git Push 和 Pull 一、设置 第一次用微信开发者工具自带的版本管理的拉取和推送功能&#xff0c;稍稍的研究了下。 1、首先要先设置 “用户”&#xff0c;名字和邮箱&#xff0c;不一定要真名&#xff0c…

2020-12-07 光棍数

由光棍数的特征可推导其商的个位数不存在偶数且只有1、3、7、9这4个数。一个数可匹配多个光棍数且必定是中间隔着0的循环数。 void 光棍数(int n) {//缘由http://ask.csdn.net/questions/3444069 做乘法运行时间超长int w 0; long long x 111111111111111, j 0;//j x*n;/…

【Linux系统】—— 初识 shell 与 Linux 中的用户

【Linux系统】—— 初识shell 与 Linux 中的用户 1 Xshell 运行原理1.1 命令行的组成1.2 外壳程序 2 Linux中的用户2.1 两种用户2.2 创建普通用户2.3 用户切换2.3.1 普通->超级2.3.2 超级->普通 3 指令的短暂提权3.1 为什么要提权3.2 sudo 指令3.3 人人都能提权吗 1 Xshe…

.NET平台使用C#设置Excel单元格数值格式

设置Excel单元格的数字格式是创建、修改和格式化Excel文档的关键步骤之一&#xff0c;它不仅确保了数据的正确表示&#xff0c;还能够增强数据的可读性和专业性。正确的数字格式可以帮助用户更直观地理解数值的意义&#xff0c;减少误解&#xff0c;并且对于自动化报告生成、财…

Android显示系统(10)- SurfaceFlinger内部结构

一、前言: 之前讲述了native层如何使用SurfaceFlinger,我们只是看到了简单的API调用,从本文开始,我们逐步进行SurfaceFlinger内部结构的分析。话不多说,莱茨狗~ 二、类图: 2.1、总体架构: 先看下SurfaceFlinger的关键成员和我们BootAnimation侧关键成员如何对应起来…

深度学习中的多通道卷积与偏置过程详解

目录 ​编辑 多通道卷积的深入理解 &#x1f50d; 卷积核的多维特性 &#x1f30c; 卷积操作的细节 &#x1f527; 多通道卷积的优势 &#x1f31f; 偏置过程的深入理解 &#x1f3af; 偏置的两种实现方式 &#x1f6e0;️ 偏置的作用与重要性 &#x1f308; 多通道卷…

易语言鼠标轨迹算法(游戏防检测算法)

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…