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

20.2 设备树中的 platform 驱动编写

一、设备树下的 platform 驱动

  platform 驱动框架分为总线、设备和驱动,总线不需要我们去管理,这个是 Linux 内核提供。在有了设备树的前提下,我们只需要实现 platform_driver 即可。

1.  修改 pinctrl-stm32.c 文件 

  先复习一下 pinctrl 子系统和 gpio子系统,pinctrl 子系统是在设备树中去配置 pin 的信息和电气属性(复用、上/下拉,速度等),gpio 子系统是初始化 gpio,比如设置 gpio 的输入输出,读取 gpio 的值等。

  这ST 针对 STM32MP1 提供的 Linux 系统中,其 pinctrl 的前提是在 platform 平台下引用,一切以使用的芯片为准。在使用 pinctrl 的时候需要先修改 pinctrl-stm32.c 文件,否则当某个引脚作为 GPIO 的时候是无法申请到的。

  首先进入 /linux/atk-mpl/linux/my_linux/linux-5.4.31/drivers/pinctrl/stm32 文件夹下,打开 pinctrl-stm32.c,修改文件:

/* 865行开始 */
static const struct pinmux_ops stm32_pmx_ops = {.get_functions_count	= stm32_pmx_get_funcs_cnt,.get_function_name	= stm32_pmx_get_func_name,.get_function_groups	= stm32_pmx_get_func_groups,.set_mux		= stm32_pmx_set_mux,.gpio_set_direction	= stm32_pmx_gpio_set_direction,.strict			= false,    // 这里原本是 true// 这里设置false就是不采用严格模式
};

  修改完成重新编译 Linux 内核:

make uImage LOADADDR=0XC2000040 -j16
/* uImage代表了要编译出来的内核镜像的格式LOADADDR=0XC2000040是指定内核加载到的起始地址*/ 

  把 uImage 复制:

cd arch/arm/boot/
sudo cp uImage /home/alientek/linux/tftpboot/ -f

  测试一下看是否成功打开 uImage:

2.  创建设备的 pinctrl 节点

  进入 /linux/atk-mpl/linux/my_linux/linux-5.4.31/arch/arm/boot/dts 文件夹下,打开 stm32mp15-pinctrl.dtsi 文件,之后 STM32MP1 的所有引脚都是在 pinctrl 文件里配置完成。pinctrl 节点加入以下内容:

3.  在设备树中创建设备节点

  这里面重点是配置 compatible 属性值,因为 platform 总线需要通过设备节点中的 compatible 属性值来匹配驱动。打开 /linux/atk-mpl/linux/my_linux/linux-5.4.31/arch/arm/boot/dts 文件夹下的 stm32mp157d-atk.dts:

4.  编写 platform 驱动的时候注意兼容属性

  使用设备树的时候,platform 驱动会通过 of_match_table 来保存兼容性值,表明此驱动兼容哪些设备。下面是 platform_driver 例程:

static const struct of_device_id led_of_match[] = {{ .compatible = "alientek,led" }, /* 兼容属性 */    // 这里面的每个元素都是兼容属性,表示兼容的设备{ /* Sentinel */ }    // 注意最后一个元素为空!!!
};MODULE_DEVICE_TABLE(of, led_of_match);    // 声明led_of_match设备匹配表    // 内核模块可以将 led_of_match 数组注册到内核中static struct platform_driver led_platform_driver = {.driver = {.name = "stm32mp1-led",.of_match_table = led_of_match,},.probe = led_probe,.remove = led_remove,
};

二、检查引脚复用配置 

1. 检查引脚 pinctrl 配置 

   在嵌入式 Linux 下,严格按照一个引脚对应一个功能设计硬件。比如 PIO 现在用作 GPIO 来驱动 LED,那么就不能把 PI0 作为其他的功能。开发版上是将 PI0 连接到了 LED0,但是 ST 官方是这样:

  这里 ST 官方是把 PI0 设置为 LCD_G5,模式为端口复用,现在只能一个 IO 复用为一个功能,所以这里需要屏蔽掉。

  继续往下看:

  这里 ST 官方是把 PI0 设置为 LCD_G5,模式为模拟输入模式,所以也要屏蔽掉。

