S3C2440的GPIO控制驱动

ARM9由于频率高(400M),内存大(64MSDRAM),可以用来在OS进行大量计算或者实现复杂的算法。但ARM处理器还有很重要的作用——控制。平时可能需要IO口和三极管开关电路来控制一些设备的开启关闭,在M3或者低端的ARM处理器上比较好实现。但是ARM9移植了linux操作系统,我们为数个能完成这项工作的一群GPIO口专门写一个驱动程序,这样只需要在上层API中调用(高级字符启动设备操作 iotcl [1])就可以实现这个常用功能。

在调试的过程中,出现了有下面的一些问题:


arm-linux-gcc

arm-linux-gcc是交叉编译工具,为什么要使用这个工具呢?因为受ARM开发板的资源性能限制,我们不可能在它里面定制的linux内核下装vim,装emacs。而编译整个内核或者编译程序需要大量计算、资源消耗。这样为了提高效率,我们优雅地转换到PC上来做这件事情。但是PC是基于X86的平台,而ARM9是ARM架构。它们能执行的二进制代码的排列组织方式(也包括大端小端模式)肯定不一样,这就需要一个转换工具,把能在X86下运行的字节码转换为能在ARM架构下运行的字节码。

安装完了这个编译工具,假设安装目录在/usr/local/arm/4.4.3/bin下。我们在终端调用时必须要使用/usr/local/arm/4.4.3/bin/arm-linux-gcc这一长串来编译代码。同样为了方便优雅地使用这个工具,我们选择在系统的环境变量里添加这个路径,这样只需要用arm-linux-gcc就可以编译了。我是这样添加:

    cd ~
    vim ./bashrc
  
    export PATH="$PATH:/usr/local/arm/4.4.3/bin"
    export PATH

我们先看一下.bashrc的作用:该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该 文件被读取。就是当前用户的环境变量。

然后编写内核驱动的Makefile文件:

    obj-m:=GPIOs.o
    CURRENT_PATH:=$(shell pwd)
    ARM_LINUX_KERNEL:=/opt/linux-2.6.32.2  
    all:
	    $(MAKE) -C $(ARM_LINUX_KERNEL) SUBDIRS=$(CURRENT_PATH)
    modulesclean:
	    rm -rf *.cmd *.o *.ko *.mod *.symvers *.order

这样运行make命令就应该开始编译GPIO内核驱动了。但是出现了错误:

S3C2410PF(0)未声明。

S3C2410_gpio_setpin未声明。

出现这样错误的第一反应是头文件未包含或者路径不对,但是检查了发现没有问题。其实文件未包含只是表面的原因,真正的原因是位于/opt/linux-2.6.32.2的内核源码树未形成,换言之,是位于PC上匹配开发板的内核源码没有经过编译[2]。


编译内核源码树

设备驱动程序是内核的一部分,所以它采用的是内核的头文件和库,而在linux发行版里可能并没有这些东西。 这就需要下载内核版本然后编译生成源码树,以准备好驱动编译所需要的头文件和库。

linux设备驱动程序说:

要想为2.6.x内核构造模块,还必须在自己的系统中配置并构造好内核树。这一要求和先前版本的内核不同 ,先前的版本只需要一头内核头文件就够了,但因为2.6内核的模块要和内核源代码中的目标文件连接

按照步骤执行下列命令:

1.进入linux-2.6.32.2这个目录,从终端输入命令:

cd /opt/linux-2.6.32.2

2.执行以下命令来使用缺省配置文件 config_w35(不同的屏幕尺寸对应的配置文件不同)

sudo cp config_mini2440_w35 .config

3.然后执行“sudo make menuconfig“,出现配置内核界面,并且这时不用做任何更改,在主菜单里选择退出,这样做是为了生成相应配置的头文件。

4.输入make 命令,开始编译内核。

这时出现了arm-linux-gcc找不到的错误[3]。这是因为执行make命令是以超级用户执行的,而超级用户的的PATH里,并没有/usr/local/arm/4.4.3/bin。

解决方法是:

1.先打开一个超级用户权限的shell:

命令:sudo –s

2.在当前shell下,设置环境变量:

命令:gedit /etc/profile

在文件末端加上 export PATH=$PATH:/usr/local/arm/4.4.3/bin,并保存。

3.执行source /etc/profile 这是避免重新启动ubuntu而又使刚刚修改的环境变量生效的方法。

/etc/profile的作用是:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行. 并从/etc/profile.d目录的配置文件中搜集shell的设置.


加载内核驱动

将编译好的GPIOs.ko文件通过ftp传给ARM开发板,用insmod GPIOs.ko命令将驱动动态加载入内核。可以通过lsmod命令查看所有动态加载的module.

cat /proc/devices查看主设备号,cat /proc/misc查看所有misc设备的次设备号。

因为GPIOs被定义为一个misc设备,而misc设备无需mknod就可以自己创建设备节点(即设备文件)


用户层App

用户层的调用主要使用了高级驱动设备ioctl控制的功能,它和read,write等调用不一样。它不进行文件读写,虽然也是打开文件进行操作。它实现的是通过设备驱动程序执行各种类型的硬件控制,比如,用户空间请求设备锁门,弹出介质,报告错误信息,改变波特率。或者像这个例子一样,改变GPIO的高低电平。

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/ioctl.h>

     int main(int argc,char **argv)
     {
     int on;
     int gpio_no;
     int fd;
	
     if( argc!=3 || 
	 sscanf(argv[1],"%d",&gpio_no)!=1 ||
         sscanf(argv[2],"%d",&on)!=1 ||
	 on <0 || on>1 ||
	 gpio_no<0 ||gpio_no>3)
	 {
		fprintf(stderr,"Usage:GPIOApp GPIO_no 0(ON)|1(OFF) \n");
	 }
    
	 fd=open("/dev/GPIOs",0);
	 if(fd<0){
		fd=open("/dev/GPIOs",0);
	 }
        if(fd<0){
		perror("Open device GPIOs");
		exit(1);
	}

	ioctl(fd,on,gpio_no);
	close(fd);
	return 0;
        }
        

在终端中调用

./GPIOApp 0 0 即可置GPIOF0口为3.3V电平 ./GPIOApp 0 1 即可置GPIOF0口为0V电平


Reference

[1].LDD3 P137

[2].http://www.360doc.com/content/12/0106/16/1317564_177703831.shtml

[3].http://blog.sina.com.cn/s/blog_7d3976fc01012c2d.html

[4].http://tieba.baidu.com/p/2055472114



Previous     Next
/
Published under (CC) BY-NC-SA in categories ARM  tagged with Driver