Linux platform tree下的单总线驱动程序设计(DHT11)
目录
概述
1 认识DHT11
1.1 DHT11特性
1.2 DHT11数据格式
1.3 DHT11与MCU通信
1.4 DHT11信号解析
1.4.1 起始信号
1.4.2 解析信号0
1.4.3 解析信号1
2 驱动开发
2.1 硬件接口
2.2 更新设备树
2.2.1 添加驱动节点
2.2.2 编译.dts
2.2.3 更新板卡中的.dtb
2.3 驱动程序实现
2.3.1 编写驱动程序
2.3.2 编写Makefile
3 测试程序
3.1 编写测试程序
3.2 编写Makefile
4 编译和运行
4.1 编译和安装驱动程序
4.2 编译和运行测试程序
5 波形分析
5.1 起始信号波形
5.2 信息bit = 0波形
5.3 信息bit = 1波形
概述
本文介绍platform tree下,如何设计一个单总线设备的驱动,根据datasheeet提供的波形图,使用代码来实现该驱动程序。然后用逻辑分析仪捕捉信号波形,分析其是否和datasheet中的波形一致。
1 认识DHT11
1.1 DHT11特性
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。 它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。

由上表可得:
温度范围: 0~ 50℃, 低于或者高于这个范围的温度不能测量
湿度范围:20~90%RH
1.2 DHT11数据格式
DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,读一次数据总共包括8bytes( 40 bit )具体格式如下:
Byte-0: 8bit 湿度 整数 数据
Byte-1: 8bit 湿度 小数 数据
Byte-2: 8bit 温度 整数 数据
Byte-3: 8bit 温度 小数 数据
byte-4: 8bit校验和( Byte-0 + Byte-1 + Byte-2 + Byte-3)
1.3 DHT11与MCU通信
Step-1: Master 发送起始信号------->dth11, 信号变化规律为 1 - > 0 -> 1
Step-2: dht11发出响应信号,信号特征为 0 ->1
Step-3:dht11发送数据bit位,总共40个bit

1.4 DHT11信号解析
1.4.1 起始信号
Step-1: Master 发出触发信号:1 -> 0, 该信号至少持续18ms
step-2: Master电平0 ->1,该电平持续20~40us
Step-3: dht11发送响应信号0->1,该电平持续80us
step-4: dht11发送信号1,准备发送数据信息,该电平持续时间80us

1.4.2 解析信号0
信号0特征:
1)0 ->1持续 50us
2)1->0持续26~28us

1.4.3 解析信号1
信号1特征:
1)0 ->1持续 50us
2)1->0持续70us

2 驱动开发
2.1 硬件接口
DHT-11与MCU之间的连接图:

在板卡ATK-DL6Y2C上DTH-11的对应接口:
GPIO4_19: DHT11-IO

硬件实物图:

DHT11引脚说明:

2.2 更新设备树
2.2.1 添加驱动节点
DHT11引脚和IMX.6ULL引脚对应关系:
GPIO4_19: DHT11-IO
.dts文件路径:
/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c/arch/arm/boot/dts/imx6ull-14x14-evk.dts
在.dts文件中添加如下代码:
//mftang: user's dht11, 2024-2-14// IO: GPIO-4-PIN19mftangdht11 {compatible = "atk-dl6y2c,dht11";pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpio_mftang_1_wire>;gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;status = "okay";};
其在imx6ull-14x14-evk.dts中位置:

2.2.2 编译.dts
编译.dts文件,并把编译生成的.dtb文件发送到NFS共享目录下,便于在板卡中操作该文件。
1)在内核根目录下使用如下命令编译.dts文件
make dtbs

2) 复制 .dtb 文件至NFS共享目录
cp arch/arm/boot/dts/imx6ull-14x14-emmc-4.3-480x272-c.dtb /home/mftang/nfs/atk_dl6y2c/

2.2.3 更新板卡中的.dtb
复制.dtb文件到相应的运行目录,然后重新板卡
cp /mnt/atk_dl6y2c/imx6ull-14x14-emmc-4.3-480x272-c.dtb /run/media/mmcblk1p1

reboot板卡后,内核会重新读取.dtb文件。然后在/proc/device-tree目录下查看板卡device tree,使用如下命令:
cd /proc/device-tree
ls -l
运行该命令后,在该目录下可以看见sensor信息,说明device已经加载到内核:

