本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文围绕树莓派上的SPI通信展开,详细介绍了基于BCM2835芯片的SPI主机与从机代码实现过程。SPI是一种高速同步串行通信接口,常用于连接微控制器与传感器、LCD、ADC等外设。文章讲解了SPI通信的基本原理、GPIO引脚配置、时钟设置、数据传输流程等内容,并通过C语言结合bcm2835库进行编程实践,涵盖初始化、数据收发、资源释放等关键步骤。压缩包中包含完整的代码与文档,适合嵌入式开发者和树莓派爱好者进行学习与实操。
树莓派SPI从机主机代码

1. SPI通信协议简介

SPI(Serial Peripheral Interface)是由Motorola提出的一种 高速、全双工、同步串行通信协议 ,广泛应用于嵌入式系统中,如传感器、显示屏、存储器等外设的连接。其核心结构由 主设备(Master) 从设备(Slave) 构成,通过四根信号线完成数据传输:MOSI(主发从收)、MISO(从发主收)、SCLK(时钟信号)、CS(片选信号)。

SPI通信具有 无需地址、速率高、接口简单 等优点,常用于短距离、高速的数据交互场景。相较于I2C和UART,SPI在速度和灵活性方面更具优势,但其 缺乏标准协议规范 ,需要主从设备之间约定好通信模式(Mode 0~3)与时序参数。

在现代嵌入式系统中,如树莓派、STM32、ESP32等平台均内置SPI控制器,支持快速连接多种外设模块,为物联网、工业控制、智能硬件等领域提供了坚实的基础。

2. 树莓派BCM2835芯片SPI接口概述

2.1 BCM2835芯片的SPI模块

树莓派的第一代型号基于Broadcom BCM2835芯片设计,该芯片集成了多个外设接口,其中SPI(Serial Peripheral Interface)是嵌入式系统中常用的高速同步串行通信接口之一。BCM2835芯片内嵌一个SPI控制器,支持主模式(Master Mode)下的数据传输。该模块为树莓派与外部SPI设备(如传感器、ADC、LCD屏等)之间的通信提供了硬件基础。

2.1.1 SPI控制器的基本结构

BCM2835的SPI控制器由一组寄存器组成,用于控制通信过程的各个方面。其基本结构如下:

寄存器名称 地址偏移 功能描述
CS (Control and Status) 0x00 控制SPI模式、启动传输、设置片选信号等
FIFO (Data FIFO) 0x04 用于数据发送与接收的FIFO缓冲区
CLK (Clock Divider) 0x08 设置SPI时钟分频,控制通信速率
DLEN (Data Length) 0x0C 设置每次传输的数据长度(字节数)
LTOH (Last Transfer Over Head) 0x10 设置最后一次传输的延迟时间
DC (DMA Control) 0x14 控制DMA操作(可选)

控制器通过这些寄存器完成SPI通信的初始化、数据传输、时钟配置等操作。SPI控制器支持DMA模式,能够实现高速数据传输,减少CPU的负担。

2.1.2 主要寄存器与功能说明

以下是对几个关键寄存器的详细说明:

  • CS寄存器
    该寄存器控制SPI的启动、传输模式、片选信号、中断使能等。例如:
  • CS[2] :设置SPI为DMA使能(DMA Enable)。
  • CS[3] :设置SPI为传输完成中断使能(Interrupt Enable)。
  • CS[4] :设置传输完成后清除FIFO缓冲区(Clear FIFO)。
  • CS[7:5] :控制片选信号(Chip Select)。

  • CLK寄存器
    该寄存器用于设置SPI的时钟频率,通过设置分频值控制SCLK(Serial Clock)的速率。BCM2835的系统时钟为250MHz,因此SPI的SCLK频率可通过公式计算:
    SCLK = Core Clock / (divider)

  • DLEN寄存器
    设置每次传输的数据长度(以字节为单位)。例如,若设置为 0x01 ,则一次传输1个字节;若为 0x10 ,则为16个字节。

  • FIFO寄存器
    数据通过FIFO寄存器进行发送和接收。写入FIFO寄存器将数据放入发送缓冲区,读取FIFO寄存器则从接收缓冲区获取数据。

示例代码:SPI寄存器初始化

以下是一个使用 mmap 直接操作寄存器的SPI初始化代码片段:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define BCM2835_PERI_BASE   0x20000000
#define BCM2835_SPI0_BASE   (BCM2835_PERI_BASE + 0x204000)

volatile unsigned int *spi0;

int main() {
    int mem_fd;
    void *spi_map;

    // 打开/dev/mem设备
    if ((mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        printf("Can't open /dev/mem\n");
        exit(-1);
    }

    // 映射SPI寄存器地址
    spi_map = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, BCM2835_SPI0_BASE);
    if (spi_map == MAP_FAILED) {
        printf("mmap error\n");
        close(mem_fd);
        exit(-1);
    }

    spi0 = (volatile unsigned int *)spi_map;

    // 设置SPI控制寄存器
    spi0[0] = 0x00;  // 清除CS寄存器
    spi0[0] = (1 << 2);  // 启用DMA
    spi0[0] |= (1 << 4); // 清除FIFO
    spi0[0] |= (1 << 6); // 设置CS0为低电平使能
    spi0[0] |= (1 << 7); // 启动SPI传输

    // 设置SCLK频率为1MHz(系统时钟250MHz,分频值为250)
    spi0[2] = 250;

    // 设置数据长度为1字节
    spi0[3] = 1;

    // 写入FIFO缓冲区
    spi0[1] = 0x55;  // 发送数据

    // 等待传输完成
    while (!(spi0[0] & (1 << 16)));

    // 关闭映射
    munmap(spi_map, 4096);
    close(mem_fd);

    return 0;
}

代码逻辑分析

  1. 内存映射 :使用 mmap() 将BCM2835的SPI寄存器地址映射到用户空间,便于直接访问硬件寄存器。
  2. 寄存器配置
    - spi0[0] (CS寄存器):清零后设置DMA、FIFO清除、片选信号和启动SPI。
    - spi0[2] (CLK寄存器):设置SCLK频率为1MHz,即系统时钟250MHz除以250。
    - spi0[3] (DLEN寄存器):设置单次传输1字节。
  3. 数据发送 :将数据写入FIFO寄存器( spi0[1] )。
  4. 等待传输完成 :通过轮询CS寄存器的第16位判断传输是否完成。

