linx下如何驱动spi

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linx下如何驱动spi相关的知识,希望对你有一定的参考价值。

1、驱动分为平台驱动、控制器驱动、设备驱动、设备。
2、拿到一个开发板后,烧上系统,那我们基本上就有了平台驱动、控制器驱动。设备驱动基本都有的,官方实现了一个设备驱动,文件是spidev.c,它是一个设备驱动,它会在开机之后自动注册一个主设备号为153的字符设备。
3、当注册了SPI设备到系统中时,会根据名字进行匹配,如果名字是spidev则会调用 spidev.c中的probe函数,随后会在/dev/device/下面生成如spidevx.x的设备文件,通过该设备文件即可操作SPI设备。
4、如何注册SPI设备到系统。在kernel/arch/arm/mach-xxxx,xxx是板子芯片型号,我用的事 mach-smdk6410.c。在里面找到spi_board_info结构体位置,例如:
static struct spi_board_info __initdata forlinx6410_mc251x_info[] =

.modalias = "mcp2515",
.platform_data = &mcp251x_info,
.irq = IRQ_EINT(16),
.max_speed_hz = 10*1000*1000,
.bus_num = 1,
.chip_select = 0,
.mode = SPI_MODE_0,
.controller_data=&s3c64xx_spi1_csinfo,
,
;
本结构体就是SPI的板级信息,会在后面被spi_register_board_info()调用,随后在系统中注册这个设备。
我们需要做的是添加我们自己的信息
static struct spi_board_info __initdata forlinx6410_mc251x_info[] =

.modalias = "mcp2515",
.platform_data = &mcp251x_info,
.irq = IRQ_EINT(16),
.max_speed_hz = 10*1000*1000,
.bus_num = 1,
.chip_select = 0,
.mode = SPI_MODE_0,
.controller_data=&s3c64xx_spi1_csinfo,
,

.modalias = "spidev", //用来匹配设备驱动,SPI的设备驱动叫spidev
.max_speed_hz = 10*1000*1000, //最大速率
.bus_num = 0, //在(0)号总线上
.chip_select = 0, //使用片选spi0_cs0
.mode = SPI_MODE_0, //SPI模式
.controller_data=&s3c64xx_spi0_csinfo,//控制器信息 ,
;
其中的static struct s3c64xx_spi_csinfo s3c64xx_spi0_csinfo =
.fb_delay=0x3,
.line=S3C64XX_GPC(3),//这个是片选控制引脚
.set_level=cs_set_level,
;
然后编译内核下载开发板上,重新启动过后就能后在/dev下面看到spidev0.0设备文件。
猜想:
写一个模块,收到填写该结构体,然后调用spi_register_board_info()来注册。我没有试过,应该是可以的。
5、使用的时候在用户应用空间中就使用open打开设备文件即可使用。
需要在封装一次。由于spidev.c仅提供数据接收与发送,但是对于具体的SPI怎么发的好像没有做。
因此我们具体的收发函数应该在此分装为如下
int fd;
fd=open(device, O_RDWR);
读函数
输入:fd文件描述符,addr读的地址,read_data读出来数据存放的地址
输出:成功操作的字节数,这个没有做好,需要改
unsigned char read_reg(int fd,unsigned char addr,unsigned char *read_data)

int ret=0;
addr=addr<<1;
addr=addr|0x80;
ret=write(fd,&addr,sizeof(unsigned char));
ret|=read(fd,read_data,sizeof(unsigned char));
return ret;

写函数
unsigned char write_reg(int fd,unsigned char addr,unsigned data)

int ret=0;
unsigned char buff[2]=0;
buff[0]=addr<<1;
buff[0]=buff[0]&0x7f;
buff[1]=data;
ret=write(fd,&buff,sizeof(buff));
return ret;

