linux中SPI相关API函数,linux spi驱动开发学习(一)-----spi子系统架构

linux spi驱动开发学习(一)-----spi子系统架构

一.spi设备(各定义在include/linux/spi.h)

structspi_device {

structdevice   dev;//设备文件

structspi_master   *master;//spi主机

u32 max_speed_hz;   //最大速率

u8  chip_select;    //片选

u8  mode;   //模式

u8  bits_per_word;  //一个字有多少位

intirq;//中断号

void*controller_state;//控制器状态

void*controller_data;//控制器数据

charmodalias[SPI_NAME_SIZE];//名字

};

2.spi传输模式:

#define SPI_CPHA        0x01            //时钟相位

#define SPI_CPOL        0x02            //时钟继续

#define SPI_MODE_0  (0|0)           //模式0

#define SPI_MODE_1  (0|SPI_CPHA)    //模式1

#define SPI_MODE_2  (SPI_CPOL|0)    //模式2

#define SPI_MODE_3  (SPI_CPOL|SPI_CPHA) //模式3

#define SPI_CS_HIGH 0x04            //片选高电平

#define SPI_LSB_FIRST   0x08            //LSB

#define SPI_3WIRE       0x10            //3线模式 SI和SO同一根线

#define SPI_LOOP        0x20            //回送模式

#define SPI_NO_CS       0x40            //单个设备占用一根SPI总线,所以没片选

#define SPI_READY       0x80            //从机拉低电平停止数据传输

3.spi设备的添加spi_new_device

structspi_device *spi_new_device(structspi_master *master,structspi_board_info *chip)

{

structspi_device   *proxy;

intstatus;

proxy = spi_alloc_device(master);   //3.1 spi设备初始化

if(!proxy)

returnNULL;

WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));

proxy->chip_select = chip->chip_select;   //片选

proxy->max_speed_hz = chip->max_speed_hz; //最大速率

proxy->mode = chip->mode; //模式

proxy->irq = chip->irq;   //中断号

strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));

proxy->dev.platform_data = (void*) chip->platform_data;

proxy->controller_data = chip->controller_data;

proxy->controller_state = NULL;

status = spi_add_device(proxy); //3.2 添加spi设备

if(status < 0) {

spi_dev_put(proxy); //增加spi设备引用计数

returnNULL;

}

returnproxy;

}

EXPORT_SYMBOL_GPL(spi_new_device);

3.1.分配spi设备

structspi_device *spi_alloc_device(structspi_master *master)

{

structspi_device   *spi;

structdevice       *dev = master->dev.parent;

if(!spi_master_get(master))//判断spi主机是否存在

returnNULL;

spi = kzalloc(sizeof*spi, GFP_KERNEL);//分配内存

if(!spi) {

dev_err(dev, "cannot alloc spi_device\n");

spi_master_put(master); //增加主机引用计数

returnNULL;

}

spi->master = master;    //设置spi主机

spi->dev.parent = dev;   //spi设备文件的父设备为spi主机设备文件的父设备

spi->dev.bus = &spi_bus_type;    //总线类型

spi->dev.release = spidev_release;   //释放方法

device_initialize(&spi->dev);    //设备初始化

returnspi;

}

EXPORT_SYMBOL_GPL(spi_alloc_device);

3.2 添加spi设备

intspi_add_device(structspi_device *spi)

{

staticDEFINE_MUTEX(spi_add_lock);

structdevice *dev = spi->master->dev.parent;

structdevice *d;

intstatus;

if(spi->chip_select >= spi->master->num_chipselect) {

dev_err(dev, "cs%d >= max %d\n",spi->chip_select,spi->master->num_chipselect);

return-EINVAL;

}

dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),spi->chip_select);

mutex_lock(&spi_add_lock);

d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev));   //查找总线上的spi设备

if(d != NULL) {//判断是否已经在使用了

dev_err(dev, "chipselect %d already in use\n",spi->chip_select);

put_device(d);

status = -EBUSY;

gotodone;

}

status = spi_setup(spi);    //调用spi主机 setup方法

if(status < 0) {

dev_err(dev, "can't setup %s, status %d\n",dev_name(&spi->dev), status);

gotodone;

}

status = device_add(&spi->dev);  //添加设备

