【区块链】C语言编程实现三叉Merkle树

目录

      • 1. Merkle树简介
      • 2. 构建Merkle树
      • 3. 生成SPV路径
      • 4. 验证SPV路径
      • 5. 三叉Merkle树创建、SPV生成及验证总程序
      • 6. 程序运行结果

1. Merkle树简介

  如上图所示,Merkle 树的叶子节点为交易序列,对每一笔交易进行 Hash(SHA 256算法) 之后,然后对得到的 Hash 节点进行拼接再次进行 Hash 运算,如此递归直到递归至根节点。

  如上图所示 Merkle 树的优点在于可快速验证某个区块是否存在指定交易,如要验证 H k H_k Hk是否存在于该区块中,只要将 H L 、 H I J 、 H M N O P 、 H A B C D E F G H_L、H_{IJ}、H_{MNOP}、H_{ABCDEFG} HLHIJHMNOPHABCDEFG节点得到,然后从下到上不断 Hash,最后与根节点的 Hash 值进行比较,就可以快速判断交易是否在该区块中。

2. 构建Merkle树

  1. 头文件及Merkle节点类型
# include <iostream>
# include <functional>
# include<string>
using namespace std;
typedef struct MerkleNode {
	MerkleNode* Left;
	MerkleNode* Middle;
	MerkleNode* Right;
	MerkleNode* parent;
	size_t data;
};
struct MerkleTree {
	MerkleNode* RootNode;
}root;
  1. 创建三叉merkle所需函数
/* 创建merkle树 */
MerkleNode* queue[100000];
int front = 0, rear = 0, new_rear = 0;
int n;
// 字符串加密
size_t encrypt(string strInput)
{
	hash<string> Hash;
	size_t hashVal = Hash(strInput);
	return hashVal;
}

// size_t转换为字符串
string type_convert(size_t input)
{
	string output = to_string(input);
	return output;
}

// 字符串拼接
string concat(string one, string two,string three)
{
	string output = one + two + three;
	return output;
}

// 初始化叶子节点
void init_leaf_node(MerkleNode*& node,size_t data)
{
	node = (MerkleNode*)malloc(sizeof(MerkleNode));
	node->data = encrypt(type_convert(data));
	node->Left = NULL;
	node->Middle = NULL;
	node->Right = NULL;
}

// 对最后一个节点进行拷贝
MerkleNode* copy_node(MerkleNode* node)
{
	MerkleNode* new_node = (MerkleNode*)malloc(sizeof(MerkleNode));
	new_node->Left = NULL;
	new_node->Middle = NULL;
	new_node->Right = NULL;
	new_node->data = node->data;
	return new_node;
}

// 初始化分枝节点
void init_branch_node(MerkleNode* Left, MerkleNode* Middle, MerkleNode* Right)
{
	MerkleNode* node = (MerkleNode*)malloc(sizeof(MerkleNode));
	node->Left = Left;
	node->Middle = Middle;
	node->Right = Right;
	Left->parent = node;
	Middle->parent = node;
	Right->parent = node;
	node->data = encrypt(concat(type_convert(Left->data), type_convert(Middle->data), type_convert(Right->data)));
	queue[new_rear++] = node;
}

// 根据队列中的节点生成其父节点
void create_new_layer()
{
	// 补足剩余节点
	int remainder = rear % 3;
	int res = 0;
	if (remainder != 0)
	{
		res = 3 - remainder;
		MerkleNode* node = queue[rear - 1];
		for (int i = 0; i < res; i++)
		{
			queue[rear++] = copy_node(node);
		}
	}

	int loop = (rear + res) / 3;


	for (int i = 0; i < loop; i++)
	{
		MerkleNode* Left = queue[front++];
		MerkleNode* Middle = queue[front++];
		MerkleNode* Right = queue[front++];
		init_branch_node(Left, Middle, Right);
	}
	rear = new_rear;
	new_rear = 0;
	front = 0;
}

// 创建merkle树
void create_merkle_tree(MerkleTree*& root)
{
	while (rear != 1)
	{
		create_new_layer();
	}
	root = (MerkleTree*)malloc(sizeof(MerkleTree));
	root->RootNode = queue[rear-1];
	root->RootNode->parent = NULL;
}