然后其他具体操作看我们的实际需要了,以上两个就可以正确读写了,你可以先用读函数读取设备各个寄存器的默认值,来观察读取是否正确。
参考技术A 首先我们想要下载驱动,我们可以卸载电脑上面下载一个鲁大师,可以通过鲁大师这款软件,然后下载驱动。 参考技术B 下载的话直接用官网点击就可以了 参考技术C 设置中查找相应的位置。走到七栋的位置进行有效处理。那就可以。

Spi,微内核与插件化

1. Spi概述

2. 谈谈Sql驱动

3. 写一个Sql驱动

4. 通过Spi机制再写一个Sql驱动

5. 总结

  1. Spi概述
    Spi全称Service Provider Interface,是一种服务发现机制。通过在指定路径下(通常为:META-INF/services文件夹下)读取。读取的内容是接口实现类,将该实现类应用起来,具体使用和操作目的在本文后续会讲解。
    在Spi机制中,可以看做有两方角色:微内核+插件。对此我们可以联想一下我们常用的开发工具Eclipse、IDEA、VScode、Atom、Sublime Text等,无一不是拥有着较大的插件生态,这些插件时如何作用起来的?
    将IDE视为微内核,以IDE运行程序为例,运行时IDE可以去回调一个接口,作为运行程序动作触发后需要进行的操作。它可以是显示控制台、统计覆盖率、打开浏览器等。这个可以提供给插件自定义的方式,就是微内核+插件微内核拥有着一个严谨的操作,负责回调各种插件,可替换的插件有效降低了系统的耦合。

  2. 谈谈Sql驱动
    这会谈Sql驱动,是因为Sql驱动也是一个插件。我们有各种Sql驱动JDBC、ODBC...。使用它们只需要把它们引入进来,这就已经呈现出插件可替换的特点了。
    为了后面能比较顺畅的进行Sql驱动的编写,在这先提一提DriverManager如何加载插件。
    先展示下加载Sql驱动的模板语句:

public static void main(String[] args) {
    
    String classPath = "org.jdbc.driver.MyDriver";
    
    try {
        Class.forName(classPath);
        DriverManager.getConnection("url", "user", "pass");
        // ...
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

我们看看DriverManager.getConnection()的源码,究竟做了什么?

public static Connection getConnection(String url,
    String user, String password) throws SQLException {
    java.util.Properties info = new java.util.Properties();

    if (user != null) {
        info.put("user", user);
    }
    if (password != null) {
        info.put("password", password);
    }

    return (getConnection(url, info, Reflection.getCallerClass()));
}

经过参数检验后,核心是调用了getConnection方法,继续看看这个方法做了什么?
代码偏多,删减一些注释和语句

private static Connection getConnection(
    String url, java.util.Properties info, Class<?> caller) throws SQLException {
    //...删
    //这里可以看到:循环了成员变量registeredDrivers
    for(DriverInfo aDriver : registeredDrivers) {
        // ...
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                println("    trying " + aDriver.driver.getClass().getName());
                Connection con = aDriver.driver.connect(url, info);
                if (con != null) {
                    // Success!
                    println("getConnection returning " + aDriver.driver.getClass().getName());
                    return (con);
                }
            } catch (SQLException ex) {
                if (reason == null) {
                    reason = ex;
                }
            }

        } else {
            println("    skipping: " + aDriver.getClass().getName());
        }

    }

    // ...删
}

让我们看看这个registeredDrivers的定义

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

显然是个数组,那它是什么时候拥有Driver的信息的呢?

在获取Sql连接前,有一个需要写并且好像并不起眼的代码:

Class.forName(classPath);

将Driver加载了一次。而这操作就是为了加载驱动。为什么写了这行代码就能加载驱动?

  1. 写一个Sql驱动
    DriverManager中,有一个DriverManager.registerDriver方法,用于将指定驱动加载入DriverManager.registeredDrivers驱动列表内。Class.forName(classPath)代码会促使驱动类内静态代码块的执行,所以只要在驱动类的静态代码块中向DriverManager注册我们自己编写的Driver,就可以成功引入我们的驱动。
public class MyDriver implements Driver {
    