if(status < 0)

dev_err(dev, "can't add %s, status %d\n",dev_name(&spi->dev), status);

else

dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));

done:

mutex_unlock(&spi_add_lock);

returnstatus;

}

EXPORT_SYMBOL_GPL(spi_add_device);

3.2.1 spi setup方法

intspi_setup(structspi_device *spi)

{

unsigned    bad_bits;

intstatus;

bad_bits = spi->mode & ~spi->master->mode_bits;    //比较spi设备的模式和spi主机支持的模式

if(bad_bits) {//存在不支持的模式

dev_err(&spi->dev, "setup: unsupported mode bits %x\n",bad_bits);

return-EINVAL;

}

if(!spi->bits_per_word)//若没设置设备的每个字含多少位

spi->bits_per_word = 8;  //则默认设置为8

status = spi->master->setup(spi); //调用spi主机的setup方法

dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s""%u bits/w, %u Hz max --> %d\n",

(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),(spi->mode & SPI_CS_HIGH) ?"cs_high, ":"",

(spi->mode & SPI_LSB_FIRST) ? "lsb, ":"",(spi->mode & SPI_3WIRE) ?"3wire, ":"",

(spi->mode & SPI_LOOP) ? "loopback, ":"",spi->bits_per_word, spi->max_speed_hz,status);

returnstatus;

}

EXPORT_SYMBOL_GPL(spi_setup);

二.spi板级设备

1.板级设备结构体

structspi_board_info {

charmodalias[SPI_NAME_SIZE];//名字

constvoid*platform_data;//平台数据

void*controller_data;//控制器数据

intirq;//中断号

u32 max_speed_hz;   //最大速率

u16 bus_num;        //spi总线编号

u16 chip_select;    //片选

u8  mode;           //模式

};

2.板级设备注册(静态注册,一般在板级初始化函数中调用)

int__init spi_register_board_info(structspi_board_infoconst*info, unsigned n)

{

structboardinfo *bi;

inti;

bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);//分配内存

if(!bi)

return-ENOMEM;

for(i = 0; i < n; i++, bi++, info++) {

structspi_master *master;

memcpy(&bi->board_info, info, sizeof(*info));//设置bi的板级信息

mutex_lock(&board_lock);

list_add_tail(&bi->list, &board_list);   //添加bi->list到全局board_list链表

list_for_each_entry(master, &spi_master_list, list) //遍历spi主机链表

spi_match_master_to_boardinfo(master, &bi->board_info);

mutex_unlock(&board_lock);

}

return0;

}

2.1.spi板级设备与spi主机匹配

staticvoidspi_match_master_to_boardinfo(structspi_master *master,structspi_board_info *bi)

{

structspi_device *dev;

if(master->bus_num != bi->bus_num)

return;

dev = spi_new_device(master, bi);

if(!dev)

dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias);

}

三.spi设备驱动

1.spi设备驱动结构体

structspi_driver {

conststructspi_device_id *id_table;//spi设备id表

int(*probe)(structspi_device *spi);//probe方法(探测到设备)

int(*remove)(structspi_device *spi);//remove方法(设备移除)

void(*shutdown)(structspi_device *spi);//shutdown方法(关闭设备)

int(*suspend)(structspi_device *spi, pm_message_t mesg);//suspend方法(挂起设备)

int(*resume)(structspi_device *spi);//resume方法(唤醒设备)

structdevice_driver    driver;//设备驱动文件

};

2.spi设备驱动注册

intspi_register_driver(structspi_driver *sdrv)

{

sdrv->driver.bus = &spi_bus_type;    //总线类型

if(sdrv->probe)//若存在probe方法

sdrv->driver.probe = spi_drv_probe;  //设置其设备驱动文件的probe方法为spi_drv_probe

if(sdrv->remove)//若存在remove方法

sdrv->driver.remove = spi_drv_remove;    //设置其设备驱动文件的remove方法为spi_drv_remove

if(sdrv->shutdown)若存在shutdown方法

sdrv->driver.shutdown = spi_drv_shutdown;    //设置其设备驱动文件的shutdown方法为spi_drv_shutdown

returndriver_register(&sdrv->driver);//注册设备驱动

}

