当前位置: 首页 > news >正文

【IMX6ULL驱动开发学习】08.马达驱动实战:驱动编写、手动注册平台设备和设备树添加节点信息

目录

一、使用设备树

1.1 修改设备树流程

二、手动创建平台设备 

三、总结(附驱动程序)


前情提要:​​​​​​​【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树_阿龙还在写代码的博客-CSDN博客

手动注册平台设备和设备树的目的都是为了构造platform_device结构体

一、使用设备树

之前的驱动编写方式,引脚信息在驱动程序里写死了,移植性差。而使用设备树,这样的程序编写方式便于在换了板子或引脚之后,只要修改设备树的节点信息即可,不需要看复杂的驱动程序,前提是有完备的驱动程序。

在驱动程序的入口函数里注册了gpio_platform_driver,

module_init(gpio_drv_init);
static int __init gpio_drv_init(void)
{/* 注册platform_driver */return platform_driver_register(&gpio_platform_driver);
}

gpio_platform_driver结构体中probe函数被调用,需要在设备树中找到与之匹配的设备节点信息,即设备节点信息也要含有这个语句.compatible = "100ask,gpiodemo"

static const struct of_device_id gpio_dt_ids[] = {{ .compatible = "100ask,gpiodemo", },{ /* sentinel */ }
};static struct platform_driver gpio_platform_driver = {.driver		= {.name	= "100ask_gpio_plat_drv",.of_match_table = gpio_dt_ids,},.probe		= gpio_drv_probe,.remove		= gpio_drv_remove,
};

1.1 修改设备树流程

在内核目录下找到设备树文件,以我的路径为例,这里给出绝对路径

  • 修改设备树:vi /home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dts
  • 增加节点信息如下:
motor {compatible = "100ask,gpiodemo";gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>, <&gpio4 20 GPIO_ACTIVE_HIGH>,<&gpio4 21 GPIO_ACTIVE_HIGH>,<&gpio4 22 GPIO_ACTIVE_HIGH>;
};
  • 一定要在/home/book/100ask_imx6ull-sdk/Linux-4.9.88目录下编译设备树:make dtbs
  • 复制到单板上,如下:
PC:
cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/开发板:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
cp /mnt/100ask_imx6ull-14x14.dtb  /boot
reboot
  • 查看设备树节点信息:ls /sys/firmware/devicetree/base/ 

  •  查看马达节点有没有被转换成平台设备(platform_device):ls /sys/bus/platform/devices/

 

 有这个motor文件表示有了对应的平台设备。

  • 进入motor文件查看,因为还没装驱动,所以没有显示驱动程序

 

  • 装载驱动后,查看motor文件: 说明驱动和设备都匹配上了

 

  • 最后就编写测试程序进行测试即可: 
./button_test /dev/motor ...

