stm32之软件SPI读写W25Q64存储器应用案例

2024-09-07 09:44

本文主要是介绍stm32之软件SPI读写W25Q64存储器应用案例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章目录

1. stm32之SPI通信协议


文章目录

  • 系列文章目录
  • 前言
  • 一、电路接线图
  • 二、应用案例代码
  • 三、应用案例分析
    • 3.1 SPI通信模块
    • 3.2 W25Q64模块
    • 3.3 主程序


前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者

本案例使用软件SPI通信的方式实现了STM32与W25Q64 Flash存储器的通信,完成了常见的Flash存储器操作如读ID、页写、扇区擦除、读取数据等。


一、电路接线图

下图所示为W25Q64模块硬件接线图,左边是W25Q64模块作为从机,右边是stm32作为主机。为了方便下一章节硬件SPI的接线,这里直接就选择了硬件SPI1外设的接线方式。其中PA4对应主机的从机选择线SPI1_NSS连接到从机的CS引脚,PA5对应主机的时钟同步线SPI1_SCK连接到从机的CLK引脚,PA6对应主机的主机输入从机输出线SPI1_MISO连接到从机的DO引脚,PA7对应主机的主机输出从机输入线SPI1_MOSI连接到从机的DI引脚。最后,W25Q64模块的VCC和GND分别接到stm32的电源正负极进行供电。

在这里插入图片描述

二、应用案例代码

MySPI.h:

#ifndef __MYSPI_H
#define __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endif

MySPI.c:

#include "stm32f10x.h"                  // Device headervoid MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}uint8_t MySPI_R_MISO(void)
{return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);MySPI_W_SS(1);MySPI_W_SCK(0);
}void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}MySPI_W_SCK(0);}return ByteReceive;
}

W25Q64.h:

#ifndef __W25Q64_H
#define __W25Q64_Hvoid W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);#endif

W25Q64_Ins.h:

#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3#define W25Q64_DUMMY_BYTE							0xFF#endif

W25Q64.c:

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"void W25Q64_Init(void)
{MySPI_Init();
}void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();MySPI_SwapByte(W25Q64_JEDEC_ID);*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);*DID <<= 8;*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);MySPI_Stop();
}void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);Timeout = 100000;while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01){Timeout --;if (Timeout == 0){break;}}MySPI_Stop();
}void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);for (i = 0; i < Count; i ++){MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaitBusy();
}void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA);MySPI_SwapByte(Address >> 16);MySPI_SwapByte(Address >> 8);MySPI_SwapByte(Address);for (i = 0; i < Count; i ++){DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}

main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;
uint16_t DID;uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];int main(void)
{OLED_Init();W25Q64_Init();OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");W25Q64_ReadID(&MID, &DID);OLED_ShowHexNum(1, 5, MID, 2);OLED_ShowHexNum(1, 12, DID, 4);W25Q64_SectorErase(0x000000);W25Q64_PageProgram(0x000000, ArrayWrite, 4);W25Q64_ReadData(0x000000, ArrayRead, 4);OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);OLED_ShowHexNum(3, 3, ArrayRead[0], 2);OLED_ShowHexNum(3, 6, ArrayRead[1], 2);OLED_ShowHexNum(3, 9, ArrayRead[2], 2);OLED_ShowHexNum(3, 12, ArrayRead[3], 2);while (1){}
}

完整工程:stm32之软件SPI读写W25Q64存储器

三、应用案例分析

从整体架构来看主要分为SPI通信模块、W25Q64模块,接下来重点分析一下SPI通信模块以及W25Q64模块。

3.1 SPI通信模块

SPI通信模块主要封装了一个模块初始化函数和三个时序单元模块。

SPI模块初始化函数

void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);MySPI_W_SS(1);MySPI_W_SCK(0);
}

这个没什么好讲的了,都是一样的套路了。

