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. 总结
-
Spi概述
Spi全称Service Provider Interface,是一种服务发现机制。通过在指定路径下(通常为:META-INF/services文件夹下)读取。读取的内容是接口实现类,将该实现类应用起来,具体使用和操作目的在本文后续会讲解。
在Spi机制中,可以看做有两方角色:微内核
+插件
。对此我们可以联想一下我们常用的开发工具Eclipse、IDEA、VScode、Atom、Sublime Text等,无一不是拥有着较大的插件生态,这些插件时如何作用起来的?
将IDE视为微内核
,以IDE运行程序为例,运行时IDE可以去回调一个接口,作为运行程序动作触发后需要进行的操作。它可以是显示控制台、统计覆盖率、打开浏览器等。这个可以提供给插件
自定义的方式,就是微内核
+插件
。微内核
拥有着一个严谨的操作,负责回调各种插件
,可替换的插件
有效降低了系统的耦合。 -
谈谈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加载了一次。而这操作就是为了加载驱动。为什么写了这行代码就能加载驱动?
- 写一个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)
的代码来加载驱动。那么有没有办法能够不用写这行代码呢?
- 通过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驱动,仅需要一点文件操作和反射操作即可完成。
- 总结
驱动的加载只是Spi功能的冰山一角,Spi机制的存在,能让业务代码影响较小的情况下,灵活的替换业务流程中的各种功能。目前典型的开源项目有:Dubbo
。Dubbo
框架中大量应用了Spi机制,使得开发者能够轻松替换Dubbo
中的各种功能,例如日志、rpc访问等。
以上是关于linx下如何驱动spi的主要内容,如果未能解决你的问题,请参考以下文章
Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)
Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤
(LINX-IPC 协议)如何查明具有 PID 的进程是不是在 C++ 中运行?