EXPORT_SYMBOL_GPL(spi_register_driver);

这里的probe方法会在设备与驱动匹配的时候给调用

参看really_probe函数的部分代码

if(dev->bus->probe) {//若总线有probe方法(spi子系统的没有)

ret = dev->bus->probe(dev);   //则调用总线的probe方法

if(ret)

gotoprobe_failed;

}

elseif(drv->probe) {//若存在设备驱动的probe方法

ret = drv->probe(dev);       //则调用设备驱动的probe方法

if(ret)

gotoprobe_failed;

}

2.1 spi_drv_probe

staticintspi_drv_probe(structdevice *dev)

{

conststructspi_driver *sdrv = to_spi_driver(dev->driver);//根据设备文件的设备驱动找到spi设备驱动

returnsdrv->probe(to_spi_device(dev));//调用spi设备驱动的probe方法

}

3.spi设备驱动注销

staticinlinevoidspi_unregister_driver(structspi_driver *sdrv)

{

if(sdrv)

driver_unregister(&sdrv->driver);    //注销设备驱动

}

四.spi主机

1.spi主机结构体

structspi_master {

structdevice   dev;//spi主机设备文件

structlist_head list;

s16 bus_num;    //spi总线号

u16 num_chipselect; //片选号

u16 dma_alignment;  //dma算法

u16 mode_bits;  //模式位

u16 flags;  //传输类型标志

spinlock_t  bus_lock_spinlock;  //spi总线自旋锁

structmutex    bus_lock_mutex;//spi总线互斥锁

boolbus_lock_flag;//上锁标志

int(*setup)(structspi_device *spi);//setup方法

int(*transfer)(structspi_device *spi,structspi_message *mesg);//传输方法

void(*cleanup)(structspi_device *spi);//cleanup方法

};

1.2.flags标志

#define SPI_MASTER_HALF_DUPLEX  BIT(0)  //半双工

#define SPI_MASTER_NO_RX    BIT(1)      //不读

#define SPI_MASTER_NO_TX    BIT(2)      //不写

2.spi主机初始化spi_alloc_master

structspi_master *spi_alloc_master(structdevice *dev, unsigned size)

{

structspi_master   *master;

if(!dev)

returnNULL;

master = kzalloc(size + sizeof*master, GFP_KERNEL);//分配内存

if(!master)

returnNULL;

device_initialize(&master->dev); //初始化主机设备文件

master->dev.class= &spi_master_class;//指定设备类spi_master_class

master->dev.parent = get_device(dev);    //设置spi主机设备的父设备

spi_master_set_devdata(master, &master[1]); //设置设备数据

returnmaster;

}

EXPORT_SYMBOL_GPL(spi_alloc_master);

3.注册spi主机

intspi_register_master(structspi_master *master)

{

staticatomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);

structdevice   *dev = master->dev.parent;//获得spi主机设备的父设备

structboardinfo    *bi;

intstatus = -ENODEV;

intdynamic = 0;

if(!dev)

return-ENODEV;

if(master->num_chipselect == 0)//判断片选个数

return-EINVAL;

if(master->bus_num < 0) {//验证spi总线编号

master->bus_num = atomic_dec_return(&dyn_bus_id);

dynamic = 1;

}

spin_lock_init(&master->bus_lock_spinlock);

mutex_init(&master->bus_lock_mutex);

master->bus_lock_flag = 0;

dev_set_name(&master->dev, "spi%u", master->bus_num);//设置spi主机设备名

status = device_add(&master->dev);   //添加spi主机设备

if(status < 0)

gotodone;

dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ?" (dynamic)":"");

mutex_lock(&board_lock);

list_add_tail(&master->list, &spi_master_list);  //spi主机list链表添加进全局spi_master_list链表

list_for_each_entry(bi, &board_list, list)      //遍历全局board_list查找bi结构体

spi_match_master_to_boardinfo(master, &bi->board_info);  //找到匹配的板级spi设备

mutex_unlock(&board_lock);

status = 0;

of_register_spi_devices(master);

done:

returnstatus;

}

EXPORT_SYMBOL_GPL(spi_register_master);

3.1 匹配spi主机和板级spi设备

staticvoidspi_match_master_to_boardinfo(structspi_master *master,structspi_board_info *bi)