该代码展示了如何直接操作BCM2835芯片的SPI寄存器,实现数据发送功能。适用于需要精细控制SPI通信的嵌入式应用场景。

2.2 SPI接口的硬件资源分布

2.2.1 树莓派上的SPI引脚位置

在树莓派的40引脚GPIO排针上,SPI接口由以下引脚组成:

信号线 引脚编号(物理) GPIO编号 功能说明
SCLK 23 GPIO11 串行时钟
MOSI 19 GPIO10 主输出从输入
MISO 21 GPIO9 主输入从输出
CE0 24 GPIO8 片选信号0
CE1 26 GPIO7 片选信号1

这些引脚默认配置为SPI模式,但也可通过GPIO复用设置进行更改。树莓派提供了两个片选信号CE0和CE1,允许同时连接两个SPI从设备。

2.2.2 硬件SPI与软件SPI的区别

特性 硬件SPI 软件SPI
实现方式 利用芯片内置SPI控制器 使用GPIO模拟SPI时序
速度 高速,可达32MHz 低速,受CPU影响大
CPU占用 极低 高,频繁中断处理
可靠性 高,稳定 低,容易受干扰
多路复用 支持多个从设备 需要手动管理时序

硬件SPI依赖BCM2835的SPI控制器,通信速度快、稳定性高,适合需要高速数据传输的场景,如ADC采集、LCD驱动等。而软件SPI通过GPIO模拟SPI时序,适用于调试或低速设备连接。

SPI通信流程图(Mermaid格式)
graph TD
    A[开始SPI通信] --> B{是否使用硬件SPI?}
    B -->|是| C[配置SPI寄存器]
    B -->|否| D[使用GPIO模拟时序]
    C --> E[设置SCLK频率]
    C --> F[设置片选信号]
    C --> G[设置数据长度]
    D --> H[设置GPIO为输出]
    D --> I[模拟SCLK时序]
    D --> J[模拟MOSI数据发送]
    J --> K[等待MISO数据接收]
    E --> L[写入FIFO缓冲区]
    F --> L
    G --> L
    L --> M[启动SPI传输]
    M --> N{是否DMA传输?}
    N -->|是| O[启用DMA通道]
    N -->|否| P[轮询FIFO状态]
    O --> Q[传输完成]
    P --> Q

该流程图展示了SPI通信从初始化到数据传输的全过程,涵盖了硬件与软件SPI的不同路径。通过流程图可以清晰理解SPI通信在不同实现方式下的控制逻辑。

2.3 SPI通信在嵌入式系统中的作用

2.3.1 数据传输的实时性与可靠性

SPI协议因其同步时钟机制和全双工通信能力,具备良好的实时性和高数据传输可靠性。在嵌入式系统中,SPI常用于以下场景:

  • 传感器数据采集 :如温度、湿度、加速度传感器等,需要高速采集和处理。
  • LCD显示控制 :图形LCD或OLED屏幕通过SPI接口与主控芯片通信,实现图像快速刷新。
  • 存储器读写 :如SPI Flash、EEPROM等非易失性存储器的数据读写操作。
  • 音频传输 :某些音频编解码器(如WM8731)通过SPI进行控制与数据传输。

SPI的高速特性使其在需要大量数据交换的场景中表现优异。例如,ADC芯片通过SPI可以实现每秒数十万次的采样,满足实时监测和处理的需求。

2.3.2 与I2C、UART协议的对比分析

特性 SPI I2C UART
通信方式 同步、全双工 同步、半双工 异步、全双工
引脚数量 4(SCLK、MOSI、MISO、CS) 2(SDA、SCL) 2(TXD、RXD)
通信速率 高,可达几十MHz 低至高速模式(400kHz~5MHz) 中等,通常为115200bps
主从架构 单主多从 单主/多主多从 点对点
数据格式 无固定格式,可定制 7/10位地址 + 数据 起始位 + 数据位 + 停止位
适用场景 高速数据传输、图形显示、存储器访问 传感器、EEPROM等低速设备 串口调试、模块间通信

SPI协议在速度和灵活性方面优于I2C和UART,适用于高速、大数据量的通信需求。I2C则在引脚数量和多设备支持方面更优,适合连接多个低速外设。UART则适用于简单的点对点通信,常用于调试和模块间数据交换。

示例代码:SPI与I2C通信速率对比

以下是一个SPI与I2C通信速率对比的伪代码示例:

import spidev
import smbus
import time

# SPI初始化
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000  # 1MHz

# I2C初始化
bus = smbus.SMBus(1)
i2c_address = 0x50

# 发送1000个字节测试SPI
start_time = time.time()
for _ in range(1000):
    spi.xfer([0x55])
end_time = time.time()
print(f"SPI传输1000字节耗时:{end_time - start_time:.6f}秒")

# 发送1000个字节测试I2C
start_time = time.time()
for _ in range(1000):
    bus.write_byte(i2c_address, 0x55)
end_time = time.time()
print(f"I2C传输1000字节耗时:{end_time - start_time:.6f}秒")

逻辑分析

  1. SPI测试 :使用 spidev 库设置SPI速率为1MHz,循环发送1000个字节,记录耗时。
  2. I2C测试 :使用 smbus 库连接I2C设备,循环写入1000个字节,记录耗时。
  3. 结果对比 :SPI通信速率显著高于I2C,适用于高速数据传输场景。

该示例验证了SPI在数据传输效率上的优势,适合在需要快速响应和大量数据交换的嵌入式系统中使用。

本章系统地介绍了BCM2835芯片的SPI模块、硬件资源分布及其在嵌入式系统中的应用。通过对寄存器结构、引脚定义、通信方式及协议对比的深入分析,读者能够全面掌握树莓派SPI通信的基础知识,为后续章节的编程实践打下坚实基础。

3. GPIO引脚配置为SPI模式

树莓派作为一款广受欢迎的嵌入式开发平台,其丰富的GPIO资源为开发者提供了极大的灵活性。在实际项目中,SPI通信常用于连接高速外设,如ADC、DAC、传感器、LCD等。要使用树莓派的SPI功能,首先需要将特定的GPIO引脚配置为SPI模式。本章将深入讲解GPIO引脚的基础知识、SPI模式的配置流程以及常用配置工具的使用方法。