// 输入数据
void init_data()
{
	cout << "请输入交易序列个数:";
	cin >> n;
	cout << "请输入每一笔交易(中间以空格隔开):";
	for (int i = 0; i < n; i++)
	{
		MerkleNode* node;
		size_t data;
		cin >> data;
		init_leaf_node(node, data);
		queue[rear++] = node;
	}
}

3. 生成SPV路径

SPV路径生成代码:

/*  生成SPV路径  */ 
size_t spv_queue[100010];
MerkleNode *sequence[100010];
int seq_front = 0, seq_rear = 0;
int spv_front = 0,spv_rear = 0;
int spv_flag[100010], flag_idx = 0;

void init_all_node_sequence(MerkleNode* node)
{
	sequence[++seq_rear] = node;
	while (seq_front != seq_rear)
	{
		MerkleNode* new_node = sequence[++seq_front];
		if (new_node->Left != NULL)
		{
			sequence[++seq_rear] = new_node->Left;
		}
		if (new_node->Middle != NULL)
		{
			sequence[++seq_rear] = new_node->Middle;
		}
		if (new_node->Right != NULL)
		{
			sequence[++seq_rear] = new_node->Right;
		}
	}
	seq_front = 0;
}

void init_spv_path(int idx,MerkleTree *root)
{
	MerkleNode* head_node = root->RootNode;
	init_all_node_sequence(head_node);

	int cnt;
	if (n % 3 == 0)
	{
		cnt = 3;
	}
	else if((n + 1) % 3 == 0) {
		cnt = n + 1;
	}
	else {
		cnt = n + 2;
	}
	int seq_idx = seq_rear - (cnt - idx);
	cout << "该交易的哈希值为:" << sequence[seq_idx]->data<< endl;

	MerkleNode* node = sequence[seq_idx];

	while (node->parent)
	{
		MerkleNode* parent = node->parent;
		if (node == parent->Left)
		{
			spv_flag[flag_idx++] = 0;
			spv_queue[++spv_rear] = parent->Middle->data;
			spv_queue[++spv_rear] = parent->Right->data;
		}
		else if (node == parent->Middle)
		{
			spv_flag[flag_idx++] = 1;
			spv_queue[++spv_rear] = parent->Left->data;
			spv_queue[++spv_rear] = parent->Right->data;
		}
		else {
			spv_flag[flag_idx++] = 2;
			spv_queue[++spv_rear] = parent->Left->data;
			spv_queue[++spv_rear] = parent->Middle->data;
		}
		node = node->parent;
	}

}

void print_spv_path(int idx)
{
	cout << "交易序列号" << idx << "的SPV路径为:";
	while (spv_front != spv_rear)
	{
		cout << spv_queue[++spv_front] << " ";
	}
	cout << endl;
	spv_front = 0;
	cout << "生成交易在其兄弟节点中的位置序列为(0为Left,1为Middle,2为Right):";
	for (int i = 0; i < flag_idx; i++)
	{
		cout << spv_flag[i] << " ";
	}
}

4. 验证SPV路径

SPV路径验证代码:

/* 验证SPV路径 */
size_t root_hash, spv_path[1010], hash_val;
int spv_length;
void judge_spv_path()
{
	cout << "请输入merkle树根节点哈希值:";
	cin >> root_hash;

	cout << "请输入SPV路径长度:";
	cin >> spv_length;
	cout << "请输入SPV路径(中间用空格隔开):";
	for (int i = 0; i < spv_length; i++)
	{
		cin >> spv_path[i];
	}

	int pos_cnt, pos_sqe[100010];
	cout << "请输入交易位置序列个数:";
	cin >> pos_cnt;
	cout << "请输入交易位置序列(中间用空格隔开):";
	for (int i = 0; i < pos_cnt; i++)
	{
		cin >> pos_sqe[i];
	}

	cout << "请输入要查询的交易哈希值:";
	cin >> hash_val;

	int path_idx = 0, pos_idx = 0;
	while (path_idx != spv_length)
	{
		size_t one = spv_path[path_idx++];
		size_t two = spv_path[path_idx++];
		if (pos_sqe[pos_idx] == 0)
		{
			hash_val = encrypt(concat(type_convert(hash_val), type_convert(one), type_convert(two)));
		}
		else if (pos_sqe[pos_idx] == 1)
		{
			hash_val = encrypt(concat(type_convert(one), type_convert(hash_val), type_convert(two)));
		}
		else {
			hash_val = encrypt(concat(type_convert(one), type_convert(two), type_convert(hash_val)));
		}
		pos_idx++;
	}
	cout << "SPV计算得到的哈希值为:" << hash_val <<endl;
	cout << "头节点哈希值为:" << root_hash << endl;
	if (hash_val == root_hash)
	{
		cout << "SPV路径验证成功!!!" << endl << endl << endl;
	}
}

