Linux驱动之GPIO函数、IO内存映射、混杂设备驱动

news2025/1/13 15:40:46

之前学习完了字符设备驱动的大体框架,现在我们就使用这个基本的框架来对硬件进行操作,例如通过指令控制led的状态,编写LED驱动。LED驱动有多种实现方式。

目录

GPIO函数

IO内存映射

混杂设备驱动


GPIO函数

首先加入需要的头文件。

#include <asm/gpio.h>

#include <mach/soc.h>

#include <mach/platform.h> 

GPIO属于资源,在内核中属于资源使用前就需要先申请,使用完就需要释放。 

使用gpio_request函数向内核申请需要的GPIO引脚。

int gpio_request(unsigned gpio, const char *label);

参数:

    gpio :GPIO引脚号

    本人使用的s5p6818,每组GPIO都有宏,然后加上组内编号。例如GPIOE13表示为              PAD_GPIO_E+13

    label:自定义标签,也可以说是名字吧,可以是NULL

返回0成功,返回小于0失败   

 使用gpio_free函数向内核释放使用的GPIO引脚。

void gpio_free(unsigned gpio);

//传入要释放的GPIO的端口号

根据s5p6818对GPIO接口操作的说明,对GPIO的操作分为:

选择复用功能    //每个引脚最多有4个复用功能,看原理图或手册选择

选择输入/输出模式

设置输出值/获取输入的值

有些引脚有不同的功能,使用前需要选择作用为GPIO,不同的板子可能对应的操作有些许差别。

我们把上面三个操作的函数介绍下,就写个小测试。

选择复用功能使用nxp_soc_gpio_set_io_func函数

void nxp_soc_gpio_set_io_func(unsigned int io, unsigned int func);

参数:

    io :GPIO的端口号

    func :使用宏来选择复用功能

 以上图为例,需要使用GPIOC17引脚作为GPIO,根据原理图GPIO功能在第二个,所以使用宏NX_GPIO_PADFUNC_1。

 选择输出输入模式使用gpio_direction_output,gpio_direction_input函数。

int gpio_direction_output(unsigned gpio, int value);

参数:

    gpio:GPIO端口号

    value:默认输出值

   

int gpio_direction_input(unsigned gpio):

//传入要设置的gpio端口号  

 设置输出值/获取输入的值使用gpio_set_value,gpio_get_value函数。

void gpio_set_value(unsigned gpio, int value);

参数:

    gpio:GPIO端口号

    value:输出的电平值

   

int gpio_get_value(unsigned gpio);

参数:

    gpio:GPIO端口号

//返回GPIO引脚的电平值

介绍完了,写一个LED的小测试程序吧。

led.h

#ifndef __LED_H
#define __LED_H

//定义命令的最大序数
#define IOC_MAX_NR 4 
//定义设备类型(幻数)
#define IOC_MAGIC 'L'

#define LED0 _IO(IOC_MAGIC,0)
#define LED1 _IO(IOC_MAGIC,1)
#define LED2 _IO(IOC_MAGIC,2)
#define LED3 _IO(IOC_MAGIC,3)


#endif

led_drv.c 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/ioctl.h>
#include <asm/gpio.h>
#include <mach/soc.h>
#include <mach/platform.h>
#include "led.h"

#define LED_MINOR 0

struct led_dest{
	int gpio;//gpio端口号
	char *name;//名称
};

//定义led的硬件信息
struct led_dest led_info[] = {
	[0] = {
		.gpio = PAD_GPIO_E+13,
		.name = "LED0",
	},
	[1] = {
		.gpio = PAD_GPIO_C+17,
		.name = "LED1",
	},
	[2] = {
		.gpio = PAD_GPIO_C+8,
		.name = "LED2",
	},
	[3] = {
		.gpio = PAD_GPIO_C+7,
		.name = "LED3",
	}
};

//设备号
dev_t dev;
//声明cdev
struct cdev led_cdev;
//设备类指针
struct class *led_class;
//设备指针
struct device *led_device;

/*
inode是文件的节点结构,用来存储文件静态信息
文件创建时,内核中就会有一个inode结构
file结构记录的是文件打开的信息
文件被打开时内核就会创建一个file结构
*/
int led_open(struct inode *inode, struct file *filp)
{
	printk("enter led_open!\n");

	return 0;
}

