SPI

前置知识

SPI(Serial Peripheral Interface,串行外设接口)是一种在嵌入式系统中广泛使用的高速、全双工、同步的串行通信协议。 常用于需要高速数据交换的场景,例如连接​​闪存(Flash)、SD卡、显示屏驱动器、ADC/DAC转换器​​以及各种​​传感器。

c1201_spi_4

SPI接口有3线、4线两种形式。其工作机制如下表所示区别:

4线SPI

c1201_spi_1

3线SPI

c1201_spi_2

接口说明

SPI接口在C1201上的位置,在40Pin上有两个SPI接口,硬件引脚分别为SPI0、SPI1;在Jetson-io配置工具中分别为spi1和spi3。

注意

Jetson Orin 系列的设备树设计,会根据不同的引脚复用模式,给硬件控制器分配不同的软件别名;
为了和其他 Jetson 系列(如 Xavier)保持兼容,在jetson-io配置工具中会给它们分配不同的编号: SPI0 → spi1; SPI1 → spi3

硬件手册/引脚图

jetson-io配置工具

引脚

说明

SPI0

spi1

19、21、23、24、26

-

SPI1

spi3

13、16、18、22、37

-

c1201_spi_21
c1201_spi_13.0

接口实验

小技巧

本章节实验中使用C1201载板的40Pin中的SPI接口进行测试

  • 实验1:测试SPI总线的回环读写;

  • 实验2:使用SPI总线读取一个RFID的电子标签;

  • 实验3:使用SPI总线读取一个气压和温度传感器;

请前置安装必要的依赖,如下:

sudo pip3 install spidev Jetson.GPIO

实验1:回环实验

  1. 通过Jetson-io设置spi引脚

c1201_spi_13

c1201_spi_14

  1. 检查spi状态

ls /dev/spi* 
/dev/spidev0.0 /dev/spidev0.1 /dev/spidev1.0 /dev/spidev1.1

spi设备节点状态正常

注意

spidev0.0 / spidev0.1:属于硬件引脚编号的同一总线 spi0,对应两个片选 CS0 和 CS1

spidev1.0 / spidev1.1:属于硬件引脚编号的同一总线 spi1,对应两个片选 CS0 和 CS1

  1. spi接口回环测试

用杜邦线短接 MOSI(Pin 19)和 MISO(Pin 21)

采用python脚本测试:

#!/usr/bin/env python3
import spidev
# 初始化SPI
spi = spidev.SpiDev()
spi.open(0, 0)  # 打开总线0,设备0 (即 /dev/spidev0.0)
spi.max_speed_hz = 1000000  # 设置SPI速度(1MHz)
spi.mode = 0b00  # 设置SPI模式为0(CPOL=0, CPHA=0)
# 准备测试数据
test_data = [0xAA, 0xBB, 0xCC]  # 可以任意更改
# 发送并接收数据
received_data = spi.xfer2(test_data)
# 打印结果
print(f"发送的数据: {[hex(x) for x in test_data]}")
print(f"接收的数据: {[hex(x) for x in received_data]}")
# 判断是否成功
if test_data == received_data:
    print("✅ SPI回环测试成功!")
else:
    print("❌ SPI回环测试失败!")
spi.close()
  1. 实验结果

在命令终端中看到脚本运行出现如下结果时,即说明SPI回环测试成功

sudo python3 rfid_loop_test.py
发送的数据: ['0xaa', '0xbb', '0xcc']
接收的数据: ['0xaa', '0xbb', '0xcc'] SPI回环测试成功!

实验2:RFID读卡实验

小技巧

本章节实验中使用C1201载板的40Pin中的SPI3接口读取一个RFID的电子标签设备MFRC-522

MFRC-522 RFID射频IC卡感应模块

MFRC-522是应用于13.56MHz非接触式通信中高集成度的读写卡芯片。 MF RC522利用了先进的调制和解调概念,集成了在13.56MHz下所有类型的被动非接触式通信方式和协议。支持14443A兼容应答器信号。 数字部分处理ISO14443A帧和错误检测。此外,还支持快速CRYPTO1加密算法,用于验证MIFARE系列产品。MFRC522支持MIFARE系列更高速的非接触式通信,双向数据传输速率高达424kbit/s。 它与主机间通信采用SPI模式。

c1201_spi_11

电气参数

  • 工作电流:13~26mA/直流3.3V

  • 空闲电流:10~13mA/直流3.3V

  • 休眠电流:<80uA

  • 峰值电流:<30mA

  • 工作频率:13.56MHz

引脚接口

