Linux ARM 驱动笔记之一 : 准备

来源:互联网 发布:linux复制到上级目录 编辑:程序博客网 时间:2024/05/21 10:11

Linux ARM 驱动笔记之一 : 准备

转载请注明:http://blog.csdn.net/zkwsr/article/details/12510439


本文以Linux ARMSPI设备驱动为例记录Linux驱动的开发中的一些套路和准备.本文内容抛开具体代码,描述相关的准备知识,描述书本中很少提及的内容.

  

1 驱动编写的套路

Linux 驱动编写归根揭底,涉及到底部,就是设置寄存器, 从设置寄存器的角度来看,Linux驱动编写分为两种套路:

1)     直接操控寄存器: 这种方式写驱动代码结构简单,直接设置控制寄存器即可,但是对程序员的硬件知识要求比较高, 一个Arm的Datasheet看上3-5遍,才有可能写出个正确的驱动.(从单片机写过程序的程序员转到linux驱动,开始容易写这种代码.)

 

注意事项:

volatile 这个关键字, 有些时候必须加上.否则变量不会及时更新,导致程序出错.

 

管脚地址示例:

 a) 使用内核设置  

 定义:S3C2410_GPB(0)//S3C2410 CPU GPIO_B 0个管脚,linux内核提供了这个对应的宏.

 设置/赋 : s3c2410_gpio_setpin(S3C2410_GPB(0),0);

 

 b) 直接使用寄存器地址


 #define  PMC_PCER  0xFFFFFC10  //sam9G15 PMC寄存器.

#definev_uint32_t volatile uint32_t
 //根据寄存器的位数设置字节长度.uint32_t, uint16_t  
 v_uint32_t *pmc_pcer; 
 //ioremap地址映射
 pmc_pcer =(uint32_t *)ioremap(PMC_PCER, 4);
  * pmc_pcer = (1u<< 5); //设置寄存器值第的值


通过直接操控寄存器就可完成设备的驱动, 但是不建议使用这种方法.

2)     不直接操控寄存器: 通过Linux内核提供的驱动框架写程序,速度快,相对于直接控制寄存器,对硬件知识要求不那么高了.在编写个规范和可靠性方面,按linux驱动框架编写会更好.驱动架构分为:控制器驱动层/Linux驱动核心层/设备驱动层. 下面以SPI驱动为例介绍.(其他类型设备类似)

 SPI驱动架构图:

 

SPI控制器驱动层:每种处理器平台都有自己的控制器驱动, 属于平台移植相关,一般厂商都会提供到linux内核中. (编译内核时将之编译进去,或者选择为module,编译后将之insmod)

//这样程序员就不用去过多了解硬件控制器部分的细节了.

SPI核心层:Linux SPI的核心部分,里面定义了相关的数据结构, 用于向上(SPI设备)提供统一的接口.提供了spi驱动相关数据结构和操作函数.

// linux-2.6.39/drivers/spi/spi.c

SPI设备驱动(设备协议驱动):针对不同设备的特定驱动.利用SPI核心层提供的接口编写协议驱动. //这个驱动是程序员需要开发的驱动.

 

2 控制器驱动模块的加载:

加载了控制器驱动后, spicore的功能才会正常使用,spidevice 才可以正常运行.

 

Linux内核编译时有 [*] [M] [ ] 三个选项:

[*] : 选中编入内核,启动时会加载.

[ M ] : Module 编译成模块,手动加载.

[ ] : 不选.

 

#make menuconfig //进行选择

#make zImage/uImage/…. 各种形式的内核镜像

#make modules //编译[M]选中的模块,然后在相关路径下找到, copy出来使用.

// 下图编译后module为 linux-2.6.39/drivers/spi/atmel_spi.ko

// 和自己写的module文件一样使用 #insmod.


 

3 设备注册形式:

  根据设备的不同存在形式进行代码的对应编写.设备的存在形式分为板载设备和动态加载设备.

 

1)      板载设备:设备启动时加载的设备, 固定在板子/机器上的设备,不进行热插拔.

linux-2.6.39/arch/arm/mach-at91/board-sam9x5cm.c


//  在spi board_info中添加设备信息, 启动时通过spi core 的__init函数spi_register_board_info()函数进行初始化.

api_register_board_info()  <-- at91_add_device_spi()

