RK3568驱动指南|第十六篇 SPI-第192章 mcp2515驱动编写:完善write和read函数
瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。
【公众号】迅为电子
【粉丝群】258811263(加群获取驱动文档+例程)
【视频观看】嵌入式学习之Linux驱动(第十六篇 SPI_全新升级)_基于RK3568
【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板
-
第192章 mcp2515驱动编写:完善write和read函数
在上个章节中对mcp2515的工作模式进行了修改,从配置模式修改为了环回模式,而在本章节将会继续对mcp2515的驱动程序进行完善,加入mcp2515的写函数和读函数,从而实现数据的发送和接收。
192.1 编写mcp2515写函数
MCP2515有三个发送缓冲器,每个发送缓冲器都具有14字节的内存空间,每个发送缓冲器的控制由TXBnCTRL寄存器管理,该寄存器决定了何时发送报文以及发送时的报文状态。该寄存器的具体内容如下所示:

需要通过该寄存器将缓冲器优先级设置为最高,缓冲器优先级由bit1-0两位所决定,当设置为11时,该发送缓冲器具有最高的发送优先级,可以通过以下代码进行设置:
#define TXB0CTRL 0x30 //发送缓冲器控制寄存器地址 mcp2515_change_regbit(TXB0CTRL, 0x03, 0x03); //只对该寄存器低两位进行修改,修改值为0x03
发送缓冲器控制寄存器TXBnCTRL为发送缓冲器的第一个字节,接下来的5个字节用来装载标准和扩展标识符以及其他报文仲裁信息。最后的8个字节用于装载等待发送报文的8个可能的数据字节,这13个字节数据由用户空间所传递,且地址是连续的,间隔为一个字节,所以可以通过以下代码进行设置:
char w_kbuf[13] = {0};
int ret;// 从用户空间复制数据到内核缓冲区
ret = copy_from_user(w_kbuf, buf, size);
if (ret) {printk("copy_from_user w_kbuf is error\n");return -1;
}// 将数据写入MCP2515寄存器
for (i = 0; i < sizeof(w_kbuf); i++) {mcp2515_write_reg(0x31 + i, w_kbuf[i]);
}
数据设置完成之后,需要将TXBnCTRL寄存器的bit3设置为1,从而启动相应缓冲器的报文发送,TXBnCTRL寄存器就是上面修改发送缓冲器优先级的寄存器,具体设置代码如下所示:
#define TXB0CTRL 0x30 //发送缓冲器控制寄存器地址
mcp2515_change_regbit(TXB0CTRL, 0x08, 0x08); //只对该寄存器bit3进行修改,将bit3设置为1
在报文发送成功后,CANINTF.TXnIF寄存器将会被置1,该寄存器内容如下所示:

