①tiny4412 Linux驱动开发之GPIO驱动程序

本次主要是通过讲解蜂鸣器的开发来讲述驱动开发的流程,平台是三星猎户座4412.

本次使用的是板载蜂鸣器为有源蜂鸣器,在这里我们先看电路图:


从电路图中我们可以看出我们的电平信号会控制NPN型三极管的方式驱动蜂鸣器,可以看出当PWM0为高电平时,蜂鸣器可以发出声音,低电平不发声音,接下来我们找PWM0是哪个引脚,如下图:


从图中可知,PWM0是GPD0_0,下面我们去找datasheet,以确定GPD0_0的地址,如下图:


从图中可以看出,GPD0_0的地址为0x1140000 + 0x00A0 = 0x114000A0,好的,我们现在已经掌握了我们所需要的数据:蜂鸣器高电平响,低电平不响,控制它的引脚为GPD0_0,它的地址为0x114000A0.接下来,我们写驱动程序,如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>

#include <asm/io.h>
#include <asm/uaccess.h>



#define BEEP_Switch _IOW('L', 0x1234, int)

struct exynos4412_beep{
    unsigned int dev_major;
    struct class *cls;
    struct device *dev;
    int value;
};

struct exynos4412_beep *beep_dev;

volatile unsigned long *gpd0_conf;
volatile unsigned long *gpd0_data;


int beep_open(struct inode *inode, struct file *filp){
    printk("-------%s--------\n", __func__);
    // 硬件初始化,设置成输出模式
    *gpd0_conf &= ~0xf;
    *gpd0_conf |= 0x01;

    return 0;
}

ssize_t beep_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos){
    printk("-------%s--------\n", __func__);

    return count;
}

ssize_t beep_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos){
    int ret = -1;
    printk("-------%s--------\n", __func__);
    
    ret = copy_from_user(&beep_dev->value, buf, count);
    if(ret > 0){
        printk("copy from user failed!\n");
        return -EFAULT;
    }

    if(beep_dev->value){
        *gpd0_data |= 0x01;
    }
    else{
        *gpd0_data &= ~0x01;
    }

    return count;
}

long beep_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
    printk("-------%s--------\n", __func__);

    switch(cmd)
    {
        case BEEP_Switch:
            if(arg){
                *gpd0_data |= 0x01;
            }
            else{
                *gpd0_data &= ~0x01;
            }
            break;

        default:
            break;
    }

    return 0;
}

int beep_close(struct inode *inode, struct file *filp){
    printk("-------%s--------\n", __func__);

    // 设置成关闭蜂鸣器
    *gpd0_data &= ~0x01;

    return 0;
}

struct file_operations beep_fops = {
    //.owner = THIS_MODULE,
    .open = beep_open,
    .read = beep_read,
    .write = beep_write,
    .unlocked_ioctl = beep_ioctl,
    .release = beep_close,
};

static __exit void beep_exit(void)
{
    printk("--------%s---------\n", __func__);
    device_destroy(beep_dev->cls, MKDEV(beep_dev->dev_major, 0));
    class_destroy(beep_dev->cls);
    unregister_chrdev(beep_dev->dev_major, "beep_drv");
    kfree(beep_dev);
}

static __init int beep_init(void)
{
    int ret = -1;
    printk("--------%s---------\n", __func__);
    // 1,实例化一个设备对象
    beep_dev = kzalloc(sizeof(struct exynos4412_beep), GFP_KERNEL);
    if(NULL == beep_dev){
        printk("kzalloc failed!\n");
        return -ENOMEM;
    }

    // 2,申请设备号
    beep_dev->dev_major = register_chrdev(0, "beep_drv", &beep_fops);
    if(beep_dev->dev_major < 0){
        printk("register failed\n");
        return -EINVAL;
    }

    // 3,创建设备类
    beep_dev->cls = class_create(THIS_MODULE, "led_cls");
    if(IS_ERR(beep_dev->cls)){
        printk("class_register failed!\n");
        ret = PTR_ERR(beep_dev);
        goto err1;
    }

    // 4,创建设备节点
    beep_dev->dev = device_create(beep_dev->cls, NULL, MKDEV(beep_dev->dev_major, 0), NULL, "beep1");
    if(IS_ERR(beep_dev->dev)){
        printk("device create failed!\n");
        ret = PTR_ERR(beep_dev->dev);
        goto err2;
    }

    // 5,硬件映射
    gpd0_conf = ioremap(0x114000a0, 8);
    gpd0_data = gpd0_conf + 1;

    return 0;

err2:
    class_destroy(beep_dev->cls);
err1:
    unregister_chrdev(beep_dev->dev_major, "beep_drv");
    return ret;
}

module_init(beep_init);
module_exit(beep_exit);

MODULE_LICENSE("GPL");

写好了驱动,接下来我们来写上层测试程序,如下:

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


#define BEEP_Switch _IOW('L', 0x1234, int)

int main(void)
{
    int fd = -1,
	val = -1;

    fd = open("/dev/beep1", O_RDWR);
    if(fd < 0){
	perror("open");
	exit(1);
    }

    while(1)
    {
	ioctl(fd, BEEP_Switch, 1);
	sleep(1);

	ioctl(fd, BEEP_Switch, 0);
	sleep(1);

	val = 1;
	write(fd, &val, 4);
	sleep(1);

	val = 0;
	write(fd, &val, sizeof(int));
	sleep(1);
    }


    return 0;
}

好的,以上是测试程序,接下来还有makefile,如下:

ifeq ($(KERNELRELEASE),)
	KERNELDIR = /home/george/1702/exynos/linux-3.5
	PWD =$(shell pwd)
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
	arm-none-linux-gnueabi-gcc beep_test.c -o beep_test
	
modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
	rm -rf *_test  *.so *.o *.ko  .tmp_versions *.mod.c *.order *.symvers 
else
	obj-m :=beep.o
endif
其中驱动的文件名为beep.c,测试程序的文件名为beep_test.c.

编译之后,运行tiny4412之后,insmod加载beep.ko,然后运行./beep_test,你就可以听到蜂鸣器隔一秒响一秒了.哈哈.

猜你喜欢

转载自blog.csdn.net/qq_23922117/article/details/78535611