Linux下SPI驱动测试程序

合集下载

linuxSPI驱动——gpio模拟spi驱动(转载)

linuxSPI驱动——gpio模拟spi驱动(转载)

linuxSPI驱动——gpio模拟spi驱动(转载)⼀:⾸先在我的平台注册platform_device,保证能让spi-gpio.c能执⾏到probe函数。

1: struct spi_gpio_platform_data {2: unsigned sck;3: unsigned mosi;4: unsigned miso;5:6: u16 num_chipselect;7: };1: //#define NCS GPIO_PB(2) //定义SS所对应的GPIO接⼝编号2: //#define SCLK GPIO_PB(0) //定义SCLK所对应的GPIO接⼝编号3: //#define MOSI GPIO_PB(4) //定义SCLK所对应的GPIO接⼝编号4: //#define MISO GPIO_PB(1)5: static struct spi_gpio_platform_data jz_spi_gpio_data = {6: .sck = GPIO_PB(0), //GPIO_SPI_SCK,7: .mosi = GPIO_PB(4), //GPIO_SPI_MOSI,8: .miso = GPIO_PB(1), //GPIO_SPI_MISO,9: .num_chipselect = 1,10: };11:12: struct platform_device jz_spi_gpio_device = {13: .name = "spi_gpio",14: .id = 0,15: .dev = {16: .platform_data = &jz_spi_gpio_data,17: },18: };注册platform device1: platform_device_register(&jz_spi_gpio_device);⼆:注册platform_driver在spi_gpio.c⾥⾯注册platform driver1: MODULE_ALIAS("platform:" DRIVER_NAME);2:3: static struct platform_driver spi_gpio_driver = {4: = DRIVER_NAME,5: .driver.owner = THIS_MODULE,6: .remove = __exit_p(spi_gpio_remove),7: };8:9: static int __init spi_gpio_init(void)10: {11: return platform_driver_probe(&spi_gpio_driver, spi_gpio_probe);12: }13: module_init(spi_gpio_init);14:15: static void __exit spi_gpio_exit(void)16: {17: platform_driver_unregister(&spi_gpio_driver);18: }19: module_exit(spi_gpio_exit);20:21:22: MODULE_DESCRIPTION("SPI master driver using generic bitbanged GPIO ");23: MODULE_AUTHOR("David Brownell");24: MODULE_LICENSE("GPL");三:具体算法分析1: struct spi_gpio {2: struct spi_bitbang bitbang; /* gpio 模拟spi算法相关的结构 */3: struct spi_gpio_platform_data pdata; /* spi platform data 对应模拟spi的四个gpio编号 */4: struct platform_device *pdev; /* 对应注册的 platform device */5: };1:2: static int __init spi_gpio_probe(struct platform_device *pdev)3: {4: int status;5: struct spi_master *master;6: struct spi_gpio *spi_gpio;7: struct spi_gpio_platform_data *pdata;8: u16 master_flags = 0;9:10: pdata = pdev->dev.platform_data; /* 存放spi的四根gpio */11: #ifdef GENERIC_BITBANG12: if (!pdata || !pdata->num_chipselect)13: return -ENODEV;14: #endif15:16: /* 申请注册四个gpio */17: status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags);18: if (status < 0) {19: return status;20: }21:22: /* alloc a spi master ,master->dev->p->driver_data = &master[1]*/23: master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio);24: if (!master) {25: status = -ENOMEM;26: goto gpio_free;27: }28: /* spi_gpio指向⼀块空间, 即指向mstaer[1]29: pdev->dev->p->driver_data = spi_gpio;30: 初始化spi_gpio31: */32: spi_gpio = spi_master_get_devdata(master);33: platform_set_drvdata(pdev, spi_gpio);34:35: spi_gpio->pdev = pdev;36: if (pdata)37: spi_gpio->pdata = *pdata;38:39: master->flags = master_flags;40: master->bus_num = pdev->id;41: master->num_chipselect = SPI_N_CHIPSEL;42: master->setup = spi_gpio_setup; /* setup ⽐如cs引脚申请 */43: master->cleanup = spi_gpio_cleanup;44: /* spi_gpio->bitbang.master = master */45: spi_gpio->bitbang.master = spi_master_get(master);46: spi_gpio->bitbang.chipselect = spi_gpio_chipselect;47: /* spi_gpio->bitbang.txrx_word 数组函数四个元素指针,分别指向spi四种mode算法函数 */ 48: if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {49: spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;50: spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;51: spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;52: spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;53: } else {54: spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;55: spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;56: spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;57: spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;58: }59: /* spi_gpio->bitbang.setup_transfer初始化传输的bits_per_word和speed */60: spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;61: spi_gpio->bitbang.flags = SPI_CS_HIGH;62: /* spi_gpio->bitbang相关算法接⼝初始化 */63: status = spi_bitbang_start(&spi_gpio->bitbang);64: if (status < 0) {65: spi_master_put(spi_gpio->bitbang.master);66: gpio_free:67: if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)68: gpio_free(SPI_MISO_GPIO);69: if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)70: gpio_free(SPI_MOSI_GPIO);71: gpio_free(SPI_SCK_GPIO);72: spi_master_put(master);73: }74:75: return status;76: }四:总之最终让spi_gpi0整个对象存放了整个gpio模拟spi的算法结构;⽽pdev->dev->p->driver_data = spi_gpio;platform device和 platform driver两者match结果是:root@CarRadio:/# ls /sys/bus/platform/devices/spi_gpio.0/ driver modalias power spi0.0 spi_master subsystem uevent root@CarRadio:/# ls /sys/bus/platform/devices/spi_gpio.0/driver/ spi_gpio.0 uevent。

