前言

SPI协议是由摩托罗拉公司提出的串行通信协议,是一种高速全双工的通信总线。常见的SPI接口包含四根线,MOSI(主设备输出,从设备输入),MISO(主设备输入,从设备输出)SCLK(时钟,决定通信速率),CS(片选,用于多从机)。所以一根SPI总线也可以挂载多个设备,是用多个片选IO来区分。

时序介绍

1.空闲状态
CS脚拉高时候SPI设备处于空闲状态。
2.起始信号与终止信号
CS脚下降沿时候为起始信号 CS脚上升沿时候为停止信号。
3.数据传输(四种工作模式)
(1)CPOL=0,串行时钟空闲状态为低电平。
(2)CPOL=1,串行时钟空闲状态为高电平。
(3)CPHA=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
(4)CPHA=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。

I2C数据结构介绍

spi_master

struct spi_master {struct device dev;//对应spi设备struct list_head list;s16          bus_num;//SPI总线号u16         num_chipselect;//片选信号的个数u16         dma_alignment;//dma buf的对齐/*控制器启动的模式位,被控制器驱动解析*/u16          mode_bits;/* bitmask of supported bits_per_word for transfers */u32         bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))/* 通信速率 */u32           min_speed_hz;u32            max_speed_hz;/* other constraints relevant to this driver */u16         flags;
#define SPI_MASTER_HALF_DUPLEX  BIT(0)      /* 不能全双工*/
#define SPI_MASTER_NO_RX    BIT(1)      /* 不能读*/
#define SPI_MASTER_NO_TX    BIT(2)      /* 不能写*/
#define SPI_MASTER_MUST_RX      BIT(3)      /* 必须有读 */
#define SPI_MASTER_MUST_TX      BIT(4)      /* 必须有写*//* lock and mutex for SPI bus locking */spinlock_t     bus_lock_spinlock;struct mutex      bus_lock_mutex;/* flag indicating that the SPI bus is locked for exclusive use */bool           bus_lock_flag;int           (*setup)(struct spi_device *spi);//时钟和spi模式的设置函数int         (*transfer)(struct spi_device *spi,struct spi_message *mesg);//SPI的通信函数指针/* called on release() to free memory provided by spi_master */void            (*cleanup)(struct spi_device *spi);bool         (*can_dma)(struct spi_master *master,struct spi_device *spi,struct spi_transfer *xfer);/*内核线程 ,SPI控制器 用到了内核线程*/bool              queued;struct kthread_worker        kworker;struct task_struct      *kworker_task;struct kthread_work       pump_messages;spinlock_t            queue_lock;struct list_head     queue;struct spi_message        *cur_msg;bool               idling;bool             busy;bool               running;bool                rt;bool             auto_runtime_pm;bool                            cur_msg_prepared;bool               cur_msg_mapped;struct completion               xfer_completion;size_t               max_dma_len;int (*prepare_transfer_hardware)(struct spi_master *master);// 准备传输前硬件初始化int (*transfer_one_message)(struct spi_master *master,struct spi_message *mesg);int (*unprepare_transfer_hardware)(struct spi_master *master);int (*prepare_message)(struct spi_master *master,struct spi_message *message);int (*unprepare_message)(struct spi_master *master,struct spi_message *message);void (*set_cs)(struct spi_device *spi, bool enable);int (*transfer_one)(struct spi_master *master, struct spi_device *spi,struct spi_transfer *transfer);void (*handle_err)(struct spi_master *master,struct spi_message *message);/* gpio chip select */int           *cs_gpios;/* DMA channels for use with core dmaengine helpers */struct dma_chan     *dma_tx;struct dma_chan     *dma_rx;/* dummy data for full duplex devices */void            *dummy_rx;void          *dummy_tx;
};

spi_device