long led_ioctl(struct file *filp, unsigned int cmd, unsigned long data)
{
	printk("enter led_ioctl!\n");

	if(_IOC_TYPE(cmd) != IOC_MAGIC)   //如果命令中的幻数不是‘x’
		return -EINVAL;               //返回错误代码表示无效参数
	if(_IOC_NR(cmd) >= IOC_MAX_NR)    //如果命令中的序列数大于4
		return -EINVAL;               //返回错误代码表示无效参数
		
	gpio_set_value(led_info[_IOC_NR(cmd)].gpio, data);
	if(data)
		printk("LED%d:OFF!\n",_IOC_NR(cmd));
	else
		printk("LED%d:ON!\n",_IOC_NR(cmd));

	return 0;
}

int led_release(struct inode *inode, struct file *filp)
{
	printk("enter led_release!\n");

	return 0;
}

//声明操作函数集合
struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.unlocked_ioctl = led_ioctl,//ioctl接口
	.release = led_release,//对应用户close接口
};

//加载函数
int led_init(void)
{
	int ret,i;
	// 1.注册字符设备驱动
	ret = register_chrdev(0, "led_demo", &led_fops);
	if(ret<0){
		printk("register_chrdev failed!\n");
		goto failure_register_chrdev;
	}
	//构建设备号
	dev = MKDEV(ret,LED_MINOR);

	printk("register_chrdev success!\n");

	// 2.注册设备类
	/*成功会在/sys/class目录下出现led_class子目录*/
	led_class = class_create(THIS_MODULE, "led_class");
	if(IS_ERR(led_class)){
		printk("class_create failed!\n");
		ret = PTR_ERR(led_class);
		goto failure_class_create;
	}

	// 3.创建设备文件
	led_device = device_create(led_class, NULL, dev,NULL, "led");
	if(IS_ERR(led_device)){
		printk("device_create failed!\n");
		ret = PTR_ERR(led_device);
		goto failure_device_create;
	}

	// 4.申请gpio资源并初始化
    //ARRAY_SIZE(arr)求数组大小的宏,原型为sizeof(arr)/sizeof(arr[0])
	for(i=0;i<ARRAY_SIZE(led_info);i++){  
		//申请gpio
		ret = gpio_request(led_info[i].gpio, led_info[i].name);
		//设置复用功能
		if(i==0)
			nxp_soc_gpio_set_io_func(led_info[i].gpio,NX_GPIO_PADFUNC_0);
		else
			nxp_soc_gpio_set_io_func(led_info[i].gpio,NX_GPIO_PADFUNC_1);
		//设置输出模式,默认高电平---灭
		gpio_direction_output(led_info[i].gpio, 1);
	}

	return 0;

failure_device_create:
	class_destroy(led_class);// 3
failure_class_create:
	unregister_chrdev(MAJOR(dev), "led_demo");// 2
failure_register_chrdev:
	return ret;
}

//卸载函数
void led_exit(void)
{
	int i;

	//释放GPIO
	for(i=0;i<ARRAY_SIZE(led_info);i++){
		//LED熄灭
		gpio_set_value(led_info[i].gpio,1);
		gpio_free(led_info[i].gpio);
	}
	//销毁设备文件
	device_destroy(led_class, dev);
	//注销设备类
	class_destroy(led_class);
	//注销字符设备驱动
	unregister_chrdev(MAJOR(dev), "led_demo");
}

//声明为模块的入口和出口
module_init(led_init);
module_exit(led_exit);


MODULE_LICENSE("GPL");//GPL模块许可证
MODULE_AUTHOR("xin");//作者
MODULE_VERSION("1.0");//版本
MODULE_DESCRIPTION("led driver!");//描述信息

如果对 led_ioctl函数中_IOC_TYPE_IOC_NR两个宏不了解可以参考上一篇文章ioctl命令的统一格式,

led_test.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "led.h"