alt text

  • SDA:片选

  • SCK:时钟

  • MOSI:主设备输出

  • MISO:主设备输入

  • GND:接地

  • RST:复位

  • VCC:3.3v电压输入

  • IRQ:中断引脚

实验步骤

  1. spi接口设置

使用jetson-io脚本设置C1201的40Pin的spi3接口

c1201_spi_15

c1201_spi_16

  1. 硬件连线

MFRC-522

C1201.Pin40排针引脚编号

说明

SDA

pin18 (SPI1_CS0)

-

SCK

pin13 (SPI1_SCK)

-

MOSI

pin37 (SPI1_MOSI)

-

MISO

pin22 (SPI1_MISO)

-

GND

pin6 (GND)

-

RST

pin31 (GPIO)

-

VCC

pin1(3.3v)

-

IRQ

无需连接

-

  1. 安装必要的工具包

sudo apt update
sudo apt install -y python3-pip python3-dev spi-tools

安装mfrc522的Python支持库

sudo pip3 install mfrc522
  1. 读取RFID数据的Python代码

#!/usr/bin/env python3
"""
Orin Nano RC522 RFID读取脚本
硬件连接:
RC522模块 -> Orin Nano引脚:
- SDA  -> Pin 18 (SPI3_CS0)
- SCK  -> Pin 13 (SPI3_SCK)
- MOSI -> Pin 37 (SPI3_DOUT)
- MISO -> Pin 22 (SPI3_DIN)
- GND  -> Pin 6 (GND)
- RST  -> Pin 31 (GPIO)
- VCC  -> Pin 1 (3.3V)
"""

import time
import spidev
import Jetson.GPIO as GPIO

# 引脚定义 (BOARD模式,物理引脚编号)
RST_PIN = 31    # RC522复位引脚