struct spi_device {struct device     dev;struct spi_master   *master;//设备对应控制器u32            max_speed_hz;u8         chip_select;//片选u8          bits_per_word;u16           mode;//模式(下面的#DEFINE)
#define SPI_CPHA    0x01            /* 时钟相位*/
#define SPI_CPOL    0x02            /*时钟极性 */
#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            /* CS是不是高位有效 */
#define SPI_LSB_FIRST   0x08            /* 先传输最低有效位 */
#define SPI_3WIRE   0x10            /* SI/SO signals shared */
#define SPI_LOOP    0x20            /* loopback mode */
#define SPI_NO_CS   0x40            /*只有一个从设备 无片选 */
#define SPI_READY   0x80            /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100           /* 两条传输线*/
#define SPI_TX_QUAD 0x200           /* 四条传输线*/
#define SPI_RX_DUAL 0x400           /*两根线接收*/
#define SPI_RX_QUAD 0x800           /*四根线接收 */int           irq;void            *controller_state;void          *controller_data;char           modalias[SPI_NAME_SIZE];int         cs_gpio;    /* chip select gpio */
};

spi_message

struct spi_message {struct list_head transfers;struct spi_device *spi;unsigned       is_dma_mapped:1;//是否使用DMA模式/* 传输完成后的部分 */void           (*complete)(void *context);void         *context;unsigned       frame_length;unsigned       actual_length;int           status;/* for optional use by whatever driver currently owns the* spi_message ...  between calls to spi_async and then later* complete(), that's the spi_master controller driver.*/struct list_head   queue;void          *state;
};

spi_transfer

struct spi_transfer {const void  *tx_buf;//发送缓冲区void     *rx_buf;//接收缓冲区unsigned len;//缓冲区字节数/*DMA传输情况下 发送接收BUFFER的DMA地址*/dma_addr_t tx_dma;dma_addr_t   rx_dma;struct sg_table tx_sg;struct sg_table rx_sg;unsigned cs_change:1;//如果设置了,则此次传输完成后将会翻转片选unsigned   tx_nbits:3;unsigned rx_nbits:3;
#define SPI_NBITS_SINGLE    0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL      0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD      0x04 /* 4bits transfer */u8     bits_per_word;u16       delay_usecs;u32     speed_hz;struct list_head transfer_list;
};

SPI控制器

在drivers/spi/spi-imx.c文件中

match

static struct platform_device_id spi_imx_devtype[] = {...{.name = "imx21-cspi",.driver_data = (kernel_ulong_t) &imx21_cspi_devtype_data,}, {.name = "imx27-cspi",.driver_data = (kernel_ulong_t) &imx27_cspi_devtype_data,}, ...
};static const struct of_device_id spi_imx_dt_ids[] = {{ .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, },{ .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, },...
};
MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
static struct platform_driver spi_imx_driver = {.driver = {.name = DRIVER_NAME,.of_match_table = spi_imx_dt_ids,.pm = IMX_SPI_PM,},.id_table = spi_imx_devtype,.probe = spi_imx_probe,.remove = spi_imx_remove,
};
module_platform_driver(spi_imx_driver);

匹配适配器成功后运行probe函数

probe