int main()
{
	char ch = 0;

	int fd = open("/dev/led",O_RDWR);
	if(fd==-1){
		perror("open");
		exit(-1);
	}

	printf("open successed!fd = %d\n",fd);

	while(1){
		ch = getchar();

		if(ch=='q')
			break;

		switch(ch){
			case '1': ioctl(fd,LED0 ,0);//1灯亮
			break;
			
			case '2': ioctl(fd,LED0,1);//1灯灭
			break;
			
			case '3': ioctl(fd,LED1,0);//2灯亮
			break;
			
			case '4': ioctl(fd,LED1,1);//2灯灭
			break;
			
			case '5': ioctl(fd,LED2,0);//3灯亮
			break;
			
			case '6': ioctl(fd,LED2,1);//3灯灭
			break;
			
			case '7': ioctl(fd,LED3,0);//4灯亮
			break;
			
			case '8': ioctl(fd,LED3,1);//4灯灭
			break;
			
			default:
				printf("error input!\n");
			break;
		}
		while(ch=getchar()!='\n' && ch!=EOF)
		sleep(1);
	}

	close(fd);
	return 0;
}

 这样我们就可以通过应用程序输入字符1到8来控制LED灯状态。 

IO内存映射

我们使用函数去控制外设,是通过读写设备上的寄存器来进行的,每个寄存器都有物理地址在芯片手册中可以查到,但不管是在用户空间还是在内核空间,一律不能去直接访问寄存器的物理地址。内核中能够直接访问的地址是内核虚拟地址,所以需要MMU(内存管理单元)将寄存器的物理地址映射到内核虚拟地址空间再进行访问,之后驱动程序访问内核虚拟地址就是在间接访问寄存器的物理地址。

先引入两个需要使用到的函数头文件。

#include <linux/io.h>

#include <mach/platform.h>

使用ioremap建立映射。

#define ioremap(cookie,size)           __ioremap(cookie,size,0)

__ioremap函数原型为(arm/mm/ioremap.c):

void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);

参数:

phys_addr:需要映射的起始IO地址

size:要映射的空间的大小

flags:要映射的IO空间和权限有关的标志

该函数返回映射后的内核虚拟地址(3G-4G),接着便可以通过读写该返回的内核虚拟地址去访问之这段I/O内存资源

使用iounmap函数解除映射。

 void iounmap(void * addr);

参数:

addr:虚拟起始地址

 访问io内存的读写函数

读I/O内存
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);

写I/O内存
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);

参数:

addr:虚拟地址

 上面使用GPIO函数来控制LED灯,现在使用io内存映射,用虚拟地址来控制一个LED(GPIOE13)灯。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/ioctl.h>
#include <linux/io.h>
#include <mach/platform.h>
#include "led.h"

#define LED_MINOR 0

//声明IO内存映射地址
void __iomem *gpioe_base = NULL;

//设备号
dev_t dev;
//声明cdev
struct cdev led_cdev;
//设备类指针
struct class *led_class;
//设备指针
struct device *led_device;

/*
inode是文件的节点结构,用来存储文件静态信息
文件创建时,内核中就会有一个inode结构
file结构记录的是文件打开的信息
文件被打开时内核就会创建一个file结构
*/
int led_open(struct inode *inode, struct file *filp)
{
	printk("enter led_open!\n");

	return 0;
}

long led_ioctl(struct file *filp, unsigned int cmd, unsigned long data)
{
	printk("enter led_ioctl!\n");

	if(_IOC_TYPE(cmd) != IOC_MAGIC)   //如果命令中的幻数不是‘x’
		return - EINVAL;              //就返回错误代码表示无效参数
	if(_IOC_NR(cmd) >= IOC_MAX_NR)    //如果命令中的序列数大于4
		return - EINVAL;              //就返回错误代码表示无效参数
	if(data)
	{
		iowrite32(ioread32(gpioe_base)|(0x1<<13),gpioe_base);
		printk("LED%d:OFF!\n",_IOC_NR(cmd));
	}
	else
	{
		iowrite32(ioread32(gpioe_base)&~(0x1<<13),gpioe_base);
		printk("LED%d:ON!\n",_IOC_NR(cmd));
	}

	return 0;
}

int led_release(struct inode *inode, struct file *filp)
{
	printk("enter led_release!\n");

	return 0;
}