# RC522寄存器定义
class MFRC522:
    # 命令定义
    PCD_IDLE = 0x00
    PCD_AUTHENT = 0x0E
    PCD_RECEIVE = 0x08
    PCD_TRANSMIT = 0x04
    PCD_TRANSCEIVE = 0x0C
    PCD_RESETPHASE = 0x0F
    PCD_CALCCRC = 0x03
    
    # 寄存器定义
    CommandReg = 0x01
    ComIEnReg = 0x02
    DivIEnReg = 0x03
    ComIrqReg = 0x04
    DivIrqReg = 0x05
    ErrorReg = 0x06
    Status1Reg = 0x07
    Status2Reg = 0x08
    FIFODataReg = 0x09
    FIFOLevelReg = 0x0A
    WaterLevelReg = 0x0B
    ControlReg = 0x0C
    BitFramingReg = 0x0D
    CollReg = 0x0E
    ModeReg = 0x11
    TxModeReg = 0x12
    RxModeReg = 0x13
    TxControlReg = 0x14
    TxASKReg = 0x15
    TxSelReg = 0x16
    RxSelReg = 0x17
    RxThresholdReg = 0x18
    DemodReg = 0x19
    MifareReg = 0x1C
    SerialSpeedReg = 0x1F
    CRCResultRegH = 0x21
    CRCResultRegL = 0x22
    ModWidthReg = 0x24
    RFCfgReg = 0x26
    GsNReg = 0x27
    CWGsCfgReg = 0x28
    ModGsCfgReg = 0x29
    TModeReg = 0x2A
    TPrescalerReg = 0x2B
    TReloadRegH = 0x2C
    TReloadRegL = 0x2D
    TCounterValueRegH = 0x2E
    TCounterValueRegL = 0x2F
    TestSel1Reg = 0x30
    TestSel2Reg = 0x31
    TestPinEnReg = 0x32
    TestPinValueReg = 0x33
    TestBusReg = 0x34
    AutoTestReg = 0x35
    VersionReg = 0x36
    AnalogTestReg = 0x37
    TestDAC1Reg = 0x38
    TestDAC2Reg = 0x39
    TestADCReg = 0x3A
    
    def __init__(self, bus=2, device=0, speed=1000000):
        """初始化RC522模块"""
        # 初始化GPIO
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(RST_PIN, GPIO.OUT)
        
        # 初始化SPI
        self.spi = spidev.SpiDev()
        
        # 尝试打开SPI设备 (根据您的ls输出,可能是spidev2.0)
        spi_devices = [(2, 0), (1, 0), (0, 0)]  # 尝试SPI2, SPI1, SPI0
        spi_opened = False
        
        for bus_try, device_try in spi_devices:
            try:
                self.spi.open(bus_try, device_try)
                self.spi.max_speed_hz = speed
                self.spi.mode = 0  # SPI模式0
                self.spi.bits_per_word = 8
                spi_opened = True
                print(f"成功打开SPI设备: /dev/spidev{bus_try}.{device_try}")
                break
            except Exception as e:
                print(f"无法打开/dev/spidev{bus_try}.{device_try}: {e}")
        
        if not spi_opened:
            raise Exception("无法打开任何SPI设备,请检查SPI配置")
        
        # 初始化RC522
        self.init()
    
    def write_register(self, address, value):
        """写入寄存器"""
        # RC522 SPI通信格式: MSB=0表示写,后跟7位地址
        address = (address << 1) & 0x7E
        self.spi.xfer([address, value])
    
    def read_register(self, address):
        """读取寄存器"""
        # RC522 SPI通信格式: MSB=1表示读,后跟7位地址
        address = ((address << 1) & 0x7E) | 0x80
        result = self.spi.xfer([address, 0])
        return result[1]
    
    def set_bitmask(self, address, mask):
        """设置寄存器位掩码"""
        current = self.read_register(address)
        self.write_register(address, current | mask)
    
    def clear_bitmask(self, address, mask):
        """清除寄存器位掩码"""
        current = self.read_register(address)
        self.write_register(address, current & (~mask))
    
    def init(self):
        """初始化RC522模块"""
        print("初始化RC522模块...")
        
        # 复位RC522
        self.reset()
        
        # 关闭CRC
        self.write_register(self.TxModeReg, 0x00)
        self.write_register(self.RxModeReg, 0x00)
        
        # 设置定时器
        self.write_register(self.TModeReg, 0x80)
        self.write_register(self.TPrescalerReg, 0xA9)
        self.write_register(self.TReloadRegL, 0xE8)
        self.write_register(self.TReloadRegH, 0x03)
        
        # 设置调制宽度
        self.write_register(self.TxASKReg, 0x40)
        self.write_register(self.ModeReg, 0x3D)
        
        # 启用天线
        self.set_bitmask(self.TxControlReg, 0x03)
        
        # 检查版本
        version = self.read_register(self.VersionReg)
        print(f"RC522版本: 0x{version:02X}")
        
        if version == 0x91:
            print("检测到MFRC522 v1.0")
        elif version == 0x92:
            print("检测到MFRC522 v2.0")
        else:
            print("未知RC522版本")
        
        print("RC522初始化完成")
    
    def reset(self):
        """复位RC522模块"""
        # 通过GPIO复位
        GPIO.output(RST_PIN, GPIO.LOW)
        time.sleep(0.001)
        GPIO.output(RST_PIN, GPIO.HIGH)
        time.sleep(0.05)
        
        # 软件复位
        self.write_register(self.CommandReg, self.PCD_RESETPHASE)
        time.sleep(0.05)
        
        # 等待复位完成
        while self.read_register(self.CommandReg) & (1 << 4):
            time.sleep(0.01)
    
    def antenna_on(self):
        """开启天线"""
        value = self.read_register(self.TxControlReg)
        if ~(value & 0x03):
            self.set_bitmask(self.TxControlReg, 0x03)
    
    def antenna_off(self):
        """关闭天线"""
        self.clear_bitmask(self.TxControlReg, 0x03)
    
    def to_card(self, command, send_data):
        """与卡片通信"""
        back_data = []
        back_length = 0
        status = 0
        irq_en = 0x00
        wait_irq = 0x00
        last_bits = None
        n = 0
        
        if command == self.PCD_AUTHENT:
            irq_en = 0x12
            wait_irq = 0x10
        if command == self.PCD_TRANSCEIVE:
            irq_en = 0x77
            wait_irq = 0x30
        
        self.write_register(self.ComIEnReg, irq_en | 0x80)
        self.clear_bitmask(self.ComIrqReg, 0x80)
        self.set_bitmask(self.FIFOLevelReg, 0x80)
        
        self.write_register(self.CommandReg, self.PCD_IDLE)
        
        # 写入数据到FIFO
        for i in range(len(send_data)):
            self.write_register(self.FIFODataReg, send_data[i])
        
        self.write_register(self.CommandReg, command)
        
        if command == self.PCD_TRANSCEIVE:
            self.set_bitmask(self.BitFramingReg, 0x80)
        
        # 等待命令完成
        i = 2000
        while True:
            n = self.read_register(self.ComIrqReg)
            i -= 1
            if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)):
                break
        
        self.clear_bitmask(self.BitFramingReg, 0x80)
        
        if i != 0:
            if (self.read_register(self.ErrorReg) & 0x1B) == 0x00:
                status = 1
                
                if n & irq_en & 0x01:
                    status = 0
                elif command == self.PCD_TRANSCEIVE:
                    n = self.read_register(self.FIFOLevelReg)
                    last_bits = self.read_register(self.ControlReg) & 0x07
                    if last_bits != 0:
                        back_length = (n - 1) * 8 + last_bits
                    else:
                        back_length = n * 8
                    
                    if n == 0:
                        n = 1
                    if n > 16:
                        n = 16
                    
                    # 读取接收到的数据
                    for i in range(n):
                        back_data.append(self.read_register(self.FIFODataReg))
            else:
                status = 0
        
        return status, back_data, back_length
    
    def request(self, req_mode=0x26):
        """寻卡请求"""
        self.write_register(self.BitFramingReg, 0x07)
        
        tag_type = [req_mode]
        status, back_data, back_bits = self.to_card(self.PCD_TRANSCEIVE, tag_type)
        
        if (status != 0) and (back_bits == 0x10):
            print("检测到卡片")
            return 1, back_data[0]
        else:
            print("未检测到卡片")
            return 0, []
    
    def anticoll(self):
        """防冲突,获取卡片序列号"""
        ser_num_check = 0
        ser_num = []
        
        self.write_register(self.BitFramingReg, 0x00)
        
        ser_num.append(0x93)
        ser_num.append(0x20)
        
        status, back_data, back_bits = self.to_card(self.PCD_TRANSCEIVE, ser_num)
        
        if status == 1:
            if len(back_data) == 5:
                for i in range(4):
                    ser_num_check = ser_num_check ^ back_data[i]
                
                if ser_num_check != back_data[4]:
                    status = 0
            else:
                status = 0
        
        return status, back_data
    
    def calculate_crc(self, data):
        """计算CRC校验值"""
        self.clear_bitmask(self.DivIrqReg, 0x04)
        self.set_bitmask(self.FIFOLevelReg, 0x80)
        
        for i in range(len(data)):
            self.write_register(self.FIFODataReg, data[i])
        
        self.write_register(self.CommandReg, self.PCD_CALCCRC)
        
        i = 0xFF
        while True:
            n = self.read_register(self.DivIrqReg)
            i -= 1
            if not ((i != 0) and not (n & 0x04)):
                break
        
        result = []
        result.append(self.read_register(self.CRCResultRegL))
        result.append(self.read_register(self.CRCResultRegH))
        
        return result
    
    def select_tag(self, ser_num):
        """选择卡片"""
        buf = []
        buf.append(0x93)
        buf.append(0x70)
        
        for i in range(5):
            buf.append(ser_num[i])
        
        crc = self.calculate_crc(buf)
        buf.append(crc[0])
        buf.append(crc[1])
        
        status, back_data, back_length = self.to_card(self.PCD_TRANSCEIVE, buf)
        
        if (status == 1) and (back_length == 0x18):
            print("卡片选择成功")
            return back_data[0]
        else:
            print("卡片选择失败")
            return 0
    
    def auth(self, auth_mode, block_addr, sector_key, ser_num):
        """验证卡片扇区"""
        buff = []
        
        # 验证指令+块地址+密钥+序列号
        buff.append(auth_mode)
        buff.append(block_addr)
        
        for i in range(len(sector_key)):
            buff.append(sector_key[i])
        
        for i in range(4):
            buff.append(ser_num[i])
        
        status, back_data, back_length = self.to_card(self.PCD_AUTHENT, buff)
        
        if not(status):
            print("验证失败")
        else:
            print("验证成功")
        
        return status
    
    def read(self, block_addr):
        """读取数据块"""
        recv_data = []
        recv_data.append(0x30)
        recv_data.append(block_addr)
        crc = self.calculate_crc(recv_data)
        recv_data.append(crc[0])
        recv_data.append(crc[1])
        
        status, back_data, back_length = self.to_card(self.PCD_TRANSCEIVE, recv_data)
        
        if not(status):
            print("读取失败")
        else:
            if len(back_data) == 16:
                print("读取成功")
                return back_data
            else:
                print("数据长度错误")
        
        return None
    
    def write(self, block_addr, write_data):
        """写入数据块"""
        buff = []
        buff.append(0xA0)
        buff.append(block_addr)
        crc = self.calculate_crc(buff)
        buff.append(crc[0])
        buff.append(crc[1])
        
        status, back_data, back_length = self.to_card(self.PCD_TRANSCEIVE, buff)
        
        if not(status) or (back_length != 4) or ((back_data[0] & 0x0F) != 0x0A):
            print("写入准备失败")
            return False
        
        print("开始写入数据...")
        
        # 发送数据
        buff = []
        for i in range(16):
            buff.append(write_data[i])
        
        crc = self.calculate_crc(buff)
        buff.append(crc[0])
        buff.append(crc[1])
        
        status, back_data, back_length = self.to_card(self.PCD_TRANSCEIVE, buff)
        
        if not(status) or (back_length != 4) or ((back_data[0] & 0x0F) != 0x0A):
            print("写入失败")
            return False
        
        print("写入成功")
        return True
    
    def read_card_uid(self):
        """读取卡片UID(主要功能)"""
        print("等待RFID卡片靠近...")
        
        # 寻卡请求
        status, tag_type = self.request()
        
        if status != 1:
            return None
        
        # 防冲突,获取序列号
        status, uid_data = self.anticoll()
        
        if status == 1:
            # 将UID转换为十六进制字符串
            uid_hex = ''.join([f'{x:02X}' for x in uid_data[:4]])
            return uid_data[:4], uid_hex
        else:
            return None
    
    def cleanup(self):
        """清理资源"""
        print("清理RC522资源...")
        try:
            # 关闭天线
            self.antenna_off()
            # 关闭SPI
            self.spi.close()
        except Exception as e:
            print(f"清理资源时出错: {e}")
        finally:
            # 清理GPIO
            GPIO.cleanup()
        print("资源清理完成")

