Linux 蜂鸣器实验-基于正点原子IMX6ULL开发板

从软件的角度考虑,蜂鸣器驱动和 LED 灯驱动是一样的,都是控制 IO 输出高低电平。本实验来编写蜂鸣器的 Linux 驱动,也算是 pinctrl 和 gpio 子系统的巩固。


1 蜂鸣器驱动原理

I.MX6U-ALPHA 开发板上的蜂鸣器通过 SNVS_TAMPER1 引脚来控制,在 Linux 下编写蜂鸣器驱动需要做以下工作
①、在设备树中添加 SNVS_TAMPER1 引脚的 pinctrl 信息。
②、在设备树中创建蜂鸣器节点,在蜂鸣器节点中加入 GPIO 信息。
③、编写驱动程序和测试 APP,和上一篇《pinctrl 和 gpio 子系统实验-基于正点原子IMX6ULL开发板》LED 驱动程序和测试 APP 基本一样

2 硬件原理图分析

Linux 蜂鸣器实验-基于正点原子IMX6ULL开发板_第1张图片

 

3 实验程序编写

3.1 修改设备树文件
1、添加 pinctrl 节点

I.MX6U-ALPHA开发板上的
BEEP使用了SNVS_TAMPER1这个PIN,打开imx6ull-alientek-emmc.dts,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_beep”的子节点,节点 内容如下所示:

		pinctrl_beep: beepprp{
			fsl,pins = <
				MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10b0
			>;
		};
第 3 行,将 SNVS_TAMPER1 这 个 PIN 复用为 GPIO5_IO01。

 2、添加 BEEP 设备节点

在根节点“/”下创建 BEEP 节点,节点名为“ beep ”,节点内容如下:
	beep{
		compatible = "alientek,beep";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_beep>;
		beep-gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
		status= "okay";
	};
4 行, pinctrl-0 属性设置蜂鸣器所使用的 PIN 对应的 pinctrl 节点。
第 5   行, beep-gpio 属性指定了蜂鸣器所使用的 GPIO
3 、检查 PIN 是否被其他外设使用
先检查 PIN SNVS_TAMPER1 这个 PIN 有没有被其他的 pinctrl 节点使用,如果有使用的话就要屏蔽掉。
再检查GPIO5_IO01 这个 GPIO 有没有被其他外设使用,如果有的话也要屏蔽掉。

编译设备树

Linux 蜂鸣器实验-基于正点原子IMX6ULL开发板_第2张图片
然后使用新编译出来的 imx6ull-alientek-emmc.dtb 文件启动 Linux 系统,进入“/proc/device-tree”目录中查看“beep”节点是否存在

Linux 蜂鸣器实验-基于正点原子IMX6ULL开发板_第3张图片

3.2 蜂鸣器驱动程序编写

 新建vscode工程,工程创建好以后新建 beep.c 文件,在 beep.c 里面输入如下内容:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BEEP_CNT 1
#define BEEP_NAME "beep"
#define BEEPOFF 0
#define BEEPON 1

/*beep 设备结构体*/
struct beep_dev
{
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    int beep_gpio;
};

struct beep_dev beep; /*BEEP*/

static int beep_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &beep;
    return 0;
}

static int beep_release(struct inode *inode, struct file *filp)
{

    return 0;
}

static ssize_t beep_write(struct file *filp, const char __user *buf,
                          size_t count, loff_t *ppos)
{
    int ret;
    unsigned char databuf[1];
    struct beep_dev *dev = filp->private_data; //得到私有数据就是得到beep结构体
    ret = copy_from_user(databuf, buf, count);
    if (ret < 0)
    {
        return -EFAULT;
    }

    if (databuf[0] == BEEPON)
    {
        gpio_set_value(dev->beep_gpio, 0); //打开蜂鸣器,低电平
    }
    else if (databuf[0] == BEEPOFF)
    {
        gpio_set_value(dev->beep_gpio, 1); //关闭蜂鸣器,高电平
    }

    return 0;
}

/*操作集*/
static const struct file_operations beep_fops = {
    .owner = THIS_MODULE,
    .write = beep_write,
    .open = beep_open,
    .release = beep_release,
};

