13 Linux 蜂鸣器

一、蜂鸣器驱动原理

  常用蜂鸣器分两种,有源蜂鸣器和无源蜂鸣器。

  它们俩的区别:有源蜂鸣器具有内置的振荡器和驱动电路,无源蜂鸣器没有;源蜂鸣器只需简单的数字信号来控制,无源蜂鸣器需要外部电路或微控制器来提供特定频率的脉冲信号。

  在 Linux 下做的工作:①设备树中添加蜂鸣器节点,在蜂鸣器节点中加入 GPIO 信息;②编写驱动程序和测试 APP。

二、硬件原理图分析

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

三、实验程序编写

1. 修改设备树文件

  在根节点 "/" 创建 BEEP 节点:

beep {
		compatible = "alientek,beep";
		status = "okay";
		beep-gpio = <&gpioc 7 GPIO_ACTIVE_HIGH>;    // 这里的GPIO_ACTIVE_HIGH也可以改为GPIO_ACTIVE_LOW 最终实验没有影响
};                                                  // 这里的GPIO_ACTIVE_HIGH指引脚设置为高电平

make dtbs    // 编译

  之后用编译后的 stm32mp157d-atk.dtb 文件启动 Linux 系统,进入 /proc/device-tree/ 目录查看 beep 节点是否存在:

2. 蜂鸣器驱动程序编写 

  进入 /linux/atk-mpl/Drivers/6_beep 该目录下,创建 Vscode 工作区和 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>
 
#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");

3. 编写测试APP

  创建新的 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"

#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/alientek/linux/atk-mpl/linux/my_linux/linux-5.4.31	# Linux内核源码路径
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

  之后编译驱动文件和测试 App。

make
arm-none-linux-gnueabihf-gcc beepApp.c -o beepApp

  把编译后的 beepApp 和 beep.ko 放在 /linux/nfs/rootfs/lib/modules/5.4.31/ 目录下

sudo cp beepApp beep.ko /home/alientek/linux/nfs/rootfs/lib/modules/5.4.31/ -f

   进入 lib/modules/5.4.31/ 该目录下,输入以下命令

depmod             # 第一次加载驱动的时候需要运行此命令
modprobe beep      # 加载驱动

  这里我出现了一个问题:

,匹配不了,无效。最终的原因是

多了一个空格,一定一定不要多那一个空格,要不然就会出现这种情况。这是成功后的

。这玩意找了我一下午,真是服了。

  输入以下命令:

./beepApp /dev/beep 1        # 开启蜂鸣器
./beepApp /dev/beep 0        # 关闭蜂鸣器

# 最后卸载驱动
rmmod beep.ko

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/262515.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Fabric:使用GoLand+Fabric-SDK-Go操作Fabric网络

遇到bug, 未完待续!!! 写在最前 前序博客已经介绍了使用命令的方式在Fabric上创建通道以及部署执行链码的方法&#xff0c;但这个过程太繁琐&#xff0c;尤其是当Fabric网络中peer节点和组织Org过多时&#xff0c;需要频繁的更改环境变量。 Hyperledger Fabric官方提供了Fabri…

2023 英特尔On技术创新大会直播 |AI科技创新的引路者

英特尔大会 前言英特尔人工智能英特尔创新技术基于英特尔架构的科学计算总结 前言 英特尔技术创新大会是一个令人激动和启发的盛会。在这次大会上&#xff0c;我有幸观看了许多令人瞩目的科技创新和前沿技术的展示。这些展示不仅展示了英特尔作为科技巨头的实力&#xff0c;更…

【C语言刷题每日一题#牛客网BC69】——空心正方形图案

目录 问题描述 思路分析 代码实现 结果测试 问题描述 思路分析 首先根据输入的描述&#xff0c;多组输入需要将scanf放在循环中来实现分析输出的规律&#xff1a;当输入为4时&#xff0c;分别在第0行和第3行&#xff08;4-1行&#xff09;&#xff0c;第0列和第3列&#xf…

使用VSC从零开始Vue.js——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务3:数据可视化

使用Visual Studio Code&#xff08;VSC&#xff09;进行Vue开发非常方便&#xff0c;下面是一些基本步骤&#xff1a; 一、下载和安装Vue 官网下载地址Download | Node.js Vue.js是基于Node.js的&#xff0c;所以首先需要安装Node.js&#xff0c;官网下载地址&#xff1a;No…

redis命令

文章目录 NoSQL数据库引入NoSQL数据库简介redis6概述和安装启动方式相关知识介绍key键操作stringlistSethashZset NoSQL数据库引入 解决CPU以及内存压力 cookie是一种存储在用户计算机上的小型文本文件&#xff0c;用于跟踪、识别用户和提供个性化的服务。 会话&#xff08;Se…

使用 ElementUI 组件构建无边框 Window 桌面应用(WinForm/WPF)

生活不可能像你想象得那么好&#xff0c;但也不会像你想象得那么糟。 我觉得人的脆弱和坚强都超乎自己的想象。 有时&#xff0c;我可能脆弱得一句话就泪流满面&#xff1b;有时&#xff0c;也发现自己咬着牙走了很长的路。 ——莫泊桑 《一生》 一、技术栈 Vite Vue3 TS E…

VS Code+MinGW 搭建Windows C++开发环境