def main():
    """主函数"""
    rc522 = None
    
    try:
        print("=" * 50)
        print("Orin Nano RC522 RFID读取程序")
        print("=" * 50)
        
        # 初始化RC522
        rc522 = MFRC522()
        
        print("\n✅ RC522模块初始化成功!")
        print("请将RFID卡片靠近读卡器...")
        print("按Ctrl+C退出程序\n")
        
        card_count = 0
        
        while True:
            # 尝试读取卡片UID
            result = rc522.read_card_uid()
            
            if result is not None:
                uid_data, uid_hex = result
                card_count += 1
                
                print(f"\n🎉 检测到RFID卡片 #{card_count}")
                print(f"卡片UID: {uid_hex}")
                print(f"字节数据: {[f'0x{x:02X}' for x in uid_data]}")
                print(f"十进制: {[int(x) for x in uid_data]}")
                print("-" * 30)
                
                # 短暂延时,避免重复读取同一张卡
                time.sleep(2)
            else:
                # 没有检测到卡片,短暂延时后继续
                time.sleep(0.1)
                
    except KeyboardInterrupt:
        print("\n\n程序被用户中断")
    except Exception as e:
        print(f"\n❌ 发生错误: {e}")
        import traceback
        traceback.print_exc()
    finally:
        if rc522 is not None:
            rc522.cleanup()
        print("程序退出")

