第二十六章 Linux 蜂鸣器实验笔记

常用的蜂鸣器有两种: 有源蜂鸣器和无源蜂鸣器,这里的有"源"不是电源,而是震荡源,有源蜂鸣器内部带有震荡 源,所以有源蜂鸣器只要通电就会叫。无源蜂鸣器内部不带震荡源,直接用直流电是驱动不起 来的,需要 2K-5K 的方波去驱动。正点原子 STM32MP1 开发板使用的是有源蜂鸣器,因此只 要给其供电就会工作,开发板所使用的有源蜂鸣器

有源蜂鸣器只要通电就会叫,所以我们可以做一个供电电路,这个供电电路通过一个 IO 来 控制其通断,一般使用三极管来搭建这个电路。为什么我们不能像控制 LED 灯一样,直接将 GPIO 接到蜂鸣器的负极,通过 IO 输出高低来控制蜂鸣器的通断。这事因为蜂鸣器工作的电流 比 LED 灯要大,直接将蜂鸣器接到开发板的 GPIO 上有可能会烧毁 IO,所以我们需要通过一 个三极管来间接的控制蜂鸣器的通断,相当于加了一层隔离。本章我们就驱动开发板上的有源 蜂鸣器,然后编写简单的测试 APP,通过 APP 来控制蜂鸣器鸣叫或关闭。

通过一个 PNP 型的三极管 8550 来驱动蜂鸣器,通过 PC7 这个 IO 来控制三 极管 Q1 的导通,当 BEEP 输出低电平的时候 Q1 导通,相当于蜂鸣器的正极连接到 3.3V 电 源,蜂鸣器形成一个通路,因此蜂鸣器会鸣叫。同理,当 BEEP 输出高电平的时候 Q1 不导通, 那么蜂鸣器就没有形成一个通路,因此蜂鸣器也就不会鸣叫。

编译设备树

复制代码
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$ make dtbs
  DTC     arch/arm/boot/dts/stm32mp157d-atk.dtb
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$

蜂鸣器驱动程序编写

复制代码
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$ ls
beepApp.c  beep.c  beep.code-workspace  Makefile
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$ vim Makefile
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$ cat Makefile
KERNELDIR := /home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd)

obj-m := beep.o

build: kernel_modules

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

clean:
        $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$ make
make -C /home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31 M=/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep modules
make[1]: 进入目录"/home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31"
  CC [M]  /mnt/hgfs/sharefile/02、Linux驱动例程/06_beep/beep.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  /mnt/hgfs/sharefile/02、Linux驱动例程/06_beep/beep.mod.o
  LD [M]  /mnt/hgfs/sharefile/02、Linux驱动例程/06_beep/beep.ko
make[1]: 离开目录"/home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31"
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$ ls
beepApp.c  beep.c  beep.code-workspace  beep.ko  beep.mod  beep.mod.c  beep.mod.o  beep.o  Makefile  modules.order  Module.symvers
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$ arm-none-linux-gnueabihf-gcc beepApp.c -o beepApp
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$

编译

复制代码
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$ cp beepApp  beep.ko   /home/user/linux/nfs/rootfs/lib/modules/5.4.31/
user@user-virtual-machine:/mnt/hgfs/sharefile/02、Linux驱动例程/06_beep$

挂载

复制代码
mount -t nfs -o nolock,nfsvers=3 192.168.1.100:/home/user/linux/nfs/ getnfs/

运行测试

将上一小节编译出来的 beep.ko 和 beepApp 这两个文件拷贝到 rootfs/lib/modules/5.4.31 目 录中,重启开发板,进入到目录 lib/modules/5.4.31 中,输入如下命令加载 beep.ko 驱动模块: depmod //第一次加载驱动的时候需要运行此命令

modprobe beep //加载驱动

复制代码
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe beep
modprobe: module beep not found in modules.dep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
aaaa             chrdevbase.ko    led.ko           modules.symbols
bbb              chrdevbaseApp    ledApp           newchrled.ko
beep.ko          dtsled.ko        modules.alias
beepApp          led              modules.dep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe beep
modprobe: module beep not found in modules.dep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ rm -f modules.*
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ depmod
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe beep
[  851.981341] beep-gpio num = 39
[  851.983033] beep: Failed to request beep-gpio
[  852.079479] beep-gpio num = 39
[  852.081170] beep: Failed to request beep-gpio
modprobe: can't load module beep (beep.ko): Device or resource busy
[root@ATK-stm32mp1]:/lib/modules/5.4.31$