static int spi_imx_probe(struct platform_device *pdev)
{struct device_node *np = pdev->dev.of_node;const struct of_device_id *of_id =of_match_device(spi_imx_dt_ids, &pdev->dev);struct spi_imx_master *mxc_platform_info =dev_get_platdata(&pdev->dev);struct spi_master *master;struct spi_imx_data *spi_imx;struct resource *res;int i, ret, num_cs, irq;if (!np && !mxc_platform_info) {dev_err(&pdev->dev, "can't get the platform data\n");return -EINVAL;}ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);if (ret < 0) {if (mxc_platform_info)num_cs = mxc_platform_info->num_chipselect;elsereturn ret;}/*申请一个SPI适配器*/master = spi_alloc_master(&pdev->dev,sizeof(struct spi_imx_data) + sizeof(int) * num_cs);if (!master)return -ENOMEM;platform_set_drvdata(pdev, master);master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);master->bus_num = pdev->id;//总线号master->num_chipselect = num_cs;//片选号spi_imx = spi_master_get_devdata(master);spi_imx->bitbang.master = master;/*从设备树获取片选IO*/for (i = 0; i < master->num_chipselect; i++) {int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);if (!gpio_is_valid(cs_gpio) && mxc_platform_info)cs_gpio = mxc_platform_info->chipselect[i];spi_imx->chipselect[i] = cs_gpio;if (!gpio_is_valid(cs_gpio))continue;ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i],DRIVER_NAME);if (ret) {dev_err(&pdev->dev, "can't get cs gpios\n");goto out_master_put;}}//设置相关通信的函数指针spi_imx->bitbang.chipselect = spi_imx_chipselect;spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;spi_imx->bitbang.txrx_bufs = spi_imx_transfer;spi_imx->bitbang.master->setup = spi_imx_setup;spi_imx->bitbang.master->cleanup = spi_imx_cleanup;spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;init_completion(&spi_imx->xfer_done);spi_imx->devtype_data = of_id ? of_id->data :(struct spi_imx_devtype_data *) pdev->id_entry->driver_data;//获取SPI的基地址并虚拟res = platform_get_resource(pdev, IORESOURCE_MEM, 0);spi_imx->base = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(spi_imx->base)) {ret = PTR_ERR(spi_imx->base);goto out_master_put;}//获取设备树中配置的中断irq = platform_get_irq(pdev, 0);if (irq < 0) {ret = irq;goto out_master_put;}//申请一个中断ret = devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0,dev_name(&pdev->dev), spi_imx);if (ret) {dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret);goto out_master_put;}//获取设备树ipgclkspi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");if (IS_ERR(spi_imx->clk_ipg)) {ret = PTR_ERR(spi_imx->clk_ipg);goto out_master_put;}//获取设备树perclkspi_imx->clk_per = devm_clk_get(&pdev->dev, "per");if (IS_ERR(spi_imx->clk_per)) {ret = PTR_ERR(spi_imx->clk_per);goto out_master_put;}ret = clk_prepare_enable(spi_imx->clk_per);if (ret)goto out_master_put;ret = clk_prepare_enable(spi_imx->clk_ipg);if (ret)goto out_put_per;//设置spi clkspi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);/** Only validated on i.mx6 now, can remove the constrain if validated on* other chips.*/if ((spi_imx->devtype_data == &imx51_ecspi_devtype_data|| spi_imx->devtype_data == &imx6ul_ecspi_devtype_data)&& spi_imx_sdma_init(&pdev->dev, spi_imx, master, res))dev_err(&pdev->dev, "dma setup error,use pio instead\n");spi_imx->devtype_data->reset(spi_imx);spi_imx->devtype_data->intctrl(spi_imx, 0);master->dev.of_node = pdev->dev.of_node;ret = spi_bitbang_start(&spi_imx->bitbang);//此函数用于将spi_bitbang的参数进行初始化if (ret) {dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);goto out_clk_put;}dev_info(&pdev->dev, "probed\n");clk_disable_unprepare(spi_imx->clk_ipg);clk_disable_unprepare(spi_imx->clk_per);return ret;out_clk_put:clk_disable_unprepare(spi_imx->clk_ipg);
out_put_per:clk_disable_unprepare(spi_imx->clk_per);
out_master_put:spi_master_put(master);return ret;
}

spi_bitbang_start

