串行通信——SPI总结

一.什么是SPI通信接口?

SPI是串行外设接口(Serial Peripheral Interface)的缩写。是美国摩托罗拉公司(Motorola)最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、同步通信总线,所以可以在同一时间发送和接收数据,SPI没有定义速度限制,通常能达到甚至超过10M/bps。

SPI有主、从两种模式,通常由一个主模块和一个或多个从模块组成(SPI不支持多主机),主模块选择一个从模块进行同步通信,从而完成数据的交换。提供时钟的为主设备(Master),接收时钟的设备为从设备(Slave),SPI接口的读写操作,都是由主设备发起,当存在多个从设备时,通过各自的片选信号进行管理。

二.SPI硬件引脚介绍

SPI通信原理很简单,需要至少4根线,单向传输时3根线,它们是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)和CS/SS(片选):

  • MISO( Master Input Slave Output)简称主入从出信号线:主设备数据输入,从设备数据输出;
  • MOSI(Master Output Slave Input)简称主出从入信号线:主设备数据输出,从设备数据输入;
  • SCLK(Serial Clock)串行时钟:为 SPI 通信提供时钟;时钟信号,由主设备产生;
  • CS/SS(Chip Select/Slave Select)为片选信号线:为 SPI 通信提供时钟;从设备使能信号,由主设备控制,一主多从时,CS/SS是从芯片是否被主芯片选中的控制信号,只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效。

SPI 通信都是由主机发起的,主机需要提供通信的时钟信号。主机通过 SPI 线连接多个从设备的结构,如下图所示:

SPI主设备和从设备都有一个串行移位寄存器,主设备通过向它的SPI串行寄存器写入一个字节来发起一次传输。

三.SPI通信过程

SPI数据通信的流程可以分为以下几步:

1、主设备发起信号,将CS/SS拉低,启动通信。

2、主设备通过发送时钟信号,来告诉从设备进行写数据或者读数据操作(采集时机可能是时钟信号的上升沿(从低到高)或下降沿(从高到低),因为SPI有四种模式,后面会讲到),它将立即读取数据线上的信号,这样就得到了一位数据(1bit)。

3、主机(Master)将要发送的数据写到发送数据缓存区(Menory),缓存区经过移位寄存器(缓存长度不一定,看单片机配置),串行移位寄存器通过MOSI信号线将字节一位一位的移出去传送给从机,同时MISO接口接收到的数据经过移位寄存器一位一位的移到接收缓存区。

4、从机(Slave)也将自己的串行移位寄存器(缓存长度不一定,看单片机配置)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。

例如,下图示例中简单模拟SPI通信流程,主机拉低NSS片选信号,启动通信,并且产生时钟信号,上升沿触发边沿信号,主机在MOSI线路一位一位发送数据0X53,在MISO线路一位一位接收数据0X46,如下图所示:

这里有一点需要着重说明一下:SPI只有主模式和从模式之分,没有读和写的说法,外设的写操作和读操作是同步完成的。若只进行写操作,主机只需忽略接收到的字节(虚拟数据);反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。

四.SPI总线时钟

SPI时钟特点主要包括:时钟速率、时钟极性和时钟相位三方面。

(一) 时钟速率

SPI总线上的主设备必须在通信开始时候配置并生成相应的时钟信号。从理论上讲,只要实际可行,时钟速率就可以是你想要的任何速率,当然这个速率受限于每个系统能提供多大的系统时钟频率,以及最大的SPI传输速率。

(二) 时钟极性

根据硬件制造商的命名规则不同,时钟极性通常写为CKP或CPOL。时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据。

CKP可以配置为1或0。这意味着你可以根据需要将时钟的默认状态(IDLE)设置为高或低。极性反转可以通过简单的逻辑逆变器实现。你必须参考设备的数据手册才能正确设置CKP和CKE。

  • CKP = 0:时钟空闲IDLE为低电平 0;
  • CKP = 1:时钟空闲IDLE为高电平1。
(三) 时钟相位

根据硬件制造商的不同,时钟相位通常写为CKE或CPHA。顾名思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿;

  • CKE = 0:在时钟信号SCK的第一个跳变沿采样;
  • CKE = 1:在时钟信号SCK的第二个跳变沿采样。

五.SPI接口优缺点

  • 优点
  1. 无起始位和停止位,因此数据位可以连续传输而不会被中断;
  2. 没有像I2C这样复杂的从设备寻址系统;
  3. 数据传输速率比I2C更高(几乎快两倍);
  4. 分离的MISO和MOSI信号线,因此可以同时发送和接收数据;
  5. 极其灵活的数据传输,不限于8位,它可以是任意大小的字;
  6. 非常简单的硬件结构。从站不需要唯一地址(与I2C不同)。从机使用主机时钟,不需要精密时钟振荡器/晶振(与UART不同)。不需要收发器(与CAN不同)。
  • 缺点
  1. 使用四根信号线(I2C和UART使用两根信号线);
  2. 无法确认是否已成功接收数据(I2C拥有此功能);
  3. 没有任何形式的错误检查,如UART中的奇偶校验位;
  4. 只允许一个主设备;
  5. 没有硬件从机应答信号(主机可能在不知情的情况下无处发送);
  6. 没有定义硬件级别的错误检查协议;
  7. 与RS-232和CAN总线相比,只能支持非常短的距离;