{

structspi_device *dev;

if(master->bus_num != bi->bus_num)//判断是否所属的spi总线

return;

dev = spi_new_device(master, bi);   //添加新的spi设备

if(!dev)

dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias);

}

在注册板级设备或主机设备的时候都会添加

spi板级设备添加进board_list链表,spi主机设备添加进spi_master_list链表

不管是先注册spi板级设备还是先注册spi主机设备

都会调用list_for_each_entry遍历对应的要匹配的设备的链表,查找是否有匹配的例子

若找到都会调用spi_match_master_to_boardinfo函数添加spi设备

4.注销spi主机

voidspi_unregister_master(structspi_master *master)

{

intdummy;

mutex_lock(&board_lock);

list_del(&master->list); //删除链表

mutex_unlock(&board_lock);

dummy = device_for_each_child(&master->dev, NULL, __unregister); //调用__unregister函数注销子设备

device_unregister(&master->dev); //注销设备

}

EXPORT_SYMBOL_GPL(spi_unregister_master);

4.1 注销挂载该总线上的spi子设备

staticint__unregister(structdevice *dev,void*null)

{

spi_unregister_device(to_spi_device(dev));

return0;

}

5.spi主机设备类

staticstructclassspi_master_class = {

.name   = "spi_master",

.owner  = THIS_MODULE,

.dev_release    = spi_master_release,

};

五.spi总线

1.spi总线结构体

structbus_type spi_bus_type = {

.name       = "spi",

.dev_attrs  = spi_dev_attrs,

.match      = spi_match_device, //匹配方法

.uevent     = spi_uevent,

.suspend    = spi_suspend,

.resume     = spi_resume,

};

EXPORT_SYMBOL_GPL(spi_bus_type);

2.设备匹配方法spi_match_device

前面的匹配方法是spi板级设备与spi主机设备的匹配方法,匹配的结果是添加新spi设备spi_new_device

这里的匹配是spi设备和spi驱动的匹配,匹配的结果是会调用spi驱动的设备驱动文件probe方法,既spi_drv_probe

staticintspi_match_device(structdevice *dev,structdevice_driver *drv)

{

conststructspi_device *spi = to_spi_device(dev);

conststructspi_driver *sdrv = to_spi_driver(drv);

if(of_driver_match_device(dev, drv))//设备文件驱动表的匹配

return1;

if(sdrv->id_table)//spi设备驱动存在支持id表

return!!spi_match_id(sdrv->id_table, spi);//spi设备驱动表的匹配

returnstrcmp(spi->modalias, drv->name) == 0;//比较spi设备的名字和spi设备驱动的名字

}

2.1 of_driver_match_device

staticinlineintof_driver_match_device(conststructdevice *dev,conststructdevice_driver *drv)

{

returnof_match_device(drv->of_match_table, dev) != NULL;//调用of_match_device函数

}

2.1.1 of_match_device

conststructof_device_id *of_match_device(conststructof_device_id *matches,conststructdevice *dev)

{

if((!matches) || (!dev->of_node))//id表和设备节点都不存在

returnNULL;//则返回

returnof_match_node(matches, dev->of_node);//调用of_match_node函数

}

EXPORT_SYMBOL(of_match_device);

2.1.1.1 of_match_node //drv->of_match_table,dev->of_node

conststructof_device_id *of_match_node(conststructof_device_id *matches,conststructdevice_node *node)

{

while(matches->name[0] || matches->type[0] || matches->compatible[0]) {//名字,类型,兼容方法有一个存在

intmatch = 1;

if(matches->name[0])//判断名字

match &= node->name && !strcmp(matches->name, node->name);

if(matches->type[0])//判断类型

match &= node->type && !strcmp(matches->type, node->type);

if(matches->compatible[0])//兼容方法

match &= of_device_is_compatible(node,matches->compatible);

if(match)//匹配

returnmatches;//返回匹配的id

matches++;  //matches指针++,指向下一个id

}

returnNULL;

}

EXPORT_SYMBOL(of_match_node);

2.2 spi_match_id

staticconststructspi_device_id *spi_match_id(conststructspi_device_id *id,conststructspi_device *sdev)