报错分析

复制代码
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe beep
[  851.981341] beep-gpio num = 39
[  851.983033] beep: Failed to request beep-gpio
[  852.079479] beep-gpio num = 39
[  852.081170] beep: Failed to request beep-gpio
modprobe: can't load module beep (beep.ko): Device or resource busy
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
aaaa             chrdevbase.ko    led.ko           modules.symbols
bbb              chrdevbaseApp    ledApp           newchrled.ko
beep.ko          dtsled.ko        modules.alias
beepApp          led              modules.dep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ lsmod
Module                  Size  Used by    Tainted: G
[root@ATK-stm32mp1]:/lib/modules/5.4.31$

beep: Failed to request beep-gpio驱动加载失败报错分析

复制代码
beep-gpio num = 39
beep: Failed to request beep‑gpio
modprobe: can't load module beep (beep.ko): Device or resource busy

Device or resource busy → GPIO39 已经被别的内核驱动占用了,gpio_request()申请失败 ,模块加载失败。 lsmod输出为空,代表 beep 模块根本没有加载成功,没有驻留内核。

为什么 GPIO39 被占用,两个最常见原因

  1. 设备树 DTS 里面这个 GPIO 被其他节点使用(最高概率) STM32MP1,引脚在设备树被别的外设节点配置,内核启动时就已经把这个 GPIO 申请走了,你的 beep 驱动再去gpio_request(39)直接报忙。
  2. 上一次你的 dtsled/led 驱动,退出时没有释放 gpio,rmmod 没有正常执行,gpio 资源泄漏。

注意:如果你是设备树驱动 ,不要手动写gpio_request()! 设备树驱动应该用 gpiod_get() / devm_gpiod_get(),从 dts 节点拿 gpio,不要硬写死 gpio 编号 39。硬编码 gpio 号在 STM32MP1 非常容易冲突。

板子上排查命令

1. 查看哪个内核模块占用 GPIO

复制代码
cat /sys/kernel/debug/gpio

找到 gpio 39,看gpio-39后面写的 owner 是谁,就知道哪个驱动抢占了这个引脚。

如果 /sys/kernel/debug/gpio 不存在,开启 debugfs:

复制代码
mount -t debugfs none /sys/kernel/debug
cat /sys/kernel/debug/gpio

2. 检查设备树(重中之重)

  • 如果你 beep 驱动是 platform 设备树驱动:不要在代码写死 gpio=39,要从 dts 节点读取 gpio 编号。

    //错误写法:硬编码
    gpio_request(39,"beep-gpio");

    //正确设备树驱动写法,从dts拿引脚
    gpiod = devm_gpiod_get(&pdev->dev, "beep", GPIOD_OUT_LOW);

查内核日志确认占用者‌

bash

dmesg | grep -i gpio

第一步:改设备树,避开内置驱动

把 stm32mp157d-atk.dts 里的 beep 节点改名,compatible 也改:

复制代码
mybeep {
    compatible = "alientek,mybeep";
    status = "okay";
    beep-gpio = <&gpioc 7 GPIO_ACTIVE_HIGH>;
};

# 1. 最关键:内核启动日志里谁在碰 beep/gpio-39
dmesg | grep -i beep

# 2. 平台总线上有没有 beep 驱动/设备(不依赖 /beep 节点也可能注册)
ls /sys/bus/platform/drivers/ | grep -i beep
ls /sys/bus/platform/devices/ | grep -i beep

# 3. 当前加载了哪些模块
lsmod

# 4. 有没有开机自动加载 beep 模块的配置
cat /etc/modules 2>/dev/null
cat /etc/rc.local 2>/dev/null
ls /etc/init.d/ 2>/dev/null | grep -i beep

