首页 动态 版块 商城 我的
 退出
游客  
等级:
金币:
经验值:

SPI串行外设接口 树莓派Pico/ESP32开发板Python与C++例程【入门教程】

  8846 
 0
 4
RASPI   ydxhuo.com 

Image


一:SPI介绍

SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步的通信协议,常用于微控制器与外围设备(如传感器、显示屏、ADC/DAC 等)之间的短距离通信。


1.SPI 采用主从(Master-Slave)架构,特点包括:


高速传输速率(通常可达几 Mbps)

全双工通信(可同时发送和接收数据)

同步通信(使用时钟信号同步数据传输)

支持多个从设备


2.SPI 工作逻辑:

SPI 通信主要通过 4 根线实现:

SCLK(Serial Clock):时钟线,由主机产生,用于同步数据传输

MOSI(Master Out Slave In):主机输出,从机输入

MISO(Master In Slave Out):主机输入,从机输出

CS/SS(Chip Select/Slave Select):片选线,用于选择通信的从设备(低电平有效)


3.SPI 工作流程:

主机通过拉低某个从设备的 CS 线选中该设备

主机产生 SCLK 时钟信号

数据在时钟信号的同步下,通过 MOSI 和 MISO 线同时传输

每个时钟周期传输一位数据

通信结束后,主机拉高 CS 线释放该从设备


树莓派 Pico的SPI 有 2 个硬件 SPI 控制器:SPI0 和 SPI1,默认引脚分配如下:其中CS 引脚可任意选择 GPIO 引脚。

spi控制器SCK引脚MOSI引脚MISO引脚
SPI0SPI2GP3GP4
SPI1GP10GP11GP12


二:Python示例

以下示例展示如何使用树莓派 Pico 的 SPI 接口进行通信,包含主机和从机两种模式的代码。

from machine import Pin, SPI
import time

# 初始化SPI0为主机模式
spi = SPI(0,
          baudrate=1000000,  # 1MHz
          polarity=0,        # 时钟空闲时为低电平
          phase=0,           # 数据在时钟上升沿采样
          sck=Pin(2),        # SCK引脚
          mosi=Pin(3),       # MOSI引脚
          miso=Pin(4))       # MISO引脚

# 初始化CS引脚
cs = Pin(5, Pin.OUT, value=1)  # 初始化为高电平(未选中)

def send_receive_data(data):
    cs.value(0)  # 拉低CS,选中从设备
    time.sleep_us(10)  # 短暂延时
    
    # 创建接收缓冲区(与发送数据长度相同)
    rx_data = bytearray(len(data))
    
    # 发送并接收数据
    spi.write_readinto(data, rx_data)
    
    time.sleep_us(10)
    cs.value(1)  # 拉高CS,释放从设备
    return rx_data

# 主循环
while True:
    # 发送数据
    tx_data = bytearray([0x01, 0x02, 0x03, 0x04])
    rx_data = send_receive_data(tx_data)
    
    print(f"发送: {list(tx_data)}")
    print(f"接收: {list(rx_data)}")
    
    time.sleep(1)


1.代码说明:

主机模式代码(spi_master.py):

初始化 SPI0 为主机模式,设置波特率为 1MHz

配置 SCLK、MOSI、MISO 和 CS 引脚

send_receive_data函数实现:拉低 CS 选中设备、发送数据并接收响应、最后释放 CS

主循环中周期性发送数据并打印收发内容

从机模式代码(spi_slave.py):

初始化 SPI0 为从机模式,参数需与主机匹配

监听 CS 引脚状态,当被选中时进行数据收发

接收主机数据的同时发送预设的响应数据

简单修改响应数据,以便观察通信效果

2.测试方法:

准备两块树莓派 Pico 板

分别将主机代码和从机代码上传到两块 Pico

按如下方式连接线路:

主机 SCLK(GP2)→ 从机 SCLK(GP2)

主机 MOSI(GP3)→ 从机 MOSI(GP3)

主机 MISO(GP4)→ 从机 MISO(GP4)

主机 CS(GP5)→ 从机 CS(GP5)

两块 Pico 共地(GND 相连)

分别给两块 Pico 上电,通过串口监视器观察通信结果

3.注意事项:

主从设备的 SPI 参数(波特率、极性、相位)必须一致

通信距离不宜过长,通常在几米以内

多从机情况下,每个从机需要独立的 CS 线

高速通信时可能需要考虑线路阻抗匹配

通过 SPI 协议,树莓派 Pico 可以高效地与各种外设进行通信,适用于需要高速数据传输的场景。


三:ESP32/C++示例

这里以ESP32开发板为例,使用Arduino IDE开发框架,Arduino已经内置的 <SPI.h> 库可以非常方便地操作 SPI。ESP32 拥有多个 SPI 控制器(如 HSPI / VSPI),以下示例使用默认的 VSPI 引脚。

#include <SPI.h>