{

while(id->name[0]) {//id表的成员的名字域不为空

if(!strcmp(sdev->modalias, id->name))//则判断其名字是否与spi设备的名字一样

returnid;//一样则返回该id

id++;   //id表指针++,指向下一个id

}

returnNULL;

}

六 spi消息和spi传输

1.spi消息结构体

structspi_message {

structlist_head    transfers;//spi传输事务链表头

structspi_device   *spi;//所属spi设备

unsigned    is_dma_mapped:1;

void(*complete)(void*context);

void*context;

unsigned    actual_length;

intstatus;//传输状态

structlist_head    queue;

void*state;

};

2.初始化spi消息

staticinlinevoidspi_message_init(structspi_message *m)

{

memset(m, 0, sizeof*m);

INIT_LIST_HEAD(&m->transfers);   //初始化spi消息的事务链表头

}

3.添加传输事务到spi传输链表

staticinlinevoidspi_message_add_tail(structspi_transfer *t,structspi_message *m)

{

list_add_tail(&t->transfer_list, &m->transfers);

}

4.spi传输结构体

structspi_transfer {

constvoid*tx_buf;//发送缓冲区指针

void*rx_buf;//接收缓冲区指针

unsigned    len;    //消息长度

dma_addr_t  tx_dma; //DMA发送地址

dma_addr_t  rx_dma; //DMA接收地址

unsigned    cs_change:1;

u8      bits_per_word;  //一个字多少位

u16     delay_usecs;    //毫秒级延时

u32     speed_hz;   //速率

structlist_head transfer_list;//传输链表头

};

七.spi子系统的初始化spi_init

staticint__init spi_init(void)

{

intstatus;

buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);  //分配数据收发缓冲区

if(!buf) {

status = -ENOMEM;

gotoerr0;

}

status = bus_register(&spi_bus_type);   //注册spi总线

if(status < 0)

gotoerr1;

status = class_register(&spi_master_class); //注册spi主机类 "/sys/class/spi_master"

if(status < 0)

gotoerr2;

return0;

err2:

bus_unregister(&spi_bus_type);

err1:

kfree(buf);

buf = NULL;

err0:

returnstatus;

}

postcore_initcall(spi_init);    //入口声明 #define postcore_initcall(fn)    __define_initcall("2",fn,2)

八.spi子系统的API

1.spi读 spi_read

staticinlineintspi_read(structspi_device *spi, u8 *buf,size_tlen)

{

structspi_transfer t = {

.rx_buf     = buf,

.len        = len,

};

structspi_message  m;

spi_message_init(&m);   //spi消息初始化(初始化传输事务链表头)

spi_message_add_tail(&t, &m);   //添加spi传输到spi消息传输链表

returnspi_sync(spi, &m);//spi同步传输

}

2.spi写 spi_write

staticinlineintspi_write(structspi_device *spi,constu8 *buf,size_tlen)

{

structspi_transfer t = {

.tx_buf     = buf,

.len        = len,

};

structspi_message  m;

spi_message_init(&m);   //spi消息初始化(初始化传输事务链表头)

spi_message_add_tail(&t, &m);   //添加spi传输到spi消息传输链表

returnspi_sync(spi, &m);//spi同步传输

}

spi的读写操作都是初始化一个spi_transfer传输结构体,并将其添加进spi消息传输事务链表中

然后通过spi_sync来同步读写操作,接着看下spi_sync的具体代码

2.1 spi_sync

intspi_sync(structspi_device *spi,structspi_message *message)

{

return__spi_sync(spi, message, 0);//调用__spi_sync函数

}

EXPORT_SYMBOL_GPL(spi_sync);

2.1.1 __spi_sync函数

staticint__spi_sync(structspi_device *spi,structspi_message *message,intbus_locked)

{

DECLARE_COMPLETION_ONSTACK(done);   //声明一个工作队列

intstatus;

structspi_master *master = spi->master;//获取spi主机

message->complete = spi_complete;    //设置spi消息的complete方法为spi_complete

message->context = &done;    //设置spi消息的context

if(!bus_locked)

mutex_lock(&master->bus_lock_mutex); //上锁

status = spi_async_locked(spi, message);

if(!bus_locked)

mutex_unlock(&master->bus_lock_mutex);   //解锁

if(status == 0) {

wait_for_completion(&done); //等待完成

status = message->status;    //设置spi消息传输状态

}

message->context = NULL;

returnstatus;

}