//声明操作函数集合
struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.unlocked_ioctl = led_ioctl,//ioctl接口
	.release = led_release,//对应用户close接口
};

//加载函数
int led_init(void)
{
	int ret;
	// 1.注册字符设备驱动
	ret = register_chrdev(0, "led_demo", &led_fops);
	if(ret<0){
		printk("register_chrdev failed!\n");
		goto failure_register_chrdev;
	}
	//构建设备号
	dev = MKDEV(ret,LED_MINOR);

	printk("register_chrdev success!\n");

	// 2.注册设备类
	/*成功会在/sys/class目录下出现led_class子目录*/
	led_class = class_create(THIS_MODULE, "led_class");
	if(IS_ERR(led_class)){
		printk("class_create failed!\n");
		ret = PTR_ERR(led_class);
		goto failure_class_create;
	}

	// 3.创建设备文件
	led_device = device_create(led_class, NULL, dev,NULL, "led");
	if(IS_ERR(led_device)){
		printk("device_create failed!\n");
		ret = PTR_ERR(led_device);
		goto failure_device_create;
	}

	// 4.IO内存映射
	gpioe_base = ioremap(PHY_BASEADDR_GPIOE, SZ_64);
	if(IS_ERR_OR_NULL(gpioe_base)){//失败
		printk("ioremap failed!\n");
		ret = -ENOMEM;
		goto failure_ioremap;
	}
	//初始化
	//设置复用功能 alt0  26 27位清0   addr:base+0x20
	
	iowrite32(ioread32(gpioe_base+0x20)&~(0x3<<26),gpioe_base+0x20);
	
	//设置输出模式  outenb 13位 置1   addr:base+0x04
	iowrite32(ioread32(gpioe_base+0x04)|(0x1<<13),gpioe_base+0x04);
	
	//设置默认值为高电平 out 13位 置1   addr:base
	iowrite32(ioread32(gpioe_base)|(0x1<<13),gpioe_base);


	return 0;
	
failure_ioremap:
	device_destroy(led_class, dev);
failure_device_create:
	class_destroy(led_class);
failure_class_create:
	unregister_chrdev(MAJOR(dev), "led_demo");
failure_register_chrdev:
	return ret;
}

//卸载函数
void led_exit(void)
{
	//解除IO映射
	iounmap(gpioe_base);
	//销毁设备文件
	device_destroy(led_class, dev);
	//注销设备类
	class_destroy(led_class);
	//注销字符设备驱动
	unregister_chrdev(MAJOR(dev), "led_demo");
}

//声明为模块的入口和出口
module_init(led_init);
module_exit(led_exit);


MODULE_LICENSE("GPL");//GPL模块许可证
MODULE_AUTHOR("xin");//作者
MODULE_VERSION("2.0");//版本
MODULE_DESCRIPTION("led driver!");//描述信息

gpioe_base = ioremap(PHY_BASEADDR_GPIOE, SZ_64);

PHY_BASEADDR_GPIOE这是内核定义的GPIOE的物理地址,SZ_64为64个字节

iowrite32(ioread32(gpioe_base+0x20)&~(0x3<<26),gpioe_base+0x20);

通过手册得知设置复用功能的寄存器在基地址上偏移0x20个地址。

先使用ioread32(gpioe_base+0x20)读取整个寄存器32位的值,然后ioread32(gpioe_base+0x20)&~(0x3<<26)把26和27位位置零,设置为GPIO功能,最后再写回去。初始化另外两个也是一样的意思,只是操作的寄存器不一样。

通过GPIO操作函数和io内存映射比较发现,内核gpio操作函数是通过封装IO内部映射来实现的,使用比较方便,但是只能用于GPIO操作。IO内存映射可以用于任意寄存器操作的场合

混杂设备驱动

在Linux驱动中,会把一些无法归类的设备定义为混杂设备——misc,它们是拥有着共同的特性的简单字符设备,它们的特点是共享统一的主设备号10,但每个设备可以选择一个单独的次设备号。

使用混杂设备需要头文件。

#include <linux/miscdevice.h>

混杂设备驱动相比于传统的字符设备来说,初始化很简单。字符设备需要先初始化cdev结构体,混杂设备也需要初始化miscdevice 结构体。