# 5. 反编译当前运行的设备树,搜哪些节点引用了 gpioc 7
dtc -I fs /proc/device-tree -o /tmp/current.dts 2>/dev/null
grep -n "gpioc" /tmp/current.dts | grep "7"
grep -n -B2 -A4 "beep" /tmp/current.dts

# 6. 确认你重启过没有
uptime

name
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ dmesg | grep -i beep
[  244.864570] beep node not find!
[  244.970058] beep node not find!
[12674.755009] beep node not find!
[12674.881331] beep node not find!
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls /sys/bus/platform/drivers/ | grep -i
beep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls /sys/bus/platform/devices/ | grep -i
beep
mybeep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ lsmod
Module                  Size  Used by    Tainted: G
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /etc/modules 2>/dev/null
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /etc/rc.local 2>/dev/null
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls /etc/init.d/ 2>/dev/null | grep -i b
eep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ dtc -I fs /proc/device-tree -o /tmp/cur
rent.dts 2>/dev/null
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ grep -n "gpioc" /tmp/current.dts | grep
"7"
grep: /tmp/current.dts: No such file or directory
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ grep -n -B2 -A4 "beep" /tmp/current.dts
grep: /tmp/current.dts: No such file or directory
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ uptime
03:48:03 up  3:48,  load average: 0.00, 0.00, 0.11
[root@ATK-stm32mp1]:/lib/modules/5.4.31$

# 1. 内核符号表里有没有 beep 相关函数(最关键,能直接看出有没有内置驱动)
cat /proc/kallsyms | grep -i beep

# 2. 字符设备里有没有 beep
cat /proc/devices | grep -i beep

# 3. sysfs 全局搜 beep(设备、类、驱动都能搜到)
find /sys -name "*beep*" 2>/dev/null

# 4. /dev 下有没有 beep 相关设备节点
ls -la /dev/ | grep -i beep

# 5. 看 gpio-39 能不能通过 sysfs 导出(能导出说明没被占,不能导出说明确实被占)
echo 39 > /sys/class/gpio/export 2>&1
ls /sys/class/gpio/gpio39 2>/dev/null && echo "EXPORT OK" || echo "EXPORT FAILED"

# 6. dtc 为什么失败,看报错
which dtc
dtc -I fs /proc/device-tree -o /tmp/current.dts

# 7. 看 pinctrl 里 PC7 的状态
cat /sys/kernel/debug/pinctrl/pinmux-pins 2>/dev/null | grep -i "pin 39\|PC7\|gpio-39"