5. 三叉Merkle树创建、SPV生成及验证总程序

# define _CRT_SECURE_NO_DEPRECATE
# include <iostream>
# include <functional>
# include<string>
using namespace std;
typedef struct MerkleNode {
	MerkleNode* Left;
	MerkleNode* Middle;
	MerkleNode* Right;
	MerkleNode* parent;
	size_t data;
};
struct MerkleTree {
	MerkleNode* RootNode;
}root;

/* 创建merkle树 */
MerkleNode* queue[100000];
int front = 0, rear = 0, new_rear = 0;
int n;
// 字符串加密
size_t encrypt(string strInput)
{
	hash<string> Hash;
	size_t hashVal = Hash(strInput);
	return hashVal;
}

// size_t转换为字符串
string type_convert(size_t input)
{
	string output = to_string(input);
	return output;
}

// 字符串拼接
string concat(string one, string two,string three)
{
	string output = one + two + three;
	return output;
}

// 初始化叶子节点
void init_leaf_node(MerkleNode*& node,size_t data)
{
	node = (MerkleNode*)malloc(sizeof(MerkleNode));
	node->data = encrypt(type_convert(data));
	node->Left = NULL;
	node->Middle = NULL;
	node->Right = NULL;
}

// 对最后一个节点进行拷贝
MerkleNode* copy_node(MerkleNode* node)
{
	MerkleNode* new_node = (MerkleNode*)malloc(sizeof(MerkleNode));
	new_node->Left = NULL;
	new_node->Middle = NULL;
	new_node->Right = NULL;
	new_node->data = node->data;
	return new_node;
}

// 初始化分枝节点
void init_branch_node(MerkleNode* Left, MerkleNode* Middle, MerkleNode* Right)
{
	MerkleNode* node = (MerkleNode*)malloc(sizeof(MerkleNode));
	node->Left = Left;
	node->Middle = Middle;
	node->Right = Right;
	Left->parent = node;
	Middle->parent = node;
	Right->parent = node;
	node->data = encrypt(concat(type_convert(Left->data), type_convert(Middle->data), type_convert(Right->data)));
	queue[new_rear++] = node;
}

// 根据队列中的节点生成其父节点
void create_new_layer()
{
	// 补足剩余节点
	int remainder = rear % 3;
	int res = 0;
	if (remainder != 0)
	{
		res = 3 - remainder;
		MerkleNode* node = queue[rear - 1];
		for (int i = 0; i < res; i++)
		{
			queue[rear++] = copy_node(node);
		}
	}

	int loop = (rear + res) / 3;


	for (int i = 0; i < loop; i++)
	{
		MerkleNode* Left = queue[front++];
		MerkleNode* Middle = queue[front++];
		MerkleNode* Right = queue[front++];
		init_branch_node(Left, Middle, Right);
	}
	rear = new_rear;
	new_rear = 0;
	front = 0;
}

// 创建merkle树
void create_merkle_tree(MerkleTree*& root)
{
	while (rear != 1)
	{
		create_new_layer();
	}
	root = (MerkleTree*)malloc(sizeof(MerkleTree));
	root->RootNode = queue[rear-1];
	root->RootNode->parent = NULL;
}

// 输入数据
void init_data()
{
	cout << "请输入交易序列个数:";
	cin >> n;
	cout << "请输入每一笔交易(中间以空格隔开):";
	for (int i = 0; i < n; i++)
	{
		MerkleNode* node;
		size_t data;
		cin >> data;
		init_leaf_node(node, data);
		queue[rear++] = node;
	}
}



/*  生成SPV路径  */ 
size_t spv_queue[100010];
MerkleNode *sequence[100010];
int seq_front = 0, seq_rear = 0;
int spv_front = 0,spv_rear = 0;
int spv_flag[100010], flag_idx = 0;