这里要注意一下的就是PA4(SS)、PA5(SCK)、PA7(MOSI)引脚配置为推挽输出模式(GPIO_Mode_Out_PP),用于主设备主动输出信号。PA6(MISO)配置为上拉输入模式(GPIO_Mode_IPU),表示从设备的数据通过此引脚传输到主设备。

最后再设置一下总线的初始状态即可,设置 SS 引脚为高电平,即取消选择从设备,然后初始化时钟引脚为低电平。

三个时序单元模块:

三个时序单元分别是起始信号、终止信号以及交换一个字节。

起始信号、终止信号:

void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}

起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平

交换一个字节:

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{uint8_t i, ByteReceive = 0x00;for (i = 0; i < 8; i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}MySPI_W_SCK(0);}return ByteReceive;
}

交换一个字节(模式0):
CPOL=0:空闲状态时,SCK为低电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

交换一个字节的时序这里用的是模式0。起始信号之后先立刻将数据的最高位B7移出,然后在SCK的第一个上升沿后读入B7,然后在SCK的第一个下降沿移出次高位B6,然后在SCK的第二个上升沿读入B6,然后在SCK的第二个下降沿移出B5…如此循环8次,最终在SCK的第八个上升沿读入B0完成一个字节的交换。

至于关于代码部分的按位与(提取字节中的每一位)和按位或(设置字节中的某位)以及左移、右移操作的解析在我之前的一篇文章里已经详细地分析过了,如果有不懂的就翻回去看看就行了,这里就不再累述了。

文章传送门:计算机常见运算之左移操作、右移操作以及按位与、按位或

3.2 W25Q64模块

W25Q64模块主要由模块初始化函数、写使能、页编程(写入数据)、读取数据、忙等待、读取ID号以及扇区擦除等模块组成。

W25Q64模块初始化函数:

void W25Q64_Init(void)
{MySPI_Init();
}

调用MySPI_Init()函数,完成引脚配置和SPI的初始化。

写使能:

void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);// 发送写使能命令MySPI_Stop();
}

发送写使能命令,以允许后续的写入或擦除操作。

页编程(写入数据):

void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count) 
{uint16_t i;W25Q64_WriteEnable(); // 写使能MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM); // 发送页编程命令MySPI_SwapByte(Address >> 16); // 发送地址高8位MySPI_SwapByte(Address >> 8);  // 发送地址中8位MySPI_SwapByte(Address);       // 发送地址低8位for (i = 0; i < Count; i ++) { // 发送要写入的数据MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaitBusy(); // 等待存储器空闲
}

页编程操作首先通过W25Q64_WriteEnable()函数启用写操作,然后发送页编程命令W25Q64_PAGE_PROGRAM,接着发送地址,并逐字节将数据写入存储器。

关于依次提取3字节24位中的每一个字节的问题有几点要注意一下,假设我传入的地址是Address = 0x123456,那么我们就需要依次发送0x12、0x34、0x56,那如何提取呢?

我们可以这样操作:

原始值Address:0x123456
0001 0010 0011 0100 0101 0110

右移十六位—> 0x12
0000 0000 0000 0000 0001 0010

右移八位—> 0x1234
0000 0000 0001 0010 0011 0100

可以看到如果是右移八位,那么得到的是0x1234,但是我们想要的是0x34才对啊?但其实代码里这样写也是没问题的,因为MySPI_SwapByte函数的形参是uint8_t,传入0x1234就只会接收低八位的0x34,但是这样的话理解起来就有点那啥了。

其实我们还可以这样做,直接右移以后再做个按位与操作就可以了,比如MySPI_SwapByte((Address >> 8) & 0xFF);这样就直接得到0x34了,当然第三位也需要执行按位与操作,MySPI_SwapByte(Address & 0xFF);

两种方法都可以,我这里只是提一下。

读数据:

void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count) 
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA); // 发送读取数据命令MySPI_SwapByte(Address >> 16); // 发送地址高8位MySPI_SwapByte(Address >> 8);  // 发送地址中8位MySPI_SwapByte(Address);       // 发送地址低8位for (i = 0; i < Count; i ++) { // 读取数据DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();
}

读取数据命令W25Q64_READ_DATA,发送要读取的地址,然后通过SPI逐字节读取指定数量的数据到DataArray中。

忙等待:

void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);// 读取状态寄存器1Timeout = 100000;while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01){// 检查忙标志位Timeout --;if (Timeout == 0){break;}}MySPI_Stop();
}

发送读取状态寄存器1命令,进入循环等待存储器空闲(即状态寄存器的最低位变为0),避免在存储器仍处于忙碌时进行新的操作。

读厂商、设备ID:

void W25Q64_ReadID(uint8_t *MID, uint16_t *DID) 
{MySPI_Start(); // 片选低电平,开始通信MySPI_SwapByte(W25Q64_JEDEC_ID); // 发送读取ID命令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); // 接收制造商ID*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); // 接收设备ID高8位*DID <<= 8;*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE); // 接收设备ID低8位MySPI_Stop(); // 片选高电平,结束通信
}
  • 首先,调用MySPI_Start()开始SPI通信,然后发送读取设备ID命令W25Q64_JEDEC_ID。随后,通过MySPI_SwapByte函数接收制造商ID和设备ID。
  • 制造商ID(MID)为一个字节,设备ID(DID)为两个字节。

扇区擦除:

void W25Q64_SectorErase(uint32_t Address) 
{W25Q64_WriteEnable(); // 写使能MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); // 发送扇区擦除命令MySPI_SwapByte(Address >> 16); // 发送地址高8位MySPI_SwapByte(Address >> 8);  // 发送地址中8位MySPI_SwapByte(Address);       // 发送地址低8位MySPI_Stop();W25Q64_WaitBusy(); // 等待存储器空闲
}

扇区擦除命令W25Q64_SECTOR_ERASE_4KB用于擦除特定地址所在的扇区,地址通过三次发送高、中、低8位地址来指定。

3.3 主程序

主程序整体代码逻辑大概是,首先读取从机的ID信息并显示在OLED屏幕上,然后执行一个扇区擦除操作,接着在该扇区中写入4个字节的数据,最后将写入的数据读取出来,并在OLED上显示。

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;// 制造商ID (Manufacturer ID)
uint16_t DID;// 设备ID (Device ID)uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};
uint8_t ArrayRead[4];int main(void)
{// 硬件初始化OLED_Init();W25Q64_Init();OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");// 读取并显示W25Q64的ID信息W25Q64_ReadID(&MID, &DID);OLED_ShowHexNum(1, 5, MID, 2);OLED_ShowHexNum(1, 12, DID, 4);// 擦除W25Q64的一个扇区并写入数据W25Q64_SectorErase(0x000000);W25Q64_PageProgram(0x000000, ArrayWrite, 4);// 读取数据并显示W25Q64_ReadData(0x000000, ArrayRead, 4);OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);OLED_ShowHexNum(2, 12, ArrayWrite[3], 2);OLED_ShowHexNum(3, 3, ArrayRead[0], 2);OLED_ShowHexNum(3, 6, ArrayRead[1], 2);OLED_ShowHexNum(3, 9, ArrayRead[2], 2);OLED_ShowHexNum(3, 12, ArrayRead[3], 2);while (1){}
}

这篇关于stm32之软件SPI读写W25Q64存储器应用案例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1144719

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

10. 文件的读写

10.1 文本文件 操作文件三大类: ofstream:写操作ifstream:读操作fstream:读写操作 打开方式解释ios::in为了读文件而打开文件ios::out为了写文件而打开文件,如果当前文件存在则清空当前文件在写入ios::app追加方式写文件ios::trunc如果文件存在先删除,在创建ios::ate打开文件之后令读写位置移至文件尾端ios::binary二进制方式

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、