二、手动创建平台设备 

  • 编写dev.c文件
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>static struct resource my_drv_resource[] = {{.start          = 115,.end            = 115,.flags          = IORESOURCE_IRQ,},{.start          = 116,.end            = 116,.flags          = IORESOURCE_IRQ,},{.start          = 117,.end            = 117,.flags          = IORESOURCE_IRQ,},{.start          = 118,.end            = 118,.flags          = IORESOURCE_IRQ,},
};static struct platform_device gpio_platform_device = {.name           = "100ask_gpio_plat_drv",.id             = 0,.num_resources  = ARRAY_SIZE(my_drv_resource),// =4 宏来计算数组大小.resource       = my_drv_resource,
};static int __init gpio_dev_init(void)
{/* 注册platform_driver */return platform_device_register(&gpio_platform_device);
}static void __exit gpio_dev_exit(void)
{/* 反注册platform_driver */platform_device_unregister(&gpio_platform_device);
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(gpio_dev_init);
module_exit(gpio_dev_exit);MODULE_LICENSE("GPL");

 其中,由这个名字"100ask_gpio_plat_drv"找到与之匹配的平台驱动(platform_driver)

static struct platform_device gpio_platform_device = {.name           = "100ask_gpio_plat_drv",.id             = 0,.num_resources  = ARRAY_SIZE(my_drv_resource),// =4 宏来计算数组大小.resource       = my_drv_resource,
};
  •  修改Makefile文件
KERN_DIR =  /home/book/100ask_imx6ull-sdk/Linux-4.9.88 # 板子所用内核源码的目录all:make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o button_test button_test.c
clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.order  button_test# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.oobj-m += gpio_drv.o
obj-m += gpio_dev.o
  •  编译:make
  • 设备树节点信息取消:因为内核里有了设备树节点信息,需要改回去或者添加disabled状态
motor {compatible = "100ask,gpiodemo";gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>, <&gpio4 20 GPIO_ACTIVE_HIGH>,<&gpio4 21 GPIO_ACTIVE_HIGH>,<&gpio4 22 GPIO_ACTIVE_HIGH>;status = "disabled";
};
  • 编译:make dtbs
  • 更新设备树:
PC:
cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/开发板:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
cp /mnt/100ask_imx6ull-14x14.dtb  /boot
reboot
  • 查看设备树节点信息:ls /sys/firmware/devicetree/base/

        状态为禁止状态,设备树节点信息被我们禁用了 

 

  • 查看马达节点有没有被转换成平台设备(platform_device):ls /sys/bus/platform/devices/,这里肯定也是没有的

 

  •  装载驱动程序、查看是否匹配上、查看设备节点

 这个平台驱动支持名为"100ask_gpio_plat_drv"的设备,设备节点也出来了

 

三、总结(附驱动程序)

这个驱动程序,在入口函数里注册了平台驱动,这个平台驱动可以支持来自设备树的平台设备.compatible = "100ask,gpiodemo",和来自自己手动创建的平台设备.name = "100ask_gpio_plat_drv"。匹配规则有4种,这里用的是最简单的。当平台driver匹配到平台device时,驱动程序里的probe函数就被调用,probe函数里可以从设备树里得到引脚信息,也可以从手动注册的平台device里得到所谓的资源。

static const struct of_device_id gpio_dt_ids[] = {{ .compatible = "100ask,gpiodemo", },{ /* sentinel */ }
};static struct platform_driver gpio_platform_driver = {.driver		= {.name	= "100ask_gpio_plat_drv",.of_match_table = gpio_dt_ids,//设备树信息},.probe		= gpio_drv_probe,.remove		= gpio_drv_remove,
};

 驱动程序:

#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>struct gpio_desc{int gpio;int irq;char name[128];int key;struct timer_list key_timer;
} ;static struct gpio_desc *gpios;
static int count;/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;/* 马达引脚设置数字 */
static int g_motor_pin_ctrl[8]= {0x2,0x3,0x1,0x9,0x8,0xc,0x4,0x6};
static int g_motor_index = 0;void set_pins_for_motor(int index)
{int i;for (i = 0; i < 4; i++){gpio_set_value(gpios[i].gpio, g_motor_pin_ctrl[index] & (1<<i) ? 1 : 0);}
}void disable_motor(void)
{int i;for (i = 0; i < 4; i++){gpio_set_value(gpios[i].gpio, 0);}
}/* int buf[2];* buf[0] = 步进的次数, > 0 : 逆时针步进; < 0 : 顺时针步进* buf[1] = mdelay的时间*/
static ssize_t motor_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int ker_buf[2];int err;int step;if (size != 8)return -EINVAL;err = copy_from_user(ker_buf, buf, size);if (ker_buf[0] > 0){/* 逆时针旋转 */for (step = 0; step < ker_buf[0]; step++)
{set_pins_for_motor(g_motor_index);mdelay(ker_buf[1]);g_motor_index--;if (g_motor_index == -1)g_motor_index = 7;}}else{/* 顺时针旋转 */ker_buf[0] = 0 - ker_buf[0];for (step = 0; step < ker_buf[0]; step++)
{set_pins_for_motor(g_motor_index);mdelay(ker_buf[1]);g_motor_index++;if (g_motor_index == 8)g_motor_index = 0;}
}/* 改进:旋转到位后让马达不再消耗电源 */disable_motor();return 8;    
}/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {.owner	 = THIS_MODULE,.write   = motor_write,
};/* 在入口函数 */
static int gpio_drv_probe(struct platform_device *pdev)
{int err = 0;int i;struct device_node *np = pdev->dev.of_node;struct resource *res;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 从platfrom_device获得引脚信息 * 1. pdev来自c文件* 2. pdev来自设备树*/if (np){/* pdev来自设备树 : 示例reg_usb_ltemodule: regulator@1 {compatible = "100ask,gpiodemo";gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>, <&gpio5 3 GPIO_ACTIVE_HIGH>;};*/count = of_gpio_count(np);if (!count)return -EINVAL;gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);for (i = 0; i < count; i++){gpios[i].gpio = of_get_gpio(np, i);sprintf(gpios[i].name, "%s_pin_%d", np->name, i);}}else{/* pdev来自c文件 static struct resource omap16xx_gpio3_resources[] = {{.start  = 115,.end    = 115,.flags  = IORESOURCE_IRQ,},{.start  = 118,.end    = 118,.flags  = IORESOURCE_IRQ,},		};		*/count = 0;while (1){res = platform_get_resource(pdev, IORESOURCE_IRQ, count);if (res){count++;}else{break;}}if (!count)return -EINVAL;gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);for (i = 0; i < count; i++){res = platform_get_resource(pdev, IORESOURCE_IRQ, i);gpios[i].gpio = res->start;sprintf(gpios[i].name, "%s_pin_%d", pdev->name, i);}}for (i = 0; i < count; i++){err = gpio_request(gpios[i].gpio, gpios[i].name);gpio_direction_output(gpios[i].gpio, 0);}/* 注册file_operations 	*/major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv);  /* /dev/gpio_desc */gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");if (IS_ERR(gpio_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_gpio_key");return PTR_ERR(gpio_class);}device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "motor"); /* /dev/motor */return err;
}/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*/
static int gpio_drv_remove(struct platform_device *pdev)
{int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class);unregister_chrdev(major, "100ask_gpio_key");for (i = 0; i < count; i++){gpio_free(gpios[i].gpio);}return 0;
}static const struct of_device_id gpio_dt_ids[] = {{ .compatible = "100ask,gpiodemo", },{ /* sentinel */ }
};static struct platform_driver gpio_platform_driver = {.driver		= {.name	= "100ask_gpio_plat_drv",.of_match_table = gpio_dt_ids,//设备树信息},.probe		= gpio_drv_probe,.remove		= gpio_drv_remove,
};static int __init gpio_drv_init(void)
{/* 注册platform_driver */return platform_driver_register(&gpio_platform_driver);
}static void __exit gpio_drv_exit(void)
{/* 反注册platform_driver */platform_driver_unregister(&gpio_platform_driver);
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(gpio_drv_init);
module_exit(gpio_drv_exit);MODULE_LICENSE("GPL");

 

相关文章:

【IMX6ULL驱动开发学习】08.马达驱动实战:驱动编写、手动注册平台设备和设备树添加节点信息

目录 一、使用设备树 1.1 修改设备树流程 二、手动创建平台设备 三、总结&#xff08;附驱动程序&#xff09; 前情提要&#xff1a;​​​​​​​【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树_阿龙还在写代码的博客-CSDN博客 手动注册…...

直方图均衡化和自适应直方图均衡化

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 均衡化是数字图像处理中常用的一种技术&#xff0c;用于增强图像的视觉效果和对比度。&#xff0c;今天我们将实现对同一张图像的直方图均衡化和自适应直方图均衡化处理&#xff0c;学习一下两者的的基本原理和实现过程&a…...

京东门详一码多端探索与实践 | 京东云技术团队

本文主要讲述京东门详业务在支撑过程中遇到的困境&#xff0c;面对问题我们在效率提升、质量保障等方向的探索和实践&#xff0c;在此将实践过程中问题解决的思路和方案与大家一起分享&#xff0c;也希望能给大家带来一些新的启发 一、背景 1.1、京东门详介绍 1.1.1、京东门…...

数据挖掘 | 零代码采集房源数据,支持自动翻页、数据排重等

1 前言 城市规划、商业选址等应用场景中经常会对地区房价、地域价值进行数据分析&#xff0c;其中地区楼盘房价是分析数据中重要的信息参考点&#xff0c;一些互联网网站上汇聚了大量房源信息&#xff0c;通过收集此类数据&#xff0c;能够对地区房价的分析提供参考依据。 如何…...

迪米特法则

迪米特法则&#xff0c;也称为最少知识原则&#xff08;Law of Demeter&#xff09;&#xff0c;是面向对象设计中的一个原则&#xff0c;旨在降低对象之间的耦合性&#xff0c;提高系统的可维护性和可扩展性。该原则强调一个类不应该直接与其它不相关的类相互交互&#xff0c;…...

云积天赫|AIGC+营销的排头兵

AIGC生成式人工智能&#xff0c;正逐渐成为人们关注的焦点。AIGC的出现&#xff0c;标志着人工智能已经进入了一个全新的时代。AIGC的出现&#xff0c;也为营销行业带来了新的活力。那么企业该怎么利用这次AIGC浪潮&#xff0c;成为AIGC营销的排头兵呢&#xff1f;      “…...

Oracle 数据库备份

1、使用管理员账号创建对应的directory目录 登录数据库 sqlplus / as sysdba 创建directory create or replace directory dumpdir as F:\container; 2、给用户赋予使用该目录的权限 grant read,write on directory dumpdir to Scott; 查看创建的目录位置 select * fro…...

【C++】模板template

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ C       &#x1f6f0;️社区 :✈️ 进步学堂       &#x1f6f0;️欢…...

智能工厂:适应不断变化的制造世界

制造业已经从过去传统的装配线工艺流程中走了很长一段路。随着技术的进步和工业 4.0 的兴起&#xff0c;制造业正在迅速发展&#xff0c;以满足现代世界不断变化的需求。近年来出现的一个关键概念就是“智能工厂”。在这篇文章中&#xff0c;我们将探讨什么是智能工厂、它是如何…...

大数据课程I3——Kafka的消息流与索引机制

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 掌握Kafka的消息流处理; ⚪ 掌握Kafka的索引机制; ⚪ 掌握Kafka的消息系统语义; 一、Kafka消息流处理 1. Producer 写入消息 流程说明: 1. producer 要向Kafka生产消息,需要先通过…...

LVGL学习笔记 28 - 键盘keyboard

目录 1. 设置关联文本框 2. 设置模式 2.1 LV_KEYBOARD_MODE_TEXT_LOWER 2.2 LV_KEYBOARD_MODE_TEXT_UPPER 2.3 LV_KEYBOARD_MODE_SPECIAL 2.4 LV_KEYBOARD_MODE_NUMBER 2.5 LV_KEYBOARD_MODE_USER_1 ~ LV_KEYBOARD_MODE_USER_4 3. 使能弹窗模式 4. 更改按键布局 5. 事…...

【Microsoft 支持】【数据库-MySql】当您尝试从大于 5000 的 TCP 端口连接时收到错误 WSAENOBUFS (10055)

​ 一、转载原文 When you try to connect from TCP ports greater than 5000 you receive the error ‘WSAENOBUFS (10055)’ Symptoms If you try to set up TCP connections from ports that are greater than 5000, the local computer responds with the following WSAE…...

【学会动态规划】最长湍流子数组(23)

目录 动态规划怎么学&#xff1f; 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后&#xff1a; 动态规划怎么学&#xff1f; 学习一个算法没有捷径&#xff0c;更何况是学习动态规划&#xff0c; 跟我…...

【网络编程·网络层】IP协议

目录 一、IP协议的概念 二、IP协议的报头 1、四位首部长度 2、16位总长度&#xff08;解包&#xff09; 3、8位协议&#xff08;分用&#xff09; 4、16位首部校验和 5、8位生存时间 6、32位源IP和32位目的IP 7、4位版本/8位服务类型 8、16位标识 9、3位标志 10、1…...

HTML详解连载(7)

HTML详解连载&#xff08;7&#xff09; 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽结构伪类选择器作用 :nth-child&#xff08;公式&#xff09;作用举例 伪元素选择器作用注意&#xff1a; PxCoook作用盒子模型-重要组成部分 盒子模型-边框线属性名属性…...

一文打通redis中的String类型

目录 基本介绍 基本语法 常用命令解读 概述 SETNX key value SETNX key value GETRANGE key start end GETSET key value GETBIT key offset MGET key1 [key2..] STRLEN key 基本介绍 ①String是Redis最基本的类型,一个key对应一个value。 ②String类型是二进制…...

优测云服务平台|【压力测试功能升级】轻松完成压测任务

一、本次升级主要功能如下&#xff1a; 1.多份报告对比查看测试结果 2.报告新增多种下载格式 Word格式Excel格式 3.新增多种编排复杂场景的控制器 漏斗控制器并行控制器事务控制器仅一次控制器分组控制器集合点 4.新增概览页面&#xff0c;包含多种统计维度 二、报告对比…...

UseEffect中使用setState更新后获取的值为何依然是更新前

刚开始学习React的新手经常遇到这样的问题&#xff0c;使用useState去更新某个数据&#xff0c;然后再取更新后的数据&#xff0c;取发现数据并没有更新。 在 React 中&#xff0c;useState 的更新确实是异步的&#xff0c;这是由 React 的内部机制所决定的。React 会对多次状…...

去掉鼠标系列之一: 语雀快捷键使用指南

其实应该是系列之二了&#xff0c;因为前面写了一个关于Interlij IDEA的快捷键了。 为什么要写这个了&#xff0c;主要是觉得一会儿用鼠标&#xff0c;一会儿键盘&#xff0c;一点儿不酷&#xff0c;我希望可以一直用键盘&#xff0c;抛开鼠标。后面陆续记录一下各个软件的快捷…...

【Linux】Reactor模式

Reactor模式 Reactor模式的定义 Reactor反应器模式&#xff0c;也叫做分发者模式或通知者模式&#xff0c;是一种将就绪事件派发给对应服务处理程序的事件设计模式。 Reactor模式的角色构成 Reactor主要由以下五个角色构成&#xff1a; reactor模式的角色 角色解释Handle(句…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...