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测试工具

  1. spidev_test
  2. spi-tools

spi时序对比

spi api 接口

  1. spi_write

  2. spi_read

  3. spi_w8r16

  4. spi_w8r8

  5. spi_write_then_read

  6. spi_sync(同步),spi_async(异步)

  7. 上面的函数不可以同时收发数据,同时收发数据使用如下方法

        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设备相关结构体

  1. 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设备的名字
};
  1. 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 设备驱动开发相关推荐

  1. Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)

    一.前言 块设备主要为存储设备设计的框架. 在前面章节Linux下驱动开发_块设备驱动开发(内存模拟存储) 里介绍了块设备驱动编写思路,并且利用内存模拟了硬件存储,完成了块设备驱动开发测试.这一篇文章 ...

  2. 驱动程序开发:SPI设备驱动

    目录 Linux下SPI驱动简介 SPI架构概述 SPI适配器(控制器) SPI设备驱动 spi_driver注册示例 SPI 设备和驱动匹配过程 编写imc20608六轴传感器SPI驱动 设备树编写 ...

  3. 《Linux设备驱动开发详解 A》一一2.3 接口与总线

    本节书摘来华章计算机出版社<Linux设备驱动开发详解 A>一书中的第2章,第2.3节,作者:宋宝华 更多章节内容可以访问云栖社区"华章计算机"公众号查看.1 2.3 ...

  4. 虚拟字符设备驱动开发步骤

    目录 前言 字符设备驱动简介 内核驱动操作函数集合(file_operations结构体) 字符设备驱动开发步骤 .ko驱动模块的加载和卸载(module_init驱动入口.insmod驱动加载) 字 ...

  5. 《嵌入式设备驱动开发精解》——导读

    前言 嵌入式设备驱动开发精解 本书的编写主要是针对从事嵌入式软件开发人员.本书的内容主要涵盖ARM CPU以及各种常用外部设备驱动开发的方方面面,包括各种硬件接口.硬件接口协议说明以及各种外设的使用及 ...

  6. Linux 设备驱动开发思想 —— 驱动分层与驱动分离

    前面我们学习I2C.USB.SD驱动时,有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是: 1.XXX 设备驱动 2.XXX 核心层 3.XXX 主机控制器驱动 而需要我们编 ...

  7. Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用

    关与设备树的概念,我们在Exynos4412 内核移植(六)-- 设备树解析 里面已经学习过,下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构,设备树源(De ...

  8. Linux SPI设备驱动

    实现了SPI OLED外设驱动,OLED型号为SH1106. 1.主机驱动与外设驱动分离 Linux中的I2C.SPI.USB等总线驱动,都采用了主机(控制器)驱动与外设(设备)驱动分离的思想.主机端 ...

  9. 【正点原子MP157连载】第二十章 字符设备驱动开发-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

最新文章

  1. js中显示一个指定html文档,JS实现选定指定HTML元素对象中指定文本内容功能示例...
  2. python简单代码画图-Python科学画图代码分享
  3. 利用套接字实现 CS 模型
  4. C++实现基数排序(附完整源码)
  5. 基于决策树的多分类_R中基于决策树的糖尿病分类—一个零博客
  6. jQuery学习--选择器的使用
  7. Linux基础-1.Linux命令及获取帮助
  8. uni-app地址四级联动
  9. Java将多张图片合并保存到同一页PDF中
  10. android 混淆 minifyEnabled
  11. 信息学奥赛研究1:竞赛时间表、学习规划
  12. cruzer php sandisk 闪迪u盘量产工具_sandisk cruzer 32G U盘量产工具下载
  13. java使用aspose打印pdf、word文件
  14. 哈罗选了个好时点上线顺风车业务,但很可能雷声大雨点小
  15. 密码破解神器海德拉GUI界面-xhydra
  16. An exceptionCaught() event was fired, and it reached at the tail of the pipeline.
  17. 【爬虫】python爬虫从入门到放弃
  18. RTL8370N 8口千兆交换机 PCB图纸方案资料 和芯片的datasheet
  19. 基于SSM的小区物业管理系统JAVA【数据库设计、论文、源码、开题报告】
  20. c++某商店开展买一送一活动,购买两件商品时,只需支付价格较高的商品的金额。要求程序在输入两个商品的价格后,输出所应支付的金额,请根据裁判程序编写函数cut,将代码补充完整。

热门文章

  1. 【python学习.油价和美元汇率查询】
  2. 关于计算机课的课后感受,计算机课心得体会范文
  3. [SSM框架]—Spring入门
  4. DHCP服务器搭建操作步骤
  5. 一支笔的测试点_一张纸一支笔,简单一招教你自测是否有眼底黄斑病变
  6. 数据库如何转身云原生数据库
  7. 风吹柳叶(Curtain creeper)
  8. LevOJ P1685飞跃悬崖(着色问题)
  9. 电脑找不到网络许可管理器_许可到网络
  10. android bitmap回收,android BitMap回收