struct miscdevice  {

int minor;

const char *name;//设备文件名

const struct file_operations *fops;//操作函数集合

struct list_head list;

struct device *parent;

struct device *this_device;

const char *nodename;

umode_t mode;

};

//minor是次设备号,想要系统自动生成可以配置为MISC_DYNAMIC_MINOR

初始化完了 miscdevice 结构体就向内核注册这个混杂设备。

int misc_register(struct miscdevice * misc);

注销这个混杂设备。

int misc_deregister(struct miscdevice *misc);

这样就完成了一个混杂设备的使用,其实混杂设备的注册函数只创建了设备文件,其他的步骤都是在内核初始化阶段完成,在misc_init函数中完成。

那就看看使用混杂设备驱动怎么完成一个LED(GPIOE13)灯的控制吧。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/ioctl.h>
#include <linux/io.h>
#include <mach/platform.h>
#include <linux/miscdevice.h>
#include "led.h"


//声明IO内存映射地址
void __iomem *gpioe_base = NULL;

/*
inode是文件的节点结构,用来存储文件静态信息
文件创建时,内核中就会有一个inode结构
file结构记录的是文件打开的信息
文件被打开时内核就会创建一个file结构
*/
int led_open(struct inode *inode, struct file *filp)
{
	printk("enter led_open!\n");

	return 0;
}

long led_ioctl(struct file *filp, unsigned int cmd, unsigned long data)
{
	printk("enter led_ioctl!\n");
	
	if(_IOC_TYPE(cmd) != IOC_MAGIC)   //如果命令中的幻数不是‘x’
		return - EINVAL;              //就返回错误代码表示无效参数
	if(_IOC_NR(cmd) >= IOC_MAX_NR)    //如果命令中的序列数大于4
		return - EINVAL;              //就返回错误代码表示无效参数
	if(data)
	{
		iowrite32(ioread32(gpioe_base)|(0x1<<13),gpioe_base);
		printk("LED%d:OFF!\n",_IOC_NR(cmd));
	}
	else
	{
		iowrite32(ioread32(gpioe_base)&~(0x1<<13),gpioe_base);
		printk("LED%d:ON!\n",_IOC_NR(cmd));
	}

	return 0;
}

int led_release(struct inode *inode, struct file *filp)
{
	printk("enter led_release!\n");

	return 0;
}

//声明操作函数集合
struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.unlocked_ioctl = led_ioctl,//ioctl接口
	.release = led_release,//对应用户close接口
};

//分配初始化miscdevice
struct miscdevice led_dev = {
	.minor = MISC_DYNAMIC_MINOR,//系统分配次设备号
	.name = "led",//设备文件名
	.fops = &led_fops,//操作函数集合
};

//加载函数
int led_init(void)
{
	int ret;
	
	//注册miscdevice
	ret = misc_register(&led_dev);
	if(ret<0){
		printk("misc_register failed!\n");
		goto failure_misc_register;
	}

	//IO内存映射
	gpioe_base = ioremap(PHY_BASEADDR_GPIOE, SZ_64);
	if(IS_ERR_OR_NULL(gpioe_base)){//失败
		printk("ioremap failed!\n");
		ret = -ENOMEM;
		goto failure_ioremap;
	}
	
	//初始化
	//设置复用功能 alt0  26 27位清0   addr:base+0x20
	iowrite32(ioread32(gpioe_base+0x20)&~(0x3<<26),gpioe_base+0x20);

	//设置输出模式  outenb 13位 置1   addr:base+0x04
	iowrite32(ioread32(gpioe_base+0x04)|(0x1<<13),gpioe_base+0x04);
	
	//设置默认值为高电平 out 13位 置1   addr:base
	iowrite32(ioread32(gpioe_base)|(0x1<<13),gpioe_base);

	return 0;

failure_ioremap:
	misc_deregister(&led_dev);
failure_misc_register:
	return ret;
}

//卸载函数
void led_exit(void)
{
	//解除IO映射
	iounmap(gpioe_base);

	//注销miscdevice
	misc_deregister(&led_dev);
}

//声明为模块的入口和出口
module_init(led_init);
module_exit(led_exit);


