s3c2440嵌入式linux系统LED驱动(lab2)

来源:互联网 发布:淘宝店叫什么名字好听 编辑:程序博客网 时间:2024/06/06 12:36

Lab2实验报告

         我做这个实验整体过程可以分为两部分,第一部分是自己到处查找资料写的一个demodirver和测试程序都是demo,程序比较简单,但是也确实可以实现基本的功能,第二部分就是最终交上来的项目,是受到2.6.29自身led等驱动的启发。在与同学研究的过程中,发现原来kernel本身也是提供了一套自己的LED 驱动,不过没有实现具体的功能,但是给出了设置和读取引脚的关键函数,于是我在这个驱动的基础上,加上了file_operations接口,并按照试验的要求加上了具体的操作led灯的操作。下面分别来讲一下这两套程序。

1.   Demo程序

包括demo驱动和demo测试程序两部分

1.1   Demo驱动

(1)       在这个程序里,我首先,定义了操作led的三个端口或者说寄存器,这里主要是参照手册第九章I/O ports.

定义如下:

#define GPBCON (*(volatile unsigned *)0x56000010)

#define GPBDAT (*(volatile unsigned *)0x56000014)

#define GPBUP  (*(volatile unsigned *)0x56000018)

 

(2)       然后针对试验的要求,操作led3led4,所以我定义了led3led4对应的GPB7_outGPB8_out,定义如下:

#define GPB7_out (1<<(7*2))     //LED3 GPBCON

#define GPB8_out (1<<(8*2))     //LED4 GPBCON

这里也是参照手册第九章I/O ports.

GPB7_out对应的GPBCON[14,15],GPB8_out对应的GPBCON[16,17]

另外,led等对应的GPBDAT正好是对应的GPBCON位数的一半,即led3对应的是第7位,led4对应的是第8位。

(3)       定义file_operations接口

         File_operations接口是驱动和设备文件交互的标准接口,在这里我主要实现了其中的5个函数,定义如下:

struct file_operations GPIO_LED_ctl_ops ={

 

 .open = GPIO_LED_open,

 .read = GPIO_LED_read,

 .write = GPIO_LED_write,

 .ioctl = GPIO_LED_ioctl,

 .release = GPIO_LED_release,

};

具体的介绍,稍后再讲,先看一下,module_initmodule_exit函数

 

(4)       module_initmodule_exit函数

         这两个函数分别是驱动加载(insmod)和驱动卸载(rmmod)时调用的

实现如下:

module_init(S3C2410_GPIO_LED_CTL_init);

module_exit(cleanup_GPIO_LED_ctl);

他们调用的函数分别是S3C2410_GPIO_LED_CTL_initcleanup_GPIO_LED_ctl

 

S3C2410_GPIO_LED_CTL_init实现如下:

 

static int __init S3C2410_GPIO_LED_CTL_init(void)

{

    int  ret = -ENODEV;

 

    ret = GPIO_LED_CTL_init();

    if (ret)

        return ret;

    return 0;

}

 

GPIO_LED_CTL_init()中主要做了一些初始化的工作

包括初始化   

         GPBCON = GPB7_out | GPB8_out;

    GPBUP  = 0x3ff;

    GPBDAT = 0x3ff;

以及调用设备注册函数

    ret = register_chrdev(GPIO_LED_MAJOR, "gpio_led_ctl", &GPIO_LED_ctl_ops);

 

 

(5)       具体对led的控制

其实到目前为止,程序的大体框架就已经差不多了,剩下的就是具体对led的操作,因为在下文讲第二个版本的时候还要将,这里只简要介绍了ioctl

函数定义如下:

static int GPIO_LED_ioctl (struct inode * inode ,struct file * file, unsigned int cmd, unsigned long data){

    switch (cmd)

    {

    case LED_ON : { GPBDAT |=(1<<8); break;}

    case LED_OFF: { GPBDAT &=~(1<<8); break;}

    default :

        {printk ("lcd control : no cmd run  [ --kernel-- ]/n"); return (-EINVAL);}

         }

         return 0;

}

可以看到这里其实就是对led8的亮灭的一个简单控制,按照手册的要求,实现很简单。

1.2   Demo测试程序

(1)       下面讲一下我针对这个Demo driver写的Demo测试程序。程序很简单。主体部分定义如下:

#define DEVICE_NAME "/dev/gpio_led_ctl"

//define LED STATUS

#define LED_ON  0

#define LED_OFF 1

int main(void){

    int fd;

    int ret;

    char *i;

    printf("/nstart gpio_led_driver test/n/n");

    fd = open(DEVICE_NAME, O_RDWR);

    printf("fd = %d/n",fd);

    if (fd == -1){

        printf("open device %s error/n",DEVICE_NAME);

    }

    else{

        while(1){

            ioctl(fd,LED_OFF);

            sleep(1);

            ioctl(fd,LED_ON);

            sleep(1);

        }

        // close

        ret = close(fd);

        printf ("ret=%d/n",ret);

        printf ("close gpio_led_driver test/n");

    }

    return 0;

}

(2)       可以看到程序的流程是先通过设备名字打开一个设备,当然设备必须先建立,建立的命令见下文。

                   然后每隔一秒钟间隔调用ioctl接口,分别传参数LED_OFFLED_ON这样就可以看到led4灯每隔一秒亮灭一次。

(3)       建立设备,命令很简单,针对我在驱动里面用到的设备号