void init_all_node_sequence(MerkleNode* node)
{
	sequence[++seq_rear] = node;
	while (seq_front != seq_rear)
	{
		MerkleNode* new_node = sequence[++seq_front];
		if (new_node->Left != NULL)
		{
			sequence[++seq_rear] = new_node->Left;
		}
		if (new_node->Middle != NULL)
		{
			sequence[++seq_rear] = new_node->Middle;
		}
		if (new_node->Right != NULL)
		{
			sequence[++seq_rear] = new_node->Right;
		}
	}
	seq_front = 0;
}

void init_spv_path(int idx,MerkleTree *root)
{
	MerkleNode* head_node = root->RootNode;
	init_all_node_sequence(head_node);

	int cnt;
	if (n % 3 == 0)
	{
		cnt = 3;
	}
	else if((n + 1) % 3 == 0) {
		cnt = n + 1;
	}
	else {
		cnt = n + 2;
	}
	int seq_idx = seq_rear - (cnt - idx);
	cout << "该交易的哈希值为:" << sequence[seq_idx]->data<< endl;

	MerkleNode* node = sequence[seq_idx];

	while (node->parent)
	{
		MerkleNode* parent = node->parent;
		if (node == parent->Left)
		{
			spv_flag[flag_idx++] = 0;
			spv_queue[++spv_rear] = parent->Middle->data;
			spv_queue[++spv_rear] = parent->Right->data;
		}
		else if (node == parent->Middle)
		{
			spv_flag[flag_idx++] = 1;
			spv_queue[++spv_rear] = parent->Left->data;
			spv_queue[++spv_rear] = parent->Right->data;
		}
		else {
			spv_flag[flag_idx++] = 2;
			spv_queue[++spv_rear] = parent->Left->data;
			spv_queue[++spv_rear] = parent->Middle->data;
		}
		node = node->parent;
	}

}

void print_spv_path(int idx)
{
	cout << "交易序列号" << idx << "的SPV路径为:";
	while (spv_front != spv_rear)
	{
		cout << spv_queue[++spv_front] << " ";
	}
	cout << endl;
	spv_front = 0;
	cout << "生成交易在其兄弟节点中的位置序列为(0为Left,1为Middle,2为Right):";
	for (int i = 0; i < flag_idx; i++)
	{
		cout << spv_flag[i] << " ";
	}
}

/* 验证SPV路径 */
size_t root_hash, spv_path[1010], hash_val;
int spv_length;
void judge_spv_path()
{
	cout << "请输入merkle树根节点哈希值:";
	cin >> root_hash;

	cout << "请输入SPV路径长度:";
	cin >> spv_length;
	cout << "请输入SPV路径(中间用空格隔开):";
	for (int i = 0; i < spv_length; i++)
	{
		cin >> spv_path[i];
	}

	int pos_cnt, pos_sqe[100010];
	cout << "请输入交易位置序列个数:";
	cin >> pos_cnt;
	cout << "请输入交易位置序列(中间用空格隔开):";
	for (int i = 0; i < pos_cnt; i++)
	{
		cin >> pos_sqe[i];
	}

	cout << "请输入要查询的交易哈希值:";
	cin >> hash_val;

	int path_idx = 0, pos_idx = 0;
	while (path_idx != spv_length)
	{
		size_t one = spv_path[path_idx++];
		size_t two = spv_path[path_idx++];
		if (pos_sqe[pos_idx] == 0)
		{
			hash_val = encrypt(concat(type_convert(hash_val), type_convert(one), type_convert(two)));
		}
		else if (pos_sqe[pos_idx] == 1)
		{
			hash_val = encrypt(concat(type_convert(one), type_convert(hash_val), type_convert(two)));
		}
		else {
			hash_val = encrypt(concat(type_convert(one), type_convert(two), type_convert(hash_val)));
		}
		pos_idx++;
	}
	cout << "SPV计算得到的哈希值为:" << hash_val <<endl;
	cout << "头节点哈希值为:" << root_hash << endl;
	if (hash_val == root_hash)
	{
		cout << "SPV路径验证成功!!!" << endl << endl << endl;
	}
}