//加载spi devide 时会调用. //在深挖就找不到了,暂时放在这.

 

  2) 动态加载设备: 加载设备驱动时加载上去的设备.

动态设备添加例子:

152     spi_master =spi_busnum_to_master(SPI_BUS);

153     spi_device =spi_alloc_device(spi_master);

154     pdev =bus_find_device_by_name(spi_device->dev.bus, NULL, buff);

155     if (pdev) {

156        spi_dev_put(spi_device);

157     } else {

158         status =spi_add_device(spi_device);       

159     }                                                                                                                                                                         

160    

161    put_device(&spi_master->dev);

 

其他的驱动也有类似的方法.

 

4 设备驱动编写方法:

关于设备驱动的写法,没有捷径.多读代码,多写代码.设备驱动起初最好的方法就是—看内核源码,设备驱动最好的例子. 以spi驱动为例:

~/linux-2.6.39/drivers/spi/spidev.c就是第一个需要看的例子.参照这个例子就能写出spi设备驱动. 当然,spidev.c提供的仅仅是一种方式.还有其他的方式—internet资源模仿. 例如:git,github,…. 最终写出漂亮的驱动.

 

5 驱动的makefile

1 ifneq ($(KERNELRELEASE),)                                                                                                                                                     

  2     #module name not same any file

 3     MODULE_NAME := EEPROM

  4     #module with init and exit

 5     RESMAIN_CORE_OBJS :=eeprom.o

 6     RESMAIN_GLUE_OBJS := twi.o

 7     $(MODULE_NAME)-objs :=  $(RESMAIN_CORE_OBJS) $(RESMAIN_GLUE_OBJS)

 8     obj-m := $(MODULE_NAME).o

  9else

 10    PWD=$(shell pwd)

 11    KERNEL_SRC=~/linux-2.6.39

 12default:

 13     # -C input kernel src -M reback thismakefile

 14     #$(MAKE) -C $(KERNEL_SRC) M=$(PWD)LDDINC=$(PWD) modules

 15    $(MAKE) -C $(KERNEL_SRC) M=$(PWD) modules

 16clean:

 17    rm *.o *.ko #and other generated code

 18endif

 

6 关于中断号

写中断函数的时候,需要这样一个函数:

 intrequest_irq(unsigned int irq, irq_handler_t handler,

 unsigned long flags, const char *devname, void*dev_id);

其中 unsignedint irq, 是中断号,这个中断号如何选取,怎么能和板子上的某个GPIO引脚对应上呢?下面介绍一下查找中断号的方法.

首先看CPUdatasheet,有类似下图的一张表格,列出了0-31个号码(具体CPU不同,可能编号数目不同 16 32 64…).   

内部中断: 这32是CPU的内部中断 (外设中断), 这32个中断是由各外设产生的—外设—这里应该理解成CPU的外设控制器(这样和外部设备就能区别开来了).这32个中断对应的中断编号是(0-31),对外是没有引脚的.(记住,这些中断是没办法和真实的外部设备用线连起来的, 所以长说为内部中断, 所谓内部是CPU内部,相对于外部而言的).这些内部中断普通的驱动程序员是不需要去处理的,内核控制器驱动提供者已经做好了相关的工作,仅需要调用相关的处理函数即可.

外部中断:所谓的外部中断,是在设备编程时长用到的中断, GPIO_X_Y //GPIO的X控制器第Y个引脚.这个编号从32-255,根据GPIO的引脚数量而定.那GPIO的引脚的中断线时怎么编号的呢? 是以32为基数,向上累加的.具体使用可使用相关的头文件对应的定义即可.


下面举例说明:

   例如SAM9G15芯片的 GPIO_D_21引脚对应的中断号,在linux-2.6.39/arch/arm/mach-at91/include/mach/gpio.h中定义

#define AT91_PIN_PD21  (PIN_BASE + 0x60 + 21)  //32+96+21=149

//GPIO_D_21号引脚对应的中断编号是 149.

//使用时多使用内核提供的定义,不要使用纯数字.

//那么149显然不在其上图0-31个中断号中, linux内核为了后续编写程序方便提供了映射.

//149对应到了PIOD的寄存器上的第21.然后进行处理.

//对于上层的程序员只需关心GPIO引脚对应的中断编号即可.

原创粉丝点击