if __name__ == "__main__":
    # 检查权限
    import os
    if os.geteuid() != 0:
        print("注意:此程序需要sudo权限运行")
        print("请使用: sudo python3 rc522_reader.py")
        exit(1)
    
    main()
  1. 实验结果

c1201_spi_17

实验3:气压温度传感器

小技巧

本章节实验中使用C1201载板的40Pin中的SPI1接口读取一个SPI总线的大气压强传感器GY-BMP280

GY-BMP280大气压强传感器模块

c1201_spi_12

电气参数

  • 工作电压:1.7~3.6V DC

  • 温度测量范围:0℃~65℃

  • 温度测量误差:±0.5℃ MAX±1℃;分辨率0.1℃

  • 相对湿度测量范围:0%~100% 湿度响应时间:大于1s

  • 相对湿度测量误差:±2%;湿度迟滞:±1%;分辨率0.8%

  • 气压测量范围:300~1100hPa(百帕斯卡)

  • 气压测量误差:±1hPa 分辨率0.18Pa

  • 数字接口类型:I2C(从模式3.4MHz)或SPI(3线或4线制,从模式10MHz)

  • 尺寸:15.4mm(长)*11.6mm(宽)*2.4mm(高)

  • 带M3固定螺丝孔。方便安装及固定。

引脚接口

alt text

  • VCC: 电源

  • GND: 地

  • SCL: I2C/SPI串行时钟线

  • SDA:12C串行数据线/3线SPI串行数据输入/输出端口/4线SPI串行数据输入端口

  • CSB:片选引脚,接高电平(默认)为I2C通信接口,低电平为SPI通信接口

  • SDO: I2C地址选择位/4线SPI串行输出端口

实验步骤

  1. SPI1接口设置 使用Jetson-IO脚本启用40Pin的SPI1接口

c1201_spi_18

c1201_spi_19

  1. 硬件连线

GY-BMP280

C1201.Pin40排针引脚编号

说明

VCC

pin1(3.3v)

-

GND

pin6(GND)

-

SCL

pin23 (SPI0_SCK)

SPI串行时钟线

SDA

pin19 (SPI0_MOSI)

SPI串行数据输入端口