3.1 树莓派GPIO引脚基础知识

树莓派的GPIO引脚不仅可以作为通用输入输出使用,还可以通过复用功能实现特定的外设接口,如SPI、I2C和UART。掌握这些引脚的功能和配置方法,是进行SPI通信的前提。

3.1.1 引脚编号与功能定义

树莓派的GPIO引脚编号存在多种表示方式,主要包括:

  • 物理引脚编号(BOARD) :按照物理排列编号,如1~40。
  • BCM编号(Broadcom SOC Channel) :基于SoC芯片定义的编号方式,如GPIO2、GPIO3等。
  • ** WiringPi编号**:WiringPi库使用的编号方式。

以下为树莓派3B+中与SPI相关的引脚编号对照表:

物理引脚编号 BCM编号 功能说明
19 GPIO10 MOSI(主出从入)
21 GPIO9 MISO(主入从出)
23 GPIO11 SCLK(时钟)
24 GPIO8 CE0(片选0)
26 GPIO7 CE1(片选1)

提示 :默认情况下,SPI0的MISO、MOSI、SCLK、CE0和CE1对应上述引脚。SPI1则使用不同的引脚(如GPIO16、GPIO17、GPIO18、GPIO19、GPIO20),但通常SPI0已能满足大多数需求。

3.1.2 多功能复用配置方法

每个GPIO引脚都有多种复用模式(Function Select),SPI通信需要将相关引脚设置为 ALT0 模式。具体设置方式如下:

  1. 访问GPIO寄存器 :树莓派的GPIO功能选择由 GPFSELx 寄存器控制。
  2. 计算寄存器偏移量 :每个引脚在GPFSEL寄存器中占用3位,例如GPIO10对应GPFSEL1的第30~32位。
  3. 设置ALT0模式 :将对应的3位设置为 100 (即二进制值4)表示ALT0模式。

例如,将GPIO10设置为MOSI功能(ALT0)的代码如下:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#define BCM2835_PERI_BASE 0xFE000000
#define GPIO_BASE (BCM2835_PERI_BASE + 0x200000) // GPIO registers
#define BLOCK_SIZE (4*1024)

volatile unsigned *gpio;

void setup_io() {
    int mem_fd;
    void *gpio_map;

    if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0) {
        printf("Can't open /dev/mem\n");
        exit(-1);
    }

    gpio_map = mmap(
        NULL,             // 地址由系统分配
        BLOCK_SIZE,       // 映射4K字节
        PROT_READ|PROT_WRITE, // 可读写
        MAP_SHARED,       // 共享映射
        mem_fd,           // 文件描述符
        GPIO_BASE         // GPIO寄存器起始地址
    );

    close(mem_fd);

    if (gpio_map == MAP_FAILED) {
        printf("mmap error %p\n", gpio_map);
        exit(-1);
    }

    gpio = (volatile unsigned *)gpio_map;
}

void set_gpio_alt(int pin, int alt) {
    int reg = pin / 10;
    int shift = (pin % 10) * 3;
    gpio[reg] = (gpio[reg] & ~(7 << shift)) | (alt << shift);
}

int main() {
    setup_io();
    set_gpio_alt(10, 4); // 设置GPIO10为ALT0
    return 0;
}

逐行解释:

  • setup_io() :打开 /dev/mem ,将GPIO寄存器地址映射到用户空间,以便直接访问硬件寄存器。
  • set_gpio_alt(pin, alt) :根据引脚号和功能模式设置寄存器中的对应位。
  • main() :调用函数将GPIO10设置为ALT0,即SPI0的MOSI功能。

注意 :此方式为底层寄存器操作,适合理解硬件原理,但实际开发建议使用更高层的库如 wiringPi bcm2835 简化配置。

3.2 SPI模式的配置流程

配置树莓派的SPI接口需要明确指定哪些引脚用于SPI通信,并启用相应的SPI控制器。这一过程可以通过设备树(Device Tree)机制进行配置。

3.2.1 设定MISO、MOSI、SCLK和CE引脚

SPI通信需要以下基本引脚:

  • MISO (Master In Slave Out):主机接收数据。
  • MOSI (Master Out Slave In):主机发送数据。
  • SCLK (Serial Clock):同步时钟。
  • CE (Chip Enable):片选信号,控制从机是否响应。

在树莓派中,这些引脚默认已配置为SPI功能,但有时可能被其他功能占用,需要手动确认。

3.2.2 使用设备树配置SPI接口

设备树(Device Tree)是一种描述硬件配置的机制。树莓派使用 .dts (设备树源文件)来定义硬件接口。我们可以通过修改设备树启用或配置SPI接口。

操作步骤如下:

  1. 查看当前设备树配置
ls /boot/overlays | grep spi

输出可能包括:

spi0-2cs.dtbo
spi0-hw-cs.dtbo
spi1-1cs.dtbo
  1. 编辑 /boot/config.txt 文件
sudo nano /boot/config.txt

添加以下行以启用SPI0接口并使用硬件片选(CE0):

dtoverlay=spi0-hw-cs
  1. 重启树莓派
sudo reboot
  1. 验证SPI设备节点
ls /dev/spi*

输出应包含:

/dev/spidev0.0  /dev/spidev0.1

表示SPI0已启用,支持两个从设备(CE0和CE1)。

说明 spidev0.0 对应CE0, spidev0.1 对应CE1。

3.3 配置工具与命令行操作

除了手动修改设备树文件,树莓派还提供了图形化和命令行工具来配置SPI接口,简化操作流程。

3.3.1 raspi-config工具的使用

raspi-config 是树莓派官方提供的配置工具,支持图形化界面操作。

操作步骤:

  1. 打开终端,输入以下命令:
sudo raspi-config
  1. 使用方向键选择 Interfacing Options SPI

  2. 选择 Yes 启用SPI接口。

  3. 按照提示完成操作,最后选择 Finish 并重启系统。

优势 :简单直观,适合新手快速配置SPI接口。

3.3.2 手动修改/boot/config.txt文件

通过编辑 /boot/config.txt 文件,可以手动控制设备树加载选项。