2. 检查 gpio 占用

  上一节检查 PI0 引脚有没有被复用多个设备。当我们将一个引脚作为 GPIO 的时候,一定要检查当前设备树里面是否有其他设备也用到了这个 GPIO,保证设备树中只有一个设备树在使用这个 GPIO。

三、实验程序编写

  首先就要修改设备树文件,设备树里面加上我们需要的设备信息。需要创建 LED0 引脚的 pinctrl 节点,另外需要创建的 LED0 的设备节点。虽然之前完成了,但是没有编译:

cd linux/atk-mpl/linux/my_linux/linux-5.4.31
make dtbs -j32# 将编译好的stm32mp157d-atk.dtb文件复制到
sudo cp stm32mp157d-atk.dtb /home/alientek/linux/tftpboot/ -f

1. platform 驱动程序编写

  在 cd linux/atk-mpl/Drivers/ 文件夹下,新建 18_dtsplatform,在里面新建 Vscode 工作区,并新建 leddriver.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_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 LEDDEV_CNT		1				/* 设备号长度 	*/
#define LEDDEV_NAME		"dtsplatled"	/* 设备名字 	*/
#define LEDOFF 			0
#define LEDON 			1/* leddev设备结构体 */
struct leddev_dev{dev_t devid;				/* 设备号	*/struct cdev cdev;			/* cdev		*/struct class *class;		/* 类 		*/struct device *device;		/* 设备		*/	struct device_node *node;	/* LED设备节点 */int gpio_led;				/* LED灯GPIO标号 */
};struct leddev_dev leddev; 		/* led设备 *//** @description		: LED打开/关闭* @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return 			: 无*/
void led_switch(u8 sta)
{if (sta == LEDON)gpio_set_value(leddev.gpio_led, 0);else if (sta == LEDOFF)gpio_set_value(leddev.gpio_led, 1);
}static int led_gpio_init(struct device_node *nd)
{int ret;/* 从设备树中获取GPIO */leddev.gpio_led = of_get_named_gpio(nd, "led-gpio", 0);if(!gpio_is_valid(leddev.gpio_led)) {printk(KERN_ERR "leddev: Failed to get led-gpio\n");return -EINVAL;}/* 申请使用GPIO */ret = gpio_request(leddev.gpio_led, "LED0");if (ret) {printk(KERN_ERR "led: Failed to request led-gpio\n");return ret;}/* 将GPIO设置为输出模式并设置GPIO初始电平状态 */gpio_direction_output(leddev.gpio_led,1);return 0;
}/** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{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;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];if (ledstat == LEDON) {led_switch(LEDON);} else if (ledstat == LEDOFF) {led_switch(LEDOFF);}return 0;
}/* 设备操作函数 */
static struct file_operations led_fops = {.owner = THIS_MODULE,.open = led_open,.write = led_write,
};/** @description		: flatform驱动的probe函数,当驱动与* 					  设备匹配以后此函数就会执行* @param - dev 	: platform设备* @return 			: 0,成功;其他负值,失败*/
static int led_probe(struct platform_device *pdev)	// 如果设备和驱动匹配成功,先去初始化pinctrl配置的IO,再去执行probe函数
{	int ret;printk("led driver and device was matched!\r\n");/* 初始化 LED */ret = led_gpio_init(pdev->dev.of_node);	// of_node连接着设备树的设备节点if(ret < 0)return ret;/* 1、申请设备号 */ret = alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);if(ret < 0) {pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", LEDDEV_NAME, ret);goto free_gpio;}/* 2、初始化cdev  */leddev.cdev.owner = THIS_MODULE;cdev_init(&leddev.cdev, &led_fops);/* 3、添加一个cdev */ret = cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);if(ret < 0)goto del_unregister;/* 4、创建类      */leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);if (IS_ERR(leddev.class)) {goto del_cdev;}/* 5、创建设备 */leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);if (IS_ERR(leddev.device)) {goto destroy_class;}return 0;
destroy_class:class_destroy(leddev.class);
del_cdev:cdev_del(&leddev.cdev);
del_unregister:unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
free_gpio:gpio_free(leddev.gpio_led);return -EIO;
}/** @description		: platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev 	: platform设备* @return 			: 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{gpio_set_value(leddev.gpio_led, 1); 	/* 卸载驱动的时候关闭LED */gpio_free(leddev.gpio_led);	/* 注销GPIO */cdev_del(&leddev.cdev);				/*  删除cdev */unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */device_destroy(leddev.class, leddev.devid);	/* 注销设备 */class_destroy(leddev.class); /* 注销类 */return 0;
}/* 匹配列表(设备树才有的)*/	// 
// 这里的省略了获取compatible属性值,因为这里的匹配列表直接和设备匹配
static const struct of_device_id led_of_match[] = {{ .compatible = "alientek,led" },{ /* Sentinel */ }
};MODULE_DEVICE_TABLE(of, led_of_match);		// 声明 led_of_match/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver		= {/* 设置这个 platform 驱动的名字为“stm32mp1-led”后,当驱动加载成功以后就会在/sys/bus/platform/drivers/目录下存在一个名为“stm32mp1-led”的文件 */.name	= "stm32mp1-led",			/* 驱动名字,用于和设备匹配 */.of_match_table	= led_of_match, /* 设备树匹配表 		 */},.probe		= led_probe,.remove		= led_remove,
};/** @description	: 驱动模块加载函数* @param 		: 无* @return 		: 无*/
static int __init leddriver_init(void)
{return platform_driver_register(&led_driver);	// 注册platform平台
}/** @description	: 驱动模块卸载函数* @param 		: 无* @return 		: 无*/
static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);	// 卸载platform平台
}module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