    private static Driver driver;
    // 加载驱动
    static {
        try {
            driver = new MyDriver();
            // 向DriverManager注册驱动
            DriverManager.registerDriver(driver);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    // ... 删
}

驱动需要实现java.sql.Driver接口,在静态代码块中注册即可实现引入驱动。至于获取连接,只需要在MyDriver.connect中返回一个Connection,就可以在DriverManager.getConnection中获取到该Connection

然而,这样的操作虽然实现了插件化(驱动就是插件),但我们每次在获取Connection前,都需要写一次Class.forName(classPath)的代码来加载驱动。那么有没有办法能够不用写这行代码呢?

  1. 通过Spi机制再写一个Sql驱动
    答案当然是有的。上述方式实现了插件化,但严格意义上不算Spi,Spi中还有一个文件夹尚未使用,这个文件夹到底有什么用处?
    从上面的驱动加载可以知道,我们需要规避的是加载驱动的这个动作,那为了在用户使用时规避这个动作,就需要一个管理类来做这个操作,并且该管理类需要获取到classPath类路径。
    所以,这个文件夹META-INF/services的意义,就是告诉读取插件的类,需要读取的插件的类路径在XXX地方(充当配置文件的意义)。
    为了充分了解整个过程,我们谈谈DriverManger中使用到的插件读取类ServiceLoader

DriverManager中,有这么一段静态代码块:

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

loadInitialDrivers内部,有这么一段代码:

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

这个ServiceLoader充当的就是插件读取类的角色。
返回的loadedDrivers是一个迭代器,所以我们需要查看迭代器的方法:

private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            // PREFIX定义: private static final String PREFIX = "META-INF/services/";
            // 此处的service是ServiceLoader<Driver>中的Driver
            String fullName = PREFIX + service.getName();//看这里
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;
    nextName = null;
    Class<?> c = null;
    try {
        // 加载对应的类(插件)
        c = Class.forName(cn, false, loader);//看这里
    } catch (ClassNotFoundException x) {
        fail(service,
             "Provider " + cn + " not found");
    }
    if (!service.isAssignableFrom(c)) {
        fail(service,
             "Provider " + cn  + " not a subtype");
    }
    try {
        S p = service.cast(c.newInstance());
        providers.put(cn, p);
        return p;
    } catch (Throwable x) {
        fail(service,
             "Provider " + cn + " could not be instantiated",
             x);
    }
    throw new Error();          // This cannot happen
}

有上述代码可知,ServiceLoader在文件夹META-INF/services/+接口全类名的文件中读取插件类路径。所以利用这个Spi机制,ServiceLoader就能在迭代器获取到类信息,并且在nextService方法中帮助我们实现驱动类的加载。

所以对于使用Spi机制来加载驱动的插件提供者来说,只需要在META-INF/services/java.sql.Driver文件下,写一行插件名。

org.jdbc.driver.MyDriver

这样DriverManager就可以通过ServiceLoader实现的Spi机制,帮助我们加载驱动。

对于Spi机制的内核方来说,就是需要实现Spi机制来加载插件提供者提供的插件,加载插件后进行什么操作,就看实际业务情况了。感兴趣的小伙伴,不妨尝试自己写一个简易的DriverManager以及ServiceLoader,来加载自己刚写的Sql驱动,仅需要一点文件操作和反射操作即可完成。

  1. 总结
    驱动的加载只是Spi功能的冰山一角,Spi机制的存在,能让业务代码影响较小的情况下,灵活的替换业务流程中的各种功能。目前典型的开源项目有:DubboDubbo框架中大量应用了Spi机制,使得开发者能够轻松替换Dubbo中的各种功能,例如日志、rpc访问等。

以上是关于linx下如何驱动spi的主要内容,如果未能解决你的问题,请参考以下文章

Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)

Qt的窗口如何显示在spi屏幕上

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤

(LINX-IPC 协议)如何查明具有 PID 的进程是不是在 C++ 中运行?

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