官方文档是最香香的&#xff1a;https://code.visualstudio.com/docs/cpp/config-mingw 文章目录 1、一些非常不友好的名词1.1 什么TMD是 GNU、MinGW、GCC、gcc、g&#xff1f;1.2 MSVC 2、获取g编译器3、VS Code单文件编译和调试流程3.1 安装插件3.2 单个源文件编译运行3.3 ta…

(1)(1.10) SiK Radio v1

文章目录 前言 1 概述 2 连接无线电台 3 参数说明 前言 本文介绍了如何将 3DR Radio v1 连接到飞行控制器。你还应阅读 SiK Radio v2&#xff0c;其中包含更详细的用户指南和功能列表。 1 概述 3DR 无线电设备是在自动驾驶仪和地面站之间建立遥测连接的最简单方法。 3DR…

HarmonyOS应用事件打点开发指导

简介 传统的日志系统里汇聚了整个设备上所有程序运行的过程流水日志&#xff0c;难以识别其中的关键信息。因此&#xff0c;应用开发者需要一种数据打点机制&#xff0c;用来评估如访问数、日活、用户操作习惯以及影响用户使用的关键因素等关键信息。 HiAppEvent 是在系统层面…

宋仕强论道之华强北存在的价值(二十九)

华强北特点是小快灵&#xff0c;主要服务散布在世界各地游离态的小体量硬件研发生产企业&#xff0c;这种企业也很难找到大一点的供应商来配套&#xff0c;以前覆盖珠三角长三角&#xff0c;现在是辐射世界各地。华强北是一个大集散地&#xff0c;据华强电子网统计&#xff0c;…

ubuntu 18.04 共享屏幕

用于windows远程ubuntu 1. sudo apt install xrdp 2. 配置 sudo vim /etc/xrdp/startwm.sh 把最下面的test和exec两行注释掉&#xff0c;添加一行 gnome-session 3.安装dconf-editor : sudo apt-get install dconf-editor 关闭require encrytion org->gnome->desktop…

【数据结构】五、数组与广义表

目录 一、定义 二、计算数组元素地址 三、稀疏矩阵快速转置 稀疏矩阵的表示 稀疏矩阵快速转置 四、广义表 一、定义 我们所熟知的一维、二维数组的元素是原子类型。广义表中的元素除了原子类型还可以是另一个线性表。当然所有的数据元素仍然属于同一类型。 这里的数组可…

深圳市城市更新区域关注程度分析数据,tiff格式,附数据可视化,精细到区县

基本信息. 数据名称: 深圳市城市更新区域关注程度分析数据 数据格式: tiff 时间版本&#xff1a;2022年 数据几何类型: 无 数据精度&#xff1a;区县 数据坐标系: WGS84 数据来源&#xff1a;网络公开数据 数据可视化. www.bajidata.com

微信小程序-textarea组件字数实时更新

一、前言 本文实现的是在小程序中&#xff0c;textarea文本框输入文字后&#xff0c;实时显示文字的字数&#xff0c;获取更好的用户输入体验以及提示。 下图是实现的效果 二、代码实现 2-1、wxml代码 <view style"padding: 30rpx;"><view style"…

【已解决】解决Springboot项目访问本地图片等静态资源无法访问的问题

今天在开发一个招聘系统的时候&#xff0c;有投递简历功能&#xff0c;有投递就会有随之而来的查看简历对吧&#xff0c;我投递过的简历&#xff0c;另存为一个文件夹&#xff0c;就是说本地磁盘(或者服务器)有一个专门存放投递过的简历的文件夹&#xff0c;用于存放PDF&#x…

Java最全面试题专题---5、Spring面试题(1)

Spring概述&#xff08;10&#xff09; 什么是spring? Spring是一个轻量级Java开发框架&#xff0c;最早有Rod Johnson创建&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack&#xff08;一站式&#xff…

【jvm从入门到实战】(十) 实战篇-内存调优

内存溢出和内存泄漏&#xff1a;在Java中如果不再使用一个对象&#xff0c;但是该对象依然在GC ROOT的引用链上&#xff0c;这个对象就不会被垃圾回收器回收&#xff0c;这种情况就称之为内存泄漏。内存泄漏绝大多数情况都是由堆内存泄漏引起的。少量的内存泄漏可以容忍&#x…

基于Linphone android sdk开发Android软话机

1.Linphone简介 1.1 简介 LinPhone是一个遵循GPL协议的开源网络电话或者IP语音电话&#xff08;VOIP&#xff09;系统&#xff0c;其主要如下。使用linphone&#xff0c;开发者可以在互联网上随意的通信&#xff0c;包括语音、视频、即时文本消息。linphone使用SIP协议&#…

云原生系列3-Kubernetes

1、Kubernetes概述 k8s缩写是因为k和s之间有八个字符。k8s是基于容器技术的分布式架构方案。官网&#xff1a;https://kubernetes.io/zh-cn/ Google在 2014年开源了Kubernetes项目&#xff0c;Kubernetes是一个用于自动化部署、扩展和管理容器化应用程序的开源系统。同样类似的…

拥抱鸿蒙 - 在展讯T606平台上的探索与实践

前 言 自OpenHarmony 问世后受到了社会各界的广泛关注&#xff0c;OpenHarmony 的生态系统在如火如荼的发展。 酷派作为一家积极拥抱变化的公司&#xff0c;经过一段时间的探索与实践&#xff0c;成功实现将OpenHarmony 系统接入到展讯平台上&#xff0c;我们相信这是一个重要…