SPI总线
SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步的串行通信总线协议,广泛用于微控制器与外设(如传感器、存储器、显示屏等)之间的短距离通信。
SPI的核心特性:
**全双工通信:**数据可以同时发送和接收。
**同步传输:**通过时钟信号(SCLK)控制数据传输速率。
**主从架构:**一个主设备(Master)控制通信,多个从设备(Slave)通过片选信号(SS/CS)被选中。
**高速传输:**通常可达几十MHz(比I2C更快)。
**硬件简单:**无需复杂的地址或协议处理。
SPI传输错误处理包括超时、校验、片选和时钟同步,确保数据可靠性。
SPI具体详解在嵌入式STM32核心专栏中有文章介绍。

数码管原理

M74HC595


SPI通用驱动
Linux SPI子系统

SPI控制器驱动支持


SPI通用驱动




SPI通用设备驱动接口及关键结构体
工作模式设置

字长设置

频率设置

spi_transfer结构体

数据内容个数

用户态SPI设备驱动编写
cs
#include <stdio.h> // 标准输入输出,用于printf、perror等
#include <stdlib.h> // 标准库,用于exit等函数
#include <unistd.h> // 提供usleep等系统调用
#include <fcntl.h> // 文件控制,用于open、O_RDWR等文件操作宏
#include <sys/ioctl.h> // IO控制,用于ioctl系统调用
#include <string.h> // 字符串操作,用于memset
#include <linux/spi/spidev.h> // SPI设备相关的结构体和宏定义
// SPI通信参数宏定义
#define DATA_BITS 8 // SPI通信的位宽,8位
#define DATA_SPEED 400000 // SPI通信速率,400KHz
int main(int argc, const char *argv[])
{
int ret = 0; // 函数返回值临时存储
int mode = SPI_MODE_0; // SPI工作模式:MODE0 (CPOL=0, CPHA=0)
int speed = DATA_SPEED; // SPI通信速率
int bits = DATA_BITS; // SPI位宽
int fd; // SPI设备文件描述符
int i = 0; // 数码管位选择和数字索引计数器
int pos = 0; // 数码管位选控制值
// 共阴极7段数码管段码表 (0-F的显示编码)
// 每一位对应一个段:0x3f = 0b00111111 对应数字0的显示
unsigned char num[20] = {
0x3f, // 0: 段码对应显示数字0
0x06, // 1: 段码对应显示数字1
0x5b, // 2: 段码对应显示数字2
0x4f, // 3: 段码对应显示数字3
0x66, // 4: 段码对应显示数字4
0x6d, // 5: 段码对应显示数字5
0x7d, // 6: 段码对应显示数字6
0x07, // 7: 段码对应显示数字7
0x7f, // 8: 段码对应显示数字8
0x6f, // 9: 段码对应显示数字9
0x77, // A: 段码对应显示字母A
0x7c, // B: 段码对应显示字母B
0x39, // C: 段码对应显示字母C
0x5e, // D: 段码对应显示字母D
0x79, // E: 段码对应显示字母E
0x71, // F: 段码对应显示字母F
};
struct spi_ioc_transfer tr; // SPI传输结构体,用于ioctl方式的SPI通信
unsigned char buf[2]; // SPI发送缓冲区:buf[0]位选, buf[1]段码
// 初始化SPI传输结构体,将所有成员置0
memset(&tr, 0 ,sizeof(tr));
// 检查命令行参数:必须传入SPI设备文件路径(如/dev/spidev0.0)
if(argc != 2)
{
printf("usage: %s <device-file>\n",argv[0]); // 提示正确用法
exit(-1); // 退出程序
}
// 打开SPI设备文件,读写模式
fd = open(argv[1],O_RDWR);
if(fd < 0)
{
perror("open"); // 打印打开失败原因
exit(-1); // 退出程序
}
// 设置SPI工作模式
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if(ret < 0)
{
perror("ioctl mode"); // 打印设置模式失败原因
exit(-1); // 退出程序
}
// 设置SPI每字位数(8位)
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if(ret < 0)
{
perror("ioctl bits"); // 打印设置位宽失败原因
exit(-1); // 退出程序
}
// 设置SPI最大通信速率
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if(ret < 0)
{
perror("ioctl speed"); // 打印设置速率失败原因
exit(-1); // 退出程序
}
// 打印SPI配置信息,确认配置成功
printf("spi mode: %d\n",mode);
printf("bits per word: %d\n",bits);
printf("max speed: %d KHz\n",speed/1000);
// 无限循环:轮流显示4位数码管
while(1)
{
// 计算位选值:1 << (i%4) 依次选中第0/1/2/3位数码管
// 例如i=0 → pos=1(0x01), i=1 → pos=2(0x02), i=2 → pos=4(0x04), i=3 → pos=8(0x08)
pos = 1 << (i % 4);
buf[0] = pos; // 第1个字节:数码管位选控制
buf[1] = num[i]; // 第2个字节:对应位置要显示的数字段码
#if 1
// 方式1:直接通过write函数发送SPI数据(简单方式)
if(write(fd,buf,2) < 0)
perror("write"); // 打印发送失败原因
#else
// 方式2:通过ioctl+spi_ioc_transfer结构体发送(标准SPI操作方式)
tr.tx_buf = (unsigned long)buf; // 发送缓冲区地址
tr.len = 2; // 发送数据长度(2字节)
ioctl(fd,SPI_IOC_MESSAGE(1),&tr);// 发送1组SPI消息
#endif
i++; // 计数器自增,切换到下一位
if(i == 4) // 只控制4位数码管,到第4位后重置为0
i = 0;
usleep(3000); // 延时3ms,控制显示刷新速度,避免闪烁
}
return 0;
}
SPI设备驱动