2.1.1.1 spi_async_locked 函数

intspi_async_locked(structspi_device *spi,structspi_message *message)

{

structspi_master *master = spi->master;//获取spi主机

intret;

unsigned longflags;

spin_lock_irqsave(&master->bus_lock_spinlock, flags);    //上自旋锁

ret = __spi_async(spi, message);    //调用了spi异步同步方法

spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);   //解自旋锁

returnret;

}

EXPORT_SYMBOL_GPL(spi_async_locked);

2.1.1.1.1 __spi_async函数(异步)

staticint__spi_async(structspi_device *spi,structspi_message *message)

{

structspi_master *master = spi->master;

//主机为半双工或spi设备为3线设备

if((master->flags & SPI_MASTER_HALF_DUPLEX)|| (spi->mode & SPI_3WIRE)) {

structspi_transfer *xfer;

unsigned flags = master->flags;

list_for_each_entry(xfer, &message->transfers, transfer_list) {  //遍历spi消息的传输事务链表

if(xfer->rx_buf && xfer->tx_buf)//判断接收或发送缓冲区是否为空

return-EINVAL;

if((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)//检验无spi数据发送的情况

return-EINVAL;

if((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)//检验无spi数据接收的情况

return-EINVAL;

}

}

message->spi = spi;  //设置spi消息所属的spi设备

message->status = -EINPROGRESS;  //设置spi消息的传输状态

returnmaster->transfer(spi, message);//调用spi主机的transfer方法,收发spi信息给spi设备

}

3.spi先写后读 spi_write_then_read

intspi_write_then_read(structspi_device *spi,constu8 *txbuf, unsigned n_tx,u8 *rxbuf, unsigned n_rx)

{

staticDEFINE_MUTEX(lock);

intstatus;

structspi_message  message;

structspi_transfer x[2];//声明两个spi传输结构体

u8  *local_buf;

if((n_tx + n_rx) > SPI_BUFSIZ)//验证发送和接收的数据总和是否溢出

return-EINVAL;

spi_message_init(&message);     //spi消息初始化(初始化传输事务链表头)

memset(x, 0, sizeofx);//

if(n_tx) {

x[0].len = n_tx;

spi_message_add_tail(&x[0], &message);  //添加spi传输到spi消息传输链表

}

if(n_rx) {

x[1].len = n_rx;

spi_message_add_tail(&x[1], &message);  //添加spi传输到spi消息传输链表

}

if(!mutex_trylock(&lock)) {//尝试上锁 失败

local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);    //则分配local_buf内存

if(!local_buf)

return-ENOMEM;

} else

local_buf = buf;    //指向默认分配好内存的缓冲区(spi_init中初始化的)

memcpy(local_buf, txbuf, n_tx); //发送缓冲区的内容复制到local_buf中

x[0].tx_buf = local_buf;        //发送的spi传输结构体

x[1].rx_buf = local_buf + n_tx; //接收的spi传输结构体

status = spi_sync(spi, &message);   //spi同步传输--发送数据

if(status == 0)

memcpy(rxbuf, x[1].rx_buf, n_rx);   //接收返回的数据复制到rxbuf中

if(x[0].tx_buf == buf)

mutex_unlock(&lock);    //解锁

else

kfree(local_buf);   //释放内存

returnstatus;

}

EXPORT_SYMBOL_GPL(spi_write_then_read);

4.spi写8位后读8位数据 spi_w8r8

staticinlinessize_t spi_w8r8(structspi_device *spi, u8 cmd)

{

ssize_t status;

u8  result;

status = spi_write_then_read(spi, &cmd, 1, &result, 1); //写8位读8位

return(status < 0) ? status : result;

}

5.spi写8位数据后读16位数据 spi_w8r16

staticinlinessize_t spi_w8r16(structspi_device *spi, u8 cmd)

{

ssize_t status;

u16 result;

status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2);  //写8位读16位

return(status < 0) ? status : result;

}

这里的API是内核空间使用的接口,设备驱动程序调用这些API直接操作spi的读写操作,来完成任务

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值