配置示例:

# 启用SPI0并使用硬件片选
dtoverlay=spi0-hw-cs

# 启用SPI1(若需要)
dtoverlay=spi1-1cs

参数说明:

  • dtoverlay=spi0-hw-cs :启用SPI0接口,并使用硬件片选(CE0和CE1)。
  • dtoverlay=spi1-1cs :启用SPI1接口,使用一个片选(CE0)。

验证配置是否生效:

dmesg | grep spi

输出应包含类似以下信息:

[    5.678901] spi spi0: controller initialized
[    5.678912] spidev spi0.0: SPI device registered

表示SPI控制器和设备节点已成功初始化。

总结与扩展

本章从GPIO引脚的基础知识入手,详细介绍了树莓派上SPI引脚的编号方式和复用配置方法。随后讲解了如何通过设备树机制启用SPI接口,并给出了具体的代码示例和配置命令。最后,介绍了使用 raspi-config 工具和手动修改 /boot/config.txt 的两种方式,帮助开发者灵活配置SPI通信环境。

在下一章中,我们将深入探讨SPI通信的主从架构、时序控制和数据格式等内容,进一步夯实SPI编程的理论基础。

4. SPI主机与从机通信原理

SPI(Serial Peripheral Interface)通信协议的核心特性之一是其 主从架构 ,即由一个 主机 (Master)控制整个通信过程,而一个或多个 从机 (Slave)响应主机的请求并交换数据。本章将深入剖析SPI主从通信的工作机制,包括角色定义、时序控制、数据格式、握手机制及常见问题处理,帮助开发者在树莓派等嵌入式平台上高效实现SPI通信。

4.1 SPI通信的主从架构

SPI是一种典型的 单主多从 通信协议,主机负责生成时钟信号(SCLK)并发起通信,从机则通过片选信号(CS)被选中后参与数据交换。

4.1.1 主机与从机的角色定义

  • 主机(Master)
  • 控制时钟信号(SCLK)的频率和极性;
  • 启动和结束数据传输;
  • 管理片选信号(CS),选择通信的从设备;
  • 提供MOSI(Master Out Slave In)数据输出;
  • 接收来自从机的MISO(Master In Slave Out)数据。

  • 从机(Slave)

  • 仅在CS信号有效时响应主机;
  • 根据SCLK同步接收MOSI上的数据;
  • 在SCLK的控制下将数据通过MISO发送给主机;
  • 通常不主动发起通信,只能被动响应。

4.1.2 片选信号(CS)的作用

片选信号(Chip Select)是SPI通信中用于 选择当前通信的从设备 的控制信号。每个从设备都有一个独立的CS引脚。主机通过将某个从机的CS拉低(低电平有效)来激活该设备,其他未被选中的从设备则保持静默。

CS信号的关键作用包括:
功能 描述
设备选择 多从设备共享MISO、MOSI、SCLK时,CS用于指定目标
功耗控制 非选中状态可降低从设备功耗
通信同步 有些从设备要求CS下降沿作为通信启动信号

💡 例如,在树莓派上使用SPI0接口时,CE0(GPIO8)和CE1(GPIO7)是两个片选引脚,支持连接两个SPI从设备。

4.2 通信时序与数据格式

SPI通信的时序由 时钟极性(CPOL) 时钟相位(CPHA) 共同决定,这两者定义了数据采样和发送的时机。此外,数据帧结构与传输顺序也影响通信的正确性。

4.2.1 时钟极性与相位(CPOL/CPHA)

SPI通信的四种标准模式由CPOL(Clock Polarity)和CPHA(Clock Phase)组合而成:

模式 CPOL CPHA 数据采样边沿 数据变化边沿
Mode 0 0 0 上升沿 下降沿
Mode 1 0 1 下降沿 上升沿
Mode 2 1 0 下降沿 上升沿
Mode 3 1 1 上升沿 下降沿

💡 主机与从机必须设置相同的CPOL和CPHA模式,否则会导致数据采样错误。

示例:Mode 0 的时序图(使用mermaid流程图表示)
sequenceDiagram
    主机->>从机: SCLK=0 (CPOL=0)
    主机->>从机: 数据在上升沿采样 (CPHA=0)
    主机->>从机: 数据在下降沿变化

4.2.2 数据帧结构与传输顺序

SPI通信的数据帧通常为 8位或16位 ,但也可以是其他长度,具体取决于从设备的设计。数据传输顺序分为两种:

  • MSB First (高位先传):默认模式,高位先发送;
  • LSB First (低位先传):某些设备要求低位先传。
示例:发送数据0xA5(二进制:10100101)
  • MSB First :依次发送 1, 0, 1, 0, 0, 1, 0, 1
  • LSB First :依次发送 1, 0, 1, 0, 0, 1, 0, 1(顺序反转)

💡 树莓派的BCM2835 SPI控制器支持配置数据传输顺序,可通过寄存器设置 SPI0_TXH LSBFE 位来控制。

4.3 SPI通信的握手机制

SPI通信虽然没有类似I2C的应答机制,但其通过 同步时钟控制 实现高效的数据交换。主机控制SCLK,从机根据时钟同步发送和接收数据。

4.3.1 同步启动与数据交换过程

SPI通信的典型数据交换过程如下:

sequenceDiagram
    主机->>从机: 拉低CS,启动通信
    主机->>从机: 输出SCLK,开始传输
    主机->>从机: MOSI输出第一个bit
    从机-->>主机: MISO输出第一个bit
    ... 重复发送其余bit ...
    主机->>从机: 传输完成后拉高CS,结束通信

💡 每次传输一个bit时,主从设备同时交换数据,即“发送一个bit,接收一个bit”,因此SPI是 全双工 通信协议。

4.3.2 常见通信错误与处理策略

在实际应用中,SPI通信可能遇到以下问题:

错误类型 原因 解决策略
数据错位 CPOL/CPHA配置不一致 检查并统一通信模式
无响应 从机未上电或CS未正确拉低 检查电源与片选引脚
数据错误 时钟频率过高导致采样失败 降低SCLK频率
无法初始化 设备树未启用SPI 修改 /boot/config.txt 启用SPI
数据不一致 缓冲区未清空或读写顺序错误 清空缓冲区,确保读写同步
示例代码:SPI通信失败的排查