spi_device结构体
cs
struct spi_device {
// 1. 设备模型核心成员(继承自device)
struct device dev;
// 2. SPI总线核心参数(硬件相关)
struct spi_master *master; // 指向所属的SPI控制器(主机)
u32 max_speed_hz; // 最大通信速率(Hz)
u8 chip_select; // 片选号(CS线编号)
u8 mode; // SPI模式(SPI_MODE_0 ~ SPI_MODE_3)
#define SPI_CPHA 0x01 // 时钟相位
#define SPI_CPOL 0x02 // 时钟极性
#define SPI_MODE_0 (0|0) // CPOL=0, CPHA=0(最常用)
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
// 3. 数据格式参数
u8 bits_per_word; // 每字位数(如8/16位)
u16 delay_usecs; // 片选切换延时(微秒)
u8 cs_change:1; // 片选是否保持有效(0=释放,1=保持)
// 4. 标识与匹配相关
const char *modalias; // 设备别名(用于驱动匹配)
u32 speed_hz; // 实际使用的通信速率(可覆盖max_speed_hz)
int irq; // 设备中断号(若有)
// 5. 私有数据(驱动层自定义)
void *dev_data; // 驱动绑定的私有数据(替代旧版drvdata)
};

spi_driver结构体
cs
// SPI驱动核心结构体
struct spi_driver {
// 1. 继承自device_driver,是驱动模型的核心成员
struct device_driver driver;
// 2. 设备匹配成功时的回调函数(最核心)
// 当SPI设备和驱动匹配时,内核会调用此函数,用于初始化设备
int (*probe)(struct spi_device *spi);
// 3. 设备移除/驱动卸载时的回调函数
// 用于释放probe中申请的资源(如内存、中断、DMA等)
void (*remove)(struct spi_device *spi);
// 4. 设备匹配表(可选,用于匹配SPI设备)
// 匹配规则:根据spi_device的modalias/name等匹配
const struct spi_device_id *id_table;
// 5. 可选:休眠/唤醒回调(用于电源管理)
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
int (*resume)(struct spi_device *spi);
};

设备驱动中消息封装及数据传输
cs
struct spi_message {
// 1. 关联的SPI设备(传输的目标设备)
struct spi_device *spi;
// 2. 传输链表:挂载多个spi_transfer(核心)
struct list_head transfers;
// 3. 传输状态与统计
unsigned is_dma_mapped:1; // 是否启用DMA映射
int status; // 传输结果(0=成功,负数=失败)
unsigned int actual_length; // 实际传输的字节数
// 4. 异步传输回调(可选)
void (*complete)(void *context); // 传输完成回调
void *context; // 回调函数的上下文参数
// 5. 内部同步用(一般驱动层不直接操作)
struct list_head queue;
void *state;
};