2.3 驱动程序实现
2.3.1 编写驱动程序
创建drv_dht11.c,并在该文件中编写驱动程序,驱动程序代码地址
/***************************************************************
Copyright 2024-2029. All rights reserved.
文件名 : drv_14_dht11.c
作者 : tangmingfei2013@126.com
版本 : V1.0
描述 : dht11 驱动程序, GPIO4_PIN19-----DHT11 IO port
其他 : 无
日志 : 初版V1.0 2024/1/30 使用方法:
1) 在.dts文件中定义节点信息//mftang: user's dht11, 2024-2-14// IO: GPIO-4-PIN19mftangdht11 {compatible = "atk-dl6y2c,dht11";pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpio_mftang_1_wire>;gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>;status = "okay";};2) 在驱动匹配列表
static const struct of_device_id dht11_of_match[] = {{ .compatible = "atk-dl6y2c,dht11" },{ } // Sentinel
};
***************************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ktime.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_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define DEVICE_NAME "treedht11" // dev/treedht11/* dht11dev设备结构体 */
struct dht11stru_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */struct device_node *node; /* dht11设备节点 */int userdht11; /* dht11 GPIO标号*/struct gpio_desc *pin;
};struct dht11stru_dev dht11dev; /* dht11设备 */ int us_low_array[40];
int us_low_index;
int us_array[40];
int time_array[40];
int us_index;/*dht11 driver
*/
static void dht11_release( void )
{gpiod_direction_output(dht11dev.pin, 1);
}static void dht11_start(void)
{gpiod_direction_output(dht11dev.pin, 1);mdelay(30);gpiod_set_value( dht11dev.pin, 0);mdelay(20);gpiod_set_value(dht11dev.pin, 1);udelay(40);gpiod_direction_input(dht11dev.pin);
}static int dht11_wait_ack(void)
{int timeout_us = 20000;/* 等待低电平 */while (gpiod_get_value(dht11dev.pin) && --timeout_us){udelay(1);}if (!timeout_us){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return -1;}/* 现在是低电平 *//* 等待高电平 */timeout_us = 200;while (!gpiod_get_value(dht11dev.pin) && --timeout_us){udelay(1);}if (!timeout_us){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return -1;}/* 现在是高电平 *//* 等待低电平 */timeout_us = 200;while (gpiod_get_value(dht11dev.pin) && --timeout_us){udelay(1);}if (!timeout_us){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return -1;}return 0;
}static int dht11_read_byte( unsigned char *datalist )
{int i;int us = 0;unsigned char data = 0;int timeout_us = 200;u64 pre, last;for (i = 0; i < 8; i++){/* 现在是低电平 *//* 等待高电平 */timeout_us = 400;us = 0;while (!gpiod_get_value(dht11dev.pin) && --timeout_us){udelay(1);us++;}if (!timeout_us){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return -1;}us_low_array[us_low_index++] = us;/* 现在是高电平 *//* 等待低电平,累加高电平的时间 */timeout_us = 20000000;us = 0;/* set another gpio low */pre = ktime_get_boot_ns();while (1) {last = ktime_get_boot_ns();if (last - pre >= 40000)break;}if (gpiod_get_value(dht11dev.pin)){/* get bit 1 */data = (data << 1) | 1;/* 当前位的高电平未结束, 等待 */timeout_us = 400;us = 0;while (gpiod_get_value(dht11dev.pin) && --timeout_us){udelay(1);us++;}if (!timeout_us){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return -1;}}else{/* get bit 0 */data = (data << 1) | 0;}}*datalist = data;return 0;
}static int dht11_get_value( unsigned char *data )
{unsigned long flags;int i;local_irq_save(flags); // 关中断us_index = 0;us_low_index = 0;/* 1. 发送高脉冲启动DHT11 */dht11_start();/* 2. 等待DHT11就绪 */if (dht11_wait_ack()){local_irq_restore(flags); // 恢复中断printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return -EAGAIN;}/* 3. 读5字节数据 */for (i = 0; i < 5; i++){if (dht11_read_byte(&data[i])){local_irq_restore(flags); // 恢复中断printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return -EAGAIN;}}/* 4. 释放总线 */dht11_release();local_irq_restore(flags); // 恢复中断/* 5. 根据校验码验证数据 */if (data[4] != (data[0] + data[1] + data[2] + data[3])){printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return -1;}return 0;
}/*linux driver 驱动接口: 实现对应的open/read/write等函数,填入file_operations结构体
*/
static ssize_t dht11_drv_read ( struct file *file, char __user *buf, size_t size, loff_t *offset)
{unsigned char data[4];int err;if( !dht11_get_value( data ) ){printk(" %s line %d \r\n", __FUNCTION__, __LINE__);err = copy_to_user(buf, data, 4);return 4;}return -1;
}static int dht11_drv_close(struct inode *node, struct file *file)
{printk(" %s line %d \r\n", __FUNCTION__, __LINE__);return 0;
}static int dht11_drv_open(struct inode *inode, struct file *filp)
{filp->private_data = &dht11dev; /* 设置私有数据 */return 0;
}/* 定义driver的file_operations结构体
*/
static struct file_operations dht11_fops = {.owner = THIS_MODULE,.read = dht11_drv_read,.open = dht11_drv_open,.release = dht11_drv_close,
};/* 1. 从platform_device获得GPIO* 2. gpio=>irq* 3. request_irq*/
static int dht11_probe(struct platform_device *pdev)
{printk("dht11 driver and device was matched!\r\n");/* 1. 获得硬件信息 */dht11dev.pin = gpiod_get(&pdev->dev, NULL, 0);if (IS_ERR(dht11dev.pin)){printk("%s line %d get pin parameter error! \n", __FUNCTION__, __LINE__);}/* 2. device_create */device_create( dht11dev.class, NULL, MKDEV( dht11dev.major, 0 ), NULL, DEVICE_NAME); // device name return 0;
}static int dht11_remove(struct platform_device *pdev)
{printk("%s line %d\n", __FUNCTION__, __LINE__);device_destroy( dht11dev.class, MKDEV( dht11dev.major, 0));gpiod_put(dht11dev.pin);return 0;
}static const struct of_device_id atk_dl6y2c_dht11[] = {{ .compatible = "atk-dl6y2c,dht11" },{ },
};/* 1. 定义platform_driver */
static struct platform_driver dht11_driver = {.probe = dht11_probe,.remove = dht11_remove,.driver = {.name = "atk_dht11",.of_match_table = atk_dl6y2c_dht11,},
};/* 2. 在入口函数注册platform_driver
*/
static int __init dht11_init(void)
{int err;printk("%s line %d\n",__FUNCTION__, __LINE__);/* register file_operations */dht11dev.major = register_chrdev( 0, DEVICE_NAME, /* device name */&dht11_fops); /* create the device class */dht11dev.class = class_create(THIS_MODULE, "dht11_class");if (IS_ERR(dht11dev.class)) {printk("%s line %d\n", __FUNCTION__, __LINE__);unregister_chrdev( dht11dev.major, DEVICE_NAME);return PTR_ERR( dht11dev.class );}err = platform_driver_register(&dht11_driver); return err;
}/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数* 卸载platform_driver*/
static void __exit dht11_exit(void)
{printk("%s line %d\n", __FUNCTION__, __LINE__);platform_driver_unregister(&dht11_driver);class_destroy(dht11dev.class);unregister_chrdev(dht11dev.major, DEVICE_NAME);
}/*4. 驱动入口和出口函数
*/
module_init(dht11_init);
module_exit(dht11_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("tangmingfei2013@126.com");
2.3.2 编写Makefile
在驱动程序同级目录中创建Makefile,然后编写如下代码
PWD := $(shell pwd)KERNEL_DIR=/home/mftang/linux_workspace/study_atk_dl6y2c/kernel/atk-dl6u2c
ARCH=arm
CROSS_COMPILE=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-export ARCH CROSS_COMPILEobj-m:= drv_14_dht11.oall:$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modulesclean:rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.order *.symvers
3 测试程序
3.1 编写测试程序
编写一个测试程序,目的是验证驱动程序是否能正常工作
/***************************************************************
Copyright 2024-2029. All rights reserved.
文件名 : test_14_dht11.c
作者 : tangmingfei2013@126.com
版本 : V1.0
描述 : 测试dth11驱动程序
其他 : 无
日志 : 初版V1.0 2024/02/15***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>#define DEV_FILE "/dev/treedht11"int main(void)
{int fd;int count_run = 0;unsigned char data[4];fd = open(DEV_FILE, 0);if (fd == -1){printf("can not open file: %s \n", DEV_FILE);return -1;}while( count_run < 10000){count_run++;if (read(fd, data, 4) == 4) {printf("get humidity : %d.%d\n", data[0], data[1]);printf("get temprature: %d.%d\n", data[2], data[3]);} else {perror("read dht11 device fail!\n");}sleep(1);}close(fd);return 0;
}
3.2 编写Makefile
CFLAGS= -Wall -O2
CC=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc
STRIP=/home/ctools/gcc-linaro-4.9.4-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-striptest_14_dht11: test_14_dht11.o$(CC) $(CFLAGS) -o test_14_dht11 test_14_dht11.o$(STRIP) -s test_14_dht11clean:rm -f test_14_dht11 test_14_dht11.o
4 编译和运行
4.1 编译和安装驱动程序
1) 编译驱动程序,并将其copy到NFS的共享目录中,方便在板卡中安装该程序

2) 在板卡中安装该驱动程序 , 使用命令
insmod dev_14_dth11.ko

安装成功后,使用命令查看驱动
ls /dev -l

4.2 编译和运行测试程序
1) 编译测试程序,并将其copy到NFS的共享目录中,方便在板卡中运行该程序

2)在板卡中运行测试程序

5 波形分析
在板卡上运行测试程序,然后使用逻辑分析仪捕捉DHT11-IO上的波形,分析其信号特征,以更好的理解驱动程序。

5.1 起始信号波形
datasheet 上提供的波形

逻辑分析仪上捕捉的波形:

查看电平持续时间:

5.2 信息bit = 0波形
datasheet 上提供的波形

逻辑分析仪上捕捉的波形:

5.3 信息bit = 1波形
datasheet 上提供的波形

逻辑分析仪上捕捉的波形:

相关文章:
Linux platform tree下的单总线驱动程序设计(DHT11)
目录 概述 1 认识DHT11 1.1 DHT11特性 1.2 DHT11数据格式 1.3 DHT11与MCU通信 1.4 DHT11信号解析 1.4.1 起始信号 1.4.2 解析信号0 1.4.3 解析信号1 2 驱动开发 2.1 硬件接口 2.2 更新设备树 2.2.1 添加驱动节点 2.2.2 编译.dts 2.2.3 更新板卡中的.dtb 2.3 驱…...
自研爬虫框架的经验总结(理论及方法)
背景: 由于业务需要,承接一部分的数据采集工作。目前市场内的一些通用框架不太适合。故而进行了自研。 对比自研和目前成熟的框架,自研更灵活适配,可以自己组装核心方法;后者对于新场景的适配需要对框架本身有较高的理…...
配置基于 AWS CRT 的 HTTP 客户端
基于 AWS CRT 的 HTTP 客户端包括同步 AwsCrtHttpClient 和异步 AwsCrtAsyncHttpClient。基于 AWS CRT 的 HTTP 客户端具有以下 HTTP 客户端优势: 更快的 SDK 启动时间 更小的内存占用空间 降低的延迟时间 连接运行状况管理 DNS 负载均衡 SDK 中基于 AWS CRT …...
挑战杯 基于LSTM的天气预测 - 时间序列预测
0 前言 🔥 优质竞赛项目系列,今天要分享的是 机器学习大数据分析项目 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/po…...
我为什么不喜欢关电脑?
程序员为什么不喜欢关电脑? 你是否注意到,程序员们似乎从不关电脑?别以为他们是电脑上瘾,实则是有他们自己的原因!让我们一起揭秘背后的原因,看看程序员们真正的“英雄”本色! 一、上大学时。 …...
Unity【角色/摄像机移动控制】【1.角色移动】
本文主要总结实现角色移动的解决方案。 1. 创建脚本:PlayerController 2. 创建游戏角色Player,在Player下挂载PlayerController脚本 3. 把Camera挂载到Player的子物体中,调整视角,以实现相机跟随效果 3. PlayerController脚本代码…...
Oracle12cR2之Job定时作业调度器详解
Oracle12cR2之Job定时作业调度器详解 文章目录 Oracle12cR2之Job定时作业调度器详解1.Oracle Job1. 关于Job2. 使用方法 2. Job详细说明1. 查看Job的相关视图2.SYS.DBA_JOBS视图字段详细说明 3. 创建及查看Job1. 创建Job2. 查看运行中的Job 1.Oracle Job 1. 关于Job 在 Oracle…...
python自学...
一、稍微高级一点的。。。 1. 闭包(跟js差不多) 2. 装饰器 就是spring的aop 3. 多线程...
Message Pack 协议详解及应用
文章目录 一、Message Pack是什么二、Message Pack的语法规则三、Message Pack相关链接四、Message Pack应用场景五、MessagePack 兼容性与特点 一、Message Pack是什么 Message Pack是一种高效的二进制序列化格式,用于在不同的应用程序之间进行数据交换。它类似于J…...
智慧社区管理系统:构建未来的生活模式
在这个信息化、智能化的时代,我们期待的不再是简单的居住空间,而是一个集安全、便捷、舒适、环保于一体的智能化社区。为此,我们推出了全新的智慧社区管理系统,旨在将先进的科技力量引入社区管理,为居民提供更优质的生…...
Rocky 8.9 Kubespray v2.24.0 在线部署 kubernetes v1.28.6 集群
文章目录 1. 简介2. 预备条件3. 基础配置3.1 配置hostname3.2 配置互信 4. 配置部署环境4.1 在线安装docker4.2 启动容器 kubespray4.3 编写 inventory.ini4.4 关闭防火墙、swap、selinux4.5 配置内核模块 5. 部署6. 集群检查 1. 简介 kubespray 是一个用于部署和管理 Kuber…...
新版AI系统ChatGPT源码支持GPT-4/支持AI绘画去授权
源码获取方式 搜一搜:万能工具箱合集 点击资源库直接进去获取源码即可 如果没看到就是待更新,会陆续更新上 新版AI系统ChatGPT网站源码支持GPT-4/支持AI绘画/Prompt应用/MJ绘画源码/PCH5端/免授权,支持关联上下文,意间绘画模型…...
学习鸿蒙基础(5)
一、honmonyos的page路由界面的路径 新建了一个page,然后删除了。运行模拟器的时候报错了。提示找不到这个界面。原来是在路由界面没有删除这个page。新手刚接触找了半天才找到这个路由。在resources/base/profile/main_pages.json 这个和微信小程序好类似呀。 吐槽…...
Tuxera NTFS2024最新中文版支持M1/M2/M3苹果全系机型
Tuxera NTFS的传输速度会受到多种因素的影响,包括硬件配置、文件大小、存储设备的性能等。因此,无法给出具体的传输速度数值。 不过,根据一些用户的使用经验和测试数据,Tuxera NTFS的传输速度通常都非常快,能够满足大…...
【Python】OpenCV-图片添加水印处理
图片添加水印处理 1. 引言 图像处理中的水印添加是一种常见的操作,用于在图片上叠加一些信息或标识。本文将介绍如何使用OpenCV库在图片上添加水印,并通过详细的代码注释来解释每一步的操作。 2. 代码示例 以下是一个使用OpenCV库的简单代码示例&…...
Milvus数据库介绍
参考:https://www.xjx100.cn/news/1726910.html?actiononClick Milvus 基于FAISS、Annoy、HNSW 等向量搜索库构建,核心是解决稠密向量相似度检索的问题。在向量检索库的基础上,Milvus 支持数据分区分片、数据持久化、增量数据摄取、标量向量…...
notepad++的下载与使用
1.进入官网下载 https://notepad-plus-plus.org/ 点击下载即可 2.选择中文简体 3.建议安装在D盘 其余步骤按照指示就行 4.安装后这几个是必选的 设置完成后就可以写中文了 以此为例 结果为...
论UI的糟糕设计:以百度网盘为例
上面这一排鼠标一经过就会弹出来(不是点才弹出来),然后挡住你的各种操作, 弹出来时你就必须等它消失,卡一下才能操作。 在用户顺畅地操作内容时,经常就卡一下、卡一下、卡一下…… 1、比如鼠标从下到上&am…...
【Spring】三级缓存
目录标题 触发所有未加载的实例a - 开始getBean( doGetBean) - 获取单例beangetSingleton() - 获取单例beancreateBean(doCreateBean) - 创建beancreateBeanInstance - 创建并返回beanaddSingletonFactory -放三级缓存populateBea…...
CVE-2016-3088(ActiveMQ任意文件写入漏洞)
漏洞描述 1、漏洞编号:CVE-2016-3088 2、影响版本:Apache ActiveMQ 5.x~5.13.0 在 Apache ActiveMQ 5.12.x~5.13.x 版本中,默认关闭了 fileserver 这个应用(不过,可以在conf/jetty.xml 中开启);…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
