SPI从原理到应用

1.简介

SPI(Serial Peripheral Interface)是一种同步串行通信协议,用于在微控制器、传感器、存储器和其他外围设备之间进行数据交换

SPI协议使用多路单向通信方式,其中包括一个主设备(通常为微控制器或主机)和一个或多个从设备(从器件)。主设备通过控制时钟信号(clk以及数据输入输出线(MISO、MOSI片选线(CS与从设备进行通信。

SPI通信基于全双工传输模式,主设备和从设备之间可以同时发送和接收数据。通信过程由主设备发起,其通过选择特定的片选线来选中从设备。数据交换通过时钟信号同步进行,主设备控制时钟的频率和极性。主设备通过MOSI(主设备输出、从设备输入)线将数据传输到从设备,从设备通过MISO(从设备输出、主设备输入)线将响应数据传输回主设备。

2.SPI传输流程

  1. 主设备选择从设备:主设备通过将片选线(SS/CS)拉低来选择要与之通信的从设备。片选线一般连接到多个从设备,通过只选中其中一个从设备与之通信。

  2. 传输数据:主设备产生时钟信号,并通过主设备输出(MOSI)线将数据发送到从设备,从设备使用从设备输出(MISO)线将响应数据返回给主设备。

  3. 时钟信号同步:主设备通过时钟信号来同步数据传输。时钟信号的频率和极性由主设备控制。根据SPI协议的配置,数据可以在时钟的上升沿或下降沿进行采样和传输。

  4. 数据传输顺序:数据在每个时钟周期内进行位传输,通常是最高位(MSB)优先。主设备和从设备在每个时钟周期传输一个位,直到完成完整的数据传输。

  5. 循环传输:SPI通信往往是循环传输(full duplex),主设备和从设备可以同时发送和接收数据。主设备发送数据的同时,从设备也可以将响应数据发送给主设备。

  6. 传输结束:当数据传输完成后,主设备通过拉高片选线来通知从设备传输结束。

3.gpio模拟spi

#include 
#include 

// 定义GPIO引脚的寄存器地址(示例)
#define GPIO_MOSI    *((volatile uint32_t*) 0x40020000)  // MOSI引脚数据寄存器
#define GPIO_MISO    *((volatile uint32_t*) 0x40020004)  // MISO引脚数据寄存器
#define GPIO_CLOCK   *((volatile uint32_t*) 0x40020008)  // 时钟引脚数据寄存器
#define GPIO_CS      *((volatile uint32_t*) 0x4002000C)  // 片选引脚数据寄存器

void spi_init() 
{
    // 初始化SPI相关GPIO引脚及其他配置
    // 例如,设置引脚方向、片选寄存器的初始状态等

    // 将MOSI和时钟引脚设置为输出模式
    GPIO_MOSI |= (1 << 0);
    GPIO_CLOCK |= (1 << 0);
    // 将MISO引脚设置为输入模式
    GPIO_MISO &= ~(1 << 0);
    // 将片选引脚设置为输出模式
    GPIO_CS |= (1 << 0);
}

void spi_transfer(uint8_t *tx_buffer, uint8_t *rx_buffer, int length) 
{
    int i, j;
    
    for (i = 0; i < length; i++) 
   {
        // 片选使能
        GPIO_CS &= ~(1 << 0);
        
        for (j = 7; j >= 0; j--) 
        {
            // 发送位数据
            GPIO_MOSI = (tx_buffer[i] >> j) & 0x01;
            
            // 上升沿触发时钟信号
            GPIO_CLOCK |= (1 << 0);
            
            // 接收位数据
            rx_buffer[i] |= (GPIO_MISO & 0x01) << j;
            
            // 下降沿触发时钟信号
            GPIO_CLOCK &= ~(1 << 0);
        }
        
        // 片选禁用
        GPIO_CS |= (1 << 0);
    }
}

int main() 
{
    uint8_t tx_buffer[4] = {0x12, 0x34, 0x56, 0x78};
    uint8_t rx_buffer[4] = {0};

    spi_init();  // 初始化SPI模拟

    spi_transfer(tx_buffer, rx_buffer, sizeof(tx_buffer));  // 进行数据传输

    // 打印接收到的数据
    for (int i = 0; i < sizeof(rx_buffer); i++) 
    {
        printf("接收到的数据[%d] = 0x%02X\n", i, rx_buffer[i]);
    }

    return 0;
}

4.linux应用层spi驱动

/**
  ******************************************************************************
  * @file    spifpdriver.c
  * @author  cj
  * @version V1.0
  * @date    2019/7
  * @brief
  ******************************************************************************
  * @attention spi应用层驱动
  ******************************************************************************
  */

#include "spiFPdriver.h"
#include "gpio.h"
#include "board.h"
#include 
#include 
#include 

#ifdef SPI_FINGER_PRINT

void SPI_SSEL(int val)
{
    spi_control(g_board.fd_gpio, 0, val);
}

void SPI_FastRead(const uint8_t adress, uint8_t *buf, const int len)
{
    int bufSize = 6144;
    int readIndex = 0;
    uint8_t tx[6144] = {0};
    memset(tx, adress, 6144);
    uint8_t rx[6144] = {0};
    while(1)
    {
        int cnt = ((len - readIndex) > bufSize) ? bufSize : (len - readIndex);
        struct spi_ioc_transfer tr =
        {
            .tx_buf = (unsigned long)tx,    //发送缓存区
            .rx_buf = (unsigned long)rx,    //接收缓存区
            .len = (unsigned int)cnt,
            .delay_usecs = 0,               //发送时间间隔
            .speed_hz = 12000000,           //总线速率
            .bits_per_word = 8,             //收发的一个字的二进制位数
        };

        if(ioctl(g_board.fd_spi, SPI_IOC_MESSAGE(1), &tr) < 1)
        {
            qDebug("SPI_FastRead error\n");
            break;
        }

        for (int i = 0; i < cnt; i ++)
        {
            if (rx[i] != 255)
            {
                *buf++ = rx[i];
                readIndex ++;
            }
        }

        if(readIndex == len)
        {
            break;
        }
    }
}

uint8_t SPI_ReadWrite(uint8_t address)
{
    uint8_t tx[1] = {address};
    uint8_t rx[1] = {0};
    struct spi_ioc_transfer tr =
    {
        .tx_buf = (unsigned long)tx,    //发送缓存区
                .rx_buf = (unsigned long)rx,    //接收缓存区
                .len = 1,
                .delay_usecs = 0,               //发送时间间隔
                .speed_hz = 12000000,           //总线速率
                .bits_per_word = 8,             //收发的一个字的二进制位数
    };
    if(ioctl(g_board.fd_spi, SPI_IOC_MESSAGE(1), &tr) < 1)
    {
        qDebug("SPI_ReadWrite error\n");
    }

    return rx[0];
}

static void GPIO_InitSet()
{
    SPI_SSEL(0);
    SPI_ReadWrite(0x4a);
    SPI_ReadWrite(0x04);
    SPI_SSEL(1);

    /* fix a leakage problem */
    SPI_SSEL(0);
    SPI_ReadWrite(0x4F);
    SPI_ReadWrite(0x01);
    SPI_SSEL(1);

    SPI_SSEL(1);
    SPI_SSEL(0);
    SPI_SSEL(1);
}

void SPIFP_driver_Init(void)
{
    /* spi init */
    g_board.fd_spi = open("/dev/spidev0.0", O_RDWR);
    if(g_board.fd_spi < 0)
    {
        printf("bsp_init():can't open spidev0.0\n");
    }

    /* gpio init */
    g_board.fd_gpio = open("/dev/gpio_ctl", O_RDWR);
    if(g_board.fd_gpio < 0)
    {
        printf("bsp_init():can't open gpio\n");
    }

    uint8_t mode = SPI_MODE_0;
    uint8_t bits = 8;
    uint32_t speed = 12000000;//max =12MHz

    if(ioctl(g_board.fd_spi, SPI_IOC_WR_MODE, &mode) == -1)
        qDebug("fpDriver_init: can't set spi mode\n");

    if(ioctl(g_board.fd_spi, SPI_IOC_RD_MODE, &mode) == -1)
        qDebug("fpDriver_init: can't get spi mode\n");

    if(ioctl(g_board.fd_spi, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1)
        qDebug("fpDriver_init: can't set bits per word\n");

    if(ioctl(g_board.fd_spi, SPI_IOC_RD_BITS_PER_WORD, &bits) == -1)
        qDebug("fpDriver_init: can't get bits per word\n");

    if(ioctl(g_board.fd_spi, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1)
        qDebug("fpDriver_init: can't set max speed hz\n");

    if(ioctl(g_board.fd_spi, SPI_IOC_RD_MAX_SPEED_HZ, &speed) == -1)
        qDebug("fpDriver_init: can't get max speed hz\n");

    GPIO_InitSet();
}

#endif

5.liunx驱动层spi.ko

#include 
#include 
#include 
#include 
#include 

#define SPI_DRIVER_DEV_NAME "spi_driver"

static int spi_driver_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
    struct spi_device* spi = filp->private_data;
    uint8_t buffer[4] = {0};

    switch (cmd) 
   {
        case SPI_DRIVER_READ:
            // 执行SPI读取逻辑
            // 使用spi_sync_transfer()等函数进行读取
            break;
        
        case SPI_DRIVER_WRITE:
            // 执行SPI写入逻辑
            // 使用spi_sync_transfer()等函数进行写入
            break;
        
        default:
            return -EINVAL;
    }

    return 0;
}

static long spi_driver_unlocked_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
    return spi_driver_ioctl(filp, cmd, arg);
}

static const struct file_operations spi_driver_fops = 
{
    .unlocked_ioctl = spi_driver_unlocked_ioctl,
};

static int spi_driver_probe(struct spi_device *spi)
{
    struct device *dev;

    // 创建设备文件
    dev = device_create(spi_driver_class, NULL, spi->dev.dev, NULL, SPI_DRIVER_DEV_NAME);
    if (IS_ERR(dev)) 
    {
        return PTR_ERR(dev);
    }

    dev_set_drvdata(&dev->dev, spi);

    return 0;
}

static int spi_driver_remove(struct spi_device *spi)
{
    struct device *dev = dev_get_drvdata(&spi->dev);
    
    device_remove_file(dev, &dev_attr_data);
    device_destroy(spi_driver_class, spi->dev.dev);

    return 0;
}

static const struct of_device_id spi_driver_of_match[] = 
{
    { .compatible = "your_device_compatible_string" }, // 根据设备兼容字符串进行匹配
    { },
};
MODULE_DEVICE_TABLE(of, spi_driver_of_match);

static struct spi_driver spi_driver = 
{
    .probe = spi_driver_probe,
    .remove = spi_driver_remove,
    .driver = 
    {
        .name = "spi_driver",
        .owner = THIS_MODULE,
        .of_match_table = spi_driver_of_match,
    },
};

static struct class *spi_driver_class;

static int __init spi_driver_init(void)
{
    int ret;

    spi_driver_class = class_create(THIS_MODULE, "spi_driver");
    if (IS_ERR(spi_driver_class)) 
    {
        return PTR_ERR(spi_driver_class);
    }

    ret = spi_register_driver(&spi_driver);
    if (ret < 0)
   {
        class_destroy(spi_driver_class);
        return ret;
    }

    return 0;
}

static void __exit spi_driver_exit(void)
{
    spi_unregister_driver(&spi_driver);
    class_destroy(spi_driver_class);
}

module_init(spi_driver_init);
module_exit(spi_driver_exit);

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

你可能感兴趣的:(LINUX,从单片机到freertos,spi)