MODULE_LICENSE("GPL");//GPL模块许可证
MODULE_AUTHOR("xin");//作者
MODULE_VERSION("3.0");//版本
MODULE_DESCRIPTION("led driver!");//描述信息

可以发现通过混杂设备驱动的框架来编写LED驱动,加载函数中轻便了不少,有许多的步骤都是内核完成。

 好了,以上就介绍完了字符设备通过GPIO函数或者IO内存映射来实现驱动,以及混杂设备怎么实现驱动的过程。有什么问题和建议欢迎在评论区中提出来哟。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

欧盟立法者签署公开信,近万人联名“暂停高级AI研发”

来源丨CoinTelegraph 编辑丨liuruiWeb3CN.Pro ChatGPT 曾经的势头有多猛烈如今就被行业大佬抵制的就有多严重。 近日&#xff0c;十几位欧盟 (EU) 政客签署了“暂停高级AI研发”的公开信&#xff0c;呼吁 AI &#xff08;人工智能&#xff09;的“安全”发展&#xff0c;特斯拉…

【Android -- 软技能】聊聊高效开发的一些套路与实践

前言 在开发中&#xff0c;编码我们有分层架构、设计模式做为套路来高效开发&#xff0c;但你也知道编码不是开发的全部&#xff0c;一个完全的开发流程用面向对象思想来概括&#xff0c;它分为OOA&#xff08;面向对象分析&#xff09;、OOD&#xff08;面向对象设计&#xf…

Flutter - 动画使用及自定义动画组件(tabbar跳动动画或文字抖动)

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 Flutter - 动画组件&#xff08;tabbar跳动动画或文字抖动&#xff09; 序效果图动画简介动画类型Flutter动画的一些概念 常用动画实现隐式动画Tween动画Curve动画H…

低代码/无代码平台在软件开发中的应用

随着技术的不断发展&#xff0c;软件开发也在不断地进步。低代码/无代码平台已经成为软件开发的一个新的趋势。在这篇文章中&#xff0c;我们将深入探讨低代码/无代码平台在软件开发中的应用&#xff0c;包括它们的优势、如何选择合适的平台以及如何使用这些平台来开发高质量的…

机器学习 CarRentalData数据集分析和预测

介绍数据集 fuelType&#xff1a;燃料类型 rating&#xff1a;评级 renterTripsTaken&#xff1a;租房者出行 reviewCount&#xff1a;审阅计数 location.city&#xff1a;位置.城市 location.country&#xff1a;地点.国家/地区 location.latitude&#xff1a;位置.纬度 loca…

STM32按键实验中连接按键的GPIO管脚是上拉输入还是下拉输入

一、理解 关于STM32按键实验中连接按键的GPIO管脚是配置为上拉输入还是下拉输入的理解&#xff1a; 以江科大自动协教学视频按键输入实验为例&#xff1a; &#xff08;1&#xff09;按键KEY0<——>PE4 按键另一端接GND &#xff08;2&#xff09;按键KEY1<——&…

入门教学 | 快速了解集简云

集简云是一款超级软件连接器,无需开发,无需代码知识就可以轻松打通数百款软件之间的数据连接,构建自动化与智能化的业务流程。通过自动化业务流程,每月可节省企业数百甚至数万小时的人工成本。 集简云是什么? 集简云是一款超级软件连接器,无需开发,无需代码知识,就可以…

在现成的3D打印机上进行实验理论:一种数据孪生的攻击探测框架

在现成的3D打印机上提供了一种DT中攻击探测框架的DT解决方案的实验演示&#xff0c;作为说明性CPMS资源。通过网络安全DT对打印机正常运行、异常运行和攻击三种情况下的实验数据进行收集和分析&#xff0c;得出攻击检测结果。实验装置概述如下图所示。该实验研究是在现实世界设…

刚刚入职Android开发的应届生,该如何走向架构师

相信有不少从事Android开发的朋友&#xff0c;在工作一两年后会陷入一段迷茫期&#xff0c;有的是在工作中遇到了瓶颈&#xff0c;感觉无法突破&#xff1b;有的是想进阶成为架构师&#xff0c;但不知道如何进阶&#xff0c;因此产生了一些烦恼。为此小编在这里分享Android开发…