以下是一个使用 bcm2835 库进行SPI通信的代码片段:

#include <bcm2835.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    // 初始化BCM2835库
    if (!bcm2835_init()) {
        printf("BCM2835初始化失败\n");
        return 1;
    }

    // 设置SPI0为主机模式
    bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // 高位先发
    bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);               // 模式0
    bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_64); // 时钟分频

    // 使能CE0片选
    bcm2835_spi_chipSelect(BCM2835_SPI_CS0);

    // 发送数据
    uint8_t tx_data = 0xA5;
    uint8_t rx_data = bcm2835_spi_transfer(tx_data);

    printf("发送数据: 0x%02X, 接收数据: 0x%02X\n", tx_data, rx_data);

    // 关闭BCM2835库
    bcm2835_close();
    return 0;
}
代码逐行解析:
  • bcm2835_init() :初始化SPI控制器;
  • bcm2835_spi_setBitOrder() :设置数据发送顺序;
  • bcm2835_spi_setDataMode() :设置通信模式;
  • bcm2835_spi_setClockDivider() :设置SCLK频率(系统时钟/64);
  • bcm2835_spi_chipSelect() :选择使用CE0片选;
  • bcm2835_spi_transfer() :发送一个字节并返回接收到的字节;
  • bcm2835_close() :释放资源。

💡 若接收到的数据为0x00或随机值,说明通信失败,应检查硬件连接、电源、时钟配置和片选信号。

小结(非总结,而是内容延伸)

SPI通信虽然结构简单、速率高,但其依赖于 精确的时序配置 硬件连接正确性 。在实际开发中,建议使用逻辑分析仪(如Saleae Logic)捕获SPI信号,辅助调试通信问题。下一章将详细介绍如何使用 bcm2835 库进行SPI编程,包括初始化、数据收发、参数配置等实用技巧。

5. 使用bcm2835库进行SPI编程

在嵌入式开发中,高效的硬件通信离不开优秀的库支持。 bcm2835 是一个专为树莓派 BCM2835 系列芯片设计的 C 语言库,它封装了底层寄存器操作,提供了便捷的 API 接口,特别适用于 GPIO、SPI、I2C 等外设的访问。本章将围绕 bcm2835 库的使用,深入讲解如何通过该库进行 SPI 编程,包括库的安装配置、SPI 接口初始化、参数设置、以及数据的发送与接收实现,为后续实战项目打下坚实基础。

5.1 bcm2835库的安装与配置

5.1.1 安装依赖与编译步骤

在使用 bcm2835 库之前,首先需要在树莓派系统中完成安装和配置。该库依赖于标准的编译工具链和 Linux 内核支持。

安装依赖
sudo apt-get update
sudo apt-get install build-essential libtool autoconf automake
下载与编译
wget http://www.airspaybar.com/bcm2835/files/bcm2835-1.71.tar.gz
tar zxvf bcm2835-1.71.tar.gz
cd bcm2835-1.71
./configure
make
sudo make check
sudo make install

逻辑分析
- wget :从官网下载源码包;
- tar zxvf :解压 .tar.gz 格式压缩包;
- ./configure :生成 Makefile 配置文件;
- make :编译源码;
- sudo make check :执行测试验证是否编译成功;
- sudo make install :将编译好的库文件安装到系统目录中。

参数说明:
  • 版本兼容性 :确保树莓派操作系统(如 Raspbian)与库版本兼容;
  • 权限问题 :需要 sudo 权限进行安装,因为会写入系统 /usr/local/lib 等目录;
  • 交叉编译 :若用于嵌入式交叉编译环境,需指定 --host 参数。

5.1.2 库函数的基本使用方法

安装完成后,即可在 C 程序中调用 bcm2835 提供的 API。以下是基本使用步骤:

示例代码:初始化库并打印版本信息
#include <bcm2835.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    if (!bcm2835_init())
    {
        printf("BCM2835 初始化失败。\n");
        return 1;
    }

    printf("BCM2835 库版本: %s\n", bcm2835_version());

    bcm2835_close();
    return 0;
}

逻辑分析
- bcm2835_init() :初始化库,映射寄存器;
- bcm2835_version() :获取当前库版本信息;
- bcm2835_close() :释放资源,结束调用。

编译命令:
gcc -o test_bcm2835 test_bcm2835.c -lbcm2835
表格:常用库函数概览
函数名 功能说明
bcm2835_init() 初始化库,映射寄存器
bcm2835_close() 释放资源,结束库调用
bcm2835_spi_begin() 启动 SPI 模块
bcm2835_spi_setClockDivider() 设置 SPI 时钟分频
bcm2835_spi_transfer() 发送并接收一个字节数据

5.2 初始化与配置SPI接口

5.2.1 初始化SPI通信模块

使用 bcm2835 库进行 SPI 通信前,必须先启用 SPI 模块并进行基本配置。

示例代码:初始化 SPI 模块
#include <bcm2835.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    if (!bcm2835_init())
    {
        printf("BCM2835 初始化失败。\n");
        return 1;
    }

    if (!bcm2835_spi_begin())
    {
        printf("SPI 初始化失败。\n");
        bcm2835_close();
        return 1;
    }

    printf("SPI 初始化成功。\n");

    bcm2835_spi_end();
    bcm2835_close();
    return 0;
}

逻辑分析
- bcm2835_spi_begin() :启用 SPI 控制器并配置为默认模式(Mode 0);
- bcm2835_spi_end() :关闭 SPI 模块;
- 该函数内部会自动配置 SPI 的引脚为复用模式(MISO、MOSI、SCLK)。

5.2.2 设置通信参数(模式、速率)

SPI 通信具有四种标准模式(Mode 0~3),由 CPOL 和 CPHA 决定。此外,还需要设置通信速率(时钟频率)。

示例代码:设置 SPI 模式与速率
#include <bcm2835.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    if (!bcm2835_init())
    {
        printf("BCM2835 初始化失败。\n");
        return 1;
    }

    if (!bcm2835_spi_begin())
    {
        printf("SPI 初始化失败。\n");
        bcm2835_close();
        return 1;
    }

    // 设置 SPI 模式为 Mode 1 (CPOL=0, CPHA=1)
    bcm2835_spi_setDataMode(BCM2835_SPI_MODE1);

    // 设置 SPI 时钟频率为 1MHz
    bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_256);

    printf("SPI 模式设置为 Mode 1,频率设置为 1MHz。\n");

    bcm2835_spi_end();
    bcm2835_close();
    return 0;
}