CSB

pin24 (SPI0_CS0)

-

SDO

pin21 (SPI0_MISO)

-

  1. 安装依赖库

sudo pip3 install spidev Jetson.GPIO
  1. 读取传感器数据脚本代码

#!/usr/bin/env python3
"""
GY-BMP280大气压强传感器SPI0接口读取脚本 - 滚动显示版本
每秒自动新增一条数据,在终端上滚动显示
"""

import time
import spidev
import Jetson.GPIO as GPIO
import os
import sys

# 引脚定义 (基于BOARD模式,物理引脚编号)
CSB_PIN = 24  # CSB片选引脚,必须设置为低电平启用SPI模式

# BMP280寄存器地址定义
BMP280_CHIP_ID = 0xD0
BMP280_RESET = 0xE0
BMP280_STATUS = 0xF3 
BMP280_CTRL_MEAS = 0xF4
BMP280_CONFIG = 0xF5
BMP280_PRESS_MSB = 0xF7
BMP280_PRESS_LSB = 0xF8
BMP280_PRESS_XLSB = 0xF9
BMP280_TEMP_MSB = 0xFA
BMP280_TEMP_LSB = 0xFB
BMP280_TEMP_XLSB = 0xFC

# 校准参数寄存器地址
BMP280_DIG_T1 = 0x88
BMP280_DIG_T2 = 0x8A
BMP280_DIG_T3 = 0x8C
BMP280_DIG_P1 = 0x8E
BMP280_DIG_P2 = 0x90
BMP280_DIG_P3 = 0x92
BMP280_DIG_P4 = 0x94
BMP280_DIG_P5 = 0x96
BMP280_DIG_P6 = 0x98
BMP280_DIG_P7 = 0x9A
BMP280_DIG_P8 = 0x9C
BMP280_DIG_P9 = 0x9E

