Linux下SPI驱动测试程序.docx

上传人:b****2 文档编号:17019251 上传时间:2023-07-21 格式:DOCX 页数:11 大小:342.54KB
下载 相关 举报
Linux下SPI驱动测试程序.docx_第1页
第1页 / 共11页
Linux下SPI驱动测试程序.docx_第2页
第2页 / 共11页
Linux下SPI驱动测试程序.docx_第3页
第3页 / 共11页
Linux下SPI驱动测试程序.docx_第4页
第4页 / 共11页
Linux下SPI驱动测试程序.docx_第5页
第5页 / 共11页
Linux下SPI驱动测试程序.docx_第6页
第6页 / 共11页
Linux下SPI驱动测试程序.docx_第7页
第7页 / 共11页
Linux下SPI驱动测试程序.docx_第8页
第8页 / 共11页
Linux下SPI驱动测试程序.docx_第9页
第9页 / 共11页
Linux下SPI驱动测试程序.docx_第10页
第10页 / 共11页
Linux下SPI驱动测试程序.docx_第11页
第11页 / 共11页
亲,该文档总共11页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Linux下SPI驱动测试程序.docx

《Linux下SPI驱动测试程序.docx》由会员分享,可在线阅读,更多相关《Linux下SPI驱动测试程序.docx(11页珍藏版)》请在冰点文库上搜索。

Linux下SPI驱动测试程序.docx

Linux下SPI驱动测试程序

 

Linux下SPI驱动测试程序

 Linux下的SPI总线驱动

(一) 2013-04-1215:

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 :

在LinuxSourceCode中修改arch/arm/mach-s3c2440/文件,加入头文件:

#include 

#include<../mach-s3c2410/include/mach/>

然后加入如下代码:

staticstructspi_board_infos3c2410_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, 

               }  

};  

staticstructs3c2410_spi_infos3c2410_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文件

找到

configS3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 

       bool 

       help  

        SPIGPIOconfigurationcodeforBUS0whenconnectedto 

        GPE11,GPE12andGPE13. 

  

configS3C24XX_SPI_BUS1_GPG5_GPG6_GPG7 

       bool 

      help  

         SPIGPIOconfigurationcodeforBUS1whenconnectedto 

         GPG5,GPG6andGPG7.

修改为

configS3C24XX_SPI_BUS0_GPE11_GPE12_GPE13 

       bool"S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13" 

       help  

        SPIGPIOconfigurationcodeforBUS0whenconnectedto 

        GPE11,GPE12andGPE13. 

  

configS3C24XX_SPI_BUS1_GPG5_GPG6_GPG7 

       bool"S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7" 

      help  

         SPIGPIOconfigurationcodeforBUS1whenconnectedto 

         GPG5,GPG6andGPG7.

Step5:

最后makemenuconfig配置,选中SystemType和SPIsupport相应文件

 

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\中,跟踪下

staticstructresources3c_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,

      }

};

staticu64s3c_device_spi0_dmamask=0xffffffffUL;

structplatform_devices3c_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中,曾经添加了如下代码

staticstructspi_board_infos3c2410_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));注册。

下面来跟踪下这个函数干了些什么事情。

int__init spi_register_board_info(structspi_board_infoconst*info,unsignedn)

{

      structboardinfo    *bi;

      bi=kmalloc(sizeof(*bi)+n*sizeof*info,GFP_KERNEL);

      if(!

bi)

             return-ENOMEM;

      bi->n_board_info=n;

//把s3c2410_spi0_board的信息都拷贝到结构体

      memcpy(bi->board_info,info,n*sizeof*info);

      mutex_lock(&board_lock);

      list_add_tail(&bi->list,&board_list);

      mutex_unlock(&board_lock);

      return0;

}

在这个函数里面,是把s3c2410_spi0_board的信息都拷贝到结构体

structboardinfo{

      structlist_head      list;

      unsigned        n_board_info;

      structspi_board_info    board_info[0];

};这里使用编程技巧定义个元素为0的数组,目的是接收s3c2410_spi0_borad里面的不确定元素,因为事先不知道元素的多少。