2. 测试 APP 编写

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define LEDOFF 	0
#define LEDON 	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];/* 打开led驱动 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED 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
CURRENT_PATH := $(shell pwd)obj-m := leddriver.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

  编译 leddriver.c 和 ledApp.c 文件:

make -j32
arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp

  将编译好的 ledApp 和 leddriver.ko 文件复制:

sudo cp ledApp leddriver.ko /home/alientek/linux/nfs/rootfs/lib/modules/5.4.31/ -f

  开启开发板,输入一下命令:

cd lib/modules/5.4.31/
depmod
modprobe leddriver.ko

  之后就会看到这样的消息:

  在设备节点中有 gpioled,在驱动文件中有 stm32mp1-led 驱动文件,设备和驱动匹配成功后才会出现上图中的消息。

  测试 App:

./ledApp /dev/dtsplatled 1    // 打开LED
./ledApp /dev/dtsplatled 0    // 关闭LED/* 有一点忘记 dtsplatled 怎么来的,复习了一下是在最开始创建设备名字,在创建类,创建设备用到了,所以名字为 dtsplatled*/

   卸载驱动:rmmod leddriver.ko

总结

  这一节是在设备树下使用platform,先是修改了 pinctrl-stm32.c 文件,这个是根据具体的芯片来修改这个的,这里修改了严格模式。之后创建设备的 pinctrl 节点,这里是配置io口和电气设置的。然后在设备树中创建设备节点,这里面需要添加 pinctrl-names 和 pinctrl-0。最后要检查 引脚 pinctrl 的配置和 gpio 占用情况,因为 Linux 下必须严格按照一个引脚对应一个功能设计硬件。

  程序中:

  ① 把之前的驱动入口函数(驱动模块加载)转移到了 probe 函数中,将驱动出口函数(驱动模块卸载)转移到了 remove 函数;

  ② 因为有设备树的前提下,新增匹配列表,这里面最重要的就是匹配设备的 compatible,注意最后一行需要留空,类似下图:

   ③ 上一节新增的 platform 驱动结构体,.driver里面成员变量 .name = XXX 和 .of_match_table,.name 在驱动和设备匹配成功后会在 /sys/bus/platform/drivers/目录下新建一个名为"XXX" 的文件夹,.of_match_table 是让之前读取 status 属性等获取设备树代码没有的关键。