class BMP280_SPI0:
    """BMP280 SPI0接口驱动类"""
    
    def __init__(self):
        """初始化BMP280传感器"""
        print("初始化BMP280传感器...")
        
        # 初始化GPIO
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(CSB_PIN, GPIO.OUT)
        
        # 重要: CSB引脚必须设置为低电平启用SPI模式
        GPIO.output(CSB_PIN, GPIO.LOW)
        print("✅ CSB引脚设置为低电平,启用SPI模式")
        
        # 初始化SPI - 使用SPI0总线
        self.spi = spidev.SpiDev()
        
        try:
            # 使用SPI0总线,设备0
            self.spi.open(0, 0)
            self.spi.max_speed_hz = 1000000
            self.spi.mode = 0
            self.spi.bits_per_word = 8
            self.spi.lsbfirst = False
            print("✅ 成功打开SPI设备: /dev/spidev0.0")
        except Exception as e:
            print(f"❌ 无法打开SPI0设备: {e}")
            raise e
        
        # 初始化BMP280
        self.init_bmp280()
        print("✅ BMP280传感器初始化完成")
    
    def write_register(self, register, value):
        """写入寄存器"""
        tx_data = [register & 0x7F, value]
        self.spi.xfer(tx_data)
    
    def read_register(self, register):
        """读取寄存器"""
        tx_data = [register | 0x80, 0x00]
        rx_data = self.spi.xfer(tx_data)
        return rx_data[1]
    
    def read_registers(self, register, length):
        """连续读取多个寄存器"""
        tx_data = [register | 0x80] + [0x00] * length
        rx_data = self.spi.xfer(tx_data)
        return rx_data[1:]
    
    def init_bmp280(self):
        """初始化BMP280传感器"""
        print("开始初始化BMP280传感器...")
        
        # 检查芯片ID
        chip_id = self.read_register(BMP280_CHIP_ID)
        print(f"读取芯片ID: 0x{chip_id:02X}")
        
        if chip_id != 0x58:
            raise Exception(f"错误的芯片ID: 0x{chip_id:02X},预期: 0x58")
        
        print(f"✅ 检测到BMP280传感器 (芯片ID: 0x{chip_id:02X})")
        
        # 读取校准参数
        self.read_calibration_data()
        
        # 软复位
        self.write_register(BMP280_RESET, 0xB6)
        time.sleep(0.01)
        
        # 等待传感器就绪
        while self.read_register(BMP280_STATUS) & 0x01:
            time.sleep(0.01)
        
        # 配置传感器
        ctrl_meas = (0x02 << 5) | (0x02 << 2) | 0x03
        self.write_register(BMP280_CTRL_MEAS, ctrl_meas)
        
        config = (0x05 << 5) | (0x04 << 2) | 0x00
        self.write_register(BMP280_CONFIG, config)
        
        print("✅ BMP280配置完成")
    
    def read_calibration_data(self):
        """读取校准参数"""
        print("读取BMP280校准参数...")
        
        self.dig_T1 = self.read_uint16(BMP280_DIG_T1)
        self.dig_T2 = self.read_int16(BMP280_DIG_T2)
        self.dig_T3 = self.read_int16(BMP280_DIG_T3)
        
        self.dig_P1 = self.read_uint16(BMP280_DIG_P1)
        self.dig_P2 = self.read_int16(BMP280_DIG_P2)
        self.dig_P3 = self.read_int16(BMP280_DIG_P3)
        self.dig_P4 = self.read_int16(BMP280_DIG_P4)
        self.dig_P5 = self.read_int16(BMP280_DIG_P5)
        self.dig_P6 = self.read_int16(BMP280_DIG_P6)
        self.dig_P7 = self.read_int16(BMP280_DIG_P7)
        self.dig_P8 = self.read_int16(BMP280_DIG_P8)
        self.dig_P9 = self.read_int16(BMP280_DIG_P9)
        
        print("✅ 校准参数读取完成")
    
    def read_uint16(self, register):
        """读取16位无符号整数 (小端序)"""
        data = self.read_registers(register, 2)
        return data[0] | (data[1] << 8)
    
    def read_int16(self, register):
        """读取16位有符号整数 (小端序)"""
        data = self.read_registers(register, 2)
        value = data[0] | (data[1] << 8)
        if value > 32767:
            value -= 65536
        return value
    
    def read_raw_data(self):
        """读取原始温度和气压数据"""
        # 读取气压数据 (20位)
        press_data = self.read_registers(BMP280_PRESS_MSB, 3)
        raw_pressure = (press_data[0] << 12) | (press_data[1] << 4) | (press_data[2] >> 4)
        
        # 读取温度数据 (20位)
        temp_data = self.read_registers(BMP280_TEMP_MSB, 3)
        raw_temperature = (temp_data[0] << 12) | (temp_data[1] << 4) | (temp_data[2] >> 4)
        
        return raw_temperature, raw_pressure
    
    def compensate_temperature(self, raw_temperature):
        """补偿温度计算 (返回摄氏度)"""
        var1 = ((raw_temperature >> 3) - (self.dig_T1 << 1)) * self.dig_T2 >> 11
        var2 = (((raw_temperature >> 4) - self.dig_T1) * ((raw_temperature >> 4) - self.dig_T1) >> 12) * self.dig_T3 >> 14
        t_fine = var1 + var2
        temperature = (t_fine * 5 + 128) >> 8
        return temperature / 100.0, t_fine
    
    def compensate_pressure(self, raw_pressure, t_fine):
        """补偿气压计算 (返回hPa)"""
        var1 = t_fine - 128000
        var2 = var1 * var1 * self.dig_P6
        var2 = var2 + ((var1 * self.dig_P5) << 17)
        var2 = var2 + (self.dig_P4 << 35)
        var1 = ((var1 * var1 * self.dig_P3) >> 8) + ((var1 * self.dig_P2) << 12)
        var1 = ((1 << 47) + var1) * self.dig_P1 >> 33
        
        if var1 == 0:
            return 0
        
        p = 1048576 - raw_pressure
        p = (((p << 31) - var2) * 3125) // var1
        var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25
        var2 = (self.dig_P8 * p) >> 19
        
        p = ((p + var1 + var2) >> 8) + (self.dig_P7 << 4)
        return p / 25600.0
    
    def calculate_altitude(self, pressure, sea_level_pressure=1013.25):
        """根据气压计算海拔高度"""
        altitude = 44330.0 * (1.0 - pow(pressure / sea_level_pressure, 0.1903))
        return altitude
    
    def read_sensor_data(self):
        """读取完整的传感器数据"""
        try:
            raw_temperature, raw_pressure = self.read_raw_data()
            temperature, t_fine = self.compensate_temperature(raw_temperature)
            pressure = self.compensate_pressure(raw_pressure, t_fine)
            altitude = self.calculate_altitude(pressure)
            
            return {
                'temperature': temperature,
                'pressure': pressure,
                'altitude': altitude
            }
        except Exception as e:
            print(f"读取传感器数据错误: {e}")
            return None
    
    def cleanup(self):
        """清理资源"""
        try:
            self.write_register(BMP280_CTRL_MEAS, 0x00)
            self.spi.close()
        except Exception as e:
            print(f"清理资源时出错: {e}")
        finally:
            GPIO.cleanup()

def clear_screen():
    """清屏函数"""
    os.system('clear')

