imx6ul spi 设备驱动开发
imx6ul spi 设备驱动开发
- spi设备树格式
- spi设备树配置
- spi 驱动 设备树解析
- spi设备驱动使用
- spi通用设备驱动
- spi测试工具
- spi时序对比
- spi api 接口
- spi设备相关结构体
- spi 总线相关结构
- 申明spi设备方法
spi设备树格式
spi设备在设备树里像描述i2c设备一样,需要在spi控制器节点里用子节点描述spi设备节点:
&spi0 { /* spi控制器节点 */...cs-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>, <&pio 0 6 GPIO_ACTIVE_HIGH>;/* 片选的io口需与下面的spi设备节点一致 */spidev0 {compatible = "nanopi,spidev"; /* 此属性值用于与spi设备驱动匹配 */reg = <0>; /*spi设备是没有设备地址的, 这里是指使用spi控制器的cs-gpios里的第几个片选io */status = "okay"; /* status属性值为"okay"表示spidev0设备使能, "disabled"表示设备没有使用*/spi-max-frequency = <10000000>; /* 指定spi设备的最大工作时钟 */...buswidth = <8>; /* 传输以8位为单位 */mode = <0>; /* 使用第几种工作时序(CPOL, CPHA) *//*但在现用的内核源码里发现, spi设备的工作时序并不是用mode属性值来指定的*//* 如CPOL需要设1, 则只需在spi设备节点里加上"spi-cpol"属性即可; CPOL设0,则不写"spi-cpol"属性即可 *//* CPHA设1时, 则在设备节点里加上"spi-cpha"属性即可 *//* 还可以加入自定义的属性,用于指定工作时序方式及其它功能设置等 */};
};
spi设备树配置
&ecspi2 {compatible = "fsl,imx51-ecspi";fsl,spi-num-chipselects = <2>;cs-gpios = <&gpio4 22 0>,<&gpio3 20 0>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_ecspi2>,<&pinctrl_ecspi2_cs>;status = "okay";vs10xx-ctrl@0{compatible = "vs10xx-ctrl";cs-gpios = <&gpio4 22 0>;reg = <0>;//选择第一个片选引脚 MX6UL_PAD_CSI_DATA01__GPIO4_IO22// spi-cpol;// spi-cpha;spi-cpool和spi-cpha选择spi模式spi-max-frequency = <7000000>;status = "okay";};vs10xx-data@1{compatible = "vs10xx-data";cs-gpios = <&gpio3 20 0>;//spi片选reg = <1>;//选择第2个片选引脚 MX6UL_PAD_LCD_DATA15__GPIO3_IO20// spi-cpol;// spi-cpha; spi-cpool和spi-cpha选择spi模式spi-max-frequency = <9000000>;//spi最大频率status = "okay";};
};pinctrl_ecspi2: ecspi2grp {fsl,pins = <MX6UL_PAD_CSI_DATA03__ECSPI2_MISO 0x100b1MX6UL_PAD_CSI_DATA02__ECSPI2_MOSI 0x100b1MX6UL_PAD_CSI_DATA00__ECSPI2_SCLK 0x100b1>;
};
pinctrl_ecspi2_cs: ecspi2_csgrp {fsl,pins = <MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x80000000MX6UL_PAD_LCD_DATA15__GPIO3_IO20 0x80000000>;
};
spi 驱动 设备树解析
spi总线驱动路径
driver/spi/spi.c
spi 设备树属性解析:
/*** of_register_spi_devices() - Register child devices onto the SPI bus* @master: Pointer to spi_master device** Registers an spi_device for each child node of master node which has a 'reg'* property.*/
static void of_register_spi_devices(struct spi_master *master)
{struct spi_device *spi;struct device_node *nc;int rc;u32 value;if (!master->dev.of_node)return;//遍历总线设备节点for_each_available_child_of_node(master->dev.of_node, nc) {/* Alloc an spi_device */spi = spi_alloc_device(master);if (!spi) {dev_err(&master->dev, "spi_device alloc error for %s\n",nc->full_name);spi_dev_put(spi);continue;}/* Select device driver */if (of_modalias_node(nc, spi->modalias,sizeof(spi->modalias)) < 0) {dev_err(&master->dev, "cannot find modalias for %s\n",nc->full_name);spi_dev_put(spi);continue;}/* Device address */rc = of_property_read_u32(nc, "reg", &value);//读取reg属性if (rc) {dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",nc->full_name, rc);spi_dev_put(spi);continue;}spi->chip_select = value;/* Mode (clock phase/polarity/etc.) */if (of_find_property(nc, "spi-cpha", NULL))//根所给的名字查找对应的属性spi->mode |= SPI_CPHA;if (of_find_property(nc, "spi-cpol", NULL))spi->mode |= SPI_CPOL;if (of_find_property(nc, "spi-cs-high", NULL))spi->mode |= SPI_CS_HIGH;if (of_find_property(nc, "spi-3wire", NULL))spi->mode |= SPI_3WIRE;if (of_find_property(nc, "spi-lsb-first", NULL))spi->mode |= SPI_LSB_FIRST;/* Device DUAL/QUAD mode */if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) //读取spi-tx-bus-width属性值{switch (value) {case 1:break;case 2:spi->mode |= SPI_TX_DUAL;break;case 4:spi->mode |= SPI_TX_QUAD;break;default:dev_warn(&master->dev,"spi-tx-bus-width %d not supported\n",value);break;}}if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) //读取spi-rx-bus-width属性值{switch (value) {case 1:break;case 2:spi->mode |= SPI_RX_DUAL;break;case 4:spi->mode |= SPI_RX_QUAD;break;default:dev_warn(&master->dev,"spi-rx-bus-width %d not supported\n",value);break;}}/* Device speed */rc = of_property_read_u32(nc, "spi-max-frequency", &value);//读取spi最大传输速率if (rc) {dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",nc->full_name, rc);spi_dev_put(spi);continue;}spi->max_speed_hz = value;/* IRQ */spi->irq = irq_of_parse_and_map(nc, 0);/* Store a pointer to the node in the device structure */of_node_get(nc);spi->dev.of_node = nc;/* Register the new device */request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias);rc = spi_add_device(spi);if (rc) {dev_err(&master->dev, "spi_device register error %s\n",nc->full_name);spi_dev_put(spi);}}
}
** of_find_property: **
通过所给的名字找到相应节点的属性
** of_property_read_u32 **
读取节点属性值
spi设备驱动使用
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))static void pabort(const char *s)
{perror(s);abort();
}static const char *device = "/dev/spidev1.1";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;static void transfer(int fd)
{int ret;uint8_t tx[] = { //要发送的数据数组0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0x40, 0x00, 0x00, 0x00, 0x00, 0x95,0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,0xF0, 0x0D,};uint8_t rx[ARRAY_SIZE(tx)] = {0, }; //接收的数据数据struct spi_ioc_transfer tr = { //声明并初始化spi_ioc_transfer结构体.tx_buf = (unsigned long)tx,.rx_buf = (unsigned long)rx,.len = ARRAY_SIZE(tx),.delay_usecs = delay,.speed_hz = speed,.bits_per_word = bits,};//SPI_IOC_MESSAGE(1)的1表示spi_ioc_transfer的数量ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); //ioctl默认操作,传输数据if (ret < 1)pabort("can't send spi message");for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { //打印接收缓冲区if (!(ret % 6)) //6个数据为一簇打印puts("");printf("%.2X ", rx[ret]);}puts("");
}static void print_usage(const char *prog) //参数错误则打印帮助信息
{printf("Usage: %s [-DsbdlHOLC3]\n", prog);puts(" -D --device device to use (default /dev/spidev1.1)\n"" -s --speed max speed (Hz)\n"" -d --delay delay (usec)\n"" -b --bpw bits per word \n"" -l --loop loopback\n"" -H --cpha clock phase\n"" -O --cpol clock polarity\n"" -L --lsb least significant bit first\n"" -C --cs-high chip select active high\n"" -3 --3wire SI/SO signals shared\n");exit(1);
}static void parse_opts(int argc, char *argv[])
{while (1) {static const struct option lopts[] = { //参数命令表{ "device", 1, 0, 'D' },{ "speed", 1, 0, 's' },{ "delay", 1, 0, 'd' },{ "bpw", 1, 0, 'b' },{ "loop", 0, 0, 'l' },{ "cpha", 0, 0, 'H' },{ "cpol", 0, 0, 'O' },{ "lsb", 0, 0, 'L' },{ "cs-high", 0, 0, 'C' },{ "3wire", 0, 0, '3' },{ "no-cs", 0, 0, 'N' },{ "ready", 0, 0, 'R' },{ NULL, 0, 0, 0 },};int c;c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);if (c == -1)break;switch (c) {case 'D': //设备名device = optarg;break;case 's': //速率speed = atoi(optarg);break;case 'd': //延时时间delay = atoi(optarg);break;case 'b': //每字含多少位bits = atoi(optarg);break;case 'l': //回送模式mode |= SPI_LOOP;break;case 'H': //时钟相位mode |= SPI_CPHA;break;case 'O': //时钟极性mode |= SPI_CPOL;break;case 'L': //lsb 最低有效位mode |= SPI_LSB_FIRST;break;case 'C': //片选高电平mode |= SPI_CS_HIGH;break;case '3': //3线传输模式mode |= SPI_3WIRE;break;case 'N': //没片选mode |= SPI_NO_CS;break;case 'R': //从机拉低电平停止数据传输mode |= SPI_READY;break;default: //错误的参数print_usage(argv[0]);break;}}
}int main(int argc, char *argv[])
{int ret = 0;int fd;parse_opts(argc, argv); //解析传递进来的参数fd = open(device, O_RDWR); //打开设备文件if (fd < 0)pabort("can't open device");/** spi mode //设置spi设备模式*/ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); //写模式if (ret == -1)pabort("can't set spi mode");ret = ioctl(fd, SPI_IOC_RD_MODE, &mode); //读模式if (ret == -1)pabort("can't get spi mode");/** bits per word //设置每个字含多少位*/ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); //写 每个字含多少位if (ret == -1)pabort("can't set bits per word");ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits); //读 每个字含多少位if (ret == -1)pabort("can't get bits per word");/** max speed hz //设置速率*/ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); //写速率if (ret == -1)pabort("can't set max speed hz");ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); //读速率if (ret == -1)pabort("can't get max speed hz");//打印模式,每字多少位和速率信息printf("spi mode: %d\n", mode);printf("bits per word: %d\n", bits);printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);transfer(fd); //传输测试close(fd); //关闭设备return ret;
}
这里整理下ioctl的命令:
SPI_IOC_RD_MODE //读 模式
SPI_IOC_RD_LSB_FIRST //读 LSB
SPI_IOC_RD_BITS_PER_WORD //读 每字多少位
SPI_IOC_RD_MAX_SPEED_HZ //读 最大速率
SPI_IOC_WR_MODE //写 模式
SPI_IOC_WR_LSB_FIRST //写 LSB
SPI_IOC_WR_BITS_PER_WORD //写 每字多少位
SPI_IOC_WR_MAX_SPEED_HZ //写 最大速率
SPI_IOC_MESSAGE(n) //传输n个数据包
spi通用设备驱动
driver/spi/spidev.c
只需要在设备树中设置好spi属性,就可以启动此驱动文件。
spi测试工具
- spidev_test
- spi-tools
spi时序对比
spi api 接口
spi_write
spi_read
spi_w8r16
spi_w8r8
spi_write_then_read
spi_sync(同步),spi_async(异步)
上面的函数不可以同时收发数据,同时收发数据使用如下方法
struct spi_message msg; //spi控制器的传输操作是以消息来提交. 一条消息里可有多个传输操作struct spi_transfer x = { //每个spi_transfer对象来描述收、发、收发操作 .tx_buf = txbuf, //指定发出数据的缓冲区地址.rx_buf = rxbuf, //指定接收数据的缓冲区地址.len = strlen(txbuf), //收发数据的长度}; spi_message_init(&msg); //初始化消息对象spi_message_add_tail(&x, &msg); //把传输对象增加到消息对象里spi_sync(spi, &msg); //提交消息对象到控制器
链接: spi通信接口分析
spi设备相关结构体
- spi_device
struct spi_device {struct device dev; //基于device成员扩展, 在/sys/bus/spi/devie目录有相应的子目录(名为spi%d.%d)struct spi_master *master; //spi控制器对象的地址u32 max_speed_hz; //设备工作时钟最大多少HZu8 chip_select; u8 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */u8 bits_per_word; //传输的数据以多少位为单位int irq; //中断号void *controller_state;void *controller_data; //给控制器的驱动使用 char modalias[SPI_NAME_SIZE]; //spi设备的名字
};
- spi_driver
// spi设备驱动类型
struct spi_driver {const struct spi_device_id *id_table;int (*probe)(struct spi_device *spi);int (*remove)(struct spi_device *spi);void (*shutdown)(struct spi_device *spi);int (*suspend)(struct spi_device *spi, pm_message_t mesg);int (*resume)(struct spi_device *spi);struct device_driver driver; //基于device_driver扩展, 驱动模型
};extern int spi_register_driver(struct spi_driver *sdrv);
static inline void spi_unregister_driver(struct spi_driver *sdrv);
spi 总线相关结构
spi总线:
struct bus_type spi_bus_type = {.name = "spi",.dev_attrs = spi_dev_attrs,.match = spi_match_device,.uevent = spi_uevent,.pm = &spi_pm,
};
EXPORT_SYMBOL_GPL(spi_bus_type);static int spi_match_device(struct device *dev, struct device_driver *drv)
{const struct spi_device *spi = to_spi_device(dev);const struct spi_driver *sdrv = to_spi_driver(drv);...if (sdrv->id_table)return !!spi_match_id(sdrv->id_table, spi); //如设备驱动有id_table则按id_table里指定的设备名来匹配 return strcmp(spi->modalias, drv->name) == 0; //如设备驱动没有id_table则按名字来匹配
}
申明spi设备方法
1.设备树
2.在script.bin里声明spi设备. 这种方法只适用于SOC里spi控制器, 不适用基于GPIO口的控制器
[spi_devices]
spi_dev_num = 1 //表示spi设备的个数[spi_board0] //第0个spi设备
modalias = "spidev" //设备名
max_speed_hz = 33000000 //设备最高的工作时钟
bus_num = 0 //此设备是连接在编号为0的控制器上的
chip_select = 0 //使用控制器的第0个片选
mode = 0 //工作时序的方式
full_duplex = 1 //全双工, 表示MISO,MOSI都用上
manual_cs = 0 //表示片选线是由控制器自动控制的
3.在内核源码声明spi_board_info对象, 再内核初始化函数里spi_register_board_info注册设备信息
struct spi_board_info myspi_info = {.modalias = "spidev",.controller_data = GPIOA(20), //注意这里是指定设备的片选脚, 需查看控制器驱动代码里对controller_data成员的使用。.max_speed_hz = 100000, //100Khz.bus_num = 0, //控制器的编号.chip_select = 0, //第0个片选.mode = SPI_MODE_2,
};
imx6ul spi 设备驱动开发相关推荐
- Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)
一.前言 块设备主要为存储设备设计的框架. 在前面章节Linux下驱动开发_块设备驱动开发(内存模拟存储) 里介绍了块设备驱动编写思路,并且利用内存模拟了硬件存储,完成了块设备驱动开发测试.这一篇文章 ...
- 驱动程序开发:SPI设备驱动
目录 Linux下SPI驱动简介 SPI架构概述 SPI适配器(控制器) SPI设备驱动 spi_driver注册示例 SPI 设备和驱动匹配过程 编写imc20608六轴传感器SPI驱动 设备树编写 ...
- 《Linux设备驱动开发详解 A》一一2.3 接口与总线
本节书摘来华章计算机出版社<Linux设备驱动开发详解 A>一书中的第2章,第2.3节,作者:宋宝华 更多章节内容可以访问云栖社区"华章计算机"公众号查看.1 2.3 ...
- 虚拟字符设备驱动开发步骤
目录 前言 字符设备驱动简介 内核驱动操作函数集合(file_operations结构体) 字符设备驱动开发步骤 .ko驱动模块的加载和卸载(module_init驱动入口.insmod驱动加载) 字 ...
- 《嵌入式设备驱动开发精解》——导读
前言 嵌入式设备驱动开发精解 本书的编写主要是针对从事嵌入式软件开发人员.本书的内容主要涵盖ARM CPU以及各种常用外部设备驱动开发的方方面面,包括各种硬件接口.硬件接口协议说明以及各种外设的使用及 ...
- Linux 设备驱动开发思想 —— 驱动分层与驱动分离
前面我们学习I2C.USB.SD驱动时,有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是: 1.XXX 设备驱动 2.XXX 核心层 3.XXX 主机控制器驱动 而需要我们编 ...
- Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用
关与设备树的概念,我们在Exynos4412 内核移植(六)-- 设备树解析 里面已经学习过,下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构,设备树源(De ...
- Linux SPI设备驱动
实现了SPI OLED外设驱动,OLED型号为SH1106. 1.主机驱动与外设驱动分离 Linux中的I2C.SPI.USB等总线驱动,都采用了主机(控制器)驱动与外设(设备)驱动分离的思想.主机端 ...
- 【正点原子MP157连载】第二十章 字符设备驱动开发-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7
1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...
最新文章
- js中显示一个指定html文档,JS实现选定指定HTML元素对象中指定文本内容功能示例...
- python简单代码画图-Python科学画图代码分享
- 利用套接字实现 CS 模型
- C++实现基数排序(附完整源码)
- 基于决策树的多分类_R中基于决策树的糖尿病分类—一个零博客
- jQuery学习--选择器的使用
- Linux基础-1.Linux命令及获取帮助
- uni-app地址四级联动
- Java将多张图片合并保存到同一页PDF中
- android 混淆 minifyEnabled
- 信息学奥赛研究1:竞赛时间表、学习规划
- cruzer php sandisk 闪迪u盘量产工具_sandisk cruzer 32G U盘量产工具下载
- java使用aspose打印pdf、word文件
- 哈罗选了个好时点上线顺风车业务,但很可能雷声大雨点小
- 密码破解神器海德拉GUI界面-xhydra
- An exceptionCaught() event was fired, and it reached at the tail of the pipeline.
- 【爬虫】python爬虫从入门到放弃
- RTL8370N 8口千兆交换机 PCB图纸方案资料 和芯片的datasheet
- 基于SSM的小区物业管理系统JAVA【数据库设计、论文、源码、开题报告】
- c++某商店开展买一送一活动,购买两件商品时,只需支付价格较高的商品的金额。要求程序在输入两个商品的价格后,输出所应支付的金额,请根据裁判程序编写函数cut,将代码补充完整。