六.SPI总线应用示例

SPI(Serial Peripheral Interface)总线的代码示例因平台和编程语言的不同而有所差异。以下是一些不同环境下的SPI总线操作的基本代码片段:

在Linux内核驱动开发中的SPI操作示例(C语言):

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>

static struct spi_device *spi_dev;

static int __init my_spi_driver_init(void)
{
    // 定义SPI设备结构体参数
    static struct spi_board_info board_info = {
        .modalias     = "my_spi_device",
        .bus_num      = 0,          // SPI总线编号
        .chip_select  = 1,          // 片选线编号
        .max_speed_hz = 1000000,    // 最大传输速率
    };

    // 注册SPI主控制器
    spi_dev = spi_new_device(spi_busnum_to_master(0), &board_info);
    if (!spi_dev) {
        printk(KERN_ERR "Failed to register SPI device\n");
        return -ENODEV;
    }

    // 开始与SPI设备通信,如读写操作前需确保设备已初始化
    spi_setup(spi_dev);

    // 示例:向SPI设备写入数据
    static u8 tx_buf[] = {0x01, 0x02, 0x03};
    static u8 rx_buf[3];

    spi_message mesg;
    spi_transfer t = {
        .tx_buf       = tx_buf,
        .rx_buf       = rx_buf,
        .len          = sizeof(tx_buf),
        .speed_hz     = spi_dev->max_speed_hz,
        .bits_per_word= 8,
    };

    mesg.transfers = &t;
    mesg.num_transfers = 1;

    // 发送并接收数据
    int status = spi_sync(spi_dev, &mesg);
    if (status < 0)
        printk(KERN_ERR "Error in spi_sync: %d\n", status);

    return 0;
}

static void __exit my_spi_driver_exit(void)
{
    spi_unregister_device(spi_dev);
}

module_init(my_spi_driver_init);
module_exit(my_spi_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A Simple SPI Driver Example");

在Arduino或其他AVR单片机上的SPI操作示例(C++):

#include <SPI.h>

// 设置SPI模式、速度等
void setup() {
  SPI.begin(); // 初始化SPI接口
  SPI.setBitOrder(MSBFIRST); // 设置高位优先(或LSBFIRST设置低位优先)
  SPI.setDataMode(SPI_MODE0); // 设置SPI时序模式(根据器件手册选择正确的模式)
  SPI.setClockDivider(SPI_CLOCK_DIV4); // 设置SPI时钟速度,这里是CPU频率的1/4

  digitalWrite(SS_PIN, HIGH); // 如果使用硬件SS引脚,确保它为高电平以释放从设备
}

void loop() {
  // 写入并读取SPI Flash的一个字节
  byte address = 0x10; // 假设地址
  byte dataToWrite = 0xAA; // 要写入的数据

  digitalWrite(SS_PIN, LOW); // 拉低SS信号开始通信
  SPI.transfer(address); // 发送地址
  SPI.transfer(dataToWrite); // 发送数据
  digitalWrite(SS_PIN, HIGH); // 结束通信,拉高SS信号

  delayMicroseconds(10); // 等待足够时间让写操作完成(具体延迟取决于器件)

  digitalWrite(SS_PIN, LOW); // 再次启动通信以读取数据
  SPI.transfer(address | 0x80); // 对于读操作,可能需要设置特定的地址位
  byte readData = SPI.transfer(0x00); // 接收数据,同时发送一个不重要的字节
  digitalWrite(SS_PIN, HIGH); // 结束通信
  
  Serial.println(readData); // 打印读取到的数据
}
  • 39
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
stm32spi通信是指在STM32微控制器中使用SPI串行外设接口)进行通信的一种方式。SPI是一种全双工、同步的串行通信协议,适用于芯片间的高速数据传输。在STM32中,可以通过引脚配置和SPI相关的寄存器设置来实现SPI通信。 在SPI通信中,通常有一个主设备和一个或多个从设备。主设备负责发起通信并控制数据传输的时序,而从设备则根据主设备的指令进行响应。 在STM32中,可以使用相关的库函数和头文件来实现SPI通信。例如,可以使用W25Q64.h和MySPI.h这两个头文件来初始化SPI接口和定义相关的函数。其中,W25Q64.h中定义了一些函数,如W25Q64_Init()用于初始化W25Q64芯片,W25Q64_ReadID()用于读取芯片的ID,W25Q64_PageProgram()用于向芯片写入数据,W25Q64_SectorErase()用于擦除扇区,W25Q64_ReadData()用于读取数据。而MySPI.h中定义了MySPI_Init()用于初始化SPI接口,MySPI_Start()用于启动SPI传输,MySPI_Stop()用于停止SPI传输,MySPI_SwapByte()用于交换数据字节。 通过调用这些函数和使用SPI的相关寄存器,我们可以实现STM32与其他设备之间的SPI通信。例如,可以使用W25Q64芯片进行数据存储和读取,通过SPI接口与STM32进行通信。 总结起来,stm32spi通信是一种使用SPI接口进行通信的方法,通过在STM32中使用相应的库函数和头文件,我们可以初始化SPI接口并实现与其他设备之间的数据传输。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [STM32——SPI通信](https://blog.csdn.net/NRWHF/article/details/129482253)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值