逻辑分析
- bcm2835_spi_setDataMode() :设置 SPI 通信模式;
- BCM2835_SPI_MODE0 ~ BCM2835_SPI_MODE3 :对应四种标准模式;
- bcm2835_spi_setClockDivider() :设置时钟分频值,决定 SPI 速率;
- 分频值选择:
- BCM2835_SPI_CLOCK_DIVIDER_65536 = 65536 → 最慢;
- BCM2835_SPI_CLOCK_DIVIDER_2 = 2 → 最快(约为 125 MHz);
- 常用值为 256(1 MHz)或 64(约 3.9 MHz)。

表格:SPI 模式与 CPOL/CPHA 对应关系
模式编号 CPOL CPHA 说明
Mode 0 0 0 时钟空闲为低电平,上升沿采样
Mode 1 0 1 时钟空闲为低电平,下降沿采样
Mode 2 1 0 时钟空闲为高电平,下降沿采样
Mode 3 1 1 时钟空闲为高电平,上升沿采样

5.3 数据发送与接收函数

5.3.1 单字节与多字节数据传输

SPI 是一种全双工通信协议,每次发送一个字节数据的同时也会接收一个字节数据。 bcm2835 提供了 bcm2835_spi_transfer() 函数用于单字节传输,也支持多字节传输函数。

示例代码:单字节发送与接收
#include <bcm2835.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    if (!bcm2835_init())
    {
        printf("BCM2835 初始化失败。\n");
        return 1;
    }

    if (!bcm2835_spi_begin())
    {
        printf("SPI 初始化失败。\n");
        bcm2835_close();
        return 1;
    }

    uint8_t tx_data = 0xA5;
    uint8_t rx_data = bcm2835_spi_transfer(tx_data);

    printf("发送数据: 0x%02X,接收数据: 0x%02X\n", tx_data, rx_data);

    bcm2835_spi_end();
    bcm2835_close();
    return 0;
}

逻辑分析
- bcm2835_spi_transfer() :发送一字节并返回接收的一字节;
- 常用于与 SPI 从设备进行交互,如读取传感器数据、发送控制指令等。

示例代码:多字节发送与接收
#include <bcm2835.h>
#include <stdio.h>

#define BUF_SIZE 4

int main(int argc, char **argv)
{
    if (!bcm2835_init())
    {
        printf("BCM2835 初始化失败。\n");
        return 1;
    }

    if (!bcm2835_spi_begin())
    {
        printf("SPI 初始化失败。\n");
        bcm2835_close();
        return 1;
    }

    uint8_t tx_buffer[BUF_SIZE] = {0x01, 0x02, 0x03, 0x04};
    uint8_t rx_buffer[BUF_SIZE];

    bcm2835_spi_transfernb(tx_buffer, rx_buffer, BUF_SIZE);

    for (int i = 0; i < BUF_SIZE; i++)
    {
        printf("发送[%d]: 0x%02X, 接收[%d]: 0x%02X\n", i, tx_buffer[i], i, rx_buffer[i]);
    }

    bcm2835_spi_end();
    bcm2835_close();
    return 0;
}

逻辑分析
- bcm2835_spi_transfernb() :发送和接收指定长度的缓冲区数据;
- tx_buffer :待发送数据;
- rx_buffer :接收数据;
- 适用于连续数据块传输,如图像数据、ADC 转换结果等。

5.3.2 接收缓冲区与状态检查

在实际开发中,应加入状态检查机制,确保数据完整性和通信稳定性。

示例代码:带状态检查的 SPI 通信
#include <bcm2835.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    if (!bcm2835_init())
    {
        printf("BCM2835 初始化失败。\n");
        return 1;
    }

    if (!bcm2835_spi_begin())
    {
        printf("SPI 初始化失败。\n");
        bcm2835_close();
        return 1;
    }

    uint8_t tx_data = 0xA5;
    uint8_t rx_data = bcm2835_spi_transfer(tx_data);

    if (rx_data != 0xFF) // 假设 0xFF 表示无效数据
    {
        printf("有效接收数据: 0x%02X\n", rx_data);
    }
    else
    {
        printf("接收到无效数据,可能通信失败。\n");
    }

    bcm2835_spi_end();
    bcm2835_close();
    return 0;
}

逻辑分析
- 通过判断接收值是否为无效值(如 0xFF),可以识别通信是否正常;
- 在实际项目中,建议使用 CRC 校验、重传机制等方式增强数据可靠性。

Mermaid 流程图:SPI 通信状态检查流程
graph TD
    A[开始SPI通信] --> B[发送数据]
    B --> C[接收数据]
    C --> D{接收数据是否有效?}
    D -- 是 --> E[处理有效数据]
    D -- 否 --> F[记录错误或重传]
    E --> G[结束通信]
    F --> G

总结

本章围绕 bcm2835 库的 SPI 编程进行了系统讲解,从库的安装、SPI 模块初始化,到通信参数设置,再到数据发送与接收的实现,层层递进地展示了树莓派平台下 SPI 编程的核心流程。下一章将继续深入 SPI 通信的初始化设置,包括模式选择与时钟频率配置,帮助开发者掌握完整的 SPI 通信控制能力。

6. SPI初始化设置(模式、时钟频率)

在树莓派上进行SPI通信之前,必须对SPI模块进行初始化设置,包括选择合适的通信模式和设定时钟频率。本章将详细介绍SPI通信模式的选择、时钟频率的配置机制以及完整的初始化流程,并通过代码示例说明如何使用bcm2835库进行相关设置。

6.1 SPI通信模式的选择

SPI协议定义了四种标准通信模式,它们由时钟极性(CPOL)与时钟相位(CPHA)两个参数决定。不同的从设备对模式的要求不同,因此主机在初始化SPI接口时必须正确设置该参数以确保与从机的兼容性。

6.1.1 四种标准SPI模式(Mode 0~3)