ARM—Linux下SPI设备的添加与驱动实现

ARM—Linux下SPI设备的添加与驱动实现

策 略 和 接 口。 面 向用 户 接 口层 提 供 统 一 的 接 口, 便 S I 以 P
设 备 驱 动 通 过 总线 控 制 器 进 行 数 据 收 发 , 主 体 框 架 程 序 其
是 GP O模 拟 S I 序 s ibt a g C和 可 进 行 同/ 步 消 I P 时 p i n. b 异
d vc 置 定 义 S I 机 信 号 与 从 机 的 片 选 信 号 和 中 断 ei e配 P主
信 号 。si ei 配 置 指 定 具 体 S I 备 对 应 的 S I 机 p dvc — e P设 P 主
号 、 用的 S I 信模 式与传 输速度 和其使 用 的中断等 。 采 P通
构 图如图 l 示 。 所
甲台依赖 层(1 n1 eidL V r Pa 1R l ae1 e 、
2 借 用 通 用 s i e . 动 新 设 备 p —d v C驱
对于一些常规的 S I 备 , 存储器 、 度传感 器等 , P设 如 温 可 以将 其 连 接 到 选 定 的 S I主机 控 制 器 总 线 上 , 用 系 统 P 借 提 供 的 通 用 si e . p —d v C程 序 直 接 实 现 串 行 通 信 操 作 。需 要 做 的 是 在 平 台 匹 配 文 件 ma h X X C中 添 加 或 修 改 片 c— X.

息传 输 等操 作 的 s iC p. 。用 户 接 口层 即设 备 驱 动 层 , 用 为 户 提 供 了通 过 S I 线 访 问 具 体 设 备 的 接 口 , 括 S I P总 包 P 设 备 和 S I 动 程 序 。ARM —L n x下 S I总 线 的层 次 结 P驱 iu P
件 mah X X C 如 ma h s d c 1 . 。 c— X . , c m k 1 0 C

Linux内核驱动之spi子系统(二)用户空间spi驱动

Linux内核驱动之spi子系统(二)用户空间spi驱动

一概念用户空间驱动就是指在用户空间实现的驱动程序。

可以认为它跟普通的用户程序没有什么两样,它使用用户进程空间和栈,下面来看看用户态驱动的优点和缺点优点:1 可以和整个C库链接2 驱动的问题不会导致整个系统挂起3 容易调试缺点:1 中断和DMA在用户态是无法使用的(现在在用户态其实也是可以使用中断和DMA但是比较麻烦,比如可以使用poll函数来等待基于GPIO的中断,同样也可以使用/dev/mem来使用DMA)2 响应时间慢, 因为需要上下文切换在客户和硬件之间传递信息或动作所以总的来说对于一个较为慢速的不需要实时交互的设备,可以使用用户态的驱动来实现。

二spi用户态驱动spi用户驱动主要是依赖于内核模块spidev,该模块实现了一个通用的spi驱动,在用户空间调用该通用驱动提供的接口,操作相应的spi设备,其实这就是spi用户态驱动。

下面是配置内核模块spidev图1首先进入Device Drive==>,然后进入如上图1所示spi support图2如上图2选择用户态spi设备驱动,这样内核模块spidev就配置成功一添加spi_board_info首先需要在内核的arch/arm/mach-*/board-*.c 即BSP信息中添加一个spi_board_info(无论是用户态spi驱动还是内核态spi驱动都需要这样),同时名字必须是“spidev”,那么为什么要这么做了,这就是从 2.6内核起linux 驱动使用了一个叫做Device Model即设备模型的东东,总线,驱动和设备,总线将设备和驱动进行匹配,为了要能够使spi驱动的probe函数能够被调用,则必须存在一个设备与该驱动进行匹配,匹配成功后,spi子系统才会自动调用该驱动的probe函数,对于没有使用热插拔机制总线的设备比如platform,,spi,i2c,则必须手动在BSP信息中间手动添加设备信息,spi总线就是通过添加spi_board_info,来触发内核创建spi设备(struct spi_device),i2c则是添加i2c_board_info。

嵌入式Linux下AD7714与SPI接口及驱动的实现

嵌入式Linux下AD7714与SPI接口及驱动的实现

jc o dfrni cn i ao m t , ets r uth w ed vr ok ol l n e a persli acrt. et f ie tl ann c r e rt t e lso st r e rsnr a adt m l euts cua e as g l i e h e s h i w n hs e
21 0 1钲
仪 表 技 术 与 传 感 器
I sr me t T c n q e a d S n o nt u n e h iu n e sr
201 1
第 4期
N . o4
嵌 入 式 L n x- 7 1 iu F AD 7 4与 S I接 口及 驱 动 的 实 现 P
s isr l e p ea it fc) 是 Mo rl公 司推 出 的 一 P(ei r hrln r e a pi ea t a o o
种同步串行通讯方式 , 主要用 于 MC U或 MP U与各 种外 围设 备
以串行方式进行通信 , 是一种 高速 、 同步 、 全双工 的串行通信 总
c e t r n mi in B s d o h lt r o M9 mir p o e s r¥ 2 4 n mb d e p r t n s s m iu 2 6,hs p p r in a s s o . a e n t e p af m fAR c o r c s o 3 4 0 a d e e d d o e ai y t L n x . t i a e t s o C o e
选控制线 ( S ) N S 组成 。
12 A 71 . D 74芯 片 简 介
线 。S I P 因其传输高效 、 结构简单 , 入式系统 中得到广泛 在嵌

Linux驱动:SPI驱动编写要点

Linux驱动:SPI驱动编写要点

Linux驱动:SPI驱动编写要点题外话:⾯对成功和失败,⼀个⼈有没有“冠军之⼼”,直接影响他的表现。

⼏周前剖析了Linux SPI 驱动框架,算是明⽩个所以然,对于这么⼀个庞⼤的框架,并不是每⼀⾏代码都要⾃⼰去敲,因为前⼈已经把这个框架搭建好了,作为驱动开发者的我们只需要搞清楚哪⼀部分是需要⾃⼰修改或重新编写就OK了。

结合Linux内核⾯向对象的设计思想,SPI总的设计思路⼤概是这样的:第①处:内核中抽象了SPI控制器,让spi_master成为他的象征,他的实例化对象就是与硬⽣⽣的SPI控制器对应的,在Linux内核中习惯将集成到SOC上的控制器⽤假想的platform总线来进⾏管理,于是乎spi_master的实例化就得依靠platform_device和platform_driver来联⼿完成了。

细嚼慢咽:这⾥的platform_device就相当于是spi_master的静态描述:包括⼏号控制器、寄存器地址、引脚配置等等,把这些信息以“资源”的形式挂在platform_device上,等platform_driver找到命中注定的那个他之后就可以获得这个(静态描述)资源,probe中⽤这些资源就⽣出了spi_master实例对象。

这⼀系列流程前辈们都已经做好了,我们要做的就是将这静态描述platform_device修改成和⾃⼰SOC中的spi 控制器⼀致的特性即可。

即:适当修改arch/arm/mach-s5pv210/dev-spi.c中platform_device涉及的成员。

1// SPI0的寄存器地址2static struct resource s5pv210_spi0_resource[] = {3 [0] = {4 .start = S5PV210_PA_SPI0,5 .end = S5PV210_PA_SPI0 + 0x100 - 1,6 .flags = IORESOURCE_MEM,7 },8 [1] = {9 .start = DMACH_SPI0_TX,10 .end = DMACH_SPI0_TX,11 .flags = IORESOURCE_DMA,12 },13 [2] = {14 .start = DMACH_SPI0_RX,15 .end = DMACH_SPI0_RX,16 .flags = IORESOURCE_DMA,17 },18 [3] = {19 .start = IRQ_SPI0,20 .end = IRQ_SPI0,21 .flags = IORESOURCE_IRQ,22 },23 };2425/**26 * struct s3c64xx_spi_info - SPI Controller defining structure27 * @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field.28 * @src_clk_name: Platform name of the corresponding clock.29 * @clk_from_cmu: If the SPI clock/prescalar control block is present30 * by the platform's clock-management-unit and not in SPI controller.31 * @num_cs: Number of CS this controller emulates.32 * @cfg_gpio: Configure pins for this SPI controller.33 * @fifo_lvl_mask: All tx fifo_lvl fields start at offset-634 * @rx_lvl_offset: Depends on tx fifo_lvl field and bus number35 * @high_speed: If the controller supports HIGH_SPEED_EN bit36 * @tx_st_done: Depends on tx fifo_lvl field37*/38static struct s3c64xx_spi_info s5pv210_spi0_pdata = {39 .cfg_gpio = s5pv210_spi_cfg_gpio, //将GPIO配置成SPI0引脚的函数40 .fifo_lvl_mask = 0x1ff,41 .rx_lvl_offset = 15,42 .high_speed = 1, //看s5pv210的使⽤⼿册P901可知:这是⽤来配置CH_CFG寄存器的,主要是210⽤于从设备时......43 .tx_st_done = 25,44 };4546static u64 spi_dmamask = DMA_BIT_MASK(32);4748struct platform_device s5pv210_device_spi0 = {49 .name = "s3c64xx-spi",50 .id = 0,51 .num_resources = ARRAY_SIZE(s5pv210_spi0_resource),52 .resource = s5pv210_spi0_resource,53 .dev = {54 .dma_mask = &spi_dmamask,55 .coherent_dma_mask = DMA_BIT_MASK(32),56 .platform_data = &s5pv210_spi0_pdata,//特殊的spi_master数据57 },58 };platform_device第②处:添加/修改SPI外设“静态描述”的结构。

需要了解Linux下SPI从设备驱动的编写

需要了解Linux下SPI从设备驱动的编写

需要了解Linux下SPI从设备驱动的编写需要了解Linux下SPI从设备驱动的编写SPI(Serial Peripheral Interface) 是一个同步的四线制串行线,用于连接微控制器和传感器、存储器及外围设备。

三条信号线持有时钟信号(SCLK,经常在10MHz左右)和并行数据线带有“主出,从进(MOSI)”或是“主进,从出(MISO)”信号。

数据交换的时候有四种时钟模式,模式0和模式3是最经常使用的。

每个时钟周期将会传递数据进和出。

如果没有数据传递的话,时钟将不会循环。

SPI驱动分为两类:控制器驱动:它们通常内嵌于片上系统处理器,通常既支持主设备,又支持从设备。

这些驱动涉及硬件寄存器,可能使用DMA。

或它们使用GPIO引脚成为PIO bitbangers。

这部分通常会由特定的开发板提供商提供,不用自己写。

协议驱动:它们通过控制器驱动,以SPI连接的方式在主从设备之间传递信息。

这部分涉及具体的SPI从设备,通常需要自己编写。

那么特定的目标板如何让Linux 操控SPI设备?下面以AT91SAM9260系列CAN设备驱动为例,Linux内核版本为2.6.19。

本文不涉及控制器驱动分析。

board_info提供足够的信息使得系统正常工作而不需要芯片驱动加载[cpp] view plain copy在arch/arm/mach-at91rm9200/board-at91sam9260.c中有如下代码:#include#include…….static struct spi_board_info ek_spi_devices[] = {/* spi can ,add by mrz */#if defined(CONFIG_CAN_MCP2515) {.modalias = "mcp2515",.chip_select = 0,// .controller_data = AT91_PIN_PB3,。

嵌入式Linux下基于SPI总线的网络设备驱动设计与实现

嵌入式Linux下基于SPI总线的网络设备驱动设计与实现
g n r l  ̄ T ee e d d L n xn t o kd v c rv ra c i c u e a e n S Ii d s u s do i i al n o t it e n t r e e al h mb d e i u e y w r e i ed i e r h t t r s do P ic s e r n l a dh w wr ean w ewo k e b s g y o
第 2 卷 第 2 期 9 3
VO . 9 12 No. 23
计算 机 工程 与 设 计
Co u e n ie rn n sg mp trE g n e g a dDe in i
20 年 1 月 08 2
De .2 0 c 0 8
嵌入式 Lnx i 下基于 S I u P总线的网络设备驱动设计与实现
张 晓雷 陈相 宁 , 郭 剑 ,
(.南京 大 学 网络 安 全通信 实验 室 ,江 苏 南京 2 09 ; 1 10 3 2 .瑞博 强 芯 ( 津)科技 有 限公 司 南京 研发 中心 ,江 苏 南京 20 9 ) 天 10 3
摘 要 : 于 S I 基 P 总线 的 网络 驱动设 备是 一种 新型 的 网络 设备 , 其驱动 程序设 计 尚未经 过 系统分 析 。在 分析嵌 入 式 Ln x的 iu SI P 总线特 点 的基 础上 , ¥ C 4 0为 MC 以 E C 86 为 以太 网控 制 芯 片作 为设计 实例 , 以 3 21 U、 N 2 J0 首次详 细分析 介绍 了基 于 S I P 总
其 它 事 务 。 此 , 发 基 于 S I 线 的 网络 驱 动 程 序 除 了要 涉 因 开 P总 及 Ln x i 内核 网络 驱 动 程 序 的知 识 外 , 要 对 这 类 驱 动 程 序 体 u 还 系 结 构 有 深 入 了解 。 本 文 从 工 业 实 现 的角 度 详 细 分 析 了在 嵌 入 式 Ln x 基 于 i 下 u S I 线 的 网 络 驱动 体 系 结 构 , P总 以业 界 最 新 的E 2J0以太 网 NC 86 控 制 芯 片 为 设计 实例 进 一 步 分 析 和 实现 了 以 ¥ C 40为 MC 3 21 U 的嵌 入 式 Lnx . i 2 u 4内核 下 的 以太 网驱 动 程 序 , 后 给 出总 结 。 最

linux下串口驱动测试程序

linux下串口驱动测试程序

linux下串口驱动测试程序2009-12-26 20:42:20| 分类:技术| 标签:|字号大中小订阅【转载时请注明文章出处:】为了测试串口驱动,我们需要用串口驱动测试程序先向串口写入数据,然后再从串口读出来,以证明串口驱动的正确性。

下面我们来分析一下串口驱动测试程序的流程。

1、串口程序需要的头文件#include <stdio.h> /*标准输入输出定义*/#include <stdlib.h> /*标准函数库定义*/#include <unistd.h> /*Unix 标准函数定义*/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h> /*文件控制定义*/#include <termios.h> /*PPSIX 终端控制定义*/#include <errno.h> /*错误号定义*/2、打开串口设备fd = open( "/dev/ttyS0", O_RDWR); 我们以/dev/ttyS0即一般机子上的COM1为例。

3、设置串口设备:串口设备的设置主要通过struct termio的结构体来完成:struct termio{ unsigned short c_iflag; /* 输入模式标志 */unsigned short c_oflag; /* 输出模式标志 */unsigned short c_cflag; /* 控制模式标志*/unsigned short c_lflag; /* 当前模式标志 */unsigned char c_line; /* line discipline */unsigned char c_cc[NCC]; /* 控制字 */};我们用下面函数实现具体的串口设置:int setup_port(int fd, int baud, int databits, int parity, int stopbits){struct termio term_attr;if (ioctl(fd, TCGETA, &term_attr) < 0) { //将fd所对应的终端信息存入termio结构,得到当前配置return -1;}memcpy(&oterm_attr, &term_attr, sizeof(struct termio)); //保持当前配置到oterm_attr,用于系统恢复term_attr.c_iflag &= ~(INLCR | IGNCR | ICRNL | ISTRIP);term_attr.c_oflag &= ~(OPOST | ONLCR | OCRNL);term_attr.c_lflag &= ~(ISIG | ECHO | ICANON | NOFLSH);term_attr.c_cflag &= ~CBAUD;term_attr.c_cflag |= CREAD | speed_to_flag(baud); //设置波特率//标志各种标志可选范围term_attr.c_cflag &= ~(CSIZE); //设置数据位长度switch (databits) {case 5:term_attr.c_cflag |= CS5; //设置数据位长度为5,下同break;case 6:term_attr.c_cflag |= CS6;break;case 7:term_attr.c_cflag |= CS7;break;case 8:default:term_attr.c_cflag |= CS8;break;}switch (parity) { //设置奇偶校验位case 1:term_attr.c_cflag |= (PARENB | PARODD); //奇校验break;case 2:term_attr.c_cflag |= PARENB; //偶校验term_attr.c_cflag &= ~(PARODD);break;case 0:default:term_attr.c_cflag &= ~(PARENB); //不校验break;}switch (stopbits) { //设置停止位case 2: //有停止位为两位,下同term_attr.c_cflag |= CSTOPB;break;case 1:default:term_attr.c_cflag &= ~CSTOPB;break;}term_attr.c_cc[VMIN] = 1; //更新设置,并且立即完成term_attr.c_cc[VTIME] = 0; //设置超时if (ioctl(fd, TCSETAW, &term_attr) < 0) { //将更改后的属性加载到设备中return -1;}if (ioctl(fd, TCFLSH, 2) < 0) { //将输入输出队列全部发出,清空串口中队列return -1;}return 0;}4、向串口写入数据:while ( (len = read(0, buf, MAX_BUF_SIZE)) > 0 ) { if (len == 1) { //如果当前缓冲区为空,则写入停止位,发送buf[0] = MY_END_CHAR;buf[1] = 0;write_data(fd, buf, len);break;}i = write_data(fd, buf, len); //发送缓冲区内容if (i == 0) {fprintf(stderr, "Send data error!\n");break;}}5、从串口读数据:len = MAX_BUF_SIZE;while (1) {i = read_data(fd, buf, len); //从串口读取数据if (i > 0) {//count += i;//fprintf(stderr, "Recv %d byte\n", i);printf("%s", buf);if (buf[i-1] == MY_END_CHAR) { //如果读入的倒数第二位为停止位,则中断接受程序break;}}}在具体的测试中,在PC机上gcc编译程序,然后用arm-linux-gcc编程程序发送到友善之臂2440的板子上,然后一边发送一边接受,这样如果发送的数据和接受的数据相同,则测试成功。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Linux下的SPI总线驱动(一)2013-04-12 15:08:46分类:LINUX版权所有,转载请说明转自一.SPI理论介绍SPI总线全名,串行外围设备接口,是一种串行的主从接口,集成于很多微控制器内部。

和I2C使用2根线相比,SPI总线使用4根线:MOSI (SPI 总线主机输出/ 从机输入)、MISO (SPI总线主机输入/从机输出)、SCLK(时钟信号,由主设备产生)、CS(从设备使能信号,由主设备控制)。

由于SPI总线有专用的数据线用于数据的发送和接收,因此可以工作于全双工,当前市面上可以找到的SPI外围设备包括RF芯片、智能卡接口、E2PROM、RTC、触摸屏传感器、ADC。

SCLK信号线只由主设备控制,从设备不能控制信号线。

同样,在一个基于SPI的设备中,至少有一个主控设备。

这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCLK 时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。

也就是说,主设备通过对SCLK时钟线的控制可以完成对通讯的控制。

SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。

不同的SPI 设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。

在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。

在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C 系统要稍微复杂一些。

二.SPI驱动移植我们下面将的驱动的移植是针对Mini2440的SPI驱动的移植Step1:在Linux Source Code中修改arch/arm/mach-s3c2440/文件,加入头文件:#include <linux/spi/>#include <../mach-s3c2410/include/mach/>然后加入如下代码:static struct spi_board_info s3c2410_spi0_board[] ={[0] = {.modalias = "spidev", us_num = 0, hip_select = 0, rq = IRQ_EINT9, ax_speed_hz = 500 * 1000,in_cs = S3C2410_GPG(2),.num_cs = 1, us_num = 0, pio_setup = s3c24xx_spi_gpiocfg_bus0_gpe11_12_13, odalias = "spidev",.bus_num = 1,.chip_select = 0,.irq = IRQ_EINT2,.max_speed_hz = 500 * 1000,}};static struct s3c2410_spi_info s3c2410_spi1_platdata = {.pin_cs = S3C2410_GPG(3),.num_cs = 1,.bus_num = 1,.gpio_setup = s3c24xx_spi_gpiocfg_bus1_gpg5_6_7,};Step2:在mini2440_devices[]平台数组中添加如下代码:&s3c_device_spi0,&s3c_device_spi1,Step3:最后在mini2440_machine_init函数中加入如下代码:&s3c2410_spi0_platdata;spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board)); &s3c2410_spi1_platdata;spi_register_board_info(s3c2410_spi1_board, ARRAY_SIZE(s3c2410_spi1_board)); Step4:最后需要修改arch/arm/plat-s3c24xx/KConfig文件找到config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13boolhelpSPI GPIO configuration code for BUS0 when connected toGPE11, GPE12 and GPE13.config S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7boolhelpSPI GPIO configuration code for BUS 1 when connected toGPG5, GPG6 and GPG7.修改为config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13bool "S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13"helpSPI GPIO configuration code for BUS0 when connected toGPE11, GPE12 and GPE13.config S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7bool "S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7"helpSPI GPIO configuration code for BUS 1 when connected toGPG5, GPG6 and GPG7.Step5:最后make menuconfig配置,选中System Type和SPI support相应文件Step6:执行make生成zInage,将编译好的内核导入开发板,并且编译测试程序运行即可。

好了,我们的SPI驱动移植就做好了,我们可以编写SPI测试代码进行测试。

三.SPI设备和驱动的注册在SPI子系统中,包含两类设备驱动。

一类称之为.SPI主控设备驱动,用于驱动SPI主控设备,以和SPI总线交互,读写通信数据。

另一类称之为SPI接口设备驱动,用于解析SPI主控设备驱动读取的数据,形成有意义的协议数据。

下面我们就看看SPI主控设备的注册、SPI 主控设备驱动的注册、SPI接口设备的添加、SPI接口设备的注册、SPI接口设备驱动的注册五个过程。

主控设备的注册我们在移植的Step3:最后在mini2440_machine_init函数中加入如下代码:&s3c2410_spi0_platdata;spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board));&s3c2410_spi1_platdata;spi_register_board_info(s3c2410_spi1_board, ARRAY_SIZE(s3c2410_spi1_board));这里面的s3c_device_spi0其实定义在\arch\arm\plat-s3c24xx\中,跟踪下static struct resource s3c_spi0_resource[] = {[0] = {.start = S3C24XX_PA_SPI,.end = S3C24XX_PA_SPI + 0x1f,.flags = IORESOURCE_MEM,},[1] = {.start = IRQ_SPI0,.end = IRQ_SPI0,.flags = IORESOURCE_IRQ,}};static u64 s3c_device_spi0_dmamask = 0xffffffffUL;struct platform_device s3c_device_spi0 = {.name = "s3c2410-spi",.id = 0,.num_resources = ARRAY_SIZE(s3c_spi0_resource),.resource = s3c_spi0_resource,.dev = {.dma_mask = &s3c_device_spi0_dmamask,.coherent_dma_mask = 0xffffffffUL}};EXPORT_SYMBOL(s3c_device_spi0);这样就能理解移植时添加&s3c2410_spi0_platdata代码,其实是把s3c2410_spi0_platdata作为平台设备的私有数据。

在s3c_device_spi0中就包含了设备的寄存器地址,设备名称,设备所产生的总线号,总线挂载的数目,及各种配置函数。

然后由函数platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));统一把2440所有设备进行注册。