SPI接口
一、SPI 驱动注册 / 注销相关 API
1. int spi_register_driver(struct spi_driver *driver)
-
功能 :向 Linux 内核注册一个 SPI 驱动,注册后内核会自动匹配总线上的 SPI 设备,匹配成功则调用驱动的
probe函数。 -
参数 :
struct spi_driver *driver--- 指向已初始化的spi_driver结构体实例(包含probe/remove/id_table等)。 -
返回值 :0 表示注册成功,负数(如
-EINVAL)表示失败。 -
使用示例 :
cs// 定义SPI驱动结构体 static struct spi_driver spi_demo_driver = { .driver = { .name = "spi-demo", .owner = THIS_MODULE, .of_match_table = spi_demo_of_match, }, .probe = spi_demo_probe, .remove = spi_demo_remove, .id_table = spi_demo_id_table, }; // 模块加载时注册驱动 static int __init spi_demo_init(void) { return spi_register_driver(&spi_demo_driver); } module_init(spi_demo_init);
2. void spi_unregister_driver(struct spi_driver *driver)
-
功能 :从内核注销已注册的 SPI 驱动,注销时会对已匹配的设备调用
remove函数释放资源。 -
参数 :
struct spi_driver *driver--- 要注销的 SPI 驱动实例(需和注册时的实例一致)。 -
返回值:无。
-
使用示例 :
cs// 模块卸载时注销驱动 static void __exit spi_demo_exit(void) { spi_unregister_driver(&spi_demo_driver); } module_exit(spi_demo_exit);
3. #define module_spi_driver(__spi_driver)
-
功能 :内核提供的宏,简化 SPI 驱动的注册 / 注销代码 (替代手动写
module_init/module_exit),是驱动开发的最佳实践。 -
原理 :宏会自动生成
module_init(调用spi_register_driver)和module_exit(调用spi_unregister_driver)的实现。 -
使用示例 (替代上面的
spi_register_driver/spi_unregister_driver):cs// 定义SPI驱动结构体(同上) static struct spi_driver spi_demo_driver = { .driver = { .name = "spi-demo", .owner = THIS_MODULE, .of_match_table = spi_demo_of_match, }, .probe = spi_demo_probe, .remove = spi_demo_remove, .id_table = spi_demo_id_table, }; // 一行代码完成注册/注销,无需手动写init/exit函数 module_spi_driver(spi_demo_driver);
二、SPI 数据传输相关 API
1. void spi_message_init(struct spi_message *m)
-
功能 :初始化
spi_message结构体,清空链表、重置状态(必须在使用spi_message前调用)。 -
参数 :
struct spi_message *m--- 要初始化的spi_message实例。 -
返回值:无。
-
使用示例 :
csstruct spi_message msg; spi_message_init(&msg); // 初始化message msg.spi = spi; // 绑定到目标SPI设备(spi是probe中拿到的spi_device指针)
2. void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
-
功能 :将一个
spi_transfer传输片段挂载到spi_message的传输链表尾部(一个 message 可挂载多个 transfer)。 -
参数 :
struct spi_transfer *t--- 已配置的spi_transfer实例(包含 tx_buf/rx_buf/len 等);struct spi_message *m--- 目标spi_message实例。
-
返回值:无。
-
使用示例 :
csstruct spi_transfer t = { .tx_buf = tx_data, // 发送缓冲区 .len = 2, // 发送2字节 .speed_hz = 400000,// 传输速率400KHz }; struct spi_message msg; spi_message_init(&msg); msg.spi = spi; spi_message_add_tail(&t, &msg); // 将transfer挂载到message
3. int spi_sync(struct spi_device *spi, struct spi_message *message)
-
功能 :阻塞式同步传输 整个
spi_message(等待传输完成后才返回),是内核 SPI 传输最核心的函数。 -
参数 :
struct spi_device *spi--- 目标 SPI 设备;struct spi_message *message--- 已挂载 transfer 的 spi_message 实例。
-
返回值 :0 表示传输成功,负数(如
-EIO)表示失败;传输完成后,message->status也会记录状态,message->actual_length记录实际传输字节数。 -
使用示例 :
csint ret = spi_sync(spi, &msg); if (ret < 0) { dev_err(&spi->dev, "SPI同步传输失败:%d\n", ret); return ret; } dev_dbg(&spi->dev, "传输完成,实际发送%d字节\n", msg.actual_length);
4. int spi_write(struct spi_device *spi, const void *buf, size_t len)
-
功能 :简化版 SPI 写操作 (只发不收),内部封装了
spi_message/spi_transfer的创建和传输,适合简单的写场景。 -
参数 :
struct spi_device *spi--- 目标 SPI 设备;const void *buf--- 要发送的数据缓冲区;size_t len--- 要发送的字节数。
-
返回值:成功返回发送的字节数,失败返回负数。
-
使用示例 (替代你用户态代码中的
write(fd, buf, 2)):csu8 tx_buf[2] = {0x01, 0x3f}; // 位选+段码 int ret = spi_write(spi, tx_buf, 2); if (ret != 2) { dev_err(&spi->dev, "SPI写数据失败,实际发送%d字节\n", ret); }
5. int spi_read(struct spi_device *spi, void *buf, size_t len)
-
功能 :简化版 SPI 读操作 (只收不发),内部封装了
spi_message/spi_transfer,适合简单的读场景。 -
参数 :
struct spi_device *spi--- 目标 SPI 设备;void *buf--- 接收数据的缓冲区;size_t len--- 要接收的字节数。
-
返回值:成功返回接收的字节数,失败返回负数。
-
使用示例 :
csu8 rx_buf[2] = {0}; int ret = spi_read(spi, rx_buf, 2); if (ret != 2) { dev_err(&spi->dev, "SPI读数据失败\n"); } else { dev_dbg(&spi->dev, "读取到数据:0x%02x 0x%02x\n", rx_buf[0], rx_buf[1]); }
6. int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx)
-
功能 :先写后读的组合操作(无需手动拆分两个 transfer),适合 "发送命令→接收响应" 的场景(如传感器、Flash 等)。
-
参数 :
struct spi_device *spi--- 目标 SPI 设备;const void *txbuf--- 写数据缓冲区;unsigned n_tx--- 写字节数;void *rxbuf--- 读数据缓冲区;unsigned n_rx--- 读字节数。
-
返回值:0 表示成功,负数表示失败。
-
使用示例 (发送 1 字节命令,读取 2 字节响应):
csu8 cmd = 0x01; // 读数据命令 u8 resp[2] = {0}; // 响应缓冲区 int ret = spi_write_then_read(spi, &cmd, 1, resp, 2); if (ret < 0) { dev_err(&spi->dev, "先写后读失败:%d\n", ret); }
三、API 使用场景总结
| API 分类 | 函数 / 宏 | 适用场景 |
|---|---|---|
| 驱动注册 / 注销 | spi_register_driver |
手动注册 SPI 驱动(不推荐,优先用宏) |
spi_unregister_driver |
手动注销 SPI 驱动 | |
module_spi_driver |
简化驱动注册 / 注销(推荐,一行代码搞定) | |
| 复杂传输 | spi_message_init |
初始化 message(必须) |
spi_message_add_tail |
挂载多个 transfer 到 message | |
spi_sync |
阻塞式同步传输(核心,支持多段 transfer) | |
| 简单传输 | spi_write |
只发不收的简单场景(替代手动封装 message) |
spi_read |
只收不发的简单场景 | |
spi_write_then_read |
先写命令、后读响应的场景(如传感器) |
SPI设备驱动
cs
#include <linux/kernel.h> // 内核核心头文件,提供printk等基础函数
#include <linux/module.h> // 模块管理头文件,提供模块注册/注销相关宏
#include <linux/spi/spi.h> // SPI子系统头文件,定义SPI相关结构体和API
#include <linux/fs.h> // 文件系统头文件,定义字符设备操作接口
#include <linux/slab.h> // 内核内存分配头文件,提供kmalloc等函数
#include <linux/cdev.h> // 字符设备头文件,定义cdev结构体及操作函数
#include <linux/device.h> // 设备模型头文件,用于创建设备类和设备节点
#include <linux/uaccess.h> // 内核与用户空间数据交互头文件
#include <linux/of_device.h> // 设备树匹配相关头文件
MODULE_LICENSE("GPL"); // 声明模块许可证为GPL,避免内核报警
// 共阴极7段数码管段码表,索引0-15对应显示0-F
char code[] = {
0x3f, /*display 0*/
0x06, /*display 1*/
0x5b, /*display 2*/
0x4f, /*display 3*/
0x66, /*display 4*/
0x6d, /*display 5*/
0x7d, /*display 6*/
0x07, /*display 7*/
0x7f, /*display 8*/
0x6f, /*display 9*/
0x77, /*display A*/
0x7c, /*display B*/
0x39, /*display C*/
0x5e, /*display D*/
0x79, /*display E*/
0x71 /*display F*/
};
// 自定义IOCTL命令:用于用户态向内核态发送显示控制指令
// _IOW表示用户写、内核读,'S'为魔数,0为命令号,int为参数类型
#define CMD_WRITE_DIGITAL _IOW('S',0,int)
// 设备私有数据结构体:整合字符设备和SPI设备相关资源
struct m74hc595_device{
dev_t devno; // 字符设备号(主+次设备号)
struct cdev cdev; // 字符设备核心结构体
struct class *cls; // 设备类,用于创建/dev节点
struct device *dev; // 设备实例,对应/dev下的设备文件
struct spi_device *spi; // 关联的SPI设备结构体
};
// 字符设备open函数:打开设备文件时调用
static int m74hc595_chrdev_open(struct inode *inode,struct file *file)
{
// 通过inode中的cdev成员反向获取整个m74hc595_device结构体
struct m74hc595_device *m74hc595 = container_of(inode->i_cdev, struct m74hc595_device,cdev);
// 将设备结构体绑定到file的私有数据,供后续操作使用
file->private_data = m74hc595;
return 0;
}
// 字符设备close函数:关闭设备文件时调用
static int m74hc595_chrdev_close(struct inode *inode,struct file *file)
{
return 0;
}
// SPI数据传输函数:向74HC595发送位选和段码数据
// spi:目标SPI设备;pos_code:16位数据(低8位位选,高8位数字索引)
int m74hc595_spi_transfer(struct spi_device *spi,unsigned short pos_code)
{
struct spi_message message; // SPI消息容器,组织传输片段
struct spi_transfer xfer[1] = {0}; // SPI传输片段,单片段传输
unsigned char tx[2] = {0}; // 发送缓冲区:tx[0]位选,tx[1]段码
int error; // 传输结果状态码
// 解析pos_code:低8位提取位选值,高8位作为索引查段码表
tx[0] = pos_code & 0xff;
tx[1] = code[pos_code >> 8];
// 配置SPI传输片段:指定发送缓冲区和长度
xfer[0].tx_buf = (const void *)&tx;
xfer[0].len = sizeof(tx)/sizeof(tx[0]);
// 初始化SPI消息,将传输片段添加到消息链表
spi_message_init(&message);
spi_message_add_tail(&xfer[0],&message);
// 同步执行SPI传输,等待传输完成
error = spi_sync(spi,&message);
if(error < 0)
{
printk("SPI write error: %d\n",error);
return error;
}
return 0;
}
// 字符设备IOCTL函数:处理用户态的控制指令
static long m74hc595_chrdev_ioctl(struct file *file,unsigned int cmd , unsigned long arg)
{
int ret = 0;
unsigned short pos_code; // 存储用户态传入的控制参数
// 从file私有数据中获取设备结构体
struct m74hc595_device *m74hc595 = file->private_data;
// 将用户态数据拷贝到内核态,失败则返回错误
if(copy_from_user(&pos_code,(void *)arg,sizeof(pos_code)) != 0)
{
printk("error copy_from_user\n");
return -1;
}
// 根据指令执行对应操作
switch(cmd)
{
case CMD_WRITE_DIGITAL:
// 执行SPI数据传输
ret = m74hc595_spi_transfer(m74hc595->spi,pos_code);
break;
default:
printk("cmd error \n");
break;
}
return ret;
}
// 字符设备操作集:绑定open/close/ioctl等函数
static const struct file_operations m74hc595_fops = {
.owner = THIS_MODULE, // 声明所属模块
.open = m74hc595_chrdev_open, // 打开设备函数
.release = m74hc595_chrdev_close, // 关闭设备函数
.unlocked_ioctl = m74hc595_chrdev_ioctl, // IOCTL处理函数
};
// 注册字符设备:完成设备号分配、cdev注册、设备类/节点创建
int register_m74hc595_chrdev(struct m74hc595_device *m74hc595)
{
int ret;
// 初始化cdev结构体,绑定操作集
cdev_init(&m74hc595->cdev,&m74hc595_fops);
// 动态分配字符设备号(主设备号由内核分配,次设备号从0开始)
ret = alloc_chrdev_region(&m74hc595->devno,0,1,"m74hc595");
if(ret < 0)
{
printk("Fail to alloc chrdev region\n");
goto err_alloc_chrdev_region;
}
// 将cdev添加到内核,完成字符设备注册
ret = cdev_add(&m74hc595->cdev,m74hc595->devno,1);
if(ret < 0)
{
printk("Fail to cdev add\n");
goto err_cdev_add;
}
// 创建设备类(在/sys/class/下生成m74hc595目录)
m74hc595->cls = class_create(THIS_MODULE,"m74hc595");
if(IS_ERR(m74hc595->cls))
{
printk("Fail to class create\n");
ret =PTR_ERR(m74hc595->cls);
goto err_class_create;
}
// 创建设备节点(在/dev/下生成m74hc595_device文件)
m74hc595->dev = device_create(m74hc595->cls,NULL,m74hc595->devno,NULL,"m74hc595_device");
if(IS_ERR(m74hc595->dev))
{
printk("Fail to device create\n");
ret =PTR_ERR(m74hc595->dev);
goto err_device_create;
}
return 0;
// 错误处理:反向释放已申请的资源
err_device_create:
class_destroy(m74hc595->cls);
err_class_create:
cdev_del(&m74hc595->cdev);
err_cdev_add:
unregister_chrdev_region(m74hc595->devno,1);
err_alloc_chrdev_region:
return ret;
}
// 注销字符设备:释放注册时申请的所有资源
void unregister_m74hc595_chrdev(struct m74hc595_device *m74hc595)
{
device_destroy(m74hc595->cls,m74hc595->devno);
class_destroy(m74hc595->cls);
cdev_del(&m74hc595->cdev);
unregister_chrdev_region(m74hc595->devno,1);
}
// SPI驱动probe函数:SPI设备与驱动匹配成功时执行
static int m74hc595_spi_probe(struct spi_device *spi)
{
struct m74hc595_device *m74hc595;
int ret;
// 申请设备私有数据内存(devm_kmalloc由内核自动管理释放)
m74hc595 = devm_kmalloc(&spi->dev,sizeof(*m74hc595),GFP_KERNEL);
if(!m74hc595)
{
printk("Fail to kmalloc\n");
return -ENOMEM;
}
// 保存SPI设备指针,绑定私有数据到SPI设备
m74hc595->spi = spi;
spi_set_drvdata(spi,m74hc595);
// 注册字符设备,创建用户态访问接口
ret = register_m74hc595_chrdev(m74hc595);
if(ret < 0)
{
printk("Fail to register m74hc595 chrdev\n");
return ret;
}
return 0;
}
// SPI驱动remove函数:驱动卸载/设备移除时执行
static int m74hc595_spi_remove(struct spi_device *spi)
{
// 获取绑定的私有数据,注销字符设备
struct m74hc595_device *m74hc595 = spi_get_drvdata(spi);
unregister_m74hc595_chrdev(m74hc595);
return 0;
}
// 设备树匹配表:匹配设备树中compatible为"m74hc595"的节点
static const struct of_device_id m74hc595_of_ids[] = {
{.compatible = "m74hc595",},
{},
};
// 声明设备树匹配表,供内核匹配使用
MODULE_DEVICE_TABLE(of, m74hc595_of_ids);
// SPI驱动结构体:绑定probe/remove函数和设备树匹配表
static struct spi_driver m74hc595_spi_driver = {
.driver = {
.name = "m74hc595", // 驱动名称
.of_match_table = of_match_ptr(m74hc595_of_ids), // 设备树匹配表
},
.probe = m74hc595_spi_probe, // 匹配成功时的初始化函数
.remove = m74hc595_spi_remove, // 卸载时的清理函数
};
// 宏定义:自动完成SPI驱动的注册和注销
module_spi_driver(m74hc595_spi_driver);
应用层测试代码
cs
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define CMD_WRITE_DIGITAL _IOW('S',0,int)
int main(int argc , const char *argv[])
{
int fd;
int ret;
char pos = 0;
char digital;
unsigned short value;
fd = open("/dev/m74hc595_device",O_RDWR);
if(fd < 0)
{
perror("Fail to oprn");
exit(-1);
}
while(1)
{
for(digital=0;digital<16;digital++)
{
value =( (digital<<8) | (1<<pos));
ret =ioctl(fd,CMD_WRITE_DIGITAL,(unsigned long)&value);
if(ret < 0)
{
perror("ioctl");
return -1;
}
pos = (pos+1) % 4;
sleep(1);
}
}
return 0;
}