[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /proc/kallsyms | grep -i beep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /proc/devices | grep -i beep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ find /sys -name "*beep*" 2>/dev/null
/sys/devices/platform/leds/leds/beep
/sys/devices/platform/mybeep
/sys/class/leds/beep
/sys/firmware/devicetree/base/leds/beep
/sys/firmware/devicetree/base/mybeep
/sys/firmware/devicetree/base/mybeep/beep-gpio
/sys/bus/platform/devices/mybeep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /sys/kernel/debug/pinctrl/pinmux-pi
ns 2>/dev/null | grep -i "pin 39|PC7"
[root@ATK-stm32mp1]:/lib/modules/5.4.31$

其中 两行 :

/sys/class/leds/beep

/sys/firmware/devicetree/base/leds/beep

设备树里有一个 /leds/beep 节点,被 Linux 内核标准的 leds-gpio(gpio-leds) 驱动 自动接管了。它在启动时 probe 了 /leds/beep, 拿走了 gpio-39 (PC7), 创建了 /sys/class/leds/beep------ 这就是 debugfs 里 label 显示 "beep" 的来源。

解决方案

在设备树里找到 /leds 节点,把 beep 子节点删掉或注释掉。

在 VS Code 里按 Ctrl+Shift+F 全局搜索 beep, 定位到类似下面的代码 (可能在 stm32mp157d-atk.dts 或某个 dtsi 里):

备选:直接用现成的 leds 接口 (不写驱动)

既然 /sys/class/leds/beep 已经存在,你其实可以直接用它控制蜂鸣器, 不需要自己写字符设备驱动:

复制代码
echo 1 > /sys/class/leds/beep/brightness   # 开蜂鸣器
echo 0 > /sys/class/leds/beep/brightness   # 关蜂鸣器
cat /sys/class/leds/beep/brightness        # 读当前状态

经过测试 : echo 1 > /sys/class/leds/beep/brightness # 开蜂鸣器 可以用

stm32mp157d-atk.dtsi

leds/beep 子节点已经注释掉了,led1、led2 保留,操作正确。

注释掉

复制代码
certs  CREDITS          drivers        init     Kconfig  LICENSES  mm           net                      scripts  stm32mp157d_atk.sh  usr         vmlinux.o
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$ make  dtbs
  DTC     arch/arm/boot/dts/stm32mp157d-atk.dtb
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$ cp  arch/arm/boot/dts/stm32mp157d-atk.dtb   /home/user/linux/tftpboot/
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$

[root@ATK-stm32mp1]:/lib/modules/5.4.31$ rm -f modules.*
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
aaaa           beepApp        dtsled.ko      ledApp
bbb            chrdevbase.ko  led            newchrled.ko
beep.ko        chrdevbaseApp  led.ko
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ depmod
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe beep
[  820.744063] beep-gpio num = 39
[  820.745804] beep major=241,minor=0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$

测试 有效

复制代码
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./beepApp /dev/beep 1
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./beepApp /dev/beep 0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$

所以之前不能加载是因为leds 中冲突了

完整代码

beep.c

复制代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名		: beep.c
作者	  	: 正点原子Linux团队
版本	   	: V1.0
描述	   	: 蜂鸣器驱动程序。
其他	   	: 无
论坛 	   	: www.openedv.com
日志	   	: 初版V1.0 2020/12/31 正点原子Linux团队创建
***************************************************************/
#define BEEP_CNT			1		/* 设备号个数 */
#define BEEP_NAME			"beep"	/* 名字 */
#define BEEPOFF 			0		/* 关蜂鸣器 */
#define BEEPON 				1		/* 开蜂鸣器 */

/* beep设备结构体 */
struct beep_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	struct device_node	*nd; /* 设备节点 */
	int beep_gpio;			/* beep所使用的GPIO编号		*/
};

struct beep_dev beep;	/* beep设备 */

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &beep; /* 设置私有数据 */
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;
	struct beep_dev *dev = filp->private_data;

	retvalue = copy_from_user(databuf, buf, cnt); /* 接收APP发送过来的数据 */
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	ledstat = databuf[0];		/* 获取状态值 */

	if(ledstat == BEEPON) {	
		gpio_set_value(dev->beep_gpio, 0);	/* 打开蜂鸣器 */
	} else if(ledstat == BEEPOFF) {
		gpio_set_value(dev->beep_gpio, 1);	/* 关闭蜂鸣器 */
	}
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备操作函数 */
static struct file_operations beep_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init led_init(void)
{
	int ret = 0;
	const char *str;

	/* 设置LED所使用的GPIO */
	/* 1、获取设备节点:beep */
	beep.nd = of_find_node_by_path("/beep");
	if(beep.nd == NULL) {
		printk("beep node not find!\r\n");
		return -EINVAL;
	}

	/* 2.读取status属性 */
	ret = of_property_read_string(beep.nd, "status", &str);
	if(ret < 0) 
	    return -EINVAL;

	if (strcmp(str, "okay"))
        return -EINVAL;
    
	/* 3、获取compatible属性值并进行匹配 */
	ret = of_property_read_string(beep.nd, "compatible", &str);
	if(ret < 0) {
		printk("beep: Failed to get compatible property\n");
		return -EINVAL;
	}

    if (strcmp(str, "alientek,beep")) {
        printk("beep: Compatible match failed\n");
        return -EINVAL;
    }

	/* 4、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
	beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0);
	if(beep.beep_gpio < 0) {
		printk("can't get led-gpio");
		return -EINVAL;
	}
	printk("beep-gpio num = %d\r\n", beep.beep_gpio);

	/* 5.向gpio子系统申请使用GPIO */
	ret = gpio_request(beep.beep_gpio, "BEEP-GPIO");
    if (ret) {
        printk(KERN_ERR "beep: Failed to request beep-gpio\n");
        return ret;
	}