def display_data_table(data_history, max_display_lines=20):
    """显示数据表格,支持滚动显示"""
    clear_screen()
    
    print("=" * 70)
    print("GY-BMP280大气压强传感器实时数据监测 (滚动显示模式)")
    print("=" * 70)
    print("硬件连接: SPI0总线 | 每秒自动更新 | 按Ctrl+C退出")
    print("=" * 70)
    
    # 显示表头
    print(f"{'序号':<6} {'时间(s)':<10} {'温度(°C)':<12} {'气压(hPa)':<14} {'海拔(m)':<12}")
    print("-" * 70)
    
    # 显示最新数据(最多显示max_display_lines行)
    start_idx = max(0, len(data_history) - max_display_lines)
    for i in range(start_idx, len(data_history)):
        data = data_history[i]
        seq_str = f"{i+1:<6}"
        time_str = f"{data['elapsed_time']:05.1f}"
        temp_str = f"{data['temperature']:.2f} °C"
        pressure_str = f"{data['pressure']:.2f} hPa"
        altitude_str = f"{data['altitude']:.2f} m"
        
        print(f"{seq_str} {time_str:<10} {temp_str:<12} {pressure_str:<14} {altitude_str:<12}")
    
    # 显示统计信息
    if data_history:
        latest = data_history[-1]
        print("-" * 70)
        print(f"最新数据: 温度 {latest['temperature']:.2f}°C | 气压 {latest['pressure']:.2f}hPa | 海拔 {latest['altitude']:.2f}m")
        print(f"数据统计: 总记录 {len(data_history)} 条 | 运行时间 {latest['elapsed_time']:.1f} 秒")
    
    print("=" * 70)

def main():
    """主函数"""
    bmp280 = None
    data_history = []  # 存储历史数据
    max_display_lines = 15  # 最大显示行数
    
    try:
        # 初始化BMP280传感器
        bmp280 = BMP280_SPI0()
        
        print("\n✅ BMP280传感器准备就绪!")
        print("开始读取传感器数据(每秒自动新增一条,滚动显示)...")
        print("按Ctrl+C退出程序")
        time.sleep(2)  # 给用户时间阅读提示信息
        
        read_count = 0
        start_time = time.time()
        
        while True:
            try:
                current_time = time.time()
                elapsed_time = current_time - start_time
                
                # 读取传感器数据
                sensor_data = bmp280.read_sensor_data()
                
                if sensor_data is not None:
                    # 添加时间戳并保存到历史数据
                    data_record = {
                        'elapsed_time': elapsed_time,
                        'temperature': sensor_data['temperature'],
                        'pressure': sensor_data['pressure'],
                        'altitude': sensor_data['altitude'],
                        'timestamp': current_time
                    }
                    data_history.append(data_record)
                    
                    read_count += 1
                    
                    # 更新显示
                    display_data_table(data_history, max_display_lines)
                    
                    # 每秒更新一次
                    next_update = current_time + 1.0
                    sleep_time = next_update - time.time()
                    if sleep_time > 0:
                        time.sleep(sleep_time)
                else:
                    # 读取失败时显示错误信息
                    clear_screen()
                    print("❌ 读取传感器数据失败,尝试重新读取...")
                    time.sleep(1)
                
            except KeyboardInterrupt:
                break
            except Exception as e:
                clear_screen()
                print(f"❌ 读取数据时出错: {e}")
                time.sleep(1)
        
        # 计算统计信息
        total_time = time.time() - start_time
        avg_rate = read_count / total_time if total_time > 0 else 0
        
        clear_screen()
        print("=" * 70)
        print("数据读取完成统计")
        print("=" * 70)
        print(f"总读取次数: {read_count}")
        print(f"总运行时间: {total_time:.1f} 秒")
        print(f"平均读取频率: {avg_rate:.2f} Hz")
        print("=" * 70)
        
    except KeyboardInterrupt:
        print("\n\n程序被用户中断")
    except Exception as e:
        print(f"\n❌ 发生错误: {e}")
        import traceback
        traceback.print_exc()
    finally:
        if bmp280 is not None:
            bmp280.cleanup()
        print("程序退出")

if __name__ == "__main__":
    # 检查权限
    if os.geteuid() != 0:
        print("注意:此程序需要sudo权限运行")
        print("请使用: sudo python3 bmp280_rolling_display.py")
        exit(1)
    
    # 检查依赖库
    try:
        import spidev
        import Jetson.GPIO as GPIO
    except ImportError as e:
        print(f"❌ 缺少依赖库: {e}")
        print("请安装: sudo pip3 install spidev Jetson.GPIO")
        exit(1)
    
    main()
  1. 实验结果

c1201_spi_20