模式编号 CPOL CPHA 时钟空闲状态 数据采样边沿
Mode 0 0 0 低电平 上升沿
Mode 1 0 1 低电平 下降沿
Mode 2 1 0 高电平 下降沿
Mode 3 1 1 高电平 上升沿
  • CPOL(Clock Polarity) :决定SPI时钟在空闲状态时的电平。
  • CPHA(Clock Phase) :决定数据在哪个时钟边沿被采样。

选择正确的模式是确保SPI通信正常进行的关键。例如,某些ADC芯片可能仅支持Mode 0,而某些EEPROM芯片可能使用Mode 3。因此,在开发过程中应查阅从设备的数据手册以确认其支持的SPI模式。

6.1.2 模式匹配与从机兼容性

SPI模式的匹配原则如下:

  • 主机与从机的CPOL和CPHA设置必须一致 ,否则会导致数据采样错误。
  • 若从机支持多种模式,可以选择一个主机支持的模式进行通信。
  • 在多从机系统中,若不同从机支持的模式不一致,需在切换从机时重新配置SPI模式。

在实际开发中,建议优先选择Mode 0,因为这是大多数设备默认支持的模式。

graph TD
    A[开始初始化SPI] --> B[读取从机数据手册]
    B --> C{是否支持多种SPI模式?}
    C -->|是| D[选择主机支持的模式]
    C -->|否| E[设置为主机与从机共同支持的模式]
    D --> F[配置CPOL与CPHA寄存器]
    E --> F
    F --> G[验证通信是否正常]
    G -->|正常| H[初始化完成]
    G -->|异常| I[重新选择模式并测试]

6.2 时钟频率设置

SPI通信的时钟频率决定了数据传输的速度,是影响通信效率的重要参数。树莓派的SPI控制器支持通过分频机制来设置不同的时钟频率。

6.2.1 时钟分频机制

BCM2835芯片的SPI控制器使用主频(默认为250MHz)作为时钟源,并通过分频器来生成所需的SPI时钟频率。分频系数为偶数,范围为32~65536。分频公式如下:

SPI Clock = Core Clock / (Divider)

其中,Core Clock 通常为 250MHz(可通过设备树修改)。

例如:

  • 分频值为 64 → SPI Clock = 250MHz / 64 ≈ 3.90625 MHz
  • 分频值为 128 → SPI Clock = 250MHz / 128 ≈ 1.953125 MHz

分频值的设置由 SPI0_CLK 寄存器控制,该寄存器的地址为 0x202040

6.2.2 设置合适的通信速率

选择SPI通信速率时,需考虑以下因素:

  • 从设备的最大支持速率 :例如,某些LCD模块仅支持最高10MHz的时钟频率。
  • 通信距离与噪声干扰 :高频信号更容易受到干扰,长距离通信应适当降低频率。
  • 系统负载与实时性要求 :高速通信可能增加CPU负载,影响系统稳定性。

通常建议从低速开始测试,逐步提高频率以达到最佳性能。

// 使用 bcm2835 库设置 SPI 时钟频率
#include <bcm2835.h>

int main(int argc, char **argv)
{
    if (!bcm2835_init())
        return 1;

    bcm2835_spi_begin();
    bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // 数据高位先发
    bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);              // 设置为 Mode 0
    bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_64); // 设置时钟分频为64(约3.9MHz)

    // 后续发送或接收数据操作
    uint8_t data = 0x55;
    uint8_t result = bcm2835_spi_transfer(data);

    printf("Received: 0x%02X\n", result);

    bcm2835_spi_end();
    bcm2835_close();
    return 0;
}
代码逐行解析:
  • bcm2835_init() :初始化bcm2835库。
  • bcm2835_spi_begin() :启用SPI接口。
  • bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST) :设置数据高位先发。
  • bcm2835_spi_setDataMode(BCM2835_SPI_MODE0) :设置SPI通信模式为Mode 0。
  • bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_64) :设置时钟分频值为64。
  • bcm2835_spi_transfer(data) :发送数据并接收返回值。
  • bcm2835_spi_end() bcm2835_close() :结束SPI通信并释放资源。

6.3 初始化流程示例

6.3.1 配置寄存器与调用API函数

在树莓派中,SPI模块的初始化可以通过直接操作寄存器实现,也可以使用bcm2835库封装的API函数简化开发流程。

使用寄存器配置SPI(底层方式):
volatile unsigned int* spi0_base = (unsigned int*)BCM2835_SPI0_BASE;

// 设置SPI控制寄存器(CS)
spi0_base[BCM2835_SPI0_CS / 4] = BCM2835_SPI0_CS_CLEAR_RX | BCM2835_SPI0_CS_CLEAR_TX;
// 设置SPI时钟分频
spi0_base[BCM2835_SPI0_CLK / 4] = 64; // 250MHz / 64 = ~3.9MHz
// 启用SPI
spi0_base[BCM2835_SPI0_CS / 4] |= BCM2835_SPI0_CS_TA;
使用bcm2835库配置SPI(推荐方式):
if (!bcm2835_init()) {
    fprintf(stderr, "Failed to initialize BCM2835 library\n");
    return -1;
}

bcm2835_spi_begin();
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST);
bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_64);

6.3.2 实际代码片段与参数说明

参数说明:
  • BCM2835_SPI_BIT_ORDER_MSBFIRST :高位先发(MSB First)。
  • BCM2835_SPI_MODE0 :SPI模式0(CPOL=0, CPHA=0)。
  • BCM2835_SPI_CLOCK_DIVIDER_64 :设置时钟分频为64。
流程说明:
  1. 初始化bcm2835库;
  2. 开启SPI接口;
  3. 设置数据传输顺序;
  4. 设置SPI通信模式;
  5. 设置SPI时钟频率;
  6. 发送/接收数据;
  7. 结束SPI通信。
graph TD
    A[初始化bcm2835库] --> B[开启SPI接口]
    B --> C[设置数据传输顺序]
    C --> D[设置SPI通信模式]
    D --> E[设置SPI时钟频率]
    E --> F[发送或接收数据]
    F --> G[结束SPI通信]
    G --> H[释放资源]

本章详细讲解了SPI通信模式的选择与时钟频率的配置方法,并通过实际代码示例展示了如何使用bcm2835库进行初始化设置。下一章将围绕SPI数据的发送与接收实现展开,介绍如何在主机与从机之间高效传输数据。