然后在系统编译的时候会把board_info的内存默认为0,所以赋值的时候还要自动申请内存。

memcpy(bi->board_info,info,n*sizeof*info);然后定义了同步锁,创建了链表。

list_add_tail(&bi->list,&board_list);这部分好像就到这个地方了,系统把信息保存到一块内存中,我们可以通过全局变量board_list找到这块地方。

SPI主控设备驱动的注册

在文件中

staticint__inits3c24xx_spi_init(void)

{

       returnplatform_driver_probe(&s3c24xx_spi_driver,s3c24xx_spi_probe);

}

在platform_driver_probe中会调用platform_driver_register(drv);继续跟踪

intplatform_driver_register(structplatform_driver*drv)

{

      drv->=&platform_bus_type; //总线类型

      if(drv->probe)

             drv->=platform_drv_probe;

      if(drv->remove)

             drv->=platform_drv_remove;

      if(drv->shutdown)

             drv->=platform_drv_shutdown;

      returndriver_register(&drv->driver);

}

看到没这个和SPI主控设备的注册过程中的最终挂接的总线类型是一致的。

这样SPI主控设备和SPI主控驱动都要注册到同一个总线上,总线再根据名称一样来进行匹配。

SPI接口设备的注册

我们继续看在文件

在s3c24xx_spi_probe里面我们调用spi_bitbang_start(&hw->bitbang);这就是SPI接口驱动的注册。

跟踪spi_bitbang_start函数,我们看到它调用了spi_register_master(bitbang->master);在函数spi_register_master()里面有一函数调用scan_boardinfo(master);用来扫描设备。

staticvoidscan_boardinfo(structspi_master*master)

{

      structboardinfo    *bi;

      mutex_lock(&board_lock);

      list_for_each_entry(bi,&board_list,list){//通过board_list遍历链表,取得设备信息

             structspi_board_info    *chip=bi->board_info;

             unsigned        n;

             for(n=bi->n_board_info;n>0;n--,chip++){

                    if(chip->bus_num!

=master->bus_num)

                           continue;

                    (void)spi_new_device(master,chip);

             }

      }

      mutex_unlock(&board_lock);

}

我们跟踪scan_boardinfo中的spi_new_device函数发现,spi_new_device调用spi_alloc_device,而在spi_alloc_device函数中有一句spi->=&spi_bus_type;这说明该设备就挂在全局变量spi_bus_type总线上了。

然后在spi_new_device中调用spi_add_device函数,目的我们已经看到,最终SPI接口的设备注册到了spi_bus_type上了,如果把SPI接口设备的驱动也注册到这个总线上,然后根据名称进行匹配则device和driver就配对成功。

SPI接口设备驱动的注册

我们以的驱动为例,来追踪该驱动的注册

staticint__initspidev_init(void)

{

      intstatus;

      BUILD_BUG_ON(N_SPI_MINORS>256);

      status=register_chrdev(SPIDEV_MAJOR,"spi",&spidev_fops);//注册字符设备

      if(status<0)

             returnstatus;

      spidev_class=class_create(THIS_MODULE,"spidev");

      if(IS_ERR(spidev_class)){

             unregister_chrdev(SPIDEV_MAJOR,             returnPTR_ERR(spidev_class);

      }

      status=spi_register_driver(&spidev_spi);//注册SPI接口设备驱动

      if(status<0){

             class_destroy(spidev_class);

             unregister_chrdev(SPIDEV_MAJOR,      }

      returnstatus;

}

我们跟踪下spi_register_driver

intspi_register_driver(structspi_driver*sdrv)

{

      sdrv->=&spi_bus_type;

      if(sdrv->probe)

             sdrv->=spi_drv_probe;

      if(sdrv->remove)

             sdrv->=spi_drv_remove;

      if(sdrv->shutdown)

             sdrv->=spi_drv_shutdown;

      returndriver_register(&sdrv->driver);

}

好了,我们在spi_register_driver里看到了sdrv->=&spi_bus_type;这说明我们的SPI设备驱动也接到了SPI总线上了,这个正好跟我们上面说的SPI接口设备的注册中提到的SPI设备也注册到SPI总线的,这样就可以通过驱动名匹配上了。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2