相关文章:

20.2 设备树中的 platform 驱动编写

一、设备树下的 platform 驱动 platform 驱动框架分为总线、设备和驱动&#xff0c;总线不需要我们去管理&#xff0c;这个是 Linux 内核提供。在有了设备树的前提下&#xff0c;我们只需要实现 platform_driver 即可。 1. 修改 pinctrl-stm32.c 文件 先复习一下 pinctrl 子系…...

C++实现ransac

目录 一、ransac算法原理 1.1、算法概念 1.2、图解 二、c实现ransac 2.1、设置随机样本和离群点 2.2、随机抽取样本 2.3、内点计算 2.4、更新参数 2.2、完整代码 一、ransac算法原理 1.1、算法概念 随机抽样一致性 (RANSAC) 是一种迭代方法&#xff0c;用于根据一组包…...

DNS域名解析服务

1.概述 1.1.产生原因 IP 地址:是互联网上计算机唯一的逻辑地址&#xff0c;通过IP 地址实现不同计算机之间的相互通信&#xff0c;每台联网计算机都需要通过I 地址来互相联系和分别&#xff0c;但由于P 地址是由一串容易混淆的数字串构成&#xff0c;人们很难记忆所有计算机的…...

【milkv】2、mpu6050驱动添加及测试

前言 本章介绍mpu6050的驱动添加以及测试。 其中驱动没有采用sdk提供的驱动&#xff0c;一方面需要配置irq&#xff0c;另一方面可以学习下如何通过ko方式添加驱动。 一、参考文章 驱动及测试文件编译流程&#xff1a; https://community.milkv.io/t/risc-v-milk-v-lsm6ds…...

SpringCloud Alibaba(中):服务熔断降级-Sentinel

Sentinel Sentinel是阿里巴巴开源的分布式系统流量防卫防护组件&#xff0c;主要对分布式系统中的流量进行控制、熔断降级等保护操作。Sentinel的目标是成为互联网级别分布式系统的流量防卫防护组件&#xff0c;它与系统的各个部分集成&#xff0c;保护着系统的入口和出口。 …...

模型的训练专题

训练目标在数学上指定了模型应该如何从训练数据中学习和获取能力。训练基础模型的当前现状涉及特定于模型的目标。我们设想&#xff0c;未来基础模型的训练目标将反映两个变化&#xff1a;从系统证据和评估中得出的原则性选择&#xff0c;以及跨数据源和模式提供丰富、可扩展和…...

深入解析 Azure 机器学习平台:架构与组成部分

Azure机器学习平台是Microsoft Azure提供的一种云上机器学习服务&#xff0c;为开发者和数据科学家提供了一个全面且易于使用的环境来创建、训练、部署和管理机器学习模型。本文将对Azure机器学习平台的基本架构和组成部分进行深入解析&#xff0c;帮助读者全面了解该平台的工作…...

使用百度语音识别技术实现文字转语音的Java应用

探讨如何使用百度语音识别技术将文字转换为语音的Java应用。百度语音识别技术是一种强大的语音识别服务&#xff0c;可以将输入的文字转换为自然流畅的语音输出。我们将使用Java编程语言来实现这个应用&#xff0c;并提供相应的源代码。 首先&#xff0c;我们需要准备一些前提…...

【C#学习】文件操作

文章目录 常见操作拷贝文件检测文件夹是否存在并创建判断文件是否存在删除文件夹下的所有文件保留文件夹获取指定目录下的所有文件名删除 常见操作 拷贝文件 System.IO.File.Copy(sourcePath, targetPath); 检测文件夹是否存在并创建 //if directory not exit,then establis…...

