MB85RC铁电 FRAM驱动(全志平台linux)

测试几天发现一个bug,就是无法一次读取32个字节的数据,1-31,33,128,512都试过了,唯独无法读取32个字节,驱动未报错,但是读取的都是0,找不到原因,估计应该是全志iic驱动的问题,暂时没有折腾,尽量避开32字节读取吧,32字节写入是没问题的。

使用的字符驱动,可以读写任意字节(32字节读取除外),可以使用lseek设置读写地址,首先设置内核设备数,在对应的iic节点下添加fram支持。

fram: fram@50 {compatible = "general,iic_fram";reg = <0x50>;};

驱动代码如下

// SPDX-License-Identifier: GPL-2.0+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <linux/mutex.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/uaccess.h>
#include <linux/device.h>			//自动创建/dev设备节点需要
#include <linux/kdev_t.h>           //设备号用到的头文件和宏函数
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>typedef unsigned int  u32;
typedef unsigned short u16;
typedef unsigned char  u8;typedef volatile unsigned int  vu32;
typedef volatile unsigned short vu16;
typedef volatile unsigned char  vu8;typedef unsigned int  const uc32;  /* Read Only */
typedef unsigned short const uc16;  /* Read Only */
typedef unsigned char  const uc8;   /* Read Only */#define DEVICE_NAME			"iic_fram" 	// 设备名字
#define DTS_COMPATIBLE		"general,iic_fram"	//设备树中对应的COMPATIBLE信息名称//注意:fram使用的是 MB85RC16PNF-G-JNERE1 进行测试2KB容量,FRAM的通讯有点不一样,芯片地址为4bit,然后3bit寄存器高地址,1bit读写标识,最终依旧使用的是8bit寄存器模式,但是地址范围0-7FF//设备驱动私有结构体数据定义
struct fram_type {int fram_init_finished;struct i2c_client* fram_client;struct mutex mutex_lock;//定义互斥锁struct class* device_class;		//注册后的设备节点classstruct device* device;	//注册的设备dev_t devno;			//设备号struct cdev cd;int size;				//容量信息
};
static struct fram_type* sg_fram = NULL;	//再iic设备注册后进行初始化,非NULL意味着硬件初始化正常,在probe中进行初始化//注册驱动时传入参数,参数为farm容量信息,默认为2KB,单位字节
static int SIZE = -1;				//通过注册驱动的时候传入参数,如 insmode iic_fram SIZE=4096 实现容量设置
module_param(SIZE, int, S_IRUSR);	//S_IRUSR在include/linux/stat.h/*************************************************************************************************************************
*函数        	:	int fram_read_data(struct i2c_client* client, u16 addr, u16 ByteCount, u8* pData, bool isKernel)
*功能        	:	iic驱动读取寄存器数据
*参数        	:	client:句柄;addr:寄存器地址;ByteCount:要读取的数据数量;pData:数据缓冲区;isKernel:是否是内核读取,如果是内核读取将不需要进行内核数据与用户空间数据转换
*返回        	:	<0 错误,其它:读取的字节数
*依赖			: 	底层宏定义
*作者				:	cp1300@139.com
*时间     		:	2024-03-04
*最后修改时间		:	2024-03-04
*说明        	:	
*************************************************************************************************************************/
int fram_read_data(struct i2c_client* client, u16 addr, u16 ByteCount, u8 __user* pData, bool isKernel)
{int ret;u8 addr_buff[1];u8* pkbuff;struct i2c_msg msgs[] = {//写命令{.addr = client->addr | ((addr >> 8) & 0x07),.flags = 0,.len = 1,				//数据长度为1字节的地址.buf = addr_buff,		//寄存器地址},//读取数据{.addr = client->addr | ((addr >> 8) & 0x07),.flags = I2C_M_RD,.len = ByteCount,		//数据长度为n字节的数据.buf = NULL,			//buf-等会设置}};//dev_info(&client->dev, "i2c_smbus_read_16bit_i2c_block_data addr=%d ByteCount=%d client->addr=%d\r\n", addr, ByteCount, client->addr);//申请内核内存,准备读取数据pkbuff = (u8*)kmalloc(ByteCount, GFP_KERNEL | GFP_DMA | __GFP_ZERO);	//申请内存-iic会用到DMA,内存要连续if (pkbuff == NULL){printk("fram read out of memory! kmalloc(%dB)\r\n", ByteCount);return -1;}msgs[1].buf = pkbuff;	//读取的数据存放到缓冲区//准备寄存器地址数据addr_buff[0] = addr & 0xFF;					//地址低位ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));	//写入命令,读取数据if (ret <= 0){dev_err(&client->dev, "i2c_transfer read addr=%d ByteCount=%d failed!\r\n", addr, ByteCount);ret = -1;}else{if (isKernel == true) //当前处于内核空间{memcpy(pData, pkbuff, ByteCount);ret = ByteCount;	//返回数据长度}else{ret = copy_to_user(pData, pkbuff, ByteCount);  //读取成功,将数据拷贝到用户空间if (ret != 0){printk("fram copy_to_user error(%dB)\r\n", ret);ret = -1;}else{ret = ByteCount;	//返回数据长度}}}kfree(pkbuff);	//释放申请的内存return ret;
}/*************************************************************************************************************************
*函数        	:	int fram_write_data(struct i2c_client* client, u16 addr, u16 ByteCount, u8* pData)
*功能        	:	iic驱动写入寄存器数据
*参数        	:	client:句柄;addr:寄存器地址;ByteCount:要读取的数据数量;pData:数据缓冲区
*返回        	:	<0 错误,其它:读取的字节数
*依赖			: 	底层宏定义
*作者				:	cp1300@139.com
*时间     		:	2024-03-04
*最后修改时间		:	2024-03-04
*说明        	:	
*************************************************************************************************************************/
int fram_write_data(struct i2c_client* client, u16 addr, u16 ByteCount, const u8 __user* pData)
{int ret;u8* buf;struct i2c_msg msgs[] = {//写数据命令{.addr = client->addr | ((addr >> 8) & 0x07),.flags = 0,.len = ByteCount + 1,	//数据长度为2字节的地址,n字节的数据//.buf = &reg,			//buf等会赋值,需要申请内存}};//dev_info(&client->dev, "i2c_smbus_write_16bit_i2c_block_data write addr=%d ByteCount=%d\r\n", addr, ByteCount);buf = (u8*)kmalloc(ByteCount + 1, GFP_KERNEL | GFP_DMA | __GFP_ZERO);	//申请内存-iic会用到DMA,内存要连续if (buf == NULL){dev_err(&client->dev, "i2c_smbus_write_16bit_i2c_block_data out of memory! kmalloc(%dB)\r\n", ByteCount + 1);return -1;}msgs[0].buf = buf;						//记录申请的内存//准备数据buf[0] = addr & 0xFF;					//地址低位ret = copy_from_user(&buf[1], pData, ByteCount);	//用户空间数据拷贝到内核空间if (ret != 0) {printk("fram copy_from_user error(%dB)\r\n", ret);ret = -EFAULT;}else{ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));	//写入数据if (ret == ARRAY_SIZE(msgs)){ret = ByteCount;	//返回写入的数据长度}else{dev_err(&client->dev, "i2c_transfer write addr=%d ByteCount=%d failed!\r\n", addr, ByteCount);ret = -1;}}kfree(buf);	//释放申请的内存return ret;
}/*************************************************************************************************************************
*函数        	:	int fram_Init(struct i2c_client* client)
*功能        	:	fram初始化
*参数        	:	client:iic句柄
*返回        	:	0:初始化成功;其它:初始化失败
*依赖			: 	底层宏定义
*作者				:	cp1300@139.com
*时间     		:	2024-03-03
*最后修改时间		:	2024-03-03
*说明        	:
*************************************************************************************************************************/
int fram_Init(struct i2c_client* client)
{u8 i;struct fram_type* fram = i2c_get_clientdata(client);	//获取私有数据int ret;u8 temp;if (fram == NULL){dev_err(&client->dev, "i2c_get_clientdata(client) null\r\n");return -1;}//读取地址0,只要能读取到就认为初始化成功for (i = 0; i < 3; i++){ret = fram_read_data(client, 0, 1, &temp, true);if (ret < 0){dev_err(&client->dev, "Failed to read 0x00\r\n");msleep(5);}else{dev_info(&client->dev, "addr 0x00:0x%X\r\n", temp);break;}}if (ret < 0) return -1;return 0;
}//===========================================================================================
//标准文件接口相关
/** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int device_open(struct inode* inode, struct file* filp)
{if (sg_fram == NULL){printk("fram not initialized\r\n");return -1;}//dev_info(&sg_fram->fram_client->dev, "device_open\n");return 0;
}/** @description		: 从设备读取数据* @param - filp 	: 要打开的设备文件(文件描述符)* @param - buf 	: 返回给用户空间的数据缓冲区* @param - cnt 	: 要读取的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 读取的字节数,如果为负值,表示读取失败*/
static ssize_t device_read(struct file* filp, char __user* buf, size_t cnt, loff_t* offt)
{u32 offset = (u32)*offt;int ret;if (sg_fram == NULL){printk("fram not initialized\r\n");return -1;}if (buf == NULL){printk("buf is NULL!\r\n");return -EFAULT;}if (cnt == 0 || cnt > sg_fram->size || (cnt + offset) > sg_fram->size){printk("fram Read out of range cnt=%d offt=%d(max size:%dB)\r\n", cnt, offset, sg_fram->size);return 0;}//读取数据mutex_lock(&sg_fram->mutex_lock);	//阻塞式上互斥锁,抢不到就一直阻塞ret = fram_read_data(sg_fram->fram_client, (u16)offset, (u16)cnt, buf, false);mutex_unlock(&sg_fram->mutex_lock);	//解锁return ret;
}/** @description		: 向设备写数据* @param - filp 	: 设备文件,表示打开的文件描述符* @param - buf 	: 要写给设备写入的数据* @param - cnt 	: 要写入的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 写入的字节数,如果为负值,表示写入失败*/
static ssize_t device_write(struct file* filp, const char __user* buf, size_t cnt, loff_t* offt)
{u32 offset = (u32)*offt;int ret;if (sg_fram == NULL){printk("fram not initialized\r\n");return -1;}if (buf == NULL){printk("buf is NULL!\r\n");return -EFAULT;}if (cnt == 0 || cnt > sg_fram->size || (cnt + offset) > sg_fram->size){printk("fram write out of range cnt=%d offt=%d(max size:%dB)\r\n", cnt, offset, sg_fram->size);return 0;}mutex_lock(&sg_fram->mutex_lock);	//阻塞式上互斥锁,抢不到就一直阻塞ret = fram_write_data(sg_fram->fram_client, (u16)offset, (u16)cnt, buf);mutex_unlock(&sg_fram->mutex_lock);	//解锁return ret;
}/** @description		: 设置文件读写偏移* @param - filp 	: 设备文件,表示打开的文件描述符* @param - off 	: 读写偏移* @param - whence 	: 光标参考位置* @return 			: 当前文件的纸质位置,如果为负值,表示写入失败*/
static loff_t device_llseek(struct file* filp, loff_t off, int whence)
{//struct scull_dev* dev = filp->private_data;loff_t newpos;//printk("device_llseek off=%u whence=%d\r\n", (u32)off, whence);switch (whence){case 0: //SEEK_SET 从开始的偏移{newpos = off;if (newpos >= sg_fram->size) newpos = sg_fram->size - 1;	//防止超出范围}break;case 1:	//SEEK_CUR 在当前位置加上偏移{newpos = filp->f_pos + off;if (newpos >= sg_fram->size) newpos = sg_fram->size - 1;	//防止超出范围}break;case 2: //SEEK_END 偏移位置文件结尾 之外{newpos = sg_fram->size - 1;	//不允许超出文件}break;default: return -EINVAL;}if (newpos < 0) return -EINVAL;filp->f_pos = newpos;return newpos;
}/** @description		: 关闭/释放设备* @param - filp 	: 要关闭的设备文件(文件描述符)* @return 			: 0 成功;其他 失败*/
static int device_release(struct inode* inode, struct file* filp)
{//printk("fram_release\r\n");return 0;
}//申请i2c资源,顺便进行初始化
static int device_probe(struct i2c_client *client,const struct i2c_device_id *id)
{int ret;struct fram_type* fram;dev_info(&client->dev, "fram_probe\n");//判断iic适配器是否正常if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){return -ENODEV;}//申请内核内存资源fram = devm_kzalloc(&client->dev, sizeof(struct fram_type), GFP_KERNEL);if (!fram){return -ENOMEM;}fram->fram_init_finished = 0;			//初始化未完成fram->fram_client = client;			//记录i2c接口指针fram->device_class = NULL;						//设备节点无效fram->device = NULL;						//设备节点无效//需要提前设置,否则在 fram_Init 中需要调用 i2c_get_clientdata () 将返回空i2c_set_clientdata(client, fram);		// 将fram作为i2c次设备的私有数据区中的设备驱动私有数据if (SIZE > 0){dev_info(&client->dev, "SIZE=%dB\n", SIZE &0xFFFF);}else{SIZE = 2048;	//默认为2KB}if (SIZE < 128) SIZE = 128;if (SIZE > 0x7FF) SIZE = 0x7FF;	//最大大小限制为0x7FFfram->size = SIZE;		//初始化FRAM大小dev_info(&client->dev, "FRAM SIZE:%dB\n", fram->size);//开始芯片硬件探测与初始化ret = fram_Init(client);	//初始化并设置初值,也可以通过注册驱动的时候传入参数实现初始化if (ret < 0){dev_err(&client->dev, "invalid init fram\n");return -1;}mutex_init(&fram->mutex_lock);//初始化互斥锁sg_fram = fram;						//记录全局设备数据,设备硬件初始化完成了fram->fram_init_finished = 1;			//初始化完成dev_info(&client->dev, "fram probe succeeded\n");return 0;
}//移除iic驱动
static int device_remove(struct i2c_client* client)
{struct fram_type* fram = i2c_get_clientdata(client);	//获取私有数据dev_info(&client->dev, "fram_remove\n");//释放互斥锁信号if (fram != NULL && fram->fram_init_finished){mutex_destroy(&fram->mutex_lock);}return 0;
}//===========================================================================================
//iic接口相关
static const struct of_device_id fram_match_table[] = {{.compatible = DTS_COMPATIBLE,},{},
};
MODULE_DEVICE_TABLE(of, fram_match_table);static const struct i2c_device_id fram_id[] = {{ DTS_COMPATIBLE, 0 },{},
};
MODULE_DEVICE_TABLE(i2c, fram_id);static struct i2c_driver fram_driver = {.driver = {.name		= DEVICE_NAME,.owner		= THIS_MODULE,.of_match_table = fram_match_table,},.probe    = device_probe,	//注册IIC.remove = device_remove,.id_table	= fram_id,};//module_i2c_driver(fram_driver);	//需要使用 i2c_add_driver 在 init中进行注册iic适配器,不能直接使用宏//设备驱动操作相关接口结构体
static struct file_operations sg_device_opera_fops = {.owner = THIS_MODULE,.open = device_open,		//打开驱动文件接口.read = device_read,		//读取接口.write = device_write,		//写文件接口.release = device_release,	//释放文件接口.llseek = device_llseek,	//设置文件偏移//.ioctl = device_ioctl,		//参数设置接口
};//驱动接口-驱动入口函数-初始化与申请资源
static int __init device_init(void)
{int retvalue;//printk("device_module_init\n");retvalue = i2c_add_driver(&fram_driver);	//添加iic驱动if (retvalue) {printk("%s i2c_add_driver failed! %d\n", __func__, retvalue);return -ENODEV;}if (sg_fram == NULL) //设备硬件初始化失败{printk("initialization failed!\r\n");return -EIO;}//注册字符设备驱动retvalue = alloc_chrdev_region(&sg_fram->devno, 0, 1, DEVICE_NAME);	//自动申请设备号,从0开始,申请1个if (retvalue < 0) {pr_err("alloc_chrdev_region failed!(%d)", retvalue);i2c_del_driver(&fram_driver);				//移除iic设备return retvalue;}printk("MAJOR is %d\n", MAJOR(sg_fram->devno));printk("MINOR is %d\n", MINOR(sg_fram->devno));cdev_init(&sg_fram->cd, &sg_device_opera_fops);			//字符驱动结构体初始化retvalue = cdev_add(&sg_fram->cd, sg_fram->devno, 1);	//注册字符设备驱动,数量1if (retvalue < 0) {pr_err("cdev_add failed!(%d)", retvalue);i2c_del_driver(&fram_driver);				//移除iic设备unregister_chrdev_region(sg_fram->devno, 1);	//注销一个范围的设备号return retvalue;}//自动在/dev目录下创建设备节点sg_fram->device_class = class_create(THIS_MODULE, DEVICE_NAME);	创建类if (NULL == sg_fram->device_class){printk(KERN_INFO "create calss failed\n");cdev_del(&sg_fram->cd);unregister_chrdev_region(sg_fram->devno, 1);	//注销一个范围的设备号i2c_del_driver(&fram_driver);				//移除iic设备return -1;}else{sg_fram->device = device_create(sg_fram->device_class, NULL, sg_fram->devno, NULL, DEVICE_NAME);	//创建设备if (NULL == sg_fram->device){printk(KERN_INFO "create device failed\n");cdev_del(&sg_fram->cd);unregister_chrdev_region(sg_fram->devno, 1);class_destroy(sg_fram->device_class);i2c_del_driver(&fram_driver);				//移除iic设备return -1;}}printk("%s succeeded\n", DEVICE_NAME);return 0;
}//驱动接口-驱动出口函数-注销资源
static void __exit device_exit(void)
{printk("device_module_exit\n");i2c_del_driver(&fram_driver);				//移除iic设备cdev_del(&sg_fram->cd);						//注销字符设备驱动unregister_chrdev_region(sg_fram->devno, 1);	//注销设备号device_del(sg_fram->device);printk(KERN_INFO "delete device /dev/my_char_dev \n");class_destroy(sg_fram->device_class);printk(KERN_INFO "delete device /sys/class/my_char_dev \n");}module_init(device_init);
module_exit(device_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("cp1300@139.com");

测试代码


void fram_test(void)
{int fd;u8 buff[512];int len;int i;fd = open("/dev/iic_fram", O_RDWR);if(fd < 0){printf("open iic_frame error:%d\r\n", fd);return ;}lseek(fd, 0, SEEK_SET);len = read(fd, buff, 32);if(len <= 0){printf("read iic_frame error:%d\r\n", fd);return ;}for(i = 0;i < len;i ++){printf("0x%02X \t", buff[i]);}printf("\r\n");//lseek(fd, 0, SEEK_SET);/*for(i = 0;i < len;i ++){buff[i] = i+0xF0;}len = write(fd, buff, len);if(len <= 0){printf("write iic_frame error:%d\r\n", fd);return ;}*/close(fd);}

读取32字节全部是0

试试33字节就正常了

 

仔细看底层驱动的打印信息

 sunxi-i2c sunxi-i2c2: drv-mode: dma read data end

读取32字节的时候,这个打印都结束了才提示DMA读取完成,很有可能就是因为数据都没读取完成,但是底层已经返回了,原因未知,珍爱生命,远离linux驱动,凑合着用吧(⊙o⊙)

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

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

相关文章

leetcode - 2095. Delete the Middle Node of a Linked List

Description You are given the head of a linked list. Delete the middle node, and return the head of the modified linked list. The middle node of a linked list of size n is the ⌊n / 2⌋th node from the start using 0-based indexing, where ⌊x⌋ denotes th…

python中的类与对象(3)

目录 一. 类的多继承 二. 类的封装 三. 类的多态 四. 类与对象综合练习&#xff1a;校园管理系统 一. 类的多继承 在&#xff08;2&#xff09;第四节中我们介绍了什么是类的继承&#xff0c;在子类的括号里面写入要继承的父类名。上一节我们只在括号内写了一个父类名&…

【机器人最短路径规划问题(栅格地图)】基于模拟退火算法求解

代码获取方式&#xff1a;QQ&#xff1a;491052175 或者 私聊博主获取 基于模拟退火算法求解机器人最短路径规划问题&#xff08;栅格地图&#xff09;的仿真结果 仿真结果&#xff1a; 初始解的路径规划图 收敛曲线&#xff1a; 模拟退火算法求解的路径规划图 结论&#xff…

Ubuntu20安装zabbix-agent2,对接zabbix 6.4

在Ubuntu 20.04 LTS上安装Zabbix Agent 2并与Zabbix Server 6.4对接&#xff0c;请按照以下步骤操作&#xff1a; 更新系统&#xff1a; sudo apt update sudo apt upgrade 添加Zabbix官方仓库&#xff1a; 首先&#xff0c;需要将Zabbix的官方存储库添加到你的系统中以获取Za…

【了解SpringCloud Gateway微服务网关】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要知识图谱什么是SpringCloudGateway功能特征应用场景核心概念配置文件工作原理路由谓词工厂&#xff08;内置的&#xff09;[After 路由谓词工厂](https://docs.spring.io/spring-cloud-gateway/docs/current/refere…

Mysql运维篇(七) 部署MHA--完结

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 一、MHA软件构成 Manager工具包主要包括以下几个工具&#xff1a; masterha_manger 启…

【C++】多态深入分析

目录 一&#xff0c;多态的原理 1&#xff0c;虚函数表与虚函数表指针 2&#xff0c;原理调用 3&#xff0c;动态绑定与静态绑定 二&#xff0c;抽象类 三&#xff0c;单继承和多继承关系的虚函数表 1&#xff0c;单继承中的虚函数表 2&#xff0c;多继承中的虚函数表 …

内网搭建mysql8.0并搭建主从复制详细教程!!!

一、安装mysql 1.1 mysql下载链接&#xff1a; https://downloads.mysql.com/archives/community/ 1.2 解压包并创建相应的数据目录 tar -xvf mysql-8.2.0-linux-glibc2.28-x86_64.tar.xz -C /usr/local cd /usr/local/ mv mysql-8.2.0-linux-glibc2.28-x86_64/ mysql mkdir…

Python绘图-9饼图(上)

饼图&#xff08;Pie Chart&#xff09;是一种用于表示数据分类和相对大小的可视化图形。在饼图中&#xff0c;整个圆形代表数据的总和&#xff0c;而圆形内的各个扇形则代表不同的分类或类别&#xff0c;扇形的面积大小表示该类别在整体中所占的比例。饼图通常用于展示数据的分…

Window部署Jaeger

参考&#xff1a;windows安装使用jaeger链路追踪_windows安装jaeger-CSDN博客 下载&#xff1a;Releases jaegertracing/jaeger GitHub Jaeger – Download Jaeger 目录 1、安装nssm 2、安装运行 elasticsearch 3、安装运行 3.1部署JaegerAgent 3.2部署JaegerCollec…

【全志D1-H 哪吒开发板】Debian系统安装调教和点灯指南

全志D1-H开发板【哪吒】使用Deabian系统入门 特别说明&#xff1a; 因为涉及到操作较多&#xff0c;博文可能会导致格式丢失 其中内容&#xff0c;会根据后续使用做优化调整 目录&#xff1a; 参考资料固件烧录启动调教点灯问题 〇、参考资料 官方资料 开发板-D1开发板【…

C++:函数模板整理

函数模板: 找到函数相同的实现思路&#xff0c;区别于函数的参数类型。 使用函数模板使得函数可容纳不同类型的参数实现函数功能&#xff0c;而不是当类型不同时便编译大量类型不同的函数&#xff0c;产生大量重复代码和内存占用 函数模板格式&#xff1a; template<typ…

[Vulnhub]靶场 Red

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 …

ARM64汇编02 - 寄存器与指令基本格式

最近的文章可能会有较多修改&#xff0c;请关注博客哦 异常级别 ARMv8处理器支持4种异常等级&#xff08;Exception Level&#xff0c;EL&#xff09;。 EL0 为非特权模式&#xff0c;用于运行应用程序&#xff0c;其他资源访问受限&#xff0c;权限不够。 EL1 为特权模式&…

【王道操作系统】ch1计算机系统概述-06虚拟机

文章目录 【王道操作系统】ch1计算机系统概述-06虚拟机01传统计算机02虚拟机的基本概念&#xff08;1&#xff09;第一类虚拟机管理程序&#xff08;2&#xff09; 第二类虚拟机管理程序&#xff08;3&#xff09; 两类虚拟机管理程序的对比 【王道操作系统】ch1计算机系统概述…

效果炸裂、刷爆各大视频网站的EMO到底是怎么做到的?

文章链接&#xff1a;https://arxiv.org/abs/2402.17485 今天分享的工作是刷爆各大视频平台的EMO的背后的工作原理。提出的初衷是着手解决增强发言者的头部特写视频生成中的现实感和表现力的挑战&#xff0c;重点关注音频提示与面部动作之间的动态和微妙关系。传统技术具有局限…

【HarmonyOS】鸿蒙开发之Stage模型-UIAbility的启动模式——第4.4章

UIAbi lity的启动模式简介 一共有四种:singleton,standard,specified,multion。在项目目录的:src/main/module.json5。默认开启模式为singleton(单例模式)。如下图 singleton&#xff08;单实例模式&#xff09;启动模式 每个UIAbility只存在唯一实例。任务列表中只会存在一…

测试管理进阶 | 量力而行:避免成为替罪羊

职场中,我们常常面临是否帮助他人的抉择。尽管善良是美德,但过度的好人卡可能会给自己带来麻烦。本文将探讨如何在职场中量力而行,避免成为替罪羊,以及如何保持高效和合理的职责划分。 我们在工作中常常会遇到一些需要帮助他人的情况,作为团队的一员,我们希望能够积极地协…

六、继承(一)

1 继承的引入 以往我们想分别实现描述学生、老师的类&#xff0c;可能会这样子做&#xff1a; class Student {string _name;string _number;int _tel;int id;string _address;int _age; }; class Teacher {string _name;int _level;int _tel;int id;string _address;int _ag…

【归并排序】 详细解析 动图演示 逐图解析 洛谷P1177【模板】排序 sort【快速排序】

文章目录 归并排序1.归并排序的复杂度分析2.细节解释3.归并排序动图演示3(1) 我们的拆分过程如下↓ 4.code↓ 洛谷P1177【模板】排序数据规模与约定code&#xff08;归并排序&#xff09;↓code&#xff08;sort排序【快速排序】&#xff09; 完结撒花(&#xffe3;▽&#xff…