int spi_bitbang_start(struct spi_bitbang *bitbang)
{struct spi_master *master = bitbang->master;int ret;if (!master || !bitbang->chipselect)return -EINVAL;spin_lock_init(&bitbang->lock);if (!master->mode_bits)master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;if (master->transfer || master->transfer_one_message)return -EINVAL;/*设置适配器传输消息的函数指针*/master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;master->transfer_one_message = spi_bitbang_transfer_one;if (!bitbang->txrx_bufs) {bitbang->use_dma = 0;bitbang->txrx_bufs = spi_bitbang_bufs;if (!master->setup) {if (!bitbang->setup_transfer)bitbang->setup_transfer =spi_bitbang_setup_transfer;master->setup = spi_bitbang_setup;master->cleanup = spi_bitbang_cleanup;}}/* driver may get busy before register() returns, especially* if someone registered boardinfo for devices*/ret = spi_register_master(spi_master_get(master));if (ret)spi_master_put(master);return 0;
}
static int spi_bitbang_transfer_one(struct spi_master *master,struct spi_message *m)
{struct spi_bitbang *bitbang;unsigned       nsecs;struct spi_transfer   *t = NULL;unsigned     cs_change;int           status;int          do_setup = -1;struct spi_device    *spi = m->spi;bitbang = spi_master_get_devdata(master);/* FIXME this is made-up ... the correct value is known to* word-at-a-time bitbang code, and presumably chipselect()* should enforce these requirements too?*/nsecs = 100;cs_change = 1;status = 0;list_for_each_entry(t, &m->transfers, transfer_list) {/* override speed or wordsize? */if (t->speed_hz || t->bits_per_word)do_setup = 1;/* init (-1) or override (1) transfer params */if (do_setup != 0) {if (bitbang->setup_transfer) {status = bitbang->setup_transfer(spi, t);//设置时钟极性if (status < 0)break;}if (do_setup == -1)do_setup = 0;}/* set up default clock polarity, and activate chip;* this implicitly updates clock and spi modes as* previously recorded for this device via setup().* (and also deselects any other chip that might be* selected ...)*/if (cs_change) {bitbang->chipselect(spi, BITBANG_CS_ACTIVE);//片选激活ndelay(nsecs);}cs_change = t->cs_change;if (!t->tx_buf && !t->rx_buf && t->len) {status = -EINVAL;break;}/* transfer data.  the lower level code handles any* new dma mappings it needs. our caller always gave* us dma-safe buffers.*/if (t->len) {/* REVISIT dma API still needs a designated* DMA_ADDR_INVALID; ~0 might be better.*/if (!m->is_dma_mapped)t->rx_dma = t->tx_dma = 0;status = bitbang->txrx_bufs(spi, t);//传输数据 也就是probe设置对应的spi_imx_transfer函数}if (status > 0)m->actual_length += status;if (status != t->len) {/* always report some kind of error */if (status >= 0)status = -EREMOTEIO;break;}status = 0;/* protocol tweaks before next transfer */if (t->delay_usecs)udelay(t->delay_usecs);if (cs_change &&!list_is_last(&t->transfer_list, &m->transfers)) {/* sometimes a short mid-message deselect of the chip* may be needed to terminate a mode or command*/ndelay(nsecs);bitbang->chipselect(spi, BITBANG_CS_INACTIVE);ndelay(nsecs);}}...
}

发送流程


spi_message_init();     /* 初始化spi_message */
spi_message_add_tail();/* 将spi_transfer添加到spi_message队列 */
spi_sync(); /* 同步发送 */->__spi_sync()->__spi_pump_messages()-> master->transfer_one_message()-> master->transfer_one_message = spi_bitbang_transfer_one;-> spi_imx->bitbang.txrx_bufs = spi_imx_transfer;-> bitbang->txrx_bufs(spi, t);->spi_imx_transfer()->spi_imx_pio_transfer()->spi_imx_push()->spi_imx->tx(spi_imx);//spi_imx_setupxfer中设置好了发送函数以U8来说->spi_imx_buf_tx_u8()static void spi_imx_buf_tx_u8(struct spi_imx_data *spi_imx)
{                                   type val = 0;                                                          if (spi_imx->tx_buf) {                       val = *(type *)spi_imx->tx_buf;             spi_imx->tx_buf += sizeof(type);           }                                                           spi_imx->count -= sizeof(type);                                         writel(val, spi_imx->base + MXC_CSPITXDATA);
}

函数介绍

/*注册和释放spi驱动*/
int spi_register_driver(struct spi_driver *sdrv);
void spi_unregister_driver(struct spi_driver *sdrv);static void spi_message_init(struct spi_message *m);//消息初始化
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)//将spi_transfer添加到spi_message队列
int spi_sync(struct spi_device *spi, struct spi_message *message)//同步发送
int spi_async(struct spi_device *spi, struct spi_message *message)//异步发送

linux驱动学习笔记(九)SPI相关推荐

  1. Linux 驱动学习笔记 - beep(九)

    Linux 驱动学习笔记 - beep(九) 本系列均为正点原子 Linux 驱动的学习笔记, 以便加深笔者记忆.如读者想进一步学习,可以到正点原子官网中下载资料进行学习. 添加 pinctrl 节点 ...

  2. IMX6ULL嵌入式Linux驱动学习笔记(二)

    IMX6ULL嵌入式Linux驱动学习 一.字符设备驱动 二.驱动模块的加载与卸载 三.字符设备的注册与注销 四.设备号 五.file_operations的具体实现 六.字符设备驱动框架 七.编写应 ...

  3. Linux驱动学习笔记

    驱动学习笔记 1.字符设备驱动 Linux 驱动有两种运行方式 第一种就是将驱动编译进 Linux 内核中,这样当 Linux 内核启 动的时候就会自动运行驱动程序. 第二种就是将驱动编译成模块(Li ...

  4. 讯为4412开发板Linux驱动学习笔记

    驱动理论专题一 Linux驱动程序的基本认识 有了内存管理单元,就有虚拟地址,物理地址. 驱动理论专题二 学会查看原理图 以LED2为示例 通过原理图查看到KP_COL0,赋予高电平则能点亮LED2, ...

  5. linux驱动学习笔记(三)阻塞与非阻塞IO

    Linux驱动中阻塞与非阻塞IO 前言 阻塞 非阻塞 一.等待队列 1.等待队列头 2.等待队列 模板 二.轮询 模板 总结 前言 阻塞和非阻塞io是两种不同的设备访问方式. 阻塞 阻塞IO表示在执行 ...

  6. Linux驱动学习笔记之触摸屏驱动

    触摸屏归纳为输入子系统,这里主要是针对电阻屏,其使用过程如下 当用触摸笔按下时,产生中断. 在中断处理函数处理函数中启动ADC转换x,y坐标. ADC结束,产生ADC中断 在ADC中断处理函数里上报( ...

  7. linux驱动学习笔记(2.4) scull 脚本scull_init

    向自己道歉,没能抽出更多的时间,进度如此的慢. 现在想认真学习下scull模块的这个初始化脚本 scull_init.sh #!/bin/bash # Sample init script for t ...

  8. Linux驱动学习笔记之并发控制

    Linux 设备驱动中必须解决的一个问题是多个进程对共享资源的并发访问,并发访问会导致竞态. 并发(concurrency)指的是多个执行单元同时.并行被执行,而并发的执行单元对共享资源(硬件资源和软 ...

  9. Linux驱动学习笔记 -- 驱动总线实验

    驱动总线 在Linux系统中,除了硬件总线,还有一种软件虚拟出来的总线 – 驱动总线bus 这种驱动总线的作用:软件与硬件代码分离,提高程序的复用性 驱动总线分三个部分: 三者都是在/include/ ...

最新文章

  1. linux bash字符串截取
  2. apache java windows_Apache for Windows 安装
  3. undefined symbol: sqlite3_open_v2
  4. Servlet跳转到jsp页面的几种方法
  5. openstack简易汉化
  6. boost::mpl::size相关的测试程序
  7. Java REST框架一览
  8. 数位板驱动压力测试_数位屏应如何选择?
  9. Unity MRTK语音输入
  10. 04735数据库系统原理(知识点整合)
  11. c#中panel控件有什么作用
  12. nc交换平台翻译器翻译仓库问题以及解决方法
  13. PHP小白之路1--PHP之简易留言板设计
  14. 2017-2018 Petrozavodsk Winter Training Camp, Saratov SU Contest C.Cover the Paths 贪心+DFS
  15. C++超市管理系统(MySQL)
  16. 这个超级好哎 vector 容器的 怕失效
  17. 阿里云RDS数据库如何远程访问
  18. 什么是 PHP? 为什么用 PHP? 有谁在用 PHP?
  19. wgcna 原文复现 小胶质细胞亚群在脑发育时髓鞘形成的作用 microglial
  20. ubuntu18.04企业微信乱码

热门文章

  1. java判捕获e异常类型_Java SE7新特性之捕获多种类型的异常并且重新抛出使用改进的类型检查的异常...
  2. Java SE7新特性之泛型实例创建时的类型推断
  3. 通达信api接口破解方式?
  4. RTOS如何保证实时性
  5. MPC学习笔记1:基于状态空间模型的预测控制(1)
  6. contest: UTPC Contest 10-28-22 Div. 1 (Advanced), problem: (C)
  7. 博科Brocads SAN交换机常用命令
  8. springboot整合freemarker案例
  9. 实现米思齐按键控制LED
  10. 计算机控制系统在机电设备中的应用,接口技术在机电一体化控制系统中的应用...