// 定义引脚(ESP32 默认 VSPI 引脚)
const int SCK_PIN = 18;
const int MISO_PIN = 19;
const int MOSI_PIN = 23;
const int CS_PIN   = 5;

void setup() {
  Serial.begin(115200);

  // 初始化片选引脚
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH); // 默认不选中

  // 初始化 SPI 总线(使用默认 VSPI 引脚)
  SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN);
  
  // 如果需要自定义时钟频率、位序和模式,可以使用 SPISettings
  // SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  
  Serial.println("ESP32 SPI 初始化完成");
}

void loop() {
  // 选中的配置参数:1MHz 频率,高位先发(MSBFIRST),SPI 模式 0
  SPISettings settings(1000000, MSBFIRST, SPI_MODE0);
  
  SPI.beginTransaction(settings);
  digitalWrite(CS_PIN, LOW); // 拉低片选,选中从设备

  // 发送读取命令并接收数据
  byte cmd = 0x9F;
  byte response1 = SPI.transfer(cmd);       // 发送命令,接收一个字节
  byte response2 = SPI.transfer(0x00);      // 发送空字节继续接收
  
  digitalWrite(CS_PIN, HIGH); // 拉高片选,结束传输
  SPI.endTransaction();

  // 打印结果
  Serial.print("ESP32 SPI 收到响应: 0x");
  Serial.print(response1, HEX);
  Serial.print(", 0x");
  Serial.println(response2, HEX);

  delay(2000);
}

1.代码说明

这段 ESP32 C++ 示例代码的核心逻辑可以拆解为 初始化(setup) 和 周期性数据通信(loop) 两个主要部分。它的本质流程是:建立 SPI 通信通道 打开从设备开关边送数据边收数据关闭开关打印结果。


2.初始化阶段

setup() 函数在 ESP32 开机或复位后仅执行一次,主要负责外设引脚的配置和总线准备:


2.1串口初始化 (Serial.begin)


开启串口通信(波特率 115200),用于将 SPI 通信的结果打印到电脑串口监视器中。


2.2GPIO 引脚状态设置 (pinMode & digitalWrite)


将片选引脚 CS_PIN (Pin 5) 设置为输出模式。


立即将 CS_PIN 设置为 高电平(HIGH)。因为 SPI 的片选线通常是低电平有效,拉高表示目前不与任何从设备通信,防止开机时总线产生干扰信号。


3.3SPI 硬件外设初始化 (SPI.begin)


传入定义的 SCK(18)、MISO(19)、MOSI(23)、CS(5) 引脚。


ESP32 硬件内部的 SPI 控制器完成引脚复用映射,为后续的高速收发做准备。

3.周期通信阶段

loop() 函数会无限循环执行,每次循环完成一次完整的 SPI 发送与接收流程:


配置 SPI 传输参数 (SPISettings)


定义本次通信的“工作协议”:


频率:1000000 Hz (1 MHz)。


位序:MSBFIRST(最高位先发)。


时钟模式:SPI_MODE0(空闲时时钟为低电平,在第一个上升沿采样数据)。


锁定总线并开启事务 (SPI.beginTransaction)


应用上述配置,并防止其他可能使用 SPI 的中断或任务干扰当前总线。


激活目标从设备 (digitalWrite(CS_PIN, LOW))


将 CS_PIN 拉低(LOW)。这相当于告诉挂载在总线上的目标从设备:“注意,我现在要和你说话了!”


全双工数据收发 (SPI.transfer)


第一步:执行 SPI.transfer(0x9F)。ESP32 的 MOSI 线送出 0x9F(通常是外设读取 ID 的指令),同一时间 MISO 线上从设备返还的数据被 ESP32 接收并存入 response1。


第二步:执行 SPI.transfer(0x00)。为了继续读取下一个字节的数据,主机必须继续产生时钟,因此发送一个无意义的哑数据(Dummy byte, 0x00),将从设备返回的第二个字节读取到 response2 中。


关闭从设备并释放总线 (digitalWrite & SPI.endTransaction)


将 CS_PIN 拉高(HIGH),结束本次传输,从设备回到待机状态。


释放 SPI 总线控制权,允许其他 SPI 事务运行。


结果处理与延时 (Serial.print & delay)


将收到的 response1 和 response2 以十六进制格式打印出来。


delay(2000) 让程序暂停 2 秒,随后进入下一次循环。





作者签名: www.ydxhuo.com   楼主  2025-08-25 14:47:04
回复列表
Powered by YDXHUO _VERSION
© 2017-2026 跃动芯火版权
您的IP: 216.73.216.115 , 2026-08-21 23:25:24
Powered by YDXHUO _VERSION
© 2017-2026 跃动芯火版权
(4) 分享
分享

请保存二维码或复制链接进行分享

取消
免责声明
1、本站资源,均来自网络与个人作者,所有资源和文章仅限用于学习和研究目的 。
2、不得用于商业或非法用途,否则,一切责任由该用户承担 !

侵权删除请致信 E-Mail:ydxho@qq.com
已有0次打赏