Chrome版本对应Selenium版本

1.获得浏览器版本号和驱动 浏览器版本: 119.0.6045.124 浏览器驱动版本: 119.0.6043.1 / 120.0.6051.0 访问 https://vikyd.github.io/download-chromium-history-version/ 2. 安装selenium pip install selenium4.1.1 -i http://pypi.mirrors.ustc.edu.cn/simple/ --trusted…...

Day29力扣打卡

打卡记录 美丽塔 II&#xff08;前后缀分解 单调栈&#xff09; 链接 大佬的题解 class Solution:def maximumSumOfHeights(self, a: List[int]) -> int:n len(a)suf [0] * (n 1)st [n] # 哨兵s 0for i in range(n - 1, -1, -1):x a[i]while len(st) > 1 and …...

java源码用到的设计模式

Java 中有许多常用的设计模式&#xff0c;它们是为了解决特定问题而被反复使用和验证的经验总结。以下是一些常见的 Java 设计模式&#xff1a; 创建型模式 工厂模式 (Factory Pattern): 提供一个创建对象的接口&#xff0c;但是由子类决定实例化哪个类。例如&#xff1a;java…...

high perfermance computer usage

简单记一下hpc的使用&#xff1a; hpc就是一些科研机构或者大学建立的服务器中心。我这大学的每一位学生&#xff0c;可以轻松使用hpc批量跑数据&#xff0c;也可以新建自己的server跑一些local data&#xff0c;后者每个学生账号最大是32核512G的运行内存&#xff0c;体验非常…...

51单片机+DS1302设计一个电子钟(LCD1602显示时间)

一、前言 电子钟是一种能够准确显示时间的设备&#xff0c;广泛应用于家庭、办公场所和公共场所&#xff0c;为人们提供了方便和准确的时间信息。本项目设计一个基于51单片机的电子钟&#xff0c;使用DS1302作为RTC时钟芯片&#xff0c;LCD1602作为显示屏&#xff0c;并通过串…...

vue项目中在scss代码中使用data中的变量

尽管在日常开发中&#xff0c;这类情况实际上很少出现。 VUE2: 在HTML中使用时&#xff0c;请确保将cssVars绑定在需要使用CSS变量的元素或该元素的上层元素上。 <template><div :style"cssVars"><div class"test"/></div><…...

uni-app报错“本应用使用HBuilderX x.x.x 或对应的cli版本编译,而手机端SDK版本是x.x.x不匹配的版本可能造成应用异常”

uniapp开发的一个跨平台软件&#xff0c;在安卓模拟器上启动的时候报警告&#xff1a; 官方给的解释&#xff1a;uni-app运行环境版本和编译器版本不一致的问题 - DCloud问答 解决办法有两个 方法一&#xff1a;添加忽略警告的配置 项目根目录下找到 manifest.json&#xf…...

[sd_scripts]之train

https://github.com/kohya-ss/sd-scripts/blob/main/docs/train_README-zh.mdhttps://github.com/kohya-ss/sd-scripts/blob/main/docs/train_README-zh.md 支持模型fine-tune&#xff0c;dreambooth&#xff0c;lora&#xff0c;textual inversion。 1.数据准备 在任意多个…...

samba 共享目录write permission deny问题修复 可读取内容但不可修改 删除 新增文件

关于 update/delete/write permission deny问题修复 0.首先在服务器端执行testparm -s &#xff0c;测试 Samba 配置并显示结果。需确保服务器端参数 read only No &#xff0c;共享目录有写入权限 一、若配置了允许匿名访问&#xff0c;使用匿名访问来操作smb需要做如下处理…...

UDP主要丢包原因及具体问题分析

一、主要丢包原因 1、接收端处理时间过长导致丢包&#xff1a;调用recv方法接收端收到数据后&#xff0c;处理数据花了一些时间&#xff0c;处理完后再次调用recv方法&#xff0c;在这二次调用间隔里,发过来的包可能丢失。对于这种情况可以修改接收端&#xff0c;将包接收后存入…...

