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

news2024/11/24 6:48:09

系列文章目录

1. stm32之SPI通信协议
2. stm32之软件SPI读写W25Q64存储器应用案例
3. stm32之SPI通信外设


文章目录

  • 系列文章目录
  • 前言
  • 一、电路接线图
  • 二、应用案例代码
  • 三、应用案例代码分析
    • 3.1 基本思路
    • 3.2 相关库函数介绍
    • 3.3 MySPI模块
      • 3.3.1 模块初始化
      • 3.3.2 SPI基本时序单元模块


前言

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

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


一、电路接线图

下图所示为W25Q64模块硬件接线图,左边是W25Q64模块作为从机,右边是stm32作为主机。本案例选用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_H

void 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 header

void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

void MySPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = 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);
	
	SPI_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI1, &SPI_InitStructure);
	
	SPI_Cmd(SPI1, ENABLE);
	
	MySPI_W_SS(1);
}

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

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

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);
	
	SPI_I2S_SendData(SPI1, ByteSend);
	
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
	
	return SPI_I2S_ReceiveData(SPI1);
}

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.h:

#ifndef __W25Q64_H
#define __W25Q64_H

void 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.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的基础,那么我们只需要在原有的基础上将软件SPI的接口直接改写成硬件实现就行了,具体来说就是只需要修改MySPI_Init以及MySPI_SwapByte函数的内部实现即可,这就是模块化封装的好处。我们先来看一下SPI的硬件基本结构图。

在这里插入图片描述

3.1 基本思路

  • 第一步,开启时钟,使能SPI和GPIO时钟。
  • 第二步,初始化GPIO口,配置相应GPIO口的引脚模式。
  • 第三步,配置SPI外设,调用SPI_Init函数完成初始化配置。
  • 第四步,SPI使能,调用SPI_Cmd函数开启SPI外设。

以上就是SPI初始化函数的基本思路了,接下来我们只需要在上一章软件SPI通信模块的基础上进行修改即可,把软件实现的时序用硬件SPI接口替换掉。

3.2 相关库函数介绍

老规矩,还是先来看一下操作SPI外设的相关函数。找到stm32f10x_spi.h然后拉到最后,可以发现这里很多的函数都带了个I2S,因为SPI和I2S共用同一套电路,我们不需要使用I2S,直接当它不存在就行了。

其实学完了这么多的外设可以发现,很多东西都是一个套路,像下面这些函数都是经常见的了,不过是换了个外设的名称,功能都是差不多的,这里就简单讲一下就好了。

void SPI_I2S_DeInit(SPI_TypeDef* SPIx);
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct);
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
  • SPI_I2S_DeInit,恢复缺省配置。
  • SPI_Init,SPI外设初始化。
  • SPI_StructInit,结构体变量初始化。
  • SPI_Cmd,SPI外设使能。
  • SPI_I2S_ITConfig,中断使能。
  • SPI_I2S_DMACmd,DMA使能。

接下来是SPI比较重要的两个函数,发送与接收一个字节函数

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

最后就是一些获取标志位和清除标志位的相关函数,我们主要会用到SPI_I2S_GetFlagStatus来获取TXE和RXNE标志位的状态,再配合写DR和读DR的函数就能控制时序的产生了。

FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);

ok,库函数的介绍就到这里,下面我们就进入正题吧!Let ’s go !

3.3 MySPI模块

3.3.1 模块初始化

1. 开启时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

注意:SPI1是APB2侧的外设

2. 初始化GPIO口

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
	
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = 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);

其中SCK(PA5)和MOSI(PA7)是由硬件外设控制的输出信号,所以配置为复用推挽输出。MISO(PA6)是硬件外设的输入信号,我们可以配置为上拉输入(因为输入设备可以有多个,所以不存在复用输入这种东西,直接配置为上拉输入即,普通GPIO口能输入,外设也能输入)。最后还有SS(PA4)引脚,SS引脚是软件控制的输出信号,所以配置为通用推挽输出即可。

3. 配置SPI外设

SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);

以下是关于SPI初始化配置各个参数的解析:

  • SPI_Mode:SPI的模式,这里选择SPI_Mode_Master,即SPI1 被配置为主设备。
  • SPI_Direction:这里选择SPI_Direction_2Lines_FullDuplex,SPI 工作在全双工模式。
  • SPI_DataSize:这里选择SPI_DataSize_8b,数据大小为 8 位。
  • SPI_FirstBit:这里选择为SPI_FirstBit_MSB,数据按最高有效位 (MSB) 先发送。
  • SPI_BaudRatePrescaler:这里选择为SPI_BaudRatePrescaler_128,SPI 时钟源频率被分频为 128。
  • SPI_CPOL:这里选择为SPI_CPOL_Low,空闲时钟极性SCK为低电平。
  • SPI_CPHA:这里选择为SPI_CPHA_1Edge,数据在第一个时钟边沿进行采样。
  • SPI_NSS:这里选择为SPI_NSS_Soft:NSS 信号通过软件控制,不使用硬件 NSS 管脚管理。
  • SPI_CRCPolynomial:这里选择为SPI_CRCPolynomial 设置为 7,虽然这里没有启用 CRC 校验,但必须指定一个多项式。