int main()
{

	cout << "================================================  Merkle树构建部分  ===================================================" << endl << endl;
	init_data();	// 数据初始化(输入相关数据)
	// 生成 Merkle 树
	MerkleTree* root;
	create_merkle_tree(root);
	cout << "merkle树根哈希值为:" << root->RootNode->data << endl;
	cout << "merkle树构建完成!" << endl << endl << endl;

	// 生成SPV路径
	cout << "================================================  SPV路径生成部分  ===================================================" << endl << endl;
	int idx;
	cout << "请输入交易序号(i):";
	cin >> idx;
	init_spv_path(idx, root);
	print_spv_path(idx);
	cout << endl << endl << endl;

	// 验证SPV路径
	cout << "================================================  SPV路径验证部分  ===================================================" << endl << endl;
	judge_spv_path();

	return 0;
}

6. 程序运行结果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32F10X开发环境的搭建

一、keil软件安装 找到keil软件包&#xff0c;解压缩&#xff0c;找到keil5安装软件&#xff1a; 鼠标右键选择以管理员权限运行。点击next&#xff0c;直到安装结束。 安装完成后在桌面会出现keil5软件图标&#xff1a; 然后再安装相应的芯片支持包&#xff1a;我们用的是stm…

C语言:文件操作的详解(看完一定有更深刻的理解)

目录 前言 程序文件 文件的打开和关闭 流 标准流 文件的顺序读写 写文件 fputc函数 fputs函数 fprintf函数 读文件 fgetc函数 fgets函数 fscanf函数 printf/fprintf/sprintf scanf/fscanf/sscanf 文件的随机读写 fseek函数 ftell函数 rewind函数 大多数人用…

【数据库管理操作】Mysql 创建学生数据库及对数据表进行修改

MySQL 创建学生成绩数据库 1.创建数据库 create database studentscore;创建完成之后&#xff0c;如果需要使用该数据&#xff0c;使用use命令 use studentscore;创建表前查看当前数据库中包含的表 show tables; 2.创建bclass表 create table bclass( class_id char(8) …

深度学习入门1——Optimization

Methods of optimization Stochastic Gradient Descent (SGD) use mini-batch (32/64/128) to do gradient descent SGD Momentum continue moving in the general direction as the previous iterations Build up “velocity” as a running mean of gradients Rho giv…

全国河流湖库公开数据及应用实践

关于全国河流湖口的数据&#xff0c;通常指的是各条河流流入湖泊或海洋的位置及其相关的水文、地理信息。这类数据包括但不限于以下几个方面&#xff1a; 1. 地理位置&#xff1a;每条河流的出海口或流入湖泊的具体经纬度坐标。 2. 水文特征&#xff1a;如湖口水位、流量、径…

【数据库】表的约束

目录 一、非空约束 二、主键约束 三、外键约束 四、检查约束 五、唯一性约束 一、非空约束 每个字段都要有一个是否为nul值的选择&#xff0c;这就是对数据表中将来的数据提出的约束条件。null(允许空值)&#xff1a;表示数值未确定&#xff0c;并不是数字“0”或字符“…

Avalonia笔记2 -数据集合类控件

学习笔记&#xff1a; 1. DataGrid 笔记1中已经记录&#xff1b; 2. ItemsControl 属性&#xff1a; ItemsSource&#xff1a;数据源 ItemsControl.ItemTemplate&#xff1a;单项数据模板&#xff0c;内部使用<DataTemplate> 示例&#xff1a; <ItemsContr…

学习使用xbox手柄控制小乌龟节点移动

使用xbox手柄控制小乌龟&#xff0c;首先要下载joy功能包&#xff0c;发布sensor_msgs话题也就是手柄和ros通信的话题。 下载的步骤就根据官方文档即可 joy/Tutorials/ConfiguringALinuxJoystick - ROS Wiki 这里我提供一下具体步骤 第一步 安装joy 首先安装对应系统版本的…

第十四届蓝桥杯(C/C++ 大学B组)

试题 A&#xff1a;日期统计 #include <bits/stdc.h> using namespace std;const int numbers[100] {5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7, 5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5,8, 6, 1, 8, 3, 0, 3, 7, 9, 2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, …

全局UI方法-弹窗一警告弹窗(AlertDialog)

