ARM Linux 驱动开发篇---基于 pinctrl+GPIO 子系统的蜂鸣器驱动开发(设备树版)--- Ubuntu20.04

ARM Linux 驱动开发篇---基于 pinctrl+GPIO 子系统的蜂鸣器驱动开发(设备树版)--- Ubuntu20.04

🎬 渡水无言个人主页渡水无言

专栏传送门: 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》

专栏传送门: 《freertos专栏》《STM32 HAL库专栏
⭐️流水不争先,争的是滔滔不绝

 📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生

| 省级优秀毕业生获得者 | ZEEKLOG新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生

在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录

前言

一、硬件原理分析

二、蜂鸣器驱动核心原理

三、实验程序编写

3.1、设备树修改总流程

3.1、修改设备树文件

3.1.1、添加 pinctrl 节点(配置 PIN 复用)

3.1.2、添加蜂鸣器设备节点

3.1.3、检查 PIN 是否被其他外设占用

3.2、编译设备树

 3.3、蜂鸣器驱动程序编写

3.3.1、驱动程序执行流程图

3.3.2、驱动代码

3.3.3、分析驱动代码

设备结构体设计(对应代码第 33–42 行)

私有数据传递(对应代码第 54 行)

write 函数(第 66–87 行,核心控制逻辑)

驱动入口函数 beep_init(第 112–170 行,驱动初始化)

驱动出口函数 beep_exit(第 177–186 行,资源释放)

3.4、测试APP

3.4.1测试APP执行流程图

3.4.2、程序编写

四、运行测试

4.1、编译驱动程序和测试 APP

6.2、运行测试

总结


前言

上一章我们通过pinctrlGPIO子系统完成了 LED 灯驱动开发,而蜂鸣器驱动的核心逻辑和 LED 完全一致 —— 都是控制 GPIO 输出高低电平。本章将以蜂鸣器为例,巩固 pinctrl+GPIO 子系统的使用方法,同时熟悉不同 GPIO 引脚(SNVS_TAMPER1)的设备树配置和驱动适配。


一、硬件原理分析

  • 该电路使用 PNP 型三极管 8550 驱动蜂鸣器,由SNVS_TAMPER1引脚控制。
  • SNVS_TAMPER1输出低电平时,三极管导通,蜂鸣器通电鸣叫。
  • SNVS_TAMPER1输出高电平时,三极管截止,蜂鸣器断电停止鸣叫

实验硬件:I.MX6ULL-ALPHA 开发板;

内核版本:4.1.15;

硬件原理如上图。

二、蜂鸣器驱动核心原理

蜂鸣器驱动的开发流程和 LED 完全一致,核心三步:
1、设备树中添加 SNVS_TAMPER1 引脚的 pinctrl 复用配置;
2、设备树中创建蜂鸣器节点,绑定 GPIO 信息;
3、基于字符设备驱动框架编写驱动 + 测试 APP(复用 LED 驱动逻辑)。

三、实验程序编写

3.1、设备树修改总流程

3.1、修改设备树文件

LED 灯使用SNVS_TAMPER1引脚(),需在设备树中完成 3 项配置:

添加 pinctrl 节点。

添加beep 设备节点。

检查引脚冲突。

3.1.1、添加 pinctrl 节点(配置 PIN 复用)

打开开发板设备树imx6ull-alientek-emmc.dts,在&iomuxc节点的imx6ul-evk子节点下,新增pinctrl_beep子节点,配置 SNVS_TAMPER1 的复用和电气属性

pinctrl_beep: beepgrp { fsl,pins = < MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10B0 /* beep:复用为GPIO5_IO01 */ >; };
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01:SNVS_TAMPER1 引脚复用为 GPIO5_IO01(该宏定义在imx6ull-pinfunc-snvs.h中);0x10B0:电气属性值(上下拉、驱动能力等,和 LED 配置一致)。

3.1.2、添加蜂鸣器设备节点

在设备树根节点/下创建beep节点,绑定 pinctrl 和 GPIO 信息:

beep { #address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-beep"; /* 驱动匹配标识,自定义 */ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_beep>; /* 绑定pinctrl节点 */ beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>; /* GPIO5_IO01,默认高电平(不叫) */ status = "okay"; };
pinctrl-0 = <&pinctrl_beep>:关联上一步创建的 pinctrl 节点,内核自动初始化 PIN;
beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>:指定蜂鸣器使用 GPIO5 第 1 号引脚,高电平触发(需和硬件逻辑匹配)。

3.1.3、检查 PIN 是否被其他外设占用

和 LED 驱动一样,需检查 SNVS_TAMPER1 引脚是否被其他外设占用:
        搜索SNVS_TAMPER1,确认无其他 pinctrl 节点使用该引脚;
        搜索gpio5 1,确认 GPIO5_IO01 未被其他设备节点占用;
        若有冲突,屏蔽对应配置行(加/* */)。

3.2、编译设备树

设备树编写完成以后使用如下命令重新编译设备树:

make dtbs

再把新生成的imx6ull-alientek-emmc.dtb 文件复制到tftp里,使用如下命令:

sudo cp arch /arm/boot/dts/imx6ull-alientek-emmc.dtb/home/duan/linux/tftp/ -f

然后使用新编译出来的 imx6ull-alientek-emmc.dtb 文件启动 Linux 系统。

启动成功以后进入“/proc/device-tree”目录中 查看“gpioled”节点是否存在,使用如下命令:

cd /proc/device-tree

如下图所示:

可以看到beep节点是存在的。

 3.3、蜂鸣器驱动程序编写

设备树准备好以后就可以编写驱动程序了,本期实验是在之前博客的led实验基础上修改而来。新建名为“5_gpioled”文件夹,然后在 5_gpioled 工程创建好以后新建 gpioled.c 文件,驱动程序基于字符设备驱动框架,核心是通过 GPIO 子系统 API 操作 GPIO,无需直接操作寄存器。

3.3.1、驱动程序执行流程图

3.3.2、驱动代码

 1 #include <linux/types.h> // 基本类型定义 2 #include <linux/kernel.h> // 内核核心函数(printk) 3 #include <linux/delay.h> // 延时函数 4 #include <linux/ide.h> // IDE相关宏 5 #include <linux/init.h> // 模块初始化/退出宏 6 #include <linux/module.h> // 模块核心头文件 7 #include <linux/errno.h> // 错误码定义 8 #include <linux/gpio.h> // GPIO子系统核心头文件 9 #include <linux/cdev.h> // 字符设备驱动核心头文件 10 #include <linux/device.h> // 设备类/设备节点创建 11 #include <linux/of.h> // 设备树核心头文件 12 #include <linux/of_address.h> // 设备树地址解析 13 #include <linux/of_gpio.h> // 设备树GPIO解析函数 14 #include <asm/mach/map.h> // 内存映射 15 #include <asm/uaccess.h> // 用户/内核空间数据拷贝 16 #include <asm/io.h> // IO操作函数 17 18 /*************************************************************** 19 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 22 版本 : V1.0 23 描述 : 蜂鸣器驱动程序。 26 ***************************************************************/ 27 #define BEEP_CNT 1 /* 设备号个数 */ 28 #define BEEP_NAME "beep" /* 设备名 */ 29 #define BEEPOFF 0 /* 关蜂鸣器 */ 30 #define BEEPON 1 /* 开蜂鸣器 */ 31 32 /* beep设备结构体:封装驱动所有资源 */ 33 struct beep_dev { 34 dev_t devid; /* 设备号 */ 35 struct cdev cdev; /* cdev */ 36 struct class *class; /* 类 */ 37 struct device *device; /* 设备 */ 38 int major; /* 主设备号 */ 39 int minor; /* 次设备号 */ 40 struct device_node *nd; /* 设备节点 */ 41 int beep_gpio; /* beep使用的GPIO编号 */ 42 }; 43 44 struct beep_dev beep; /* 定义beep设备结构体 */ 45 46 /* 47 * @description : 打开设备 48 * @param – inode : 传递给驱动的inode 49 * @param - filp : 设备文件,保存私有数据 50 * @return : 0 成功;其他 失败 51 */ 52 static int beep_open(struct inode *inode, struct file *filp) 53 { 54 filp->private_data = &beep; /* 绑定设备结构体到文件私有数据 */ 55 return 0; 56 } 57 58 /* 59 * @description : 向设备写数据(核心:控制蜂鸣器开关) 60 * @param - filp : 设备文件 61 * @param - buf : 用户空间写入的数据 62 * @param - cnt : 数据长度 63 * @param - offt : 文件偏移 64 * @return : 0 成功;负值 失败 65 */ 66 static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 67 { 68 int retvalue; 69 unsigned char databuf[1]; 70 unsigned char beepstat; 71 struct beep_dev *dev = filp->private_data; /* 获取设备结构体 */ 72 73 /* 从用户空间拷贝数据到内核空间 */ 74 retvalue = copy_from_user(databuf, buf, cnt); 75 if (retvalue < 0) { 76 printk("kernel write failed!\r\n"); 77 return -EFAULT; 78 } 79 80 beepstat = databuf[0]; /* 获取蜂鸣器状态(1=开,0=关) */ 81 if (beepstat == BEEPON) { 82 gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器(根据硬件逻辑调整电平) */ 83 } else if (beepstat == BEEPOFF) { 84 gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */ 85 } 86 return 0; 87 } 88 89 /* 90 * @description : 关闭/释放设备 91 * @param - filp : 要关闭的设备文件 92 * @return : 0 成功;其他 失败 93 */ 94 static int beep_release(struct inode *inode, struct file *filp) 95 { 96 return 0; 97 } 98 99 /* 设备操作函数集 */ 100 static struct file_operations beep_fops = { 101 .owner = THIS_MODULE, 102 .open = beep_open, 103 .write = beep_write, 104 .release = beep_release, 105 }; 106 107 /* 108 * @description : 驱动入口函数(加载驱动时执行) 109 * @param : 无 110 * @return : 0 成功;负值 失败 111 */ 112 static int __init beep_init(void) 113 { 114 int ret = 0; 115 116 /* 1、获取设备树节点:beep */ 117 beep.nd = of_find_node_by_path("/beep"); 118 if (beep.nd == NULL) { 119 printk("beep node not find!\r\n"); 120 return -EINVAL; 121 } else { 122 printk("beep node find!\r\n"); 123 } 124 125 /* 2、解析设备树的beep-gpio属性,获取GPIO编号 */ 126 beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0); 127 if (beep.beep_gpio < 0) { 128 printk("can't get beep-gpio\r\n"); 129 return -EINVAL; 130 } 131 printk("beep-gpio num = %d\r\n", beep.beep_gpio); 132 133 /* 3、配置GPIO为输出模式,默认高电平(关闭蜂鸣器) */ 134 ret = gpio_direction_output(beep.beep_gpio, 1); 135 if (ret < 0) { 136 printk("can't set gpio!\r\n"); 137 } 138 139 /* 4、注册字符设备驱动(标准流程) */ 140 /* 4.1 创建设备号(手动/自动分配) */ 141 if (beep.major) { 142 beep.devid = MKDEV(beep.major, 0); 143 register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME); 144 } else { 145 alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME); 146 beep.major = MAJOR(beep.devid); 147 beep.minor = MINOR(beep.devid); 148 } 149 printk("beep major=%d,minor=%d\r\n", beep.major, beep.minor); 150 151 /* 4.2 初始化cdev */ 152 beep.cdev.owner = THIS_MODULE; 153 cdev_init(&beep.cdev, &beep_fops); 154 155 /* 4.3 添加cdev到内核 */ 156 cdev_add(&beep.cdev, beep.devid, BEEP_CNT); 157 158 /* 4.4 创建类 */ 159 beep.class = class_create(THIS_MODULE, BEEP_NAME); 160 if (IS_ERR(beep.class)) { 161 return PTR_ERR(beep.class); 162 } 163 164 /* 4.5 创建设备(生成/dev/beep节点) */ 165 beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME); 166 if (IS_ERR(beep.device)) { 167 return PTR_ERR(beep.device); 168 } 169 return 0; 170 } 171 172 /* 173 * @description : 驱动出口函数(卸载驱动时执行) 174 * @param : 无 175 * @return : 无 176 */ 177 static void __exit beep_exit(void) 178 { 179 /* 注销字符设备驱动 */ 180 cdev_del(&beep.cdev); 181 unregister_chrdev_region(beep.devid, BEEP_CNT); 182 183 /* 销毁设备和类 */ 184 device_destroy(beep.class, beep.devid); 185 class_destroy(beep.class); 186 } 187 188 module_init(beep_init); 189 module_exit(beep_exit); 190 MODULE_LICENSE("GPL"); 191 MODULE_AUTHOR("zuozhongkai");
行号范围核心功能关键说明
33–42设备结构体定义封装驱动所有资源,Linux 驱动核心设计
54绑定私有数据传递设备上下文,替代全局变量
74内核 / 用户空间数据拷贝必须用 copy_from_user,禁止直接访问用户指针
82/84GPIO 电平控制GPIO 子系统 API,无需操作寄存器
117获取设备树节点of_find_node_by_path 匹配设备树节点
126解析 GPIO 属性of_get_named_gpio 转换 GPIO 编号
134配置 GPIO 输出gpio_direction_output 设置输出模式
141–168字符设备注册流程创建设备号→初始化 cdev→创建类 / 设备
177–186资源释放与 init 反向操作,避免内存泄漏
188–191模块注册 + 协议声明内核识别驱动的关键,必须声明 GPL 协议

3.3.3、分析驱动代码

设备结构体设计(对应代码第 33–42 行)

在设备结构体beep_dev中加入 beep_gpio 这个成员变量,此成员变量保存蜂鸣器 所使用的 GPIO 编号。

33 struct beep_dev { 34 dev_t devid; /* 设备号 */ 35 struct cdev cdev; /* cdev */ 36 struct class *class; /* 类 */ 37 struct device *device; /* 设备 */ 38 int major; /* 主设备号 */ 39 int minor; /* 次设备号 */ 40 struct device_node *nd; /* 设备节点 */ 41 int beep_gpio; /* beep使用的GPIO编号 */ 42 };
私有数据传递(对应代码第 54 行)

第 54 行,将设备结构体变量beep 设置为 filp 的私有数据 private_data

filp->private_data = &beep;

这种将设备结构体设置为 filp 私有数据的方法在 Linux 内核驱动里面非常常见,方便 write 里取用,这样在后续操作函数中方便地获取设备上下文,避免使用全局变量。

write 函数(第 66–87 行,核心控制逻辑)
66 static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 67 { 68 int retvalue; 69 unsigned char databuf[1]; 70 unsigned char beepstat; 71 struct beep_dev *dev = filp->private_data; /* 获取设备结构体 */ 72 73 /* 从用户空间拷贝数据到内核空间 */ 74 retvalue = copy_from_user(databuf, buf, cnt); 75 if (retvalue < 0) { 76 printk("kernel write failed!\r\n"); 77 return -EFAULT; 78 } 79 80 beepstat = databuf[0]; /* 获取蜂鸣器状态(1=开,0=关) */ 81 if (beepstat == BEEPON) { 82 gpio_set_value(dev->beep_gpio, 0); /* 打开蜂鸣器 */ 83 } else if (beepstat == BEEPOFF) { 84 gpio_set_value(dev->beep_gpio, 1); /* 关闭蜂鸣器 */ 85 } 86 return 0; 87 }
第 71 行:从filp->private_data恢复设备结构体指针,是open函数的反向操作,实现上下文传递。
第 74 行:使用copy_from_user拷贝用户空间数据到内核空间 —— 这是 Linux 内核的强制规范,禁止直接访问用户空间指针(避免内存越界、权限问题)。
第 82/84 行:通过gpio_set_value控制 GPIO 电平,完全基于 GPIO 子系统 API,无需操作底层寄存器。
注意:电平值(0/1)需根据硬件原理图调整,本实验中蜂鸣器为(低电平触发鸣叫,高电平关闭)。
驱动入口函数 beep_init(第 112–170 行,驱动初始化)
112 static int __init beep_init(void) 113 { 114 int ret = 0; 115 116 /* 1、获取设备树节点:beep */ 117 beep.nd = of_find_node_by_path("/beep"); 118 if (beep.nd == NULL) { 119 printk("beep node not find!\r\n"); 120 return -EINVAL; 121 } else { 122 printk("beep node find!\r\n"); 123 } 124 125 /* 2、解析设备树的beep-gpio属性,获取GPIO编号 */ 126 beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpio", 0); 127 if (beep.beep_gpio < 0) { 128 printk("can't get beep-gpio\r\n"); 129 return -EINVAL; 130 } 131 printk("beep-gpio num = %d\r\n", beep.beep_gpio); 132 133 /* 3、配置GPIO为输出模式,默认高电平(关闭蜂鸣器) */ 134 ret = gpio_direction_output(beep.beep_gpio, 1); 135 if (ret < 0) { 136 printk("can't set gpio!\r\n"); 137 } 138 139 /* 4、注册字符设备驱动(标准流程) */ 140 /* 4.1 创建设备号(手动/自动分配) */ 141 if (beep.major) { 142 beep.devid = MKDEV(beep.major, 0); 143 register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME); 144 } else { 145 alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME); 146 beep.major = MAJOR(beep.devid); 147 beep.minor = MINOR(beep.devid); 148 } 149 printk("beep major=%d,minor=%d\r\n", beep.major, beep.minor); 150 151 /* 4.2 初始化cdev */ 152 beep.cdev.owner = THIS_MODULE; 153 cdev_init(&beep.cdev, &beep_fops); 154 155 /* 4.3 添加cdev到内核 */ 156 cdev_add(&beep.cdev, beep.devid, BEEP_CNT); 157 158 /* 4.4 创建类 */ 159 beep.class = class_create(THIS_MODULE, BEEP_NAME); 160 if (IS_ERR(beep.class)) { 161 return PTR_ERR(beep.class); 162 } 163 164 /* 4.5 创建设备(生成/dev/beep节点) */ 165 beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME); 166 if (IS_ERR(beep.device)) { 167 return PTR_ERR(beep.device); 168 } 169 return 0; 170 }
设备树解析(第 117/126 行):
第 117 行of_find_node_by_path("/beep"):通过绝对路径找到设备树中定义的beep节点,是驱动与设备树绑定的第一步;
第 126 行of_get_named_gpio(beep.nd, "beep-gpio", 0):解析设备树中beep-gpio属性,将<&gpio5 1 GPIO_ACTIVE_HIGH>转换为内核可识别的 GPIO 编号(本实验中为 129)。


GPIO 初始化(第 134 行):gpio_direction_output将 GPIO 配置为输出模式,并设置默认电平为高(关闭蜂鸣器),替代了裸机开发中直接配置GPIOx_GDIR寄存器的操作。


字符设备注册(第 141–168 行):遵循 Linux 字符设备驱动的标准流程 —— 创建设备号→初始化 cdev→添加 cdev→创建类→创建设备,最终自动生成/dev/beep设备节点,用户层可直接操作。
驱动出口函数 beep_exit(第 177–186 行,资源释放)
177 static void __exit beep_exit(void) 178 { 179 /* 注销字符设备驱动 */ 180 cdev_del(&beep.cdev); 181 unregister_chrdev_region(beep.devid, BEEP_CNT); 182 183 /* 销毁设备和类 */ 184 device_destroy(beep.class, beep.devid); 185 class_destroy(beep.class); 186 }
核心原则:与beep_init的操作完全反向,确保驱动卸载时释放所有占用的内核资源(设备号、cdev、类、设备节点),避免内存泄漏。
第 180 行:删除 cdev 结构体,注销字符设备;
第 181 行:释放申请的设备号;
第 184 行:销毁/dev/beep设备节点;
第 185 行:销毁设备类。
无 GPIO 释放:GPIO 子系统会自动管理 GPIO 资源,无需手动释放,体现了内核子系统的「自动化管理」优势。

3.4、测试APP

3.4.1测试APP执行流程图

3.4.2、程序编写

#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 描述 : beep测试APP。 其他 : 无 使用方法 :./beepApp /dev/beep 0 关闭蜂鸣器 ./beepApp /dev/beep 1 打开蜂鸣器 ***************************************************************/ #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; } 
 测试 APP 代码和 LED 几乎一样,仅修改命名和提示信息,核心逻辑为:
校验命令行参数(必须传入设备路径和控制指令);
打开/dev/beep设备文件;
向驱动写入控制指令(1 = 开,0 = 关);
关闭设备文件。

四、运行测试

4.1、编译驱动程序和测试 APP

编写 Makefile 文件,本次实验的 Makefile 文件和之前的led实验基本一样,只是将 obj-m 变量的值改为beep.o,Makefile 内容如下所示:

KERNELDIR := /home/duan/linux/linux-imx-rel_imx_4.1.15_2.1.1_ga_alientek_v2.2 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

输入如下命令编译出驱动模块文件:

make -j32

编译成功以后就会生成一个名为“beep.ko”的驱动模块文件。

编译测试 APP

输入如下命令编译测试beepApp.c 这个测试程序:

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

4.2、运行测试

将上一小节编译出来的beep.ko和beepApp 这两个文件拷贝到 rootfs/lib/modules/4.1.15 目录中。

sudo cp beep.ko /home/duan/linux/nfs/rootfs/lib/modules/4.1.15/ -f
sudo cp beepApp /home/duan/linux/nfs/rootfs/lib/modules/4.1.15/ -f

进入到目录 lib/modules/4.1.15 中,输入如下命令加载beep.ko 驱动模块:

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

驱动加载成功以后会在终端中输出一些信息,如下图所示:

从上图可以看出,beep 这个节点找到了,并且 GPIO5_IO01 这个 GPIO 的编号为 129。

使用 beepApp 软件来测试驱动是否工作正常,输入如下命令打开蜂鸣器:

./beepApp /dev/beep 1 //打开蜂鸣器

输入上述命令,查看 I.MX6U-ALPHA 开发板上的蜂鸣器是否有鸣叫,如果鸣叫的话说明驱动工作正常。在输入如下命令关闭蜂鸣器:

./beepApp /dev/beep 0 //关闭蜂鸣器

输入上述命令以后观察 I.MX6U-ALPHA 开发板上的蜂鸣器是否停止鸣叫。如果要卸载驱动的话输入如下命令即可:

rmmod beep.ko

总结

本期博客完成了基于 pinctrl+GPIO 子系统的蜂鸣器的实验。

Read more

解锁DeepSeek潜能:Docker+Ollama打造本地大模型部署新范式

解锁DeepSeek潜能:Docker+Ollama打造本地大模型部署新范式

🐇明明跟你说过:个人主页 🏅个人专栏:《深度探秘:AI界的007》 🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、什么是Docker 2、什么是Ollama 二、准备工作 1、操作系统 2、镜像准备 三、安装 1、安装Docker 2、启动Ollama 3、拉取Deepseek大模型 4、启动Deepseek  一、引言 1、什么是Docker Docker:就像一个“打包好的App” 想象一下,你写了一个很棒的程序,在自己的电脑上运行得很好。但当你把它发给别人,可能会遇到各种问题: * “这个软件需要 Python 3.8,但我只有 Python 3.6!

By Ne0inhk
深挖 DeepSeek 隐藏玩法·智能炼金术2.0版本

深挖 DeepSeek 隐藏玩法·智能炼金术2.0版本

前引:屏幕前的你还在AI智能搜索框这样搜索吗?“这道题怎么写”“苹果为什么红”“怎么不被发现翘课” ,。看到此篇文章的小伙伴们!请准备好你的思维魔杖,开启【霍格沃茨模式】,看我如何更新秘密的【知识炼金术】,我们一起来解锁更加刺激的剧情!友情提醒:《《《前方高能》》》 目录 在哪使用DeepSeek 如何对提需求  隐藏玩法总结 几个高阶提示词 职场打工人 自媒体创作 电商实战 程序员开挂 非适用场地 “服务器繁忙”如何解决 (1)硅基流动平台 (2)Chatbox + API集成方案 (3)各大云平台 搭建个人知识库 前置准备 下载安装AnythingLLM 选择DeepSeek作为AI提供商 创作工作区 导入文档 编辑  编辑 小编寄语 ——————————————————————————————————————————— 在哪使用DeepSeek 我们解锁剧情前,肯定要知道在哪用DeepSeek!咯,为了照顾一些萌新朋友,它的下载方式我放在下面了,拿走不谢!  (1)

By Ne0inhk
【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

目录 一、前言 二、AI视频概述 2.1 什么是AI视频 2.2 AI视频核心特点 2.3 AI视频应用场景 三、通义万相介绍 3.1 通义万相概述 3.1.1 什么是通义万相 3.2 通义万相核心特点 3.3 通义万相技术特点 3.4 通义万相应用场景 四、DeepSeek + 通义万相制作AI视频流程 4.1 DeepSeek + 通义万相制作视频优势 4.1.1 DeepSeek 优势 4.1.2 通义万相视频生成优势 4.2

By Ne0inhk
【DeepSeek微调实践】DeepSeek-R1大模型基于MS-Swift框架部署/推理/微调实践大全

【DeepSeek微调实践】DeepSeek-R1大模型基于MS-Swift框架部署/推理/微调实践大全

系列篇章💥 No.文章01【DeepSeek应用实践】DeepSeek接入Word、WPS方法详解:无需代码,轻松实现智能办公助手功能02【DeepSeek应用实践】通义灵码 + DeepSeek:AI 编程助手的实战指南03【DeepSeek应用实践】Cline集成DeepSeek:开源AI编程助手,终端与Web开发的超强助力04【DeepSeek开发入门】DeepSeek API 开发初体验05【DeepSeek开发入门】DeepSeek API高级开发指南(推理与多轮对话机器人实践)06【DeepSeek开发入门】Function Calling 函数功能应用实战指南07【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:本地部署与API服务快速上手08【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:Web聊天机器人部署指南09【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:基于vLLM 搭建高性能推理服务器10【DeepSeek部署实战】基于Ollama快速部署Dee

By Ne0inhk