安卓开发: Compose 中的 Text 文本控件属性详解

Composable fun Text(text: String,modifier: Modifier Modifier,color: Color Color.Unspecified,fontSize: TextUnit TextUnit.Unspecified,fontStyle: FontStyle? null,fontWeight: FontWeight? null,fontFamily: FontFamily? null,letterSpacing: TextUnit TextU…

wps宏编辑器案例1-自定义函数使用

某公司考情和请假系统是2套独立的系统&#xff0c;人资在月底做考勤统计的时候需要把考勤系统导出的考勤信息表和OA请假流程导出的请假信息表进行合并&#xff0c;得出人员真实的考勤情况。比如员工“忠达”在考勤系统全显示缺勤&#xff0c;实际上请假系统里有4天请假&#xf…

16、Web原生组件注入(Servlet、Filter、Listener)

文章目录 1、使用Servlet API2、使用RegistrationBean 【尚硅谷】SpringBoot2零基础入门教程-讲师&#xff1a;雷丰阳 笔记 路还在继续&#xff0c;梦还在期许 1、使用Servlet API ServletComponentScan(basePackages “com.atguigu.admin”) :指定原生Servlet组件都放在那里…

全景天窗式科普数据仓库

数据仓库是一个面向主题的、集成的、随时间变化但信息本身相对稳定的数据集合&#xff0c;用于支持管理决策过程。其本质就是完成从面向业务过程数据的组织管理到面向业务分析数据的组织和管理的转变过程&#xff0c;也是商业智能BI中数据仓库的主要作用。 数据仓库 - 派可数据…

Spring Boot 定时任务

Spring Boot 提供了方便的注解来实现定时任务。下面是定时任务注解的详细教程&#xff1a; 一、添加依赖 要使用 Spring Boot 的定时任务注解&#xff0c;首先需要在项目中确认已添加过spring-boot-starter的依赖。 <dependency><groupId>org.springframework.b…

【youcans的深度学习 D01】PyTorch例程:从极简线性模型开始

欢迎关注『youcans的深度学习』系列 【youcans的深度学习 D01】PyTorch 例程&#xff1a;从极简线性模型开始 1. PyTorch 建模的基本步骤2. 线性模型的结构3. 建立 PyTorch 线性模型3.1 准备数据集3.2 定义线性模型类3.3 建立一个线性模型3.4 模型训练3.5 模型推断 4. PyTorch …

java-处理xml格式数据

处理xml格式数据 前言一、java处理xml格式数据1、 生成XML格式数据2、 解析XML格式数据 二、问题三、常用类及方法介绍 前言 dom4j是java中的XML API&#xff0c;性能优异、功能强大、开放源代码。 也是所有解析XML文件方法中最常用的&#xff01; 一、java处理xml格式数据 …

榜单发布 新能源乘用车OBC赛道进入转型升级周期

新能源汽车尤其是纯电动汽车市场的快速普及&#xff0c;也带动一批相关核心零部件厂商做大做强。比如&#xff0c;以车载充电机OBC及集成电源行业为例&#xff0c;威迈斯、富特科技等数家公司正在冲刺IPO。 目前&#xff0c;车载电源领域产品主要分为三种&#xff1a;一是单一…

步入AIGC时代,展望人工智能发展

步入AIGC时代&#xff0c;展望人工智能发展 0. 前言1. 步入 AIGC 时代1.1 人工智能简介1.2 AIGC 简介1.3 AIGC 发展与应用 2. CSIG 企业行——走进合合信息2.1 活动介绍2.2 走进合合信息 3. 文档图像处理中的底层视觉技术3.1 什么是底层视觉3.2 智能图像处理技术3.3 智能图像处…

消息中间件RabbitMQ---概述和概念 【一】

1、概述 1、大多应用中&#xff0c;可通过消息服务中间件来提升系统异步通信、扩展解耦能力 2、消息服务中两个重要概念&#xff1a; 消息代理&#xff08;message broker&#xff09;和目的地&#xff08;destination&#xff09; 当消息发送者发送消息以后&#xff0c;将由…

C语言中数据结构——顺序表

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C &#x1f525;座右铭&#xff1a;“不要等到什么都没有了&#xff0c;才下…