和菜鸟一起学OK6410之Led字符驱动

        公司待了一个下午,浑浑噩噩的,看了会androidwifi框架,还是懵懵懂懂的。都怪昨天热的睡不着,又不想开空调,唉,夏天,快过去吧。不过也算有点收获吧。吃了晚饭回到宿舍。想着,上几个实验都是看看串口的输出,没劲,好歹以前玩51FPGA什么的时候,都是做出效果来的,于是觉得,得干出点实物来啊,好吧,记得51FPGA是从led灯开始的,那么就。。。。。。。。。

         还是先上代码了:

 

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/init.h>

#include <linux/miscdevice.h>

#include <linux/delay.h>

#include <asm/uaccess.h>

#include <linux/device.h>

#include <linux/cdev.h>

#include <asm/irq.h>

#include <mach/gpio.h>

#include <plat/regs-gpio.h>

#include <plat/gpio-cfg.h>

#include <mach/hardware.h>

#include <linux/io.h>

 

#define LED_MAJOR 240

 

int led_open(struct inode *inode, struct file *filp)

{

    unsigned int tmp;

    tmp = readl(S3C64XX_GPMCON);

    tmp = (tmp & ~(0xffff) | (0x1111)); //set the GPIO output mode

    writel(tmp, S3C64XX_GPMCON);

    printk("$$$$$$$$$$$led_open$$$$$$$$$\n");

return 0;

}

 

ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)

{

    printk("$$$$$$$$$$led_read$$$$$$$$$\n");

return count;

}

 

 

ssize_t led_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)

{

    char mbuf[10];

    unsigned int tmp;

    copy_from_user(mbuf,buf,count);

    

    switch(mbuf[0])

    {

        case 0:

            tmp = readl(S3C64XX_GPMDAT);

            tmp |= (0x1);

            writel(tmp, S3C64XX_GPMDAT);

            break;

        case 1:

            tmp = readl(S3C64XX_GPMDAT);

            tmp &= ~(0x1); 

            writel(tmp, S3C64XX_GPMDAT);

            break;

        case 2:

            tmp = readl(S3C64XX_GPMDAT);

            tmp |= (0x2);

            writel(tmp, S3C64XX_GPMDAT);

            break;

        case 3:

            tmp = readl(S3C64XX_GPMDAT);

            tmp &= ~(0x2);

            writel(tmp, S3C64XX_GPMDAT);

            break;

        case 4:

            tmp = readl(S3C64XX_GPMDAT);

            tmp |= (0x4);

            writel(tmp, S3C64XX_GPMDAT);

            break;

        case 5:

            tmp = readl(S3C64XX_GPMDAT);

            tmp &= ~(0x4);

            writel(tmp, S3C64XX_GPMDAT);

            break;

        case 6:

            tmp = readl(S3C64XX_GPMDAT);

            tmp |= (0x8);

            writel(tmp, S3C64XX_GPMDAT);

            break;

        case 7:

            tmp = readl(S3C64XX_GPMDAT);

            tmp &= ~(0x8);

            writel(tmp, S3C64XX_GPMDAT);

            break;

        default:

            break;

    }

    

    printk("$$$$$$$$$$led_write$$$$$$$$$\n");

return count;

}

 

int led_release(struct inode *inode, struct file *filp)

{

    printk("$$$$$$$$$$led_release$$$$$$$$$\n");

return 0;

}

 

struct file_operations my_fops = {

    .owner = THIS_MODULE,

    .open = led_open,

    .read = led_read,

    .write = led_write,

    .release = led_release,

};

 

static int led_init(void)

{

    int rc;

    printk("Test led dev\n");

    rc = register_chrdev(LED_MAJOR, "led", &my_fops);

    if(rc < 0)

    {

        printk("register %s mychar dev error\n", "led");

        return -1;

    }

    printk("$$$$$$$$$ register led dev OK\n");

return 0;

}

 

static void led_exit(void)

{

    unregister_chrdev(LED_MAJOR, "led");

    printk("Good Bye!\n");

}

 

MODULE_LICENSE("GPL");

module_init(led_init);

module_exit(led_exit);


 

        唉,代码是越来越长了,为了实现流水灯,写得搓搓的代码,其实可以不用那么复杂的。既然写复杂了也就懒得改了。其实,流水灯就是控制GPIO口,让GPIO口输出高低电平,记得51是直接P1 = 0xFF之类的。而FPGA的话,verilog也是很简单的led=8’b11111110;嵌入式就是烦,跑系统的东西嘛,总得有个门槛,要不然谁都很容易会了,那么那些嵌入式工程师不是没饭吃了?哈哈哈哈。。。

        既然是控制GPIO口,那总得知道是哪个GPIO口吧?看看原理图吧。

 

        是GPM口,对应的,GPM0-GPM4分别是LED1-LED4。接着看看GPIO的一些寄存器吧,

        控制寄存器GPMCON,主要流水灯只要设置为输出就好了。所以代码中有写着

  

    tmp = readl(S3C64XX_GPMCON);

    tmp = (tmp & ~(0xffff) | (0x1111)); //set the GPIO output mode

    writel(tmp, S3C64XX_GPMCON);


        先读取,然后设置,然后再写进去。

 

        然后就是控制那个GPIO输出的01值了。

        就是这个GPMDAT了,对应的每一位就是每一个GPIO口的输出值了。这个是低电平点亮,高电平灭掉的,所以值为1是灭,值为0是亮。知道这些,看着代码,应该很容易理解了。

 

        接着makefile

obj-m :=led.o

 

         然后建个makemod,代码如下

 

make -C /home/eastmoon/work/linux2.6.28/ M=`pwd` modules

 


        然后只要source makemod就可以编译成led.ko

 

        万事具备了,那么为了实现驱动,我们还得写应用啊,所以说啊,搞驱动的,也得写应用,要不然,怎么知道你写的驱动好不好用。别人来看你的驱动写应用也太累了。好了,还是上代码吧:

 

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

 

#define DEVICE "/dev/myled"

 

int main(void)

{

    int fd, i;

    char buf[10] = {0, 1, 2, 3, 4, 5, 6, 7};

    fd = open(DEVICE, O_RDWR);

    if(fd < 0)

    {

        printf("Open /dev/myled file error\n");

        return -1;

    }

    

    while(1)

    {

        for(i = 0; i < 8; i += 2)

        {

            write(fd, &buf[i], 1);

            sleep(1);

        }

        for(i = 7; i > 0; i -= 2)

        {

            write(fd, &buf[i], 1);

            sleep(1);

        }

    }

    close(fd);

return 0;

}


 

        看看驱动,然后再看,应该还是很好理解的,就是个水水的流水灯嘛。

 

        然后makefile

CC = /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-gcc 

ledapp:ledapp.o

       $(CC) -o ledapp ledapp.o

ledapp.o:ledapp.c 

       $(CC) -c ledapp.c

 

clean :

       rm ledapp.o

 


        终于搞定了,累死了,还好下雷雨了,天气没有下午那么热了。然后就是板子上去看看效果了。

        至于怎么把编译好的led.ko ledapp放到板子上,方法很多,我是用SD卡的,具体看以前的blog中。

        Ok,注册成功了。

        设备文件也创建了

 

        看看,myled这个节点也有了,看来离成功不远了

        接着跑跑应用看看

        一直在写数据,流水灯也出来了。哈哈哈

 

 

        然后结束流水灯

        搞定,今天有点早,好久没下四国了,趁着周末,玩几局。收工,四国开始。。。。。

原文地址:https://www.cnblogs.com/wuyida/p/6300091.html