可以通过该寄存器来判断报文是否发送成功,由于使用的是缓冲器为0,所以这里要判断的位位bit2,判断完成之后,需要对该寄存器进行手动清零,具体判断代码如下所示:
#define CANINTF 0x2c // 等待发送完成
while (!(mcp2515_read_reg(CANINTF) & (1 << 2)));// 清除发送完成标志
mcp2515_change_regbit(CANINTF, 0x04, 0x00);
至此,关于mcp2515写函数的相关知识就总结完成了,可以将上面讲解的代码整理成一个完整的函数,具体内容如下所示:
#define TXB0CTRL 0x30 //发送缓冲器控制寄存器地址
#define CANINTF 0x2c // 写设备操作函数
ssize_t mcp2515_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) {char w_kbuf[13] = {0};int ret;int i;// 设置TXB0CTRL寄存器的部分位mcp2515_change_regbit(TXB0CTRL, 0x03, 0x03);// 从用户空间复制数据到内核缓冲区ret = copy_from_user(w_kbuf, buf, size);if (ret) {printk("copy_from_user w_kbuf is error\n");return -1;}// 将数据写入MCP2515寄存器for (i = 0; i < sizeof(w_kbuf); i++) {mcp2515_write_reg(0x31 + i, w_kbuf[i]);}// 设置TXB0CTRL寄存器的部分位,启动发送mcp2515_change_regbit(TXB0CTRL, 0x08, 0x08);// 等待发送完成while (!(mcp2515_read_reg(CANINTF) & (1 << 2)));// 清除发送完成标志mcp2515_change_regbit(CANINTF, 0x04, 0x00);return size;
}
192.2编写mcp2515读函数
在上个小节中编写了mcp2515的写函数,在本小节将编写mcp2515的读函数。
MCP2515 具有两个全接收缓冲器,当数据报文传送至某一接收缓冲器时,与该接收缓冲器对应的CANINTF.RXnIF位将置1,可以通过CANINTF.RXnIF寄存器的值来判断是否接收完成,CANINTF寄存器内容在上一节已经列出,这里不再重复,具体判断代码如下所示:
#define CANINTF 0x2c // 等待接收缓冲区满标志位被设置
while (!(mcp2515_read_reg(CANINTF) & (1 << 0)));
然后编写读数据相关的代码,接收缓冲器与发送寄存器相匹配,前5个字节用来装载标准和扩展标识符以及其他报文仲裁信息,最后的8个字节用于装载等待发送报文的8个可能的数据字节,且地址是连续的,间隔为一个字节,接收缓冲器0的标准标识符高位寄存器地址为0x61,所以可以通过以下代码进行设置:
char r_kbuf[13] = {0}; // 内核缓冲区,用于存储从设备读取的数据int i;// 从接收缓冲区读取数据到内核缓冲区for (i = 0; i < sizeof(r_kbuf); i++) {r_kbuf[i] = mcp2515_read_reg(0x61 + i);}
数据传送完成之后需要对CANINTF.RXnIF寄存器清零,并且使用copy_to_user传输到用户空间,具体代码如下所示:
// 清除接收缓冲区满标志位mcp2515_change_regbit(CANINTF, 0x01, 0x00);// 将内核缓冲区的数据复制到用户缓冲区ret = copy_to_user(buf, r_kbuf, size);if (ret) {printk("copy_to_user r_kbuf is error\n");return -1; // 返回-1表示复制数据失败}
至此,关于mcp2515读函数的相关知识就总结完成了,可以将上面讲解的代码整理成一个完整的函数,具体内容如下所示:
#define CANINTF 0x2c // 读设备操作函数,从设备读取数据到用户缓冲区
ssize_t mcp2515_read(struct file *file, char __user *buf, size_t size, loff_t *offset) {char r_kbuf[13] = {0}; // 内核缓冲区,用于存储从设备读取的数据int i;int ret;// 等待接收缓冲区满标志位被设置while (!(mcp2515_read_reg(CANINTF) & (1 << 0)));// 从接收缓冲区读取数据到内核缓冲区for (i = 0; i < sizeof(r_kbuf); i++) {r_kbuf[i] = mcp2515_read_reg(0x61 + i);}// 清除接收缓冲区满标志位mcp2515_change_regbit(CANINTF, 0x01, 0x00);// 将内核缓冲区的数据复制到用户缓冲区ret = copy_to_user(buf, r_kbuf, size);if (ret) {printk("copy_to_user r_kbuf is error\n");return -1; // 返回-1表示复制数据失败}return 0; // 返回0表示成功读取数据
}
192.3 实验程序编写
192.3.1 编写驱动程序
本实验驱动对应的网盘路径为:iTOP-3568开发板\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动程序\118_mcp2515_06\。
本实验将以191章编写完成的驱动程序为基础,添加了本章节完善的mcp2515的读和写函数,编写完成的mcp2515.c代码如下所示:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>#define CNF1 0x2a // 寄存器定义
#define CNF2 0x29
#define CNF3 0x28
#define RXB0CTRL 0x60
#define CANINTE 0x2b
#define CANCTRL 0xf // CAN控制寄存器#define TXB0CTRL 0x30 //发送缓冲器控制寄存器地址
#define CANINTF 0x2c dev_t dev_num; // 设备号
struct cdev mcp2515_cdev; // 字符设备结构体
struct class *mcp2515_class; // 设备类
struct device *mcp2515_device; // 设备
struct spi_device *spi_dev; // SPI设备指针// MCP2515芯片复位函数
void mcp2515_reset(void){int ret;char write_buf[] = {0xc0}; // 复位指令0x11000000即0xc0ret = spi_write(spi_dev, write_buf, sizeof(write_buf)); // 发送复位命令if(ret < 0){printk("spi_write is error\n"); // 打印错误信息}
}// MCP2515读寄存器函数
char mcp2515_read_reg(char reg) {char write_buf[] = {0x03, reg}; // SPI写缓冲区写入SPI读指令0x03char read_buf; // SPI读缓冲区int ret;ret = spi_write_then_read(spi_dev, write_buf, sizeof(write_buf), &read_buf, sizeof(read_buf)); // 调用SPI写读函数if (ret < 0) {printk("spi_write_then_read error\n");return ret;}return read_buf;
}// MCP2515写寄存器函数
void mcp2515_write_reg(char reg, char value) {int ret;char write_buf[] = {0x02, reg, value}; // SPI写缓冲区,用于发送写寄存器命令ret = spi_write(spi_dev, write_buf, sizeof(write_buf)); // 发送SPI写命令if (ret < 0) {printk("mcp2515_write_reg error\n");}
}// MCP2515修改寄存器位函数
void mcp2515_change_regbit(char reg, char mask, char value) {int ret;char write_buf[] = { 0x05, reg, mask, value }; // SPI写缓冲区,用于发送修改寄存器位命令ret = spi_write(spi_dev, write_buf, sizeof(write_buf)); // 发送SPI写命令if (ret < 0) {printk("mcp2515_change_regbit error\n");}
}// 打开设备文件的回调函数
int mcp2515_open(struct inode *inode, struct file *file) {return 0; // 返回成功
}// 读设备操作函数,从设备读取数据到用户缓冲区
ssize_t mcp2515_read(struct file *file, char __user *buf, size_t size, loff_t *offset) {char r_kbuf[13] = {0}; // 内核缓冲区,用于存储从设备读取的数据int i;int ret;// 等待接收缓冲区满标志位被设置while (!(mcp2515_read_reg(CANINTF) & (1 << 0)));// 从接收缓冲区读取数据到内核缓冲区for (i = 0; i < sizeof(r_kbuf); i++) {r_kbuf[i] = mcp2515_read_reg(0x61 + i);}// 清除接收缓冲区满标志位mcp2515_change_regbit(CANINTF, 0x01, 0x00);// 将内核缓冲区的数据复制到用户缓冲区ret = copy_to_user(buf, r_kbuf, size);if (ret) {printk("copy_to_user r_kbuf is error\n");return -1; // 返回-1表示复制数据失败}return 0; // 返回0表示成功读取数据
}// 写设备操作函数
ssize_t mcp2515_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) {char w_kbuf[13] = {0};int ret;int i;// 设置TXB0CTRL寄存器的部分位mcp2515_change_regbit(TXB0CTRL, 0x03, 0x03);// 从用户空间复制数据到内核缓冲区ret = copy_from_user(w_kbuf, buf, size);if (ret) {printk("copy_from_user w_kbuf is error\n");return -1;}// 将数据写入MCP2515寄存器for (i = 0; i < sizeof(w_kbuf); i++) {mcp2515_write_reg(0x31 + i, w_kbuf[i]);}// 设置TXB0CTRL寄存器的部分位,启动发送mcp2515_change_regbit(TXB0CTRL, 0x08, 0x08);// 等待发送完成while (!(mcp2515_read_reg(CANINTF) & (1 << 2)));// 清除发送完成标志mcp2515_change_regbit(CANINTF, 0x04, 0x00);return size;
}// 关闭设备文件的回调函数
int mcp2515_release(struct inode *inode, struct file *file) {return 0; // 返回成功
}// 设备文件操作集合
struct file_operations mcp2515_fops = {.open = mcp2515_open,.read = mcp2515_read,.write = mcp2515_write,.release = mcp2515_release,
};// MCP2515设备初始化函数
int mcp2515_probe(struct spi_device *spi) {int ret, value;printk("This is mcp2515_probe\n");spi_dev = spi; // 保存SPI设备指针// 分配字符设备号ret = alloc_chrdev_region(&dev_num, 0, 1, "mcp2515");if (ret < 0) {printk("alloc_chrdev_region error\n");}// 初始化字符设备cdev_init(&mcp2515_cdev, &mcp2515_fops);mcp2515_cdev.owner = THIS_MODULE;// 添加字符设备ret = cdev_add(&mcp2515_cdev, dev_num, 1);if (ret < 0) {printk("cdev_add error\n");return -1;}// 创建设备类mcp2515_class = class_create(THIS_MODULE, "spi_to_can");if (IS_ERR(mcp2515_class)) {printk("mcp2515_class error\n");return PTR_ERR(mcp2515_class);}// 创建设备mcp2515_device = device_create(mcp2515_class, NULL, dev_num, NULL, "mcp2515");if (IS_ERR(mcp2515_device)) {printk("mcp2515_device error\n");return PTR_ERR(mcp2515_device);}mcp2515_reset(); // 复位MCP2515设备value = mcp2515_read_reg(0x0e); // 读取寄存器值printk("value is %x\n", value); // 打印读取的值mcp2515_write_reg(CNF1, 0x01); // 写入寄存器配置值mcp2515_write_reg(CNF2, 0xb1);mcp2515_write_reg(CNF3, 0x05);mcp2515_write_reg(RXB0CTRL, 0x60);mcp2515_write_reg(CANINTE, 0x05);mcp2515_change_regbit(CANCTRL, 0xe0, 0x40);value = mcp2515_read_reg(0x0e); // 读取寄存器值printk("value is %x\n", value); // 打印读取的值return 0; // 返回成功
}// MCP2515 SPI设备的移除函数
static int mcp2515_remove(struct spi_device *spi) {device_destroy(mcp2515_class, dev_num);class_destroy(mcp2515_class);cdev_del(&mcp2515_cdev);unregister_chrdev_region(dev_num, 1);return 0;
}// MCP2515设备匹配表,用于设备树匹配
static const struct of_device_id mcp2515_of_match_table[] = {{ .compatible = "my-mcp2515" },{}
};// MCP2515设备ID匹配表,用于总线匹配
static const struct spi_device_id mcp2515_id_table[] = {{ "mcp2515", 0 },{}
};// MCP2515 SPI驱动结构体
static struct spi_driver spi_mcp2515 = {.probe = mcp2515_probe, // 探测函数.remove = mcp2515_remove, // 移除函数.driver = {.name = "mcp2515", // 驱动名称.owner = THIS_MODULE, // 所属模块.of_match_table = mcp2515_of_match_table, // 设备树匹配表
},.id_table = mcp2515_id_table, // 设备ID匹配表
};// 驱动初始化函数
static int __init mcp2515_init(void)
{int ret;// 注册SPI驱动ret = spi_register_driver(&spi_mcp2515);if (ret < 0) {// 注册失败,打印错误信息printk("spi_register_driver error\n");return ret;}return ret;
}// 驱动退出函数
static void __exit mcp2515_exit(void)
{// 注销SPI驱动spi_unregister_driver(&spi_mcp2515);
}module_init(mcp2515_init);
module_exit(mcp2515_exit);MODULE_LICENSE("GPL");
192.3.2 编写测试APP
本实验测试APP对应的网盘路径为:iTOP-3568开发板\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动程序\118_mcp2515_06\。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>// 主函数,程序入口点
int main(int argc, char *argv[]){int fd; // 文件描述符int i; // 循环变量// 写缓冲区,包含13个字节的数据,将发送到MCP2515char w_buf[13]= {0x66,0x08,0x22,0x33,0x08,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};// 读缓冲区,用于接收从MCP2515读取的数据char r_buf[13] = {0};// 打开MCP2515设备文件,获取文件描述符fd = open("/dev/mcp2515", O_RDWR);if(fd < 0){// 打开设备文件失败,打印错误信息并返回printf("open /dev/mcp2515 error \n");return -1;}// 将写缓冲区的数据写入设备write(fd, w_buf, sizeof(w_buf));// 从设备读取数据到读缓冲区read(fd, r_buf, sizeof(r_buf));// 打印读缓冲区的数据for(i = 0; i < 13; i++){printf("r_buf[%d] is %d\n", i, r_buf[i]);}// 关闭设备文件close(fd);return 0; // 返回0表示程序正常结束
}
上述测试app代码中第13行表示要发送给mcp2515的13个字节的数据,其中前5个字节用来装载标准和扩展标识符以及其他报文仲裁信息,最后的8个字节用于装载等待发送报文的8个可能的数据字节,第一个字节发送缓冲器标准标识符高位、第三个字节发送缓冲器扩展标识符高位、第四个字节发送缓冲器扩展标识符低位可以随意设置,这里设置的是0x66、0x22、0x33。
第二个字节为发送缓冲器标准标识符低位,该寄存器的具体内容如下所示:

其中bit3代表扩展标识符的使能位,这里需要设置为1进行使能,换算成16进制为0x08。
第5个字节为发送缓冲器数据长度码,该寄存器内容如下所示:

其中bit6需要设置为0,表示发送的报文为数据帧。而要发送的数据为8个字节,所以bit3-bit0需要设置为8,换算成16进制为0x08。
至此,关于前5个字节内容的设置就讲解完成了,而后8个字节为要发送的数据,这里随意取值即可。
192.4 运行测试
192.4.1 编译驱动程序
在上一小节中的mcp2515.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下所示:
export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += mcp2505.o #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel #这里是你的内核目录
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules #make操作
clean:make -C $(KDIR) M=$(PWD) clean #make clean操作
对于Makefile的内容注释已在上图添加,保存退出之后,来到存放mcp2515.c和Makefile文件目录下,如下图所示:

然后使用命令“make”进行驱动的编译,编译完成如下图所示:

编译完生成ft5x06_driver.ko目标文件,如下图所示:

至此驱动模块就编译成功了。
192.4.2 编译应用程序
首先进行应用程序的编译,因为测试APP是要在开发板上运行的,所以需要aarch64-linux-gnu-gcc来编译,输入以下命令,编译完成以后会生成一个app的可执行程序,如下图所示:
aarch64-linux-gnu-gcc app.c -o app