647. 回文子串 516.最长回文子序列

647. 回文子串 题目&#xff1a; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。 子字符串 是字符串中的由连续字符组成的一个序列。 具有不同开始位置或结束位置的子串&#xff0c;即使是由相…...

点云从入门到精通技术详解100篇-双传感器模式的非结构化环境检测与识别

目录 前言 国内外研究现状 可通行区域检测的研究 障碍物检测的研究...

Nginx-反向代理

反向代理 1 语法 server {listen 82; server_name www.liyong.f.com;location ~* .*(css|js|html|images). {proxy_pass http://11.22.19.81:8088; } 上面的示例的意思是&#xff1a; 当访问&#xff1a;http://www.liyong.f.com:82/static/css/OneMap.b728e2e4.css 转发到 …...

Java封装一个根据指定的字段来获取子集的工具类

工具类 ZhLambdaUtils SuppressWarnings("all") public class ZhLambdaUtils {/*** METHOD_NAME*/private static final String METHOD_NAME "writeReplace";/*** 获取到lambda参数的方法名称** param <T> parameter* param function functi…...

【HUST】网安纳米|2023年研究生纳米技术考试参考

目录 1 纳米材料是什么 2 纳米材料的结构特性 3 纳米结构的其他特性 4 纳米结构的检测技术 5 纳米材料的应用 打印建议&#xff1a;PPT彩印&#xff08;这样重点比较突出&#xff09;&#xff0c;每面12张PPT&#xff0c;简单做一下关键词目录&#xff0c;亲测可以看清。如…...

【移远QuecPython】EC800M物联网开发板的MQTT协议腾讯云数据上报

【移远QuecPython】EC800M物联网开发板的MQTT协议腾讯云数据上报 文章目录 导入库初始化设置MQTT注册回调订阅发布功能开启服务发送消息函数打包调用测试效果附录&#xff1a;列表的赋值类型和py打包列表赋值BUG复现代码改进优化总结 py打包 导入库 from TenCentYun import TX…...

关灯游戏及扩展

7.8 图形界面应用案例——关灯游戏 题目&#xff1a; [案例]游戏初步——关灯游戏。 关灯游戏是很有意思的益智游戏&#xff0c;玩家通过单击关掉(或打开)一盏灯。如果关(掉&#xff08;或打开)一个电灯&#xff0c;其周围(上下左右)的电灯也会触及开关&#xff0c;成…...

深度解析:用Python爬虫逆向破解dappradar的URL加密参数(最详细逆向实战教程,小白进阶高手之路)

特别声明:本篇文章仅供学习与研究使用,不得用做任何非法用途,请大家遵守相关法律法规 目录 一、逆向目标二、准备工作三、逆向分析 - 太详细了!3.1 逆向前的一些想法3.1.1 加密字符串属性猜测3.1.2 是否可以手动复制加密API?3.2 XHR断点调试3.3 加密前各参数属性的变化情况…...

论文笔记:AttnMove: History Enhanced Trajectory Recovery via AttentionalNetwork

AAAI 2021 1 intro 1.1 背景 将用户稀疏的轨迹数据恢复至细粒度的轨迹数据是十分重要的恢复稀疏轨迹数据至细粒度轨迹数据是非常困难的 已观察到的用户位置数据十分稀疏&#xff0c;使得未观察到的用户位置存在较多的不确定性真实数据中存在大量噪声&#xff0c;如何有效的挖…...

Django之视图层

目录 一、三板斧的使用 二、JsonReponse序列化类的使用 三、 form表单上传文件 数据准备 数据处理 (1)post请求数据 (2)文件数据获取 四、 FBV与CBV 五、CBV的源码分析 as_view 方法 一、三板斧的使用 HttpResponse 返回字符串类型render 渲染html页面&#xff0c;并…...

DAY54 392.判断子序列 + 115.不同的子序列

392.判断子序列 题目要求&#xff1a;给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是…...