这里顺便讲一下insmod的过程

        

首先把生成的led.kotest载到板子上然后:

insmod led.ko 

lsmod 

mknod  /dev/gpio_led_ctl c 97 1  新建LED的测试设备节点

 其中 /dev/gpio_led_ctl 是打开的设备名称,要和测试代码匹配 

 c代表字符设备

97是主设备好,与驱动程序匹配 

1是从设备号 只有一个选1

最后执行./test   会看到板子上 LED交替亮灭 见隔1s

 

1.3存在的问题

         其实这个简单的demo已经包含了led驱动编写的基本内容,不过还是存在不少缺点。第一就是功能不全(这其实没啥,本来就是写的一个demo),第二个是没有加入timer机制用的是sleep进行等待。有了demo的经验,接下来的工作方便很多。

2.   正式驱动

2.1 driver

这个驱动和同学讨论时无意中看到/drivers/leds 下面有一个leds-s3c24xx.c的文件,也就是一个实现好的简单驱动,虽然这个驱动没有做具体的事情,不过却提供了读写的接口,最终的驱动程序也就是在这个系统提供的驱动的基础上改进来的。

 

(1)           数据定义

static unsigned int led_table[] = {

    S3C2410_GPB5,

    S3C2410_GPB6,

    S3C2410_GPB7,

    S3C2410_GPB8,

};

static unsigned int led_cfg_table[] = {

    S3C2410_GPB5_OUTP,

    S3C2410_GPB6_OUTP,

    S3C2410_GPB7_OUTP,

    S3C2410_GPB8_OUTP,

};

具体的意义上文已经降到,这里不在赘余。

(2)           接口定义

static struct file_operations dev_fops = {

    .read = s3c2440_read,

    .write = s3c2440_write,

    .open = s3c2440_open,

    .release = s3c2440_release,};

注意,这里在demo的基础上有所改变,ioctl接口没有实现,对led的操作是直接通过readwrite进行的

(3)           read函数定义

         static ssize_t s3c2440_read(struct file *file, char __user *buf, size_t size, loff_t *offset) {

                   char value = s3c2410_gpio_getpin(led_table[2]) ? 0 : 1;

                   return value

         }

         可以看到这里直接调用了系统提供的s3c2410_gpio_getpin函数接口,注意,参数传的是led_table[2],也就是S3C2410_GPB7,因此,读的是led3的信息。

(4)           write函数定义

static ssize_t s3c2440_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) {

    char value;

    ssize_t ret_val;

    ret_val = copy_from_user(&value, buf, sizeof(char)) ? -EFAULT : sizeof(char);

    s3c2410_gpio_setpin(led_table[2], value ? 0 : 1);

    return ret_val;

}

这里直接调用了系统提供的s3c2410_gpio_setpin函数,led_table[2],也就是S3C2410_GPB7,因此是控制led3亮灭。

,其实这里的s3c2410_gpio_setpins3c2410_gpio_getpin函数,如果查看他们对应的源代码的话,就可以发现,其实也就是对读写端口的操作作了一个封装而已。

(5)           定时器的操作

这里的定时器也是参考了/drivers/leds下面的ledtrig-timer.c文件。这个文件中给出了timer的标准使用方法。

在本程序中,首先定了一个500毫秒的延迟。

然后定义了一个timer_list 类型的timer,这是kernel中定义的标准的定时器变量

在模块加载的时候,出了进行驱动相关的设置之外,还可以看到,定时器的操作

setup_timer(&led_timer, s3c2440_timer_fn, 1);

mod_timer(&led_timer, jiffies + msecs_to_jiffies(DELAY));

 

第一句是绑定timertimer中断时的处理函数

第二句是设定timer的周期。Jiffies是一个全局变量

 

s3c2440_timer_fn做的事情也很简单,就是当时钟周期结束时,

调用s3c2410_gpio_setpin(led_table[3], value ? 0 : 1);并且传的参数的值和上次调用时传的参数相反,于是这样就可以实现led4灯的亮灭控制。

2.2 test程序

测试程序很简单,就是可以接收4中输入,on,控制led3亮,off,控制led3灭,s,查看led3当前状态,q,退出程序,逻辑很简单,在此就不多做解释。

3.   遇到的问题

         其实在做的过程中,遇到的技术上的问题并不是很多,一是因为gpio端口的操作,张老师已经在课堂上讲了很多,只要认真听课,就不难理解,二是平时实验里搞kernel比较多,驱动编程以前也弄过,所以相关make file的写法,驱动的格式,包括装载,卸载过程等都比较熟悉,所以问题并不大,而且在完成实验的过程中也参照了kernelled驱动和timer的应用方法,所以并没有遇到瓶颈。而且,这个实验本身也是比较简单的。

         但是还是有一个问题耽误了我不少时间。我开发的过程是通过本地的rootfs启动,然后过载nfs servernfs serverPC机上,程序的开发和编译都是在PC机上,然后通过nfs下载到arm板上。启动和挂载的过程都很正常,但是当我从nfs server上拷贝文件到arm板的时候,发现拷贝非常慢,而且过一会就会死掉,网络连接也连不上了。后来我发现在/usr/bin下面有一个ftpget,用如下的指令

ftpget -u username -p password 10.131.250.73 remote_filename local_filename

这样比较正常,基本不会断网,而且速度也还比较快。可能以后还需要抽时间查一下。

原创粉丝点击