(Linux驱动学习 -13).SPI驱动实验
目录
一.SPI驱动相关结构体与函数
1.struct spi_master 结构体
2.申请 spi_master - spi_alloc_master
3.释放 spi_master - spi_master_put
4.向内核注册 spi_master - spi_register_master
5.注销掉 spi_master
6.struct spi_driver 结构体
7.向内核注册 spi_driver - spi_register_driver
8.注销 spi_driver - spi_unregister_driver
9.struct spi_transfer 结构体
10.struct spi_message 结构体
11.初始化 spi_message
12.将 spi_transfer 添加到 spi_message 队列中
13.SPI 同步传输数据 - spi_sync
14.SPI 异步传输数据 - spi_async
二.SPI实验 - ICM20608
1.设备树
(1).流程图
(2).设备树代码
编辑
2.驱动部分
(1).流程图
编辑
(2).驱动部分代码
3.应用程序
一.SPI驱动相关结构体与函数
1.struct spi_master 结构体
struct spi_master
{struct device dev;struct list_head list;......s16 bus_num;/* chipselects will be integral to many controllers; some others* might use board-specific GPIOs.*/u16 num_chipselect;/* some SPI controllers pose alignment requirements on DMAable* buffers; let protocol drivers know about these requirements.*/u16 dma_alignment;/* spi_device.mode flags understood by this controller driver */u16 mode_bits;/* bitmask of supported bits_per_word for transfers */u32 bits_per_word_mask;....../* limits on transfer speed */u32 min_speed_hz;u32 max_speed_hz;/* other constraints relevant to this driver */u16 flags;/* lock and mutex for SPI bus locking */spinlock_t bus_lock_spinlock;struct mutex bus_lock_mutex;/* flag indicating that the SPI bus is locked for exclusive use */bool bus_lock_flag;......int (*setup)(struct spi_device *spi);......int (*transfer)(struct spi_device *spi,struct spi_message *mesg);......int (*transfer_one_message)(struct spi_master *master,struct spi_message *mesg);......
};
2.申请 spi_master - spi_alloc_master
/*** @description: 申请 spi_master* @param - dev : 设备,一般是 platform_device 中的 dev 变量* @param - size : 私有数据大小,可以通过 spi_master_get_devdata 函数获取到这些私有数据* @return : 申请到的 spi_master*/
struct spi_master *spi_alloc_master(struct device *dev,unsigned int size)
3.释放 spi_master - spi_master_put
/*** @description: 释放 spi_master* @param - master : 要释放的 spi_master* @return : 无*/
void spi_master_put(struct spi_master *master)
4.向内核注册 spi_master - spi_register_master
/*** @description: 向内核注册 spi_master* @param - master : 要向内核注册的 spi_master* @return : 成功则返回(0),失败则返回(-1)*/
int spi_register_master(struct spi_master *master)
5.注销掉 spi_master
/*** @description: 注销 spi_master* @param - master : 要注销的 spi_master* @return : 无*/
void spi_unregister_master(struct spi_master *master)
6.struct spi_driver 结构体
struct spi_driver
{const struct spi_device_id *id_table;int (*probe)(struct spi_device *spi);int (*remove)(struct spi_device *spi);void (*shutdown)(struct spi_device *spi);struct device_driver driver;
};
7.向内核注册 spi_driver - spi_register_driver
/*** @description: 向内核注册 spi_driver* @param - sdrv : 要注册的 spi_driver* @return : 成功则返回(0),失败则返回(负值)*/
int spi_register_driver(struct spi_driver *sdrv)
8.注销 spi_driver - spi_unregister_driver
/*** @description: 注销 spi_driver* @param - sdrv : 要注销的 spi_driver* @return : 无*/
void spi_unregister_driver(struct spi_driver *sdrv)
9.struct spi_transfer 结构体
struct spi_transfer
{/* 发送数据缓冲区 */const void *tx_buf;/* 接收数据缓冲区 */ void *rx_buf;/* 数据长度 */unsigned len;dma_addr_t tx_dma;dma_addr_t rx_dma;struct sg_table tx_sg;struct sg_table rx_sg;unsigned cs_change:1;unsigned tx_nbits:3;unsigned rx_nbits:3;#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */u8 bits_per_word;u16 delay_usecs;u32 speed_hz;struct list_head transfer_list;
};
10.struct spi_message 结构体
struct spi_message
{struct list_head transfers;struct spi_device *spi;unsigned is_dma_mapped:1;....../* completion is reported through a callback */void (*complete)(void *context);原子哥在线教学:www.yuanzige.com 论坛:www.openedv.comvoid *context;unsigned frame_length;unsigned actual_length;int status;struct list_head queue;void *state;
};
11.初始化 spi_message
/*** @description: 初始化 spi_message* @param - m : 要初始化的 spi_message* @return : 无*/
void spi_message_init(struct spi_message *m)
12.将 spi_transfer 添加到 spi_message 队列中
/*** @description: 将 spi_transfer 添加到 spi_message 队列中* @param - t : 要添加至 spi_message 队列中的 spi_transfer* @param - m : spi_transfer 要加入的 spi_message* @return : 无*/
void spi_message_add_tail(struct spi_transfer *t,struct spi_message *m)
13.SPI 同步传输数据 - spi_sync
/*** @description: SPI 同步传输数据* @param - spi : 要进行数据传输的 SPI* @param - message : 要传输的 spi_message* @return : 无 */
int spi_sync(struct spi_device *spi,struct spi_message *message)
14.SPI 异步传输数据 - spi_async
/*** @description: SPI 异步传输数据* @param - spi : 要进行数据传输的 SPI* @param - message : 要传输的 spi_message* @return : 无*/
int spi_async(struct spi_device *spi,struct spi_message *message)
二.SPI实验 - ICM20608
1.设备树
(1).流程图