	/* 6、设置PC7为输出,并且输出高电平,默认关闭BEEP */
	ret = gpio_direction_output(beep.beep_gpio, 1);
	if(ret < 0) {
		printk("can't set gpio!\r\n");
	}

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (beep.major) {		/*  定义了设备号 */
		beep.devid = MKDEV(beep.major, 0);
		ret = register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);
		if(ret < 0) {
			pr_err("cannot register %s char driver [ret=%d]\n", BEEP_NAME, BEEP_CNT);
			goto free_gpio;
		}
	} else {						/* 没有定义设备号 */
		ret = alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);	/* 申请设备号 */
		if(ret < 0) {
			pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", BEEP_NAME, ret);
			goto free_gpio;
		}
		beep.major = MAJOR(beep.devid);	/* 获取分配号的主设备号 */
		beep.minor = MINOR(beep.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 */
	cdev_add(&beep.cdev, beep.devid, BEEP_CNT);
	if(ret < 0)
		goto del_unregister;
		
	/* 4、创建类 */
	beep.class = class_create(THIS_MODULE, BEEP_NAME);
	if (IS_ERR(beep.class)) {
		goto del_cdev;
	}

	/* 5、创建设备 */
	beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);
	if (IS_ERR(beep.device)) {
		goto destroy_class;
	}
	return 0;
	
destroy_class:
	class_destroy(beep.class);
del_cdev:
	cdev_del(&beep.cdev);
del_unregister:
	unregister_chrdev_region(beep.devid, BEEP_CNT);
free_gpio:
	gpio_free(beep.beep_gpio);
	return -EIO;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit led_exit(void)
{
	/* 注销字符设备驱动 */
	cdev_del(&beep.cdev);/*  删除cdev */
	unregister_chrdev_region(beep.devid, BEEP_CNT); /* 注销设备号 */
	device_destroy(beep.class, beep.devid);/* 注销设备 */
	class_destroy(beep.class);/* 注销类 */
	gpio_free(beep.beep_gpio); /* 释放GPIO */
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

beepApp.c

复制代码
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名		: beepApp.c
作者	  	: 正点原子Linux团队
版本	   	: V1.0
描述	   	: beep测试APP。
其他	   	: 无
使用方法	 :./beepApp /dev/beep  0 关闭蜂鸣器
		     ./beepApp /dev/beep  1 打开蜂鸣器
论坛 	   	: www.openedv.com
日志	   	: 初版V1.0 2020/12/31 正点原子Linux团队创建
***************************************************************/

#define BEEPOFF 	0
#define BEEPON 	1

/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
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];

	/* 打开beep驱动 */
	fd = open(filename, O_RDWR);
	if(fd < 0){
		printf("file %s open failed!\r\n", argv[1]);
		return -1;
	}

	databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 */

	/* 向/dev/beep文件写入数据 */
	retvalue = write(fd, databuf, sizeof(databuf));
	if(retvalue < 0){
		printf("BEEP Control Failed!\r\n");
		close(fd);
		return -1;
	}

	retvalue = close(fd); /* 关闭文件 */
	if(retvalue < 0){
		printf("file %s close failed!\r\n", argv[1]);
		return -1;
	}
	return 0;
}

Makefile

复制代码
KERNELDIR := /home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd)

obj-m := beep.o

build: kernel_modules

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

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
相关推荐
希和20274 小时前
免费AI视频生成工具汇总(国内优先,适合做漫剧、短视频,Obsidian笔记版)
人工智能·笔记·音视频
且从容.4 小时前
ubuntu安装flink
linux·ubuntu·flink
蒸蒸yyyyzwd5 小时前
秋招学习笔记 day109
笔记·八股
爱和冰阔落6 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨6 小时前
WSL2的安装与使用
linux·wsl
微小的鱼6 小时前
Linux的渲染与显示系统链路
android·java·linux
FPGA小迷弟6 小时前
FPGA 与千兆以太网校验和:从原理到 Verilog 实现
嵌入式硬件·fpga开发·无人机·ai编程·fpga
lisanmengmeng6 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
Android系统攻城狮7 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
只愿云淡风清7 小时前
正则表达式学习笔记 02:基础语法
笔记·学习·正则表达式