微雪电子AIO-3128C主板SPI使用介绍

spi 使用
spi是一种高速的,全双工,同步串行通信接口,用于连接微控制器、传感器、存储设备等,本文以指纹识别模块为例简单介绍spi使用。
spi工作方式
spi以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,分别是:
cs 片选信号 sclk 时钟信号 mosi 主设备数据输出、从设备数据输入 miso 主设备数据输入,从设备数据输出
linux内核用cpol和cpha的组合来表示当前spi的四种工作模式:
cpol=0,cpha=0 spi_mode_0 cpol=0,cpha=1 spi_mode_1 cpol=1,cpha=0 spi_mode_2 cpol=1,cpha=1 spi_mode_3
cpol:表示时钟信号的初始电平的状态,0为低电平,1为高电平。cpha:表示在哪个时钟沿采样,0为第一个时钟沿采样,1为第二个时钟沿采样。spi的四种工作模式波形图如下:
在内核添加自己的驱动文件
在内核源码目录kernel/drivers/spi/中创建新的驱动文件,如:spi-rockchip-firefly.c 在驱动文件所在目录下的kconfig文件添加对应的驱动文件配置,如:
@@ -525,6 +525,10 @@ config spi_rockchip_test bool rockchip spi test code depends on spi_rockchip +config spi_rockchip_firefly + bool rockchip spi firefly code + depends on spi_rockchip + # # there are lots of spi device types, with sensors and memory # being probably the most widely used ones.
在驱动文件所在目录下的makefile文件添加对应的驱动文件名,如:
+obj-$(config_spi_rockchip_firefly) += spi-rockchip-firefly.o
用make menuconfig在内核选项中选中所添加的驱动文件,如:
there is no help available for this option. │ symbol: spi_rockchip_firefly [=y] │ type : boolean │ prompt: rockchip spi firefly code │ location: │ -> device drivers │ -> spi support (spi [=y]) │ -> rockchip spi controller core support (spi_rockchip_core [=y]) │ -> rockchip spi interface driver (spi_rockchip [=y]) │ defined at drivers/spi/kconfig:528 │ depends on: spi [=y] && spi_master [=y] && spi_rockchip [=y]
定义和注册spi设备
在dts中添加spi驱动结点描述,如下所示: kernel/arch/arm/boot/dts/aio-3128c.dts
&spi0 { status = okay; max-freq = ; spidev@00 { compatible = rockchip,spi_firefly; reg = ; spi-max-frequency = ; spi-cpha = ; //spi-cpol = ; }; };
属性:
status:如果要启用spi,则设为okay,如不启用,设为disable。
spidev@00:由于本例子使用的是spi0,且使用cs0,故此处设为00,如果使用cs1,则设为01。
compatible:这里的属性必须与驱动中的结构体:of_device_id 中的成员compatible 保持一致。
reg:此处与spidev@00保持一致,本例设为:0x00;
spi-max-frequency:此处设置spi使用的最高频率。
spi-cpha,spi-cpol:spi的工作模式在此设置,本例所用的模块spi工作模式为 spi_mode_1,故设:spi-cpha = ,如果您所用设备工作模式为spi_mode0,则需在此把这两个注释掉,如果用spi_mode3,则设:spi-cpha = ;spi-cpol = 。
定义和注册spi驱动
定义spi驱动
在定义 spi 驱动之前,用户首先要定义变量 of_device_id 。 of_device_id 用于在驱动中调用dts文件中定义的设备信息,其定义如下所示:
static const struct of_device_id spidev_dt_ids[] = { { .compatible = rockchip,spi_firefly }, {}, };
此处的compatible与dts文件中的保持一致。 定义spi_driver如下所示:
static struct spi_driver spidev_spi_driver = { .driver = { .name = silead_fp, .owner = this_module, .of_match_table = of_match_ptr(spidev_dt_ids), }, .probe = spi_gsl_probe, .remove = spi_gsl_remove, };
注册spi驱动
在初始化函数static int __init spidev_init(void)中创建一个字符设备:
alloc_chrdev_region(&devno, 0,255, sileadfp);
向内核添加该设备:
spidev_major = major(devno); cdev_init(&spicdev, &spidev_fops); spicdev.owner = this_module; status = cdev_add(&spicdev,mkdev(spidev_major, 0),n_spi_minors);
创建设备类:
class_create(this_module, spidev);
向内核注册spi驱动:
spi_register_driver(&spidev_spi_driver);
如果内核启动时匹配成功,则调用该驱动的probe函数。 probe函数如下所示:
static int spi_gsl_probe(struct spi_device *spi) { struct spidev_data *spidev; int status; unsigned long minor; struct gsl_fp_data *fp_data; printk(===============spi_gsl_probe ==============\n); if(!spi) return -enomem; /* allocate driver data */ spidev = kzalloc(sizeof(*spidev), gfp_kernel); if (!spidev) return -enomem; /* initialize the driver data */ spidev->spi = spi; spin_lock_init(&spidev->spi_lock);//初始化自旋锁 mutex_init(&spidev->buf_lock);//初始化互斥锁 init_list_head(&spidev->device_entry);//初始化设备链表 //init fp_data fp_data = kzalloc(sizeof(struct gsl_fp_data), gfp_kernel); if(fp_data == null){ status = -enomem; return status; } //set fp_data struct value fp_data->spidev = spidev; mutex_lock(&device_list_lock);//上互斥锁 minor = find_first_zero_bit(minors, n_spi_minors);//在内存区中查找第一个值为0的位 if (minor devt = mkdev(spidev_major, minor); dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, silead_fp_dev);创建/dev/下设备结点 status = is_err(dev) ? ptr_err(dev) : 0; } else { dev_dbg(&spi->dev, no minor number available!\n); status = -enodev; } if (status == 0) { set_bit(minor, minors); list_add(&spidev->device_entry, &device_list);//添加进设备链表 } mutex_unlock(&device_list_lock);//解互斥锁 if (status == 0) spi_set_drvdata(spi, spidev); else kfree(spidev); printk(%s:name=%s,bus_num=%d,cs=%d,mode=%d,speed=%d\n,__func__,spi->modalias, spi->master->bus_num, spi->chip_select, spi->mode, spi->max_speed_hz);//打印spi信息 return status; }
如果注册spi驱动成功,你可以在/dev/目录下面看你到注册的驱动名称,可以在sys/class/下面看到你注册的驱动设备类。
spi读写数据过程
spi写数据
static ssize_t spidev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; ssize_t status = 0; unsigned long missing; if (count > bufsiz) return -emsgsize; spidev = filp->private_data; mutex_lock(&spidev->buf_lock); missing = copy_from_user(spidev->buffer, buf, count);//把数据从用户空间传到内核空间 if (missing == 0) { status = spidev_sync_write(spidev, count);//调用写同步函数 } else status = -efault; mutex_unlock(&spidev->buf_lock); return status; }
写同步函数:
spidev_sync_write(struct spidev_data *spidev, size_t len) { struct spi_transfer t = { .tx_buf = spidev->buffer,//发送缓冲区 .len = len,//发送数据长度 }; struct spi_message m; spi_message_init(&m);//初始化spi_message spi_message_add_tail(&t, &m);//将新的spi_transfer添加到spi_message队列尾部 return spidev_sync(spidev, &m);//同步读写 }
spi读数据
在本例所用的模块中,读数据的过程为:主机向模块写寄存器的地址及读的指令(如:地址为0xf0,读指令为0x00)模块收到读的指令后,数据以页的形式发送主机设置读的模式主机读取一页数据并存储
static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; int status = 0; int i = 0; spidev = filp->private_data; mutex_lock(&spidev->buf_lock); gsl_fp_write(spidev, 0x00, 0xf0);//读之前先向模块写读的指令及寄存器地址 while(1){ for(i=0;i 0){ printk(gsl read data success!!!\n); }else{ printk(gsl read data failed!!!); } mutex_unlock(&spidev->buf_lock); return status; } static inline unsigned int gsl_fp_getoneframe(struct spidev_data *spidev,unsigned char reg_8b) { int status,i; unsigned char buf_d[128*1+3] = {0x00,0x00}; struct spi_transfer t; t.tx_buf = buf_d; t.rx_buf = buf_d; t.len = 131; status = gsl_spidev_sync_read(spidev, &t); if (status > 0){ for(i=0;ibits_per_word = 8;//每次读的数据长度为8位 t->delay_usecs = 1;//每次读完延时 t->speed_hz = 14*1000*1000;//读的速率 t->cs_change = 1;//cs引脚电平变化 spi_message_add_tail(t, &m); return spidev_sync(spidev, &m); }
注:firefly的spi驱动是linux下通用的驱动,可以参考源码:kernel/drivers/spi/spidev.c

加热连帽衫DIY图解
晶闸管式弧焊电源常见故障及排除方法
电子芯闻早报:高通推出骁龙821 荣耀8惊喜发布
十分钟让你看懂物联网
英伟达:5nm实验芯片用INT4达到INT8的精度
微雪电子AIO-3128C主板SPI使用介绍
芯片困局待解,华为发力非手机业务
电动车控制器功率小于电机功率会怎样
国产智能手表什么时候才能反超AppleWatch
换种方式建信息系统---勤哲软件
锤子空气净化器宣称可达800+CADR 是真的吗
智能语音助手的市场环境差,其发展道路艰难
揭秘凯迪拉克上海金桥工厂
用导热塑料灯壳来解决LED芯片工作温度的散热难题
“脚踩两只船”,英伟达下代7nm GPU同时由台积电、三星代工
物联网设备为何容易受到攻击?
诺西推出智能型Wi-Fi 实现WLAN与行动通讯无缝结合
Linux的发展前景怎么样
戴姆勒斥资5亿欧元研发自动驾驶卡车
科学家创造了一种算法,能够让机器人用绕线画重新创造出各种图像