这里要注意一下的就是SPI_CPOL以及SPI_CPHA配置的是模式0。

4. SPI使能

SPI_Cmd(SPI1, ENABLE);

最后别忘了置SS为默认高电平状态,不选择从机。

MySPI_W_SS(1);

3.3.2 SPI基本时序单元模块

1. 起始与终止信号

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

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

这个跟软件SPI一样,SS引脚我们采用软件控制GPIO口的方式来进行控制。

2. 交换一个字节

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);
	
	SPI_I2S_SendData(SPI1, ByteSend);
	
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
	
	return SPI_I2S_ReceiveData(SPI1);
}

在这里我们使用的是SPI主模式全双工非连续传输的方式,这种传输方式的逻辑大概就是:

  • 第一步,等待TXE标志位为1,发送寄存器为空。
  • 第二步,写入DR,将数据写入TDR。
  • 第三步,等待RXNE标志位为1,接受寄存器非空。
  • 第四步,读取DR,从RDR中读取数据。

ok,到这里关于硬件SPI读写W25Q64存储器的分析就到这里了,剩下的还有W25Q64模块以及主程序代码逻辑的分析在软件SPI那一章已经详细地分析过了,这里就不再累述了。

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

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

相关文章

指挥中心操作台厂家哪家好?选择时需要注意哪些?

在构建高效、稳定的指挥中心过程中&#xff0c;操作台作为核心设备之一&#xff0c;其选择至关重要。面对市场上琳琅满目的指挥中心操作台厂家&#xff0c;如何挑选出既符合需求又品质卓越的合作伙伴&#xff0c;成为众多采购者关注的焦点。接下来就给大家从以下几个方面探讨指…

uniapp设置隐藏qiun-data-charts数据标签

隐藏前&#xff1a; 隐藏后&#xff1a; 具体代码实现&#xff1a; 在opts配置中传入 "dataLabel": false 即可

个性化推荐兴趣社交社交平台

1 项目介绍 社交兴趣平台是一个基于 spring boot、vue3 的社交平台&#xff0c;旨在为用户提供一个分享、交流和发现各种有趣内容的场所。 该平台的核心功能是让用户能够创建个人主页并发布自己的动态、经历、见解和创意。用户可以自由发表各种主题的内容&#xff0c;涵盖但不…

《向量数据库指南》——非结构化数据大爆发,向量数据库引领电商推荐新潮流

在当今数据驱动的时代,数据作为企业的核心资产,其价值挖掘的深度与广度直接关乎到企业的竞争力和创新能力。长久以来,结构化数据因其规整的格式和易于分析的特性,成为了数据科学家和工程师们研究的热点,其潜力在多个领域已被充分挖掘和应用。然而,随着互联网的飞速发展,…

C语言常见运算符

C语言提供了丰富的运算符&#xff0c;这些运算符用于执行各种类型的操作&#xff0c;比如算术运算、比较运算、逻辑运算、位运算等。下面是一些基本的C语言运算符分类及其示例&#xff1a; 1. 算术运算符 加法 (): a b 表示a和b的和。减法 (-): a - b 表示a和b的差。乘法 (*…

2024年Ai智能绘画Stable Diffusion软件+整合包+保姆式教程

前言 在2024年的科技浪潮中&#xff0c;一款名为Stable Diffusion的AI智能绘画软件吸引了全球的目光。它不仅为艺术家和设计师提供了无限创意的可能&#xff0c;也让我们每个人都能轻松体验绘画的乐趣。那么&#xff0c;Stable Diffusion究竟有何魅力&#xff1f;它又是如何工…

消息队列实现多人聊天

消息队列实现多人聊天 分析&#xff1a; 每个程序都有两个任务&#xff0c;一个任务是负责接收消息&#xff0c;一个任务是负责发送消息&#xff0c;通过 fork 创建子进程实现多任务。 一个进程负责接收信息&#xff0c;它只接收某种类型的消息&#xff0c;只要别的进程发送…

2024i春秋第四届长城杯网络安全大赛暨京津冀网络安全技能竞赛初赛wp-flowershop+easyre