7. SPI数据发送与接收实现

7.1 主机发送数据流程

在SPI通信中,主机负责发起通信并控制时钟信号。主机发送数据的过程通常包括数据准备、缓冲区设置、调用发送函数以及结果判断等步骤。

数据准备与缓冲区设置

在发送数据之前,主机需要将待发送的数据组织成缓冲区。通常使用数组或动态内存分配来存储数据。

// 示例:定义发送缓冲区
#define BUF_SIZE 10
uint8_t tx_buffer[BUF_SIZE] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};

发送函数的调用与结果判断

使用 bcm2835 库时,可以调用 bcm2835_spi_transfer 函数进行数据发送和接收(SPI是全双工的)。

#include <bcm2835.h>

int main(int argc, char **argv)
{
    // 初始化BCM2835库
    if (!bcm2835_init())
    {
        return 1;
    }

    // 设置SPI模式为Mode 0,时钟频率为1MHz
    bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // MSB First
    bcm2835_spi_setDataMode(BCM2835_SPI_MODE0);              // Mode 0
    bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_256); // 250MHz / 256 ≈ 976.5625kHz

    // 发送数据
    uint8_t tx_data[] = {0x01, 0x02, 0x03, 0x04};
    uint8_t rx_data[4]; // 接收缓冲区

    bcm2835_spi_transfern(tx_data, sizeof(tx_data));

    // 打印接收到的数据(用于全双工测试)
    for (int i = 0; i < sizeof(tx_data); i++)
    {
        printf("Received byte %d: 0x%02X\n", i, rx_data[i]);
    }

    // 关闭BCM2835库
    bcm2835_close();
    return 0;
}
  • tx_data[] :发送的数据数组。
  • rx_data[] :接收的数据缓冲区,SPI通信是全双工的,所以发送时也会接收到数据。
  • bcm2835_spi_transfern() :发送并接收数据,适用于连续传输多个字节。

注意 :发送过程中应确保SPI接口已正确初始化,并且时钟频率与从机设备匹配。

7.2 从机接收数据处理

在SPI通信中,从机设备通常需要对主机发送的数据进行接收和处理。从机处理包括中断响应、数据解析和错误处理等。

从机端的中断响应机制

从机可以通过硬件中断或轮询方式接收数据。对于树莓派而言,通常作为SPI主机,但从机设备(如外部MCU)可使用中断机制响应主机通信。

以下为伪代码示例:

void spi_slave_isr()
{
    uint8_t data = read_spi_register(SPI_DATA_REG); // 读取接收到的数据
    process_data(data);                             // 处理数据
}
  • read_spi_register() :读取SPI数据寄存器。
  • process_data() :根据协议解析并执行操作。

数据校验与错误处理

从机端应对接收的数据进行校验,例如CRC校验、长度校验等。常见的错误处理策略包括:

错误类型 处理方法
数据校验失败 返回错误码,请求重传
接收缓冲区溢出 清除缓冲区,重新初始化SPI接口
通信超时 启动看门狗,复位SPI模块

在从机设计中,建议使用状态机机制处理不同阶段的通信流程,以增强健壮性。

7.3 实战项目:SPI驱动外设

本节将通过三个典型SPI外设的连接与通信示例,展示SPI在实际项目中的应用。

7.3.1 连接LCD显示模块

以常见的ST7735 LCD模块为例,其使用SPI接口进行数据通信。连接方式如下:

树莓派引脚 LCD模块引脚 功能
GPIO10 MOSI 数据发送
GPIO11 SCLK 时钟信号
GPIO8 CE0 片选信号
GPIO24 DC 数据/命令选择
GPIO25 RST 复位信号

初始化代码片段(使用 bcm2835 库):

// 设置DC和RST引脚为输出
bcm2835_gpio_fsel(24, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(25, BCM2835_GPIO_FSEL_OUTP);

// 发送初始化命令
send_command(0x01); // 软件复位
delay(150);
send_command(0x11); // 退出睡眠模式

7.3.2 控制ADC采集数据

以 MCP3208 12位ADC芯片为例,它通过SPI接口将模拟信号转换为数字信号。读取通道0的电压值:

uint16_t read_adc(int channel)
{
    uint8_t tx[] = {0x06 | ((channel & 0x07) >> 2), ((channel & 0x01) << 7), 0x00};
    uint8_t rx[3] = {0};

    bcm2835_spi_transfernb(tx, rx, 3);

    return ((rx[1] << 8) & 0x300) | rx[2];
}
  • channel :指定ADC通道(0~7)。
  • tx[] :构造SPI请求数据帧。
  • rx[] :返回的12位ADC值。

7.3.3 EEPROM读写操作实现

以 Microchip 的 25AA020A EEPROM 为例,支持SPI接口,读写流程如下:

写入数据流程:
void eeprom_write_byte(uint8_t address, uint8_t data)
{
    uint8_t tx[3] = {0x02, address, data}; // 写命令 + 地址 + 数据
    bcm2835_spi_transfern(tx, 3);
    delay(5); // 等待写入完成
}
读取数据流程:
uint8_t eeprom_read_byte(uint8_t address)
{
    uint8_t tx[3] = {0x03, address, 0x00}; // 读命令 + 地址 + dummy byte
    uint8_t rx[3] = {0};
    bcm2835_spi_transfern(tx, rx, 3);
    return rx[2];
}
  • 0x02 :写入命令。
  • 0x03 :读取命令。
  • address :EEPROM地址。
  • data :写入的数据。

通过上述实战项目,开发者可以掌握如何利用SPI协议驱动各类外设,实现嵌入式系统的数据采集、显示与存储功能。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文围绕树莓派上的SPI通信展开,详细介绍了基于BCM2835芯片的SPI主机与从机代码实现过程。SPI是一种高速同步串行通信接口,常用于连接微控制器与传感器、LCD、ADC等外设。文章讲解了SPI通信的基本原理、GPIO引脚配置、时钟设置、数据传输流程等内容,并通过C语言结合bcm2835库进行编程实践,涵盖初始化、数据收发、资源释放等关键步骤。压缩包中包含完整的代码与文档,适合嵌入式开发者和树莓派爱好者进行学习与实操。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