1、描述 显示警告弹窗组件&#xff0c;可设置文本内容与响应回调。 2、属性 名称参数类型参数描述showAlertDialogParamWithConfirm | AlertDialogParamWithButtons定义并显示AlertDialog组件。 2.1、AlertDialogParamWithConfirm对象说明&#xff1a; 参数名称参数类型必填…

已注册的商标别忘了续展,新注可能难下证!

近期普推知产老杨遇到好几个网友和看过多个案例&#xff0c;以前商标名称可以申请注册下来&#xff0c;但是换字体注册不下来了&#xff0c;有的是不想续展想直接换字体申请注册&#xff0c;但是也没有下来。 这些商标名称主要是存在禁止注册或缺显&#xff0c;比如“柳林”以前…

对下载软件/文件进行校验的工具(Checksum and GPG)

前言 之前装软件一直都没有验证安装文件的习惯&#xff0c;信息安全意识不高&#xff0c;碰巧最近没啥事&#xff0c;微微写篇文章记录下校验工具&#xff08;互联网http、https、ftp 服务并没有那么安全&#xff0c;是可以被劫持篡改。老装软件选手了&#xff0c;是该养成个校…

Mac上的Gatekeeper系统跟运行时保护

文章目录 问题&#xff1a;无法打开“xxx.xxx”&#xff0c;因为无法验证开发者。macOS无法验证此App是否包含恶意软件。如何解决&#xff1f; 参考资料门禁运行时保护 问题&#xff1a;无法打开“xxx.xxx”&#xff0c;因为无法验证开发者。macOS无法验证此App是否包含恶意软件…

瑞_23种设计模式_观察者模式

文章目录 1 观察者模式&#xff08;Observer Pattern&#xff09;1.1 介绍1.2 概述1.3 观察者模式的结构1.4 观察者模式的优缺点1.5 观察者模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 JDK中提供的观察者模式实现 ★4.1 Observable类4.2 Obse…

修复ubuntu引导

一、制作ubuntu启动U盘 进入启动盘后&#xff0c;点击Try ubuntu&#xff0c;进入U盘的ubuntu系统。 二、配置和添加源 sudo add-apt-repository ppa:yannubuntu/boot-repair && sudo apt-get update三、运行 Boot Repair重新制作引导 sudo boot-repair注意&#x…

使用OpenXlab下载数据集(推荐)

OpenXLab浦源面向人工智能领域开发者和使用者,提供一站式AI开发平台。提供包括应用开发,模型免费托管,数据集下载等服务 官方网址:https://openxlab.org.cn/datasets?lang=zh-CN 提供了6622种数据集, 涉及计算机视觉、自然语言处理、多模态、通用机器学习、音频识别以及其他…

Bash and a Tough Math Puzzle 线段树维护区间gcd

还是一道很不错的题目&#xff0c;很容易想到用一棵线段树来维护区间gcd 注意用倍数来剪枝就好了&#xff0c;很是一到很好的题目的 #include<iostream> #include<vector> using namespace std; const int N 5e510; int n,q; struct Segment{int l,r;int d; }tr[…

Kubeflow文档1:介绍与架构

Kubeflow 2024/3/19版本的文档 此专栏用来展示相关的内容翻译&#xff0c;重点关注本地部署&#xff0c;关于运营商的方案&#xff0c;请自行查阅 文档地址https://www.kubeflow.org/docs/ 开始编辑时间&#xff1a;2024/3/27&#xff1b;最后编辑时间2024/3/27 Kubeflow文…

记录echarts各种地图json文件下载地址

今日绘图需要用到echarts的地图json文件&#xff0c;但是github上已经找不到了&#xff0c;后发现伟大的网友提供了地址如下&#xff1a;Index of /examples/data/asset/geohttps://echarts.apache.org/examples/data/asset/geo/ 免费下载实时更新的geoJson数据、行政区划边界…

【正点原子FreeRTOS学习笔记】————(4)FreeRTOS中断管理

这里写目录标题 一、什么是中断&#xff1f;&#xff08;了解&#xff09;二、中断优先级分组设置&#xff08;熟悉&#xff09;三、中断相关寄存器&#xff08;熟悉&#xff09;四、FreeRTOS中断管理实验&#xff08;掌握&#xff09; 一、什么是中断&#xff1f;&#xff08;…
最新文章