然后将编译完成的可执行程序拷贝到开发板上.
192.4.2 运行测试
在进行实验之前,首先要确保开发板烧写的是我们在186.1小节中编译出来的boot.img。开发板启动之后,然后使用以下命令进行驱动模块的加载,如下图所示:
insmod mcp2515.ko

然后使用“./app”运行上一小节中编译的可执行程序,运行结果如下所示:

可以看到可执行程序运行之后会将传输的13位数据依次打印出来,这里打印的是10进制,换算成16进制之后与0x66,0x08,0x22,0x33,0x08,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08相匹配,证明编写的mcp2515读函数和写函数可以正常工作。
最后使用以下命令进行驱动模块的卸载,如下图所示:
rmmod mcp2515.ko

由于没有在remove卸载函数中添加打印相关内容,所以使用rmmod命令卸载驱动之后,没有任何打印。
至此,MCP2515读函数和写函数测试实验就完成了。
相关文章:
RK3568驱动指南|第十六篇 SPI-第192章 mcp2515驱动编写:完善write和read函数
瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…...
#BI建模与数仓建模有什么区别?指标体系由谁来搭建?
问题1: 指标体系是我们数仓来搭建还是分析师来做,如何去推动? 问题2:BI建模与数仓建模有什么区别? 指标体系要想做好,其实是分两块内容的,一块是顶层设计阶段,业务指标体系的搭建&am…...
如何用Python实现三维可视化?
Python拥有很多优秀的三维图像可视化工具,主要基于图形处理库WebGL、OpenGL或者VTK。 这些工具主要用于大规模空间标量数据、向量场数据、张量场数据等等的可视化,实际运用场景主要在海洋大气建模、飞机模型设计、桥梁设计、电磁场分析等等。 本文简单…...
chrome.storage.local.set 未生效
之前chrome.storage.local.set 和 get 一直不起作用 使用以下代码运行成功。 chrome.storage.local.set({ pageState: "main" }).then(() > {console.log("Value is set");});chrome.storage.local.get(["pageState"]).then((result) > …...
泛微开发修炼之旅--30 linux-Ecology服务器运维脚本
文章链接:30 linux-ecology服务器运维脚本...
LeetCode 全排列
思路:这是一道暴力搜索问题,我们需要列出答案的所有可能组合。 题目给我们一个数组,我们很容易想到的做法是将数组中的元素进行排列,如何区分已选中和未选中的元素,容易想到的是建立一个标记数组,已经选中的…...
python实现支付宝异步回调验签
说明 python实现支付宝异步回调验签,示例中使用Django框架。 此方案使用了支付宝的pythonSDK,请一定装最新版本的,支付宝官网文档不知道多久没更新了,之前的版本pip安装会报一些c库不存在的错误; pip install alipay-…...
注意!Vue.js 或 Nuxt.js 中请停止使用.value
大家好,我是CodeQi! 一位热衷于技术分享的码仔。 当您在代码中使用.value时,必须每次都检查变量是否存在并且是引用。 这可能很麻烦,因为在运行时使用.value可能会导致错误。然而,有一个简单的解决方法,即使用unref()而不是.value。 unref()会检查变量是否是引用,并自…...
Java:JDK、JRE和JVM 三者关系
文章目录 一、JDK是什么二、JRE是什么三、JDK、JRE和JVM的关系 一、JDK是什么 JDK(Java Development Kit):Java开发工具包 JRE:Java运行时环境开发工具:javac(编译工具)、java(运行…...
Radio专业术语笔记
在收音机的 RDS (Radio Data System) 功能中,CT 代表 “Clock Time”。RDS 是一种数字广播标准,用于在调频广播中传输辅助数据,如电台名称、节目类型、交通信息等。CT 功能是其中的一部分,用于同步和显示广播电台发送的当前时间。…...
cocosCreator找出未用到的图片
最近整理项目的时候发现有些资源文件夹有点轮乱(一些历史原因导致的),而且有很多图片都是没用了的,但是没有被删除掉,还一直放在项目中,导致项目的资源文件夹比较大,而且还冗余。于是今天想着整理一下。 公开免费链接 找出未使用的图片 有好几种方法可以找出未使用的图片…...
一览 Anoma 上的有趣应用概念
撰文:Tia,Techub News 本文来源香港Web3媒体:Techub News Anoma 的目标是为应用提供通用的意图机器接口,这意味着使用 Anoma,开发人员可以根据意图和分布式意图机编写应用,而不是根据事务和特定状态机进行…...
Spring Boot集成fastjson2快速入门Demo
1.什么是fastjson2? fastjson2是阿里巴巴开发的一个高性能的Java JSON处理库,它支持将Java对象转换成JSON格式,同时也支持将JSON字符串解析成Java对象。本文将介绍fastjson2的常见用法,包括JSON对象、JSON数组的创建、取值、遍历…...
Three.js机器人与星系动态场景(二):强化三维空间认识
在上篇博客中介绍了如何快速利用react搭建three.js平台,并实现3D模型的可视化。本文将在上一篇的基础上强化坐标系的概念。引入AxesHelper辅助工具及文本绘制工具,带你快速理解camer、坐标系、position、可视区域。 Three.js机器人与星系动态场景&#x…...
java顺序查找
其中有一个常用的编程思想: 由于是遍历查找,不能用if-else来输出没有找到,而应该设置一个索引index,如果找到就将index的值设置成下标的值,如果遍历结束后index仍为初始值,才是没有找到 //2024.07.03impor…...
提升学生职务执行力的智慧校园学工管理策略
智慧校园的学工管理系统匠心独运地融入了“学生职务”这一创新模块,它紧密贴合学生的实际需求,致力于在校期间的实践经验积累和个人能力的全面提升。这个模块化身为一个便捷的综合平台,让学生们能够轻松发掘并参与到丰富多彩的校内职务中去&a…...
系统运维面试总结(shell编程)
SYNDDOS攻击,需要判断这个访问是正常访问还是信包攻击,当前这个信包发起的访问数量是多少,例如看到30个信包同时再访问时设置监控报警。 一般选用/dev/urandom生成,但其生成的随机数带有二进制乱码,所以需要tr命令…...
在数据库中,什么是主码、候选码、主属性、非主属性?
在数据库中,主码、候选码、主属性和非主属性是几个重要的概念,它们对于理解数据库的结构和数据的完整性至关重要。以下是对这些概念的详细解释: 一、主码(Primary Key) 定义:主码,也被称为主键…...
Linux-笔记 udev机制介绍
目录 前言 概念 规则文件 规则文件的命名 规则文件的语法 匹配条件 赋值指令 例子 前言 由于之前利用udev机制实现了一个自动配置某功能的项目,所以这里做一下笔记总结,什么是udev?怎么用? 概念 udev其实是linux系统中一…...
深度学习基准模型Mamba
深度学习基准模型Mamba Mamba(英文直译:眼镜蛇)具有选择性状态空间的线性时间序列建模,是一种先进的状态空间模型 (SSM),专为高效处理复杂的数据密集型序列而设计。 Mamba是一种深度学习基准模型,专为处理长序列数据而设计&…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...
Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...
aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