flowershop 如图所示&#xff0c;v8是钱的数量&#xff0c;strncpy从src复制三个字符给dest的数组里&#xff0c;最后一个元素是0&#xff0c;相当于字符串截断&#xff0c;strcmp是比较c和dest中的内容&#xff0c;如果相等就不会exit(0)&#xff1b;双击c进去看&#xff0c;c…

Pr:新建序列 - 设置

在“新建序列”对话框中&#xff0c;“设置” Settings选项卡的主要用途是选择预设或手动配置序列的参数&#xff0c;以确保与用户的素材和输出需求相匹配。 可以根据项目的具体要求自定义序列中的视频和音频格式&#xff0c;包括帧速率、分辨率、工作色彩空间、音频采样率等以…

nvm install 16.14.1报错“Node.js v16.14.1 is not yet released or available.”

原因&#xff1a; 使用命令nvm ls available&#xff0c;结果列出的可供下载的版本列表为空&#xff0c;这就是原因了 解决办法&#xff1a;修改镜像 nvm node_mirror https://npmmirror.com/mirrors/node/ nvm npm_mirror https://npmmirror.com/mirrors/npm/ 结果&#xf…

基于SpringBoot+Vue+MySQL的校园生活服务平台

系统展示 用户前台界面 管理员后台界面 系统背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不可跨域的鸿沟&#xff0c;信息的…

springboot优雅停机无法关闭进程,kill无法停止springboot必须kill -9,springboot线程池使用

背景最近项目在jenkins部署的时候发现部署很慢&#xff0c;查看部署日志发现kill命令执行后应用pid还存在&#xff0c;导致必须在60秒等待期后kill -9杀死springboot进程 应用环境 springboot <dependency><groupId>org.springframework.boot</groupId>&l…

u盘怎么制作win10启动盘_win10启动盘制作详细教程

u盘怎么制作win10启动盘&#xff1f;制作win10启动盘方法有很多&#xff0c;有官方制作方法&#xff0c;有第三方u盘启动盘制作方法&#xff0c;下面小编就教大家制作win10启动盘详细教程。 u盘怎么制作win10启动盘&#xff1f; 制作win10启动通常有两种方法&#xff1a;直接安…

带有HSE组件的S32系列芯片中各子系统如何依次启动?

《S32系列芯片——Boot详解》系列——带有HSE组件的S32系列芯片中各子系统如何依次启动&#xff1f; 一、各子系统的重置释放顺序二、启动流程2.1 安装启动过程2.2 正常启动流程 博主已开通同名公众号&#xff0c;通过文末或主页二维码关注博主&#xff0c;将为你推送最新、最细…

音乐网站-前后台登录注册搜索试听下载评论音乐分计算机毕业设计/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序

1. 前台功能模块 首页&#xff1a; 展示热门音乐、推荐音乐、最新发布。搜索框&#xff1a;支持音乐、专辑、艺人等的搜索。用户登录/注册入口。 用户注册和登录&#xff1a; 用户注册&#xff1a;输入用户名、密码、邮箱等信息。用户登录&#xff1a;输入用户名和密码。密码找…

如何在NXP源码基础上适配ELF 1开发板的PWM功能

本次源码适配项目是在NXP i.MX6ULL EVK评估板所搭载的Linux内核源码&#xff08;特定版本为Linux-imx_4.1.15&#xff09;基础上进行的&#xff0c;主要目标是通过调整功能接口引脚配置&#xff0c;使其适应ELF 1开发板。为了深入阐述这一适配过程&#xff0c;我们将以PWM功能的…

浏览器百科:网页存储篇-IndexedDB应用实例(十二)

1.引言 在现代Web开发中&#xff0c;IndexedDB作为一种强大的客户端存储技术&#xff0c;越来越受到开发者的青睐。它不仅能够存储大量结构化数据&#xff0c;还提供了高性能的查询和事务支持。在前面的文章中&#xff0c;我们已经详细介绍了IndexedDB的基本概念、使用方法以及…

RocketMQ 5.0简介

一、概述 Apache RocketMQ 自诞生以来&#xff0c;因其架构简单、业务功能丰富、具备极强可扩展性等特点被众多企业开发者以及云厂商广泛采用。历经十余年的大规模场景打磨&#xff0c;RocketMQ 已经成为业内共识的金融级可靠业务消息首选方案&#xff0c;被广泛应用于互联网、…

机器学习 第9章 聚类

目录 聚类任务性能度量距离计算原型聚类k均值&#xff08;K-Means&#xff09;算法学习向量量化 (LVQ)高斯混合聚类 密度聚类层次聚类 聚类任务 聚类是机器学习中一种重要的无监督学习方法&#xff0c;其目的是将数据集中的数据分成不同的聚类或组&#xff0c;使得同一簇内的样…