【Linux驱动】十三、SPI编程
【Linux驱动】十三、SPI编程
本文最后更新于319 天前,其中的信息可能已经过时,如有错误请发送邮件到273925452@qq.com

SPI 接口简介

SPI(Serial Peripheral Interface)接口是全双工的同步串行通讯总线,支持通过多个不同的片选信号来连接多个外设。SPI 接口通常由四根线组成,分别是提供时钟的 SCLK,提供数据输出的 MOSI(Master Out Slave In),提供数据输入的 MISO(Master In Slave Out)和提供片选信号的 CS同一时刻只能有一个 SPI 设备处于工作状态,即多个 CS 信号中某时间只能有一个有效。为了适配不同的外设 ,SPI 支持通过寄存器来配置片选信号和时钟信号的极性和相位。(imx6ull 支持 ecspi,即增强配置型 spi,这里为了与其他兼容,统一用 spi 来称呼)。


SPI 硬件连接

SPI 支持 slave 和 master 两种模式,作为 APU 来说,多数情况下是作为master 来使用的。在 master 模式下,通过不同的片选引脚 ss[n](n=0,1,2,3)来连接多个不同的设备。下图为 MASTER 模式下的 SPI 单线通讯模式框图:


SPI 通讯数据格式

在 master 模式下,ss、sclk 和 mosi 作为信号输出接口,MISO 作为信号输入接口。通过 SS 片选信号使能外部 SPI 设备,SCLK 同步数据传输。MOSI 和MISO 信号在 SCLK 上升沿变化,在下降沿锁存数据。SPI 的具体通讯格式如下图所示(默认高位在前,低位在后),输出数据为 0xD2,输入数据为 0x66。

SPI 支持不同的 SPI 时钟和 CS 片选相位和极性设置,通过设置 POL 和 PHA值的不同来设置相位和极性。POL:表示 SPICLK 的初始电平,0 为电平,1 为高电平 ;CHA:表示相位,即第一个还是第二个时钟沿采样数据,0 为第一个时钟沿,1 为第二个时钟沿。具体如下表所示:

实际时钟和相位关系如下图所示,我们常用的是模式 0 和模式 3,因为它们都是在上升沿采样数据,不用去在乎时钟的初始电平是什么,只要在上升沿采集数据就行。POL 和 PHA 怎么选择?通常去参考外接的模块的芯片手册。


SPI 控制器

SPI(Serial Peripheral Interface)控制器是一种用于实现SPI通信协议硬件模块。SPI是一种同步串行数据传输总线,常用于微控制器和外围设备之间的高速短距离通信。它使用主从架构,其中至少一个设备作为主设备,而其他设备则作为从设备。

SPI控制器负责管理SPI总线上的数据传输,包括数据的发送和接收,以及对时钟信号的控制。在Linux系统中,SPI控制器通常通过SPI子系统来驱动,该子系统提供了一套API,使得上层软件可以与SPI控制器交互,进行配置和数据传输操作。

SPI控制器的主要功能包括:

  • 产生SPI时钟信号(SCK),用于同步数据的传输。
  • 管理从设备选择信号(SS),用于选择当前要通信的从设备。
  • 控制数据的输入输出(MOSI和MISO引脚),实现数据的串行传输。
  • 支持不同的数据模式(如CPOL和CPHA),以兼容不同的SPI设备。

在Linux驱动中,SPI控制器驱动需要注册到SPI子系统,然后应用程序或更高层的驱动可以通过SPI子系统提供的接口来访问SPI控制器,进行数据交换。


SPI总线驱动模型

SPI(Serial Peripheral Interface)总线驱动模型在Linux内核中的设计遵循了“总线设备驱动”模型,这一模型旨在提高驱动程序的可重用性和可维护性,同时简化了驱动程序的开发过程。以下是SPI总线驱动模型的基本组成部分:

SPI Devices:

这表示连接到SPI总线的实际物理设备。每个spi_device都有一个与之关联的spi_driver,用于处理特定设备的读写操作。spi_device还包含了关于设备的配置信息,如芯片选择、最大速度等。

SPI Driver:

这部分驱动处理与具体SPI设备相关的逻辑,比如MPU6050这样的传感器。它负责解释设备的数据格式、命令集和特性。spi_driver通过spi_register_driver()函数注册到内核中,这样它就可以被spi_master_driver发现并与其交互。


SPI传输函数


SPI字符设备驱动模板

spi_drv.c

#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>

/* 主设备号                                                                 */
static int major = 0;
static struct class *my_spi_class;

/* spi设备结构体数据指针,指向spi客户端的结构体 */
static struct spi_device *g_spi;

static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);

/* spi异步通知结构体指针  */
struct fasync_struct *spi_fasync;


/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t spi_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    // int err;

    /* spi_transfer, 用于表示spi通信中的一个消息,可以包含消息的地址、数据方向以及要传输的数据*/
    // struct spi_transfer msgs[2];

    /* 初始化spi_msg 
        adapter:是spi适配器的结构体指针

    */

    /* copy_to_user  */
    
    return 0;
}

static ssize_t spi_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    // int err;

    /* copy_from_user  */

    // struct spi_transfer msgs[2];

    /* spi_transfer */
    
    return 0;    
}


static unsigned int spi_drv_poll(struct file *fp, poll_table * wait)
{
    //printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    poll_wait(fp, &gpio_wait, wait);
    //return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
    return 0;
}

static int spi_drv_fasync(int fd, struct file *file, int on)
{
    if (fasync_helper(fd, file, on, &spi_fasync) >= 0)
        return 0;
    else
        return -EIO;
}


/* 定义自己的file_operations结构体                                              */
static struct file_operations spi_drv_fops = {
    .owner     = THIS_MODULE,
    .read    = spi_drv_read,
    .write   = spi_drv_write,
    .poll    = spi_drv_poll,
    .fasync  = spi_drv_fasync,
};


static int spi_drv_probe(struct spi_device *spi)
{
    // struct device_node *np = client->dev.of_node;

    /* 记录spi_devic */
    g_spi = spi;

    /* 注册字符设备 */
    /* 注册file_operations     */
    major = register_chrdev(0, "100ask_spi", &spi_drv_fops);  /* /dev/gpio_desc */

    my_spi_class = class_create(THIS_MODULE, "100ask_spi_class");
    if (IS_ERR(my_spi_class)) {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "100ask_spi");
        return PTR_ERR(my_spi_class);
    }

    device_create(my_spi_class, NULL, MKDEV(major, 0), NULL, "myspi"); /* /dev/myspi */
    
    return 0;
}

static int spi_drv_remove(struct spi_device *spi)
{
    /* 反注册字符设备 */
    device_destroy(my_spi_class, MKDEV(major, 0));
    class_destroy(my_spi_class);
    unregister_chrdev(major, "100ask_spi");

    return 0;
}

/* 设备树匹配表 */
static const struct of_device_id myspi_dt_match[] = {
    { .compatible = "100ask,spidev" },
    {},
};

/* spi_driver驱动结构体 */
static struct spi_driver my_spi_driver = {
    .driver = {
           .name = "100ask_spi_drv",
           .owner = THIS_MODULE,
           .of_match_table = myspi_dt_match,     //设备树匹配表
    },
    .probe = spi_drv_probe,
    .remove = spi_drv_remove,
};


static int __init spi_drv_init(void)
{
    /* 注册spi_driver */
    return spi_register_driver(&my_spi_driver);
}

static void __exit spi_drv_exit(void)
{
    /* 删除spi_driver */
    spi_unregister_driver(&my_spi_driver);
}

/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(spi_drv_init);
module_exit(spi_drv_exit);

MODULE_LICENSE("GPL");



其他

如何查看是否成功创建了自己的SPI总线:

文末附加内容

评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