(2).设备树代码

2.驱动部分
(1).流程图
(2).驱动部分代码
#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/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm20608reg.h"#define ICM20608_CNT 1 /* 设备数量 */
#define ICM20608_NAME "icm20608" /* 设备名字 *//* 设备结构体 */
struct icm20608_dev
{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */struct device_node *nd; /* 设备结点 */int major; /* 主设备号 */void *private_data; /* 私有数据 */int cs_gpio; /* 片选所使用的 GPIO 编号 */signed int gyro_x_adc; /* 陀螺仪 X 轴原始值 */signed int gyro_y_adc; /* 陀螺仪 Y 轴原始值 */signed int gyro_z_adc; /* 陀螺仪 Z 轴原始值 */signed int accel_x_adc; /* 加速度计 X 轴原始值 */signed int accel_y_adc; /* 加速度计 Y 轴原始值 */signed int accel_z_adc; /* 加速度计 Z 轴原始值 */signed int temp_adc; /* 温度原始值 */
};static struct icm20608_dev icm20608dev;/*** @description: 从icm20608读取多个寄存器数据* @param - dev : icm20608设备* @param - reg : 要读取的寄存器首地址* @param - buf : 用于存储读到的数据* @param - len : 要读取的数据的长度* @return : 操作结果,0为成功,其他为失败*/
static int icm20608_read_regs(struct icm20608_dev *dev,u8 reg,void *buf,int len)
{int ret = -1;unsigned char txdata[1]; //发送的数据为要读取的寄存器首地址unsigned char *rxdata; //存储该函数SPI整个过程接收到的数据struct spi_message m;struct spi_transfer *t;/* 获取私有数据 */struct spi_device *spi = (struct spi_device *)dev->private_data;t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);if(!t){return -ENOMEM;}rxdata = kzalloc(sizeof(char)*len,GFP_KERNEL);if(!rxdata){goto out1;}/* 一共发送 len+1 个字节的数据,第一个字节为寄存器首地址,一共读取len个字节长度的数据 */txdata[0] = reg | 0x80; /* 写数据的时候,首寄存器地址 bit8 要置1 */t->tx_buf = txdata; /* 要发送的数据 */t->rx_buf = rxdata; /* 接收数据的缓冲区 */t->len = len + 1; /* t->len = 一个字节的寄存器首地址 + 要读取的长度 */spi_message_init(&m); /* 初始化 spi_message */spi_message_add_tail(t,&m); /* 将 spi_transfer 添加到 spi_message 队列中 */ret = spi_sync(spi,&m); /* 同步发送 */if(ret){goto out2;}memcpy(buf,rxdata + 1,len); /* 只需要读取的数据,rxdata的第一个字节的数据没有意义 */out2:kfree(rxdata); /* 释放内存 */out1:kfree(t); /* 释放内存 */return ret;
}/*** @description: 向icm20608多个寄存器写入数据* @param - dev : icm20608 设备* @param - reg : 要写的寄存器首地址* @param - buf : 要写入的数据缓冲区* @param - len : 要写入数据的长度* @return : 操作结果*/
static s32 icm20608_write_regs(struct icm20608_dev *dev,u8 reg,u8 *buf,u8 len)
{int ret = -1;unsigned char *txdata;struct spi_message m;struct spi_transfer *t;struct spi_device *spi = (struct spi_device *)dev->private_data;t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);if(!t){return -ENOMEM;}txdata = kzalloc(sizeof(char) + len,GFP_KERNEL); //第一个字节为寄存器首地址,后面的内容为要写的内容if(!txdata){goto out1;}/* 一共要发送 len+1 个字节,第一个字节为寄存器首地址,len 为要写入寄存器的集合 */*txdata = reg & ~0x80; /* 写数据的时候,寄存器首地址 bit8 要清零 */memcpy(txdata+1,buf,len); /* 将要写入的数据拷贝到缓冲区中 */t->tx_buf = txdata; /* 要发送的数据 */t->len = len+1; /* t->len = 发送的数据段长度+一个字节的寄存器首地址 */spi_message_init(&m); /* 初始化 spi_message */spi_message_add_tail(t,&m); /* 将 spi_transfer 添加到 spi_message 队列中 */ret = spi_sync(spi,&m);if(ret){goto out2;}out2:kfree(txdata);
out1:kfree(t);return ret;
}/*** @descsription: 读取icm20608指定寄存器的值,只读取一个字节* @param - dev : icm20608设备* @param - reg : 要读取的寄存器首地址* @return : 返回读取到的值 */
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev,u8 reg)
{unsigned char data;icm20608_read_regs(dev,reg,&data,1);return data;
}/*** @description: 向icm20608指定寄存器写入一个字节的数据* @param - dev : icm20608设备* @param - reg : 要写入的寄存器首地址* @param - value : 要写入的值*/
void icm20608_write_onereg(struct icm20608_dev *dev,u8 reg,u8 value)
{u8 data = value;icm20608_write_regs(dev,reg,&data,1);
}/*** @description: 读取 icm20608 的原始数据,包括三轴陀螺仪、三轴加速度计和内部温度* @param - dev : icm20608 设备,读取到的数据会存入该设备结构体内* @return : 无*/
void icm20608_readdata(struct icm20608_dev *dev)
{unsigned char data[14] = {0};icm20608_read_regs(dev,ICM20_ACCEL_XOUT_H,data,14); //读取数据并存储在 data 缓冲区中dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]);dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]);dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]);dev->temp_adc = (signed short)((data[6] << 8) | data[7]);dev->gyro_x_adc = (signed short)((data[8] << 8) | data[9]);dev->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);dev->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}/*** @description: 内部寄存器初始化函数* @param : 无* @return : 无*/
void icm20608_reginit(void)
{u8 value = 0;icm20608_write_onereg(&icm20608dev,ICM20_PWR_MGMT_1,0X80);mdelay(50);icm20608_write_onereg(&icm20608dev,ICM20_PWR_MGMT_1,0X81);mdelay(50);/* 获取 ICM20608 设备ID */value = icm20608_read_onereg(&icm20608dev,ICM20_WHO_AM_I);printk("ICM20608 ID = %#X\r\n",value);icm20608_write_onereg(&icm20608dev,ICM20_SMPLRT_DIV,0X00);icm20608_write_onereg(&icm20608dev,ICM20_GYRO_CONFIG,0X18);icm20608_write_onereg(&icm20608dev,ICM20_ACCEL_CONFIG,0X18);icm20608_write_onereg(&icm20608dev,ICM20_CONFIG,0X04);icm20608_write_onereg(&icm20608dev,ICM20_ACCEL_CONFIG2,0X04);icm20608_write_onereg(&icm20608dev,ICM20_PWR_MGMT_2,0X00);icm20608_write_onereg(&icm20608dev,ICM20_LP_MODE_CFG,0X00);icm20608_write_onereg(&icm20608dev,ICM20_FIFO_EN,0X00);
}/*** @description: 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,一般使 filp->private_data 指向设备结构体* @return : 0成功,其他失败*/
static int icm20608_open(struct inode *inode,struct file *filp)
{/* 设置私有数据 */filp->private_data = &icm20608dev;return 0;
}/*** @description: 从设备读取数据* @param - filp : 要打开的设备* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的字节数* @param - offt : 相对于文件首地址的偏移量* @return : 成功则返回(读取到的字节数),失败则返回(负值)*/
static ssize_t icm20608_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{signed int data[7];long err = 0;struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;icm20608_readdata(dev);data[0] = dev->gyro_x_adc;data[1] = dev->gyro_y_adc;data[2] = dev->gyro_z_adc;data[3] = dev->accel_x_adc;data[4] = dev->accel_y_adc;data[5] = dev->accel_z_adc;data[6] = dev->temp_adc;err = copy_to_user(buf,data,sizeof(data));return 0;
}/*** @description: 关闭/释放设备* @param : 无* @return : 0成功,其他失败*/
static int icm20608_release(struct inode *inode,struct file *filp)
{return 0;
}/* 设备操作函数 */
static const struct file_operations icm20608_ops =
{.owner = THIS_MODULE,.open = icm20608_open,.read = icm20608_read,.release = icm20608_release,
};/*** @description: probe函数,当设备与驱动匹配成功后就会执行此函数* @param - spi : spi 设备* @return : 0成功,其他失败*/
static int icm20608_probe(struct spi_device *spi)
{printk("driver and device matched!\r]n");/* 1.构建设备号 */if(icm20608dev.major){icm20608dev.devid = MKDEV(icm20608dev.major,0);register_chrdev_region(icm20608dev.devid,ICM20608_CNT,ICM20608_NAME);}else{alloc_chrdev_region(&icm20608dev.devid,0,ICM20608_CNT,ICM20608_NAME);icm20608dev.major = MAJOR(icm20608dev.devid);}/* 2.注册cdev */cdev_init(&icm20608dev.cdev,&icm20608_ops);cdev_add(&icm20608dev.cdev,icm20608dev.devid,ICM20608_CNT);/* 3.创建类 */icm20608dev.class = class_create(THIS_MODULE,ICM20608_NAME);if(IS_ERR(icm20608dev.class)){return PTR_ERR(icm20608dev.class);}/* 4.创建设备 */icm20608dev.device = device_create(icm20608dev.class,NULL,icm20608dev.devid,NULL,ICM20608_NAME);if(IS_ERR(icm20608dev.device)){return PTR_ERR(icm20608dev.device);}/* 初始化 spi_device */spi->mode = SPI_MODE_0; /* MODE0 : CPOL=0,CPHA=0 */spi_setup(spi);icm20608dev.private_data = spi; //设置私有数据/* 初始化 ICM20608 内部寄存器 */icm20608_reginit();return 0;
}/*** @description: remove函数,移除SPI驱动的时候会执行此函数* @param - spi : spi设备* @return : 成功则返回(0),失败则返回(其他值)*/
static int icm20608_remove(struct spi_device *spi)
{/* 1.删除设备 */cdev_del(&icm20608dev.cdev);unregister_chrdev_region(icm20608dev.devid,ICM20608_CNT);/* 注销掉类和设备 */device_destroy(icm20608dev.class,icm20608dev.devid);class_destroy(icm20608dev.class);return 0;
}/* 无设备树下的匹配方式 - ID列表 */
static const struct spi_device_id icm20608_id[] =
{{"alientek,icm20608",0},{}
};/* 设备树下的匹配方式 - 匹配列表 */
static const struct of_device_id icm20608_of_match[] =
{{.compatible = "alientek,icm20608"},{}
};/* SPI驱动结构体 */
static struct spi_driver icm20608_driver =
{.probe = icm20608_probe,.remove = icm20608_remove,.driver = {.owner = THIS_MODULE,.name = "icm20608",.of_match_table = icm20608_of_match, //设备树下的匹配列表},.id_table = icm20608_id, //无设备树下的 ID列表
};/*** @description: 驱动入口函数* @param : 无* @return : 无*/
static int __init icm20608_init(void)
{return spi_register_driver(&icm20608_driver);
}/*** @description: 驱动出口函数* @param : 无* @return : 无*/
static void __exit icm20608_exit(void)
{spi_unregister_driver(&icm20608_driver);
}module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaneki");
3.应用程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char *argv[])
{int ret,fd;char *filename;signed int databuf[7];unsigned char data[14];/* icm20608的原始值 */signed int gyro_x_adc,gyro_y_adc,gyro_z_adc;signed int accel_x_adc,accel_y_adc,accel_z_adc;signed int temp_adc;/* icm20608的实际值 */ float gyro_x_act,gyro_y_act,gyro_z_act;float accel_x_act,accel_y_act,accel_z_act;float temp_act;if(2 != argc){printf("Usage : ./%s <dev_path>\n",argv[0]);return -1;}filename = argv[1];fd = open(filename,O_RDONLY);if(0 > fd){perror("open error");return -1;}while(1){ret = read(fd,databuf,sizeof(databuf));/* 读取成功 */if(0 == ret){/* 得到原始值 */gyro_x_adc = databuf[0];gyro_y_adc = databuf[1];gyro_z_adc = databuf[2];accel_x_adc = databuf[3];accel_y_adc = databuf[4];accel_z_adc = databuf[5];temp_adc = databuf[6];/* 计算得到实际值 */gyro_x_act = (float)(gyro_x_adc) / 16.4;gyro_y_act = (float)(gyro_y_adc) / 16.4;gyro_z_act = (float)(gyro_z_adc) / 16.4;accel_x_act = (float)(accel_x_adc) / 2048;accel_y_act = (float)(accel_y_adc) / 2048;accel_z_act = (float)(accel_z_adc) / 2048;temp_act = ((float)(temp_adc - 25) / 326.8 + 25);printf("----原始值-----\n");printf("gx = %d,gy = %d,gz = %d\n",gyro_x_adc,gyro_y_adc,gyro_z_adc);printf("ax = %d,ay = %d,az = %d\n",accel_x_adc,accel_y_adc,accel_z_adc);printf("temp = %d\n",temp_adc);printf("----实际值----\n");printf("act gx = %.2f,act gy = %.2f,act gz = %.2f\n",gyro_x_act,gyro_y_act,gyro_z_act);printf("act ax = %.2f,act ay = %.2f,act az = %.2f\n",accel_x_act,accel_y_act,accel_z_act);printf("act temp = %.2f\n",temp_act);}usleep(100000); /* 100ms*/}close(fd);return 0;
}
相关文章:
(Linux驱动学习 -13).SPI驱动实验
目录 一.SPI驱动相关结构体与函数 1.struct spi_master 结构体 2.申请 spi_master - spi_alloc_master 3.释放 spi_master - spi_master_put 4.向内核注册 spi_master - spi_register_master 5.注销掉 spi_master 6.struct spi_driver 结构体 7.向内核注册 spi_driver -…...
Angular 框架入门教程:从安装到路由、服务与状态管理详解
一、引言 在前端开发领域,Angular 是一个强大且流行的框架。它由 Google 维护,基于 TypeScript,采用模块化设计,提供了组件化开发、依赖注入、路由、表单处理等丰富功能,旨在帮助开发者构建高效、可维护的单页应用程序…...
【华为HCIP实战课程十八】OSPF的外部路由类型,网络工程师
一、外部路由类型: 上节讲的外部路由类型,无关乎COST大小,OSPF外部路由类型1优先于外部路由类型2 二、转发地址实验拓扑 我们再SW3/R5/R6三台设备运行RIP,SW3即运行RIP又运行OSPF SW3配置rip [SW3-rip-1]ver 2 [SW3-rip-1]network 10.0.0.0 AR5去掉ospf配置和AR6配置rip…...
oss 简单命令(已亲测)
1、 服务器本地文件复制到OSS指定目录 ./ossutil cp -r /opt/post/afc/afcServer/afcenter/logs/ oss://oss-name/ScBak/20230608/ -c /opt/post/ossconfig 2、在oss服务器上创建文件夹 ./ossutil mkdir oss://oss-name/ScBak/20230608/dam -c /opt/post/ossconfig 3、查…...
申请https证书
引入证书: 当服务器使用HTTPS之前都会申请一份证书,证书是为了证明服务端公钥的权威性,服务器向浏览器传输证书,浏览器再从证书里获取公钥,证书明文数据签名。 如何理解CA签发证书的过程 a.CA会有自己的公钥 和 私钥ÿ…...
trtexec 工具使用
本文介绍trtexec工具的使用,trtexec可以实现onnx模型导出trt模型、耗时分析和模型优化分析等功能,本节将对 trtexec的运用进行介绍。 1.trtexec trtexec是官方提供的命令行工具,主要用于一下三个方面 生成模型序列化文件:由ONNX文…...
10款具备强大数据报告功能的电脑监控工具,办公电脑怎么监控
数据报告功能是电脑监控软件的重要特性,它能够帮助管理者全面了解员工的工作行为、应用使用情况,并生成详细的生产力分析报告。以下是10款具备强大数据报告功能的监控工具推荐,帮助企业有效管理和提升工作效率。 1. 固信软件 固信软件不仅是…...
如何理解Linux中的进程名
目录 一:程序的概念二:进程的概念三:线程的概念四:Linux中的进程名 一:程序的概念 程序就是采用某种特定格式编写的文本文件,该文件可以给编译器或者解释器编译和解释。编写好的程序平时存放在硬盘中。 二…...
微信红包设计流程讲解与实战分析
#1024程序员节 | 征文# 前言 微信红包作为大家耳熟能详的一种互动方式,其背后的技术支持包含多个方面。从用户发出红包到红包被抢完,涉及到的流程包括发红包、红包存储、红包拆分以及抢红包等。本文将详细介绍这一系列流程,并通过代码案例来…...
AI智能体:AI智能体(Agent)是什么?为什么要学?99%的人不知道!
为什么要学? 我们先搞清楚为什么? 最近看到 AI 创新力五问,我们日常生活中有使用 AI 来融入到我们的学习工作流嘛? 值得我们日常反省。 未来企业人才招聘测试AI创新力的五问: 您是否处于每天习惯使用 AI 的状态&am…...
NVR小程序接入平台/设备EasyNVR多个NVR同时管理的高效解决方案
在当今的数字化安防时代,视频监控系统的需求日益复杂和多样化。为了满足不同场景下的监控需求,一种高效、灵活且兼容性强的安防视频监控平台——NVR批量管理软件/平台EasyNVR应运而生。本篇探讨这一融合所带来的创新与发展。 一、NVR监测软件/设备EasyNV…...
APS开源源码解读: 排程工具 optaplanner II
上篇 排产,原则上也就是分配时间,分配资源;保证资源日历约束,保证工艺路线约束。我们看一下如何实现optaplanner 优化的 定义一个move, 一个move可能改变了分配到的资源,也可能改变了一个资源上的顺序。改变即意味着优…...
科技是把双刃剑,巧用技术改变财务预测
数字化和全球化的双向驱动,引领我国各行各业在技术革新的浪潮中不断扬帆。这一趋势不仅带来了前所未有的突破与创新,推进企业迈向数据驱动决策的新未来,同时也伴随着一些潜在的问题和挑战。科技的普及就像是一场革命,在财务管理领…...
vscode默认添加python项目的源目录路径到执行环境(解决ModuleNotFoundError: No module named问题)
0. 问题描述 vscode中编写python脚本,导入工程目录下的其他模块,出现ModuleNotFoundError: No module named 错误 在test2的ccc.py文件中执行print(sys.path) 查看路径 返回结果发现并无’/home/xxx/first_demo’的路径,所以test2下面的文…...
【每日刷题】Day143
【每日刷题】Day143 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. 200. 岛屿数量 - 力扣(LeetCode) 2. LCR 105. 岛屿的最大面积 - 力扣&…...
基于Springboot智能学习平台的设计与实现
基于Springboot智能学习平台的设计与实现 开发语言:Java 框架:springboot JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:idea 源码获取:https://download.csdn.net/downlo…...
黑马javaWeb笔记重点备份11:Web请求与响应
请求 SpringBoot内置Servlet 在Tomcat这类Web服务器中,是不识别我们自己定义的Controller的,但在tomcat中是可以识别 Servlet程序的。在SpringBoot进行web程序开发时,它内置了一个核心的Servlet程序 DispatcherServlet,称之为 核…...
H5对接海康硬盘录像机视频简单说明
开发过程中使用HTML5(通常是通过Web技术栈,如HTML、CSS、JavaScript)与海康威视(Hikvision)的硬盘录像机(DVR)进行视频对接,通常涉及以下步骤: 获取DVR的RTSP流地址:海康威视DVR支持RTSP协议,你可以通过DVR的管理界面获取每个摄像头的RTSP流地址。 使用视频播放器库…...
测试人必备的Linux常用命令大全...【全网最全面整理】
Linux常用命令大全(非常全!!!) 最近都在和Linux打交道,感觉还不错。我觉得Linux相比windows比较麻烦的就是很多东西都要用命令来控制,当然,这也是很多人喜欢linux的原因,…...
苹果AI落后两年?——深度解析苹果在AI领域的挑战与前景
# 苹果AI落后两年?——深度解析苹果在AI领域的挑战与前景 近年来,人工智能(AI)领域的技术竞争日益激烈,各大科技巨头纷纷推出突破性的AI产品。然而,关于苹果公司在AI领域的表现,最近传出一些内…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...

