DIY编程器网

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1070|回复: 0
打印 上一主题 下一主题

[待整理] 嵌入式Linux设备驱动开发之:GPIO驱动程序实例

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-10 07:33:17 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
        11.3  GPIO驱动程序实例

        11.3.1  GPIO工作原理

        FS2410开发板的S3C2410处理器具有117个多功能通用I/O(GPIO)端口管脚,包括GPIO 8个端口组,分别为GPA(23个输出端口)、GPB(11个输入/输出端口)、GPC(16个输入/输出端口)、GPD(16个输入/输出端口)、GPE(16个输入/输出端口)、GPF(8个输入/输出端口)、GPH(11个输入/输出端口)。根据各种系统设计的需求,通过软件方法可以将这些端口配置成具有相应功能(例如:外部中断或数据总线)的端口。
         
        为了控制这些端口,S3C2410处理器为每个端口组分别提供几种相应的控制寄存器。其中最常用的有端口配置寄存器(GPACON ~ GPHCON)和端口数据寄存器(GPADAT ~ GPHDAT)。因为大部分I/O管脚可以提供多种功能,通过配置寄存器(PnCON)设定每个管脚用于何种目的。数据寄存器的每位将对应于某个管脚上的输入或输出。所以通过对数据寄存器(PnDAT)的位读写,可以进行对每个端口的输入或输出。
         
        在此主要以发光二极管(LED)和蜂鸣器为例,讨论GPIO设备的驱动程序。它们的硬件驱动电路的原理图如图11.4所示。
                          http://www.eefocus.com/embedded/322841/file:///C:DOCUME~1ADMINI~1LOCALS~1Tempksohtmlwps_clip_image-12738.png

        图11.4  LED(左)和蜂鸣器(右)的驱动电路原理图

         
        在图11.4中,可知使用S3C2410处理器的通用I/O口GPF4、GPF5、GPF6和GPF7分别直接驱动LED D12、D11、D10以及D9,而使用GPB0端口驱动蜂鸣器。4个LED分别在对应端口(GPF4~GPF7)为低电平时发亮,而蜂鸣器在GPB0为高电平时发声。这5个端口的数据流方向均为输出。
         
        在表11.15中,详细描述了GPF的主要控制寄存器。GPB的相关寄存器的描述与此类似,具体可以参考S3C2410处理器数据手册。
        表11.15 GPF端口(GPF0-GPF7)的主要控制寄存器
                                                                        寄存器
                       
                                                                        地址
                       
                                                                        R/W
                       
                                                                        功能
                       
                                                                        初始值
                       
                                                                        GPFCON
                       
                                                                        0x56000050
                       
                                                                        R/W
                       
                                                                        配置GPF端口组
                       
                                                                        0x0
                       
                                                                        GPFDAT
                       
                                                                        0x56000054
                       
                                                                        R/W
                       
                                                                        GPF端口的数据寄存器
                       
                                                                        未定义
                       
                                                                        GPFUP
                       
                                                                        0x56000058
                       
                                                                        R/W
                       
                                                                        GPF端口的取消上拉寄存器
                       
                                                                        0x0
                       
                                                                        GPFCON
                       
                                                                        位
                       
                                                                        描述
                       
                                                                        GPF7
                       
                                                                        [15:14]
                       
                                                                        00 = 输入  01 = 输出  10 = EINT7  11 = 保留
                       
                                                                        GPF6
                       
                                                                        [13:12]
                       
                                                                        00 = 输入  01 = 输出  10 = EINT6  11 = 保留
                       
                                                                        GPF5
                       
                                                                        [11:10]
                       
                                                                        00 = 输入  01 = 输出  10 = EINT5  11 = 保留
                       
                                                                        GPF4
                       
                                                                        [9:8]
                       
                                                                        00 = 输入  01 = 输出  10 = EINT4  11 = 保留
                       
                                                                        GPF3
                       
                                                                        [7:6]
                       
                                                                        00 = 输入  01 = 输出  10 = EINT3  11 = 保留
                       
                                                                        GPF2
                       
                                                                        [5:4]
                       
                                                                        00 = 输入  01 = 输出  10 = EINT2  11 = 保留
                       
                                                                        GPF1
                       
                                                                        [3:2]
                       
                                                                        00 = 输入  01 = 输出  10 = EINT1  11 = 保留
                       
                                                                        GPF0
                       
                                                                        [1:0]
                       
                                                                        00 = 输入  01 = 输出  10 = EINT0  11 = 保留
                       
         
                                                                        GPFDAT
                       
                                                                        位
                       
                                                                        描述
                       
                                                                        GPF[7:0]
                       
                                                                        [7:0]
                       
                                                                        每位对应于相应的端口,若端口用于输入,则可以通过相应的位读取数据;若端口用于输出,则可以通过相应的位输出数据;若端口用于其他功能,则其值无法确定。
                       
         
                                                                        GPFUP
                       
                                                                        位
                       
                                                                        描述
                       
                                                                        GPF[7:0]
                       
                                                                        [7:0]
                       
                                                                        0:向相应端口管脚赋予上拉(pull-up)功能
                                                                        1:取消上拉功能
                       
         
        为了驱动LED和蜂鸣器,首先通过端口配置寄存器将5个相应寄存器配置为输出模式。然后通过对端口数据寄存器的写操作,实现对每个GPIO设备的控制(发亮或发声)。在下一个小节中介绍的驱动程序中,s3c2410_gpio_cfgpin()函数和s3c2410_gpio_pullup()函数将进行对某个端口的配置,而s3c2410_gpio_setpin()函数实现向数据寄存器的某个端口的输出。
         
        11.3.2  GPIO驱动程序

        GPIO驱动程序代码如下所示:
         
        /* gpio_drv.h */
        #ifndef     FS2410_GPIO_SET_H
        #define     FS2410_GPIO_SET_H
        #include    <linux/ioctl.h>
        #define     GPIO_DEVICE_NAME       "gpio"
        #define     GPIO_DEVICE_FILENAME  "/dev/gpio"
        #define     LED_NUM                  4
        #define     GPIO_IOCTL_MAGIC       &#39;G&#39;
        #define     LED_D09_SWT             _IOW(GPIO_IOCTL_MAGIC, 0, unsigned int)
        #define     LED_D10_SWT             _IOW(GPIO_IOCTL_MAGIC, 1, unsigned int)
        #define     LED_D11_SWT             _IOW(GPIO_IOCTL_MAGIC, 2, unsigned int)
        #define     LED_D12_SWT             _IOW(GPIO_IOCTL_MAGIC, 3, unsigned int)
        #define     BEEP_SWT                 _IOW(GPIO_IOCTL_MAGIC, 4, unsigned int)
        #define     LED_SWT_ON              0
        #define     LED_SWT_OFF             1
        #define     BEEP_SWT_ON             1
        #define     BEEP_SWT_OFF            0
        #endif /* FS2410_GPIO_SET_H */
         
        /* gpio_drv.c */
        #include <linux/config.h>
        #include <linux/module.h>
        #include <linux/moduleparam.h>
        #include <linux/init.h>
        #include <linux/kernel.h>   /* printk() */
        #include <linux/slab.h>        /* kmalloc() */
        #include <linux/fs.h>       /* everything... */
        #include <linux/errno.h>    /* error codes */
        #include <linux/types.h>    /* size_t */
        #include <linux/mm.h>
        #include <linux/kdev_t.h>
        #include <linux/cdev.h>
        #include <linux/delay.h>
        #include <linux/device.h>
        #include <asm/io.h>
        #include <asm/uaccess.h>
        #include <asm/arch-s3c2410/regs-gpio.h>
        #include "gpio_drv.h"
         
        static int major = 0; /* 采用字符设备号的动态分配 */
        module_param(major, int, 0); /* 以参数的方式可以指定设备的主设备号*/
         
        void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
        { /* 对某个管脚进行配置(输入/输出/其他功能)*/
             unsigned long base = S3C2410_GPIO_BASE(pin); /* 获得端口的组基地址*/
             unsigned long shift = 1;
             unsigned long mask = 0x03; /* 通常用配置寄存器的两位表示一个端口*/
             unsigned long con;
             unsigned long flags;
         
             if (pin < S3C2410_GPIO_BANKB)
             {   
                shift = 0;
                mask  = 0x01; /* 在GPA端口中用配置寄存器的一位表示一个端口*/
             }   
             mask <<= (S3C2410_GPIO_OFFSET(pin) << shift);
             local_irq_save(flags); /* 保存现场,保证下面一段是原子操作 */
             con = __raw_readl(base + 0x00);
             con &= ~mask;
            con |= function;
            __raw_writel(con, base + 0x00); /* 向配置寄存器写入新配置数据 */
            local_irq_restore(flags); /* 恢复现场 */
        }
         
        void s3c2410_gpio_pullup(unsigned int pin, unsigned int to)
        { /* 配置上拉功能 */
            unsigned long base = S3C2410_GPIO_BASE(pin); /* 获得端口的组基地址*/
            unsigned long offs = S3C2410_GPIO_OFFSET(pin);/* 获得端口的组内偏移地址 */
            unsigned long flags;
            unsigned long up;
            
            if (pin < S3C2410_GPIO_BANKB)
            {
                return;
            }
         
            local_irq_save(flags);
            up = __raw_readl(base + 0x08);
            up &= ~(1 << offs);
            up |= to << offs;
            __raw_writel(up, base + 0x08); /* 向上拉功能寄存器写入新配置数据*/
            local_irq_restore(flags);
        }
         
        void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
        { /* 向某个管脚进行输出 */
            unsigned long base = S3C2410_GPIO_BASE(pin);
            unsigned long offs = S3C2410_GPIO_OFFSET(pin);
            unsigned long flags;
            unsigned long dat;
         
            local_irq_save(flags);
            dat = __raw_readl(base + 0x04);
            dat &= ~(1 << offs);
            dat |= to << offs;
            __raw_writel(dat, base + 0x04); /* 向数据寄存器写入新数据*/
            local_irq_restore(flags);
        }
         
        int gpio_open (struct inode *inode, struct file *filp)
        { /* open操作函数:进行寄存器配置*/
            s3c2410_gpio_pullup(S3C2410_GPB0, 1); /* BEEP*/   
            s3c2410_gpio_pullup(S3C2410_GPF4, 1); /* LED D12 */   
            s3c2410_gpio_pullup(S3C2410_GPF5, 1); /* LED D11 */   
            s3c2410_gpio_pullup(S3C2410_GPF6, 1); /* LED D10 */   
            s3c2410_gpio_pullup(S3C2410_GPF7, 1); /* LED D9 */   
            s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP);
            s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
            s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF5_OUTP);
            s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF6_OUTP);
            s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF7_OUTP);
            return 0;
        }
        ssize_t gpio_read(struct file *file, char __user *buff,
                                                            size_t count, loff_t *offp)
        { /* read操作函数:没有实际功能*/
            return 0;
        }
        ssize_t gpio_write(struct file *file, const char __user *buff,
                                                             size_t count, loff_t *offp)
        { /* write操作函数:没有实际功能*/
            return 0;
        }
         
        int switch_gpio(unsigned int pin, unsigned int swt)
        { /* 向5个端口中的一个输出ON/OFF值 */
            if (!((pin <= S3C2410_GPF7) && (pin >= S3C2410_GPF4))
                             && (pin != S3C2410_GPB0))
            {
                printk("Unsupported pin");
                return 1;
            }
            s3c2410_gpio_setpin(pin, swt);
            return 0;
        }
         
        static int gpio_ioctl(struct inode *inode, struct file *file,
                                              unsigned int cmd, unsigned long arg)
        { /* ioctl函数接口:主要接口的实现。对5个GPIO设备进行控制(发亮或发声) */
            unsigned int swt = (unsigned int)arg;
            switch (cmd)
            {
                case LED_D09_SWT:
                {
                    switch_gpio(S3C2410_GPF7, swt);   
                }
                break;
         
                case LED_D10_SWT:
                {
                    switch_gpio(S3C2410_GPF6, swt);
                }
                break;
         
                case LED_D11_SWT:
                {
                    switch_gpio(S3C2410_GPF5, swt);
                }
                break;
                case LED_D12_SWT:
                {
                    switch_gpio(S3C2410_GPF4, swt);
                }
                break;
         
                case BEEP_SWT:
                {
                    switch_gpio(S3C2410_GPB0, swt);
                    break;
                }
         
                default:
                {
                    printk("Unsupported command\n");
                    break;
                }
            }
            return 0;
        }
         
        static int gpio_release(struct inode *node, struct file *file)
        { /* release操作函数,熄灭所有灯和关闭蜂鸣器 */
            switch_gpio(S3C2410_GPB0, BEEP_SWT_OFF);
            switch_gpio(S3C2410_GPF4, LED_SWT_OFF);
            switch_gpio(S3C2410_GPF5, LED_SWT_OFF);
            switch_gpio(S3C2410_GPF6, LED_SWT_OFF);
            switch_gpio(S3C2410_GPF7, LED_SWT_OFF);
            return 0;
        }
         
        static void gpio_setup_cdev(struct cdev *dev, int minor,
                struct file_operations *fops)
        { /* 字符设备的创建和注册 */
            int err, devno = MKDEV(major, minor);   
            cdev_init(dev, fops);
            dev->owner = THIS_MODULE;
            dev->ops = fops;
            err = cdev_add (dev, devno, 1);
            if (err)
            {
                printk (KERN_NOTICE "Error %d adding gpio %d", err, minor);
            }
        }
         
        static struct file_operations gpio_fops =
        { /* gpio设备的file_operations结构定义 */
            .owner   = THIS_MODULE,
            .open    = gpio_open,        /* 进行初始化配置*/
            .release = gpio_release,    /* 关闭设备*/
            .read    = gpio_read,        
            .write   = gpio_write,
            .ioctl   = gpio_ioctl,        /* 实现主要控制功能*/
        };
         
        static struct cdev gpio_devs;
        static int gpio_init(void)
        {
            int result;
            dev_t dev = MKDEV(major, 0);
         
            if (major)
            { /* 设备号的动态分配 */
                result = register_chrdev_region(dev, 1, GPIO_DEVICE_NAME);
            }
            else
            { /* 设备号的动态分配 */
                result = alloc_chrdev_region(&dev, 0, 1, GPIO_DEVICE_NAME);
                major = MAJOR(dev);
            }
            if (result < 0)
            {
                printk(KERN_WARNING "Gpio: unable to get major %d\n", major);
                return result;
            }
            gpio_setup_cdev(&gpio_devs, 0, &gpio_fops);
            printk("The major of the gpio device is %d\n", major);
            return 0;
        }
         
        static void gpio_cleanup(void)
        {
            cdev_del(&gpio_devs); /* 字符设备的注销 */
            unregister_chrdev_region(MKDEV(major, 0), 1); /* 设备号的注销*/
            printk("Gpio device uninstalled\n");
        }
         
        module_init(gpio_init);
        module_exit(gpio_cleanup);
        MODULE_AUTHOR("David");
        MODULE_LICENSE("Dual BSD/GPL");           
         
        下面列出GPIO驱动程序的测试用例:
         
        /* gpio_test.c */
        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <fcntl.h>
        #include <string.h>
        #include <sys/types.h>
        #include <sys/stat.h>
        #include "gpio_drv.h"
         
        int led_timer(int dev_fd, int led_no, unsigned int time)
        { /*指定LED发亮一段时间之后熄灭它*/
            led_no %= 4;
            ioctl(dev_fd, LED_D09_SWT + led_no, LED_SWT_ON); /* 发亮*/
            sleep(time);
            ioctl(dev_fd, LED_D09_SWT + led_no, LED_SWT_OFF); /* 熄灭 */
        }
         
        int beep_timer(int dev_fd, unsigned int time)
        {/* 开蜂鸣器一段时间之后关闭*/
            ioctl(dev_fd, BEEP_SWT, BEEP_SWT_ON); /* 发声*/
            sleep(time);
            ioctl(dev_fd, BEEP_SWT, BEEP_SWT_OFF);    /* 关闭 */
        }
         
        int main()
        {
            int i = 0;
            int dev_fd;
                /* 打开gpio设备 */
            dev_fd = open(GPIO_DEVICE_FILENAME, O_RDWR | O_NONBLOCK);
            if ( dev_fd == -1 )
            {
                printf("Cann't open gpio device file\n");
                exit(1);
            }
         
            while(1)
            {
                i = (i + 1) % 4;
                led_timer(dev_fd, i, 1);
                beep_timer(dev_fd, 1);        
            }
            close(dev_fd);
            return 0;
        }           
         
        具体运行过程如下所示。首先编译并加载驱动程序:
         
        $ make clean;make /* 驱动程序的编译*/
        $ insmod gpio_drv.ko /* 加载gpio驱动 */
        $ cat /proc/devices /* 通过这个命令可以查到gpio设备的主设备号 */
        $ mknod /dev/gpio  c  252  0  /* 假设主设备号为252, 创建设备文件节点*/
         
        然后编译并运行驱动测试程序:
         
        $ arm-linux-gcc &ndash;o gpio_test  gpio_test.c
        $ ./gpio_test
         
        运行结果为4个LED轮流闪烁,同时蜂鸣器以一定周期发出声响。
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|文字版|手机版|DIY编程器网 ( 桂ICP备14005565号-1 )

GMT+8, 2024-11-14 14:58 , 耗时 0.104765 秒, 21 个查询请求 , Gzip 开启.

各位嘉宾言论仅代表个人观点,非属DIY编程器网立场。

桂公网安备 45031202000115号

DIY编程器群(超员):41210778 DIY编程器

DIY编程器群1(满员):3044634 DIY编程器1

diy编程器群2:551025008 diy编程器群2

QQ:28000622;Email:libyoufer@sina.com

本站由桂林市临桂区技兴电子商务经营部独家赞助。旨在技术交流,请自觉遵守国家法律法规,一旦发现将做封号删号处理。

快速回复 返回顶部 返回列表