然后看下这个platform_add_devices注册函数主要干了什么事情。

在linux/drivers/base /中105行定义了这个函数。

函数调用platform_device_register()来进行注册。

然后在platform_device_regisrer中调用device_initialize(pdev->dev)platform_device_add(pdev)这俩个函数,从函数名称上我们推断一个是初始化设备信息中的dev结构体,另一个是把这个设备增加到什么地方去。

首先看初始化dev结构体。

初看下初始化了kobj相关东西,初始化链表,同步锁,还有相关标志。

然后看platform_device_add里面内容。

把其中一个pdev->=& platform_bus_type (全局变量)至此我们基本可以确定了,这个设备属于platform_bus_type。

所以这个设备的总线信息就知道了,但是总线还不知道这个设备,不过放心,在接下来的初始化过程中有一个函数bus_add_device,会让总线知道这个函数。

这样至此我们就把一个设备注册完毕,初始化了一些我们能初始化的东西。

结果之一是设备在总线上可以找到。

SPI接口设备的添加在移植的Step1中,曾经添加了如下代码static struct spi_board_info s3c2410_spi0_board[] ={[0] = {.modalias = "spidev", us_num = 0, hip_select = 0, rq = IRQ_EINT9, ax_speed_hz = 500 * 1000, //SPI的最大速率}};上面结构体来填充SPI接口的设备信息,然后通过函数spi_register_board_info(s3c2410_spi0_board,ARRAY_SIZE(s3c2410_spi0_board));注册。

相关文档
最新文档