/*驱动入口函数*/
static int __init beep_init(void)
{
    int ret = 0;
    /***********注册字符设备驱动 **************/
    /* 1、创建设备号 */
    beep.major = 0;
    if (beep.major)
    { /*给定主设备号*/
        beep.devid = MKDEV(beep.major, 0);
        ret = register_chrdev_region(beep.devid, BEEP_CNT, "BEEP_NAME");
    }
    else
    {
        ret = alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, "BEEP_NAME");
        beep.major = MAJOR(beep.devid);
        beep.minor = MINOR(beep.devid);
    }
    if (ret < 0)
    {
        goto fail_devid;
    }
    printk("beep major =%d, minor =%d \r\n", beep.major, beep.minor);

    /*2,初始化cdev*/
    beep.cdev.owner = THIS_MODULE;
    cdev_init(&beep.cdev, &beep_fops);

    /*3,添加cdev*/
    ret = cdev_add(&beep.cdev, beep.devid, BEEP_CNT);
    if (ret)
        goto fail_cdevadd;

    /*4,创建类*/
    beep.class = class_create(THIS_MODULE, BEEP_NAME);
    if (IS_ERR(beep.class))
    {
        ret = PTR_ERR(beep.class);
        goto fail_class;
    }

    /*5,创建设备*/
    beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);
    if (IS_ERR(beep.device))
    {
        ret = PTR_ERR(beep.device);
        goto fail_device;
    }

    /**************设置 BEEP 所使用的 GPIO ***************/
    /* 1、获取设备节点:beep */
    beep.nd = of_find_node_by_path("/beep");
    if (beep.nd == NULL)
    {
        ret = -EINVAL;
        goto fail_nd;
    }

    /* 2、 获取设备树中的 gpio 属性,得到 BEEP 所使用的 GPIO 编号 */
    beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpios", 0);

    if (beep.beep_gpio < 0)
    {
        ret = -EINVAL;
        goto fail_nd;
    }

    /*3,申请BEEP所使用的GPIO引脚*/
    ret = gpio_request(beep.beep_gpio, "beep-gpio");
    if (ret)
    {
        printk("Can't request beep gpio \r\n");
        goto fail_nd;
    }
    /* 4、设置 GPIO5_IO01 为输出,并且输出高电平,默认关闭 BEEP */
    ret = gpio_direction_output(beep.beep_gpio, 0);
    if (ret < 0)
    {
        goto fail_set;
    }

    /*5,输出低电平,关闭BEEP*/
    gpio_set_value(beep.beep_gpio, 0);

    return 0;

fail_set:
    gpio_free(beep.beep_gpio);
fail_nd:
    device_destroy(beep.class, beep.devid);
fail_device:
    class_destroy(beep.class);
fail_class:
    cdev_del(&beep.cdev);
fail_cdevadd:
    unregister_chrdev_region(beep.devid, BEEP_CNT);
fail_devid:
    return ret;
}

/*驱动出口函数*/
static void __exit beep_exit(void)
{
    /*关闭蜂鸣器*/
    gpio_set_value(beep.beep_gpio, 1);

    /*注销字符设备驱动*/
    cdev_del(&beep.cdev);
    unregister_chrdev_region(beep.devid, BEEP_CNT);
    device_destroy(beep.class, beep.devid);
    class_destroy(beep.class);

    gpio_free(beep.beep_gpio);
}

module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("supersmart");
3.3 编写测试 APP
 
新建名为 beepAPP.c 的文件,同以前实验ledAPP.c 内容基本一样,然后输入如下所示内容:
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*
 *argc:应用程序参数个数
 * argv[]:具体的参数内容,字符串形式
 * ./beepAPP  <0:1> 0 关蜂鸣器,1 开蜂鸣器
 * ./beepAPP /dev/beep 0 关蜂鸣器
 * ./beepAPP /dev/beep 1 开蜂鸣器
 */

#define BEEPOFF 0
#define BEEPON  1
int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename;
    unsigned char databuf[1];

    if (argc != 3)
    {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("file %s open failed!\r\n", filename);
        return -1;
    }

    databuf[0] = atoi(argv[2]); /*将字符转化为数字*/
    retvalue = write(fd, databuf, sizeof(databuf));
    if (retvalue < 0)
    {
        printf("BEEP Control Failed ! \r\n");
        close(fd);
        return -1;
    }
    
    close(fd);

    return 0;
}
4 运行测试
4.1 编译驱动程序和测试 APP
        1、编译驱动程序
        
编写
Makefile 文件
KERNELDIR := /home/znn/linux/IMX6ULL/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PAHT := $(shell pwd)
obj-m := beep.o

build :kernel_modules

kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PAHT) modules

clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PAHT) clean

        编译驱动模块

Linux 蜂鸣器实验-基于正点原子IMX6ULL开发板_第4张图片

        2、编译测试 APP

  
      使用以下命令编译生成beepAPP这个应用程序。
        
arm-linux-gnueabihf-gcc beepAPP.c -o beepAPP

4.2 运行测试

将beep.ko beepApp 这两个文件拷贝到 rootfs/lib/modules/4.1.15 目录中。
重启开发板,进入到目录 lib/modules/4.1.15 中,输入如下命令加载 beep.ko 驱动模块:
 

 

打开蜂鸣器,蜂鸣器鸣叫

关闭蜂鸣器,蜂鸣器停止鸣叫

卸载驱动

你可能感兴趣的:(arm,linux,vscode,功能测试,c++)