STM32之SPI读写W25Q128芯片

  1. SPI简介

    STM32的SPI是一个串行外设接口。它允许STM32微控制器与其他设备(如传感器、存储器等)进行高速、全双工、同步的串行通信。通常包含SCLK(串行时钟)、MOSI(主设备输出/从设备输入Master Output Slave Input)、MISO(主设备输入/从设备输出Master Input Slave Output)和NSS/CS片选信号Chip Select)这4条线,支持多个从设备连接到一个主设备上。

SPI,是一种高速的,全双工同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROMFLASH实时时钟AD转换器,还有数字信号处理器和数字信号解码器之间。

2. SPI使用步骤

我们将利用 STM32 的 SPI 来读取外部 SPI FLASH 芯片(W25Q128)为例,学习SPI。

2.1 SPI时钟SCLK

   SPI时钟特点主要包括:时钟速率时钟极性时钟相位三方面。

   时钟速率

SPI总线上的主设备必须在通信开始时候配置并生成相应的时钟信号。从理论上讲,只要实际可行,时钟速率就可以是你想要的任何速率,当然这个速率受限于每个系统能提供多大的系统时钟频率,以及最大的SPI传输速率。

时钟极性

根据硬件制造商的命名规则不同,时钟极性通常写为CKPCPOL。时钟极性和相位共同决定读取数据的方式,比如信号上升沿读取数据还是信号下降沿读取数据。

CKP可以配置为1或0,这意味着可根据需要将时钟的默认状态(IDLE)设置为高或低。极性反转可以通过简单的逻辑逆变器实现。须参考设备的数据手册才能正确设置CKPCKE

CKP = 0:时钟空闲IDLE为低电平0;

CKP = 1:时钟空闲IDLE为高电平1。

时钟相位

根据硬件制造商的不同,时钟相位通常写为CKECPHA。顾名思义,时钟相位/边沿,也就是采集数据时是在时钟信号的具体相位或者边沿;

CKE = 0:在时钟信号SCK的第一个跳变沿采样

CKE = 1:在时钟信号SCK的第二个跳变沿采样

2.2四种操作根据

SPI的时钟极性时钟相位特性可以设置4不同的SPI通信操作模式,它们的区别是定义了在时钟脉冲的哪条边沿转换(toggles)输出信号,哪条边沿采样输入信号,还有时钟脉冲的稳定电平值(就是时钟信号无效时是高还是低),详情如下所示:

Mode0:CKP=0,CKE=0当空闲态时,SCK处于低电平,数据采样是在第1个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。

Mode1:CKP=0,CKE=1当空闲态时,SCK处于低电平,数据发送是在第2个边沿,也就是SCK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。

Mode2:CKP=1,CKE=0当空闲态时,SCK处于高电平,数据采集是在第1个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。

Mode3:CKP=1,CKE=1当空闲态时,SCK处于高电平,数据发送是在第2个边沿,也就是SCK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

图中黑线采样数据的时刻蓝线SCK时钟信号

举个例子,下图是SPI Mode0读/写时序,可以看出SCK空闲状态为低电平,主机输出数据在第一个跳变沿被从机采样,主机输入数据同理。

3.STM32相关的SPI

    STM32的SPI外设可用作通讯的主机及从机, 支持最高的SCK时钟频率为fpclk/2 (STM32F103型号的芯片默认fpclk1为36MHz, fpclk2为72MHz),完全支持SPI协议的4种模式,数据帧长度可设置为8位或16位, 可设置数据MSB先行或LSB先行。它还支持双线全双工、双线单向以及单线模式。 其中双线单向模式可以同时使用MOSI及MISO数据线向一个方向传输数据,可以加快一倍的传输速度。而单线模式则可以减少硬件接线, 当然这样速率会受到影响。我们只讲解双线全双工模式

3.1时钟控制逻辑

    时钟由寄存器控制。SCK线的时钟信号,由波特率发生器根据“控制寄存器CR1”中的BR[0:2]位控制,该位是对fpclk时钟的分频因子, 对fpclk的分频结果就是SCK引脚的输出时钟频率,计算方法见表 BR位对fpclk的分频。

其中的fpclk频率是指SPI所在的APB总线频率, APB1为fpclk1,APB2为fpckl2。

通过配置“控制寄存器CR”的“CPOL位”及“CPHA”位可以把SPI设置成前面分析的4种SPI模式。

实际应用中,我们一般不使用STM32 SPI外设的标准NSS信号线,而是更简单地使用普通的GPIO,软件控制它的电平输出,从而产生通讯起始和停止信号。

3.2通讯过程

STM32使用SPI外设通讯时,在通讯的不同阶段它会对“状态寄存器SR”的不同数据位写入参数,我们通过读取这些寄存器标志来了解通讯状态。主发送器通讯过程中的是“主模式”流程,即STM32作为SPI通讯的主机端时的数据收发过程。

3.3从代码层面理解SPI

我们将利用 STM32 的 SPI 来读取外部 SPI FLASH 芯片(W25Q128),实现类似IIC的功能。

    使能 SPI2 的时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE );//PORTB 时钟使能

RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE );//SPI2 时钟使能

GPIOB端口挂在STM32的APB2时钟线上。SPI2是挂在STM32的APB1时钟线上。

配置相关引脚的复用功能

这里使用 PB13、14、15 这 3 个(SCK.、MISO、MOSI,CS 使用软件管理方式),所以设置这三个为复用 IO。

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15 复用推挽输出

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 GPIOB

SPI2的引脚在PB上,可以参考W25Q128硬件连接图。

初始化 SPI2, 设置 SPI2 工作模式

库函数中是通过 SPI_Init 函数来实现。

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)

SPI_InitTypeDef 的定义:

typedef struct

{

uint16_t SPI_Direction;

uint16_t SPI_Mode;

uint16_t SPI_DataSize;

uint16_t SPI_CPOL;

uint16_t SPI_CPHA;

uint16_t SPI_NSS;

uint16_t SPI_BaudRatePrescaler;

uint16_t SPI_FirstBit;

uint16_t SPI_CRCPolynomial;

}SPI_InitTypeDef;

SPI_BaudRatePrescaler设置 SPI 波特率预分频值决定 SPI 的时钟的参数,从不分频道 256 分频 8 个可选值

SPI_BaudRatePrescaler_256 //256 分频值

传输速度为 72M/256=281.25KHz。

初始化代码:

SPI_InitTypeDef SPI_InitStructure;

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收 8 位帧结构

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个跳变沿数据被采样

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始

SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式

SPI_Init(SPI2, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器

使能SPI2

SPI_Cmd(SPI2, ENABLE); //使能 SPI 外设

SPI2_ReadWriteByte(0xff); //④启动传输,主机发一个字节,进行一次传输,可以启动传输

SPI传输数据

发送数据函数

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)

接收数据函数

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)

查看 SPI 传输状态函数

判断数据是否传输完成,发送区是否为空
判断接收是否完成,接收区是否空

接收

SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE)

发送

SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)

设置SPI2速度函数

单独的设置分频系数的函数

//SPI 速度设置函数

//SpeedSet://SPI_BaudRatePrescaler_256 256 分频 (SPI 281.25K@sys 72M)

void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)

{

    assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));

    SPI2->CR1&=0XFFC7;

    SPI2->CR1|=SPI_BaudRatePrescaler; //设置 SPI2 速度

    SPI_Cmd(SPI2,ENABLE);

}

读写一个字节

u8 SPI2_ReadWriteByte(u8 TxData)

{

    u8 retry=0;

    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //等待发送区空

    {

       retry++;//重试

       if(retry>200)return 0;

    } //读取两百次还没有值,说明无效,返回

    SPI_I2S_SendData(SPI2, TxData); //通过外设 SPIx 发送一个数据

    retry=0;

    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //等待接收完一个 byte

    {

       retry++;

       if(retry>200)return 0;

    }

    return SPI_I2S_ReceiveData(SPI2); //返回通过 SPIx 最近接收的数据

}

W25Q128

• W25Q128 是华邦公司推出的大容量 SPI FLASH 产品,W25Q128 的容量为 128Mb,该系列还有 W25Q80/16/32/64等。

擦除

W25Q128 的最小擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。
    这样要求芯片必须有 4K 以上 SRAM 才能很好的操作。

W25QXX驱动解读

W25QXX.h

W25QXX_CS片选,值0选定,1取消

初始化SPI

读取状态寄存器

写状态寄存器

擦除一个扇区

读取 SPI FLASH

//在指定地址开始读取指定长度的数据

//pBuffer:数据存储区

//ReadAddr:开始读取的地址(24bit)

//NumByteToRead:要读取的字节数(最大 65535)

void W25QXX_Read (u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)

{

u16 i;

SPI_FLASH_CS=0; //使能器件

SPI2_ReadWriteByte(W25X_ReadData); //发送读取命令

SPI2_ReadWriteByte((u8)((ReadAddr)>>16)); //发送 24bit 地址

SPI2_ReadWriteByte((u8)((ReadAddr)>>8));

SPI2_ReadWriteByte((u8)ReadAddr);

for(i=0;i<NumByteToRead;i++)

{

pBuffer[i]=SPI2_ReadWriteByte(0XFF); //循环读数

}

SPI_FLASH_CS=1;

}

无检查写函数

//在指定地址开始写入指定长度的数据,但是要确保地址不越界!

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大65535)

//CHECK OK

void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)  

{                    

    u16 pageremain;   

    pageremain=256-WriteAddr%256; //单页剩余的字节数              

    if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节,这也是结束标识

    while(1)

    {     

        W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);

        if(NumByteToWrite==pageremain)break;//写入结束了

        else //NumByteToWrite>pageremain

        {

            pBuffer+=pageremain;

            WriteAddr+=pageremain; 

            NumByteToWrite-=pageremain;          //减去已经写入了的字节数

            if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节

            else pageremain=NumByteToWrite;       //不够256个字节了

        }

        //按照页剩余写一次,然后256个字节的写,然后写最后一页多出来的。

    };     

}

NoCheck是说可以跨扇区的写
下方表示写了一个扇区

W25QXX_Write函数

作用与 W25QXX_Flash_Read 的作用类似,不过是用来写数据到 W25Q128 里面的,其代码如下:

u8 W25QXX_BUFFER[4096];

void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)

{

    u32 secpos;

    u16 secoff;

    u16 secremain;

    u16 i;

    u8 * W25QXX_BUF;

    W25QXX_BUF=W25QXX_BUFFER;

    secpos=WriteAddr/4096;//扇区地址,每个扇区是4096,所以除以4096得到的整数就是扇区的地址标号

    secoff=WriteAddr%4096;//在扇区内的偏移

    secremain=4096-secoff;//扇区剩余空间大小

    //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用

    if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于 4096 个字节

    while(1)

    {

        W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容

        //secpos*4096是该扇区的起始地址

        for(i=0;i<secremain;i++)//校验数据

        {

            if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除,偏移地址内有数据

//擦除后的默认值是0xFFF

        }

        if(i<secremain)//需要擦除

        {

            W25QXX_Erase_Sector(secpos); //擦除这个扇区

            for(i=0;i<secremain;i++) //复制

            {

                W25QXX_BUF[i+secoff]=pBuffer[i];

            }

            W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区

        }

        else

            W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);

        //如果扇区剩余空间足够,直接写入扇区剩余区间.

        //是否需要写入下一个扇区

        if(NumByteToWrite==secremain)break;//写入结束了

        else//写入未结束

        {

            secpos++;//扇区地址增 1

            secoff=0;//偏移位置为 0

            pBuffer+=secremain; //指针偏移

            WriteAddr+=secremain; //写地址偏移

            NumByteToWrite-=secremain; //字节数递减

            if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完

            else secremain=NumByteToWrite; //下一个扇区可以写完了

        }

    };

}

//跟无检查页写入的逻辑一致。

该函数可以在 W25Q128 的任意地址开始写入任意长度(必须不超过 W25Q128 的容量)的数据。

先获得首地址(WriteAddr)所在的扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过,再先看看是否要擦除,如果不要,则直接写入数据即可,如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。

擦除的最小单位是扇区,也就是4K。所以在擦除之前我们先将这个扇区的数据读取出来,保存在缓存区。在缓存中将对应的地址更新之后,一次性将数据写到对应的sector之中。

当所需要写入的数据长度超过一个扇区的长度的时候,我们先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样的操作,如此循环,直到写入结束。

    我理解,整个STM32读写W25Q128是不断封装SPI_I2S_SendData和ReadData的一个过程,先封装到读写一个字节ReadWriteByte,再封装到读写一个扇区W25QXX_Write_NoCheck,W25QXX_Write。整个过程比较标准,无需太多改动。

4.STM32之SPI实战

    在实现STM32的SPI通讯之前,先做个小实验,实现USART串口通讯。因为调试STM32的SPI通讯可以把结果通过串口打印到电脑上显示,方便观察结果。

STM32CubeMX学习笔记(6)——USART串口使用_unused(huart)-CSDN博客

5.Keil调试代码

    今天学了Keil的debug功能,刚开始程序卡在了HAL_INIT这里,这是个很奇怪的问题。CubeMx生成的代码段,什么都没有做就是无法调试。原来要在CubeMx里面勾选一个调试功能。

在KEIL中勾选Use-MicroLib库

因为调用了printf,这是一个C++里面的功能,需要重映射。代码如下。

新建一个retarget.c文件。

#include "stdio.h"

#include "stm32f1xx_hal.h"

#include "usart.h"

#pragma import(__use_no_semihosting_swi)

#pragma import(__use_no_semihosting)

void _sys_exit(int x) {

    x = x;

}

struct __FILE  {

    int handle;

    /* Whatever you require here. If the only file you are using is */

    /* standard output using printf() for debugging, no file handling */

    /* is required. */

};

/* FILE is typedef’ d in stdio.h. */

FILE __stdout;

void _ttywrch(int ch){};

   

int fputc(int ch, FILE *f)

{

    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);

    return ch;

}

这段代码也是调了好久,网站上的或多或少不太对。

有了上面的代码,main函数就可以调用printf函数,打印到串口显示出来。

Stm32 debug停留在"BKPT 0xAB"或者"SWI 0xAB"的解决办法。

通过百度网盘分享的文件:SPI
链接:https://pan.baidu.com/s/1qzSFFV8-Vhrb0NzqbBAeCg?pwd=sshc 
提取码:sshc 

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

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

相关文章

Linux系统编程 --- 多线程

线程&#xff1a;是进程内的一个执行分支&#xff0c;线程的执行粒度&#xff0c;要比进程要细。 一、线程的概念 1、Linux中线程该如何理解 地址空间就是进程的资源窗口。 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1…

聊聊场景及场景测试

在我们进行测试过程中&#xff0c;有一种黑盒测试叫场景测试&#xff0c;我们完全是从用户的角度去理解系统&#xff0c;从而可以挖掘用户的隐含需求。 场景是指用户会使用这个系统来完成预定目标的所有情况的集合。 场景本身也代表了用户的需求&#xff0c;所以我们可以认为…

SpringBoot+Vue在线商城(电子商城)系统-附源码与配套论文

摘 要 随着互联网技术的发展和普及&#xff0c;电子商务在全球范围内得到了迅猛的发展&#xff0c;已经成为了一种重要的商业模式和生活方式。电子商城是电子商务的重要组成部分&#xff0c;是一个基于互联网的商业模式和交易平台&#xff0c;通过网络进行产品和服务的销售。…

计算机图形学 | 动画模拟

动画模拟 布料模拟 质点弹簧系统&#xff1a; 红色部分很弱地阻挡对折 Steep connection FEM:有限元方法 粒子系统 粒子系统本质上就是在定义个体和群体的关系。 动画帧率 VR游戏要不晕需要达到90fps Forward Kinematics Inverse Kinematics 只告诉末端p点&#xff0c;中间…

Delphi5实现色板程序——滑块型组件实例

效果图 参考 Delphi程序设计基础&#xff1a;教程、实验、习题 代码 unit Unit1;interfaceusesSysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Dialogs, Forms,Form, Formprpt, ExtCtrls, StdCtrls;typeTForm1 class(MForm)Label1: TLabel;Label2: …

公式编辑器 -vue-formula-editor

前言 公式编辑旨在帮助用户使用可视化的前提&#xff0c;能便捷的使用平台&#xff0c;例如低代码平台使用广泛 vue-formula-editor vue-formula-editor是一款开源的Vue公式计算组件&#xff0c;可以帮助开发者快速集成公式编辑 在线体验 demo & 源码 安装 npm i vue-form…

[Python学习日记-9] Python中的运算符

简介 计算机可以进行的运算有很多种&#xff0c;但可不只加减乘除这么简单&#xff0c;运算按种类可分为算数运算、比较运算、逻辑运算、赋值运算、成员运算、身份运算、位运算&#xff0c;而本篇我们暂只介绍算数运算、比较运算、逻辑运算、赋值运算 算数运算 一、运算符描述…

FunHPC算力平台评测

作为内测老用户&#xff0c;已经用DeepLn平台&#xff08;现改名为FunHPC平台&#xff09;好久了&#xff0c;一路见证了平台从最初100多人的小群到现在满群的状态&#xff0c;FunHpc平台确实在一步步的走向成熟&#xff0c;一步步的变大。趁着现在活动的时间&#xff0c;发篇文…

XSS反射型和DOM型+DOM破坏

目录 第一关 源码分析 payload 第二关 源码分析 payload 第三关 源码分析 payload 第四关 源码分析 payload 第五关 源码分析 payload 第六关 源码分析 第七关 源码分析 方法一&#xff1a;构造函数 方法二&#xff1a;parseInt 方法三&#xff1a;locat…

项目问题 | CentOS 7停止维护导致yum失效的解决办法

目录 centos停止维护意味着yum相关源伴随失效。 报错&#xff1a; 解决方案&#xff1a;将图中四个文件替换掉/etc/yum.repos.d/目录下同名文件 资源提交在博客头部&#xff0c;博客结尾也提供文件源码内容 CentOS-Base.repo CentOS-SCLo-scl.repo CentOS-SCLo-scl-rh.rep…

HTML5服装电商网上商城模板源码

文章目录 1.设计来源1.1 主界面1.2 购物车界面1.3 电子产品界面1.4 商品详情界面1.5 联系我们界面1.6 各种标签演示界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 【博主推荐】&#xff1a;前些天发…

【系统分析师】-综合知识-系统架构

1、设计模式 1&#xff09;观察者模式定义了对象间的一种一对多依赖关系&#xff0c;使得每当一个对象改变状态&#xff0c;则所有依赖于它的对象都会得到通知并被自动更新【消息订阅】。在该模式中&#xff0c;发生改变的对象称为观察目标&#xff0c;被通知的对象称为观察者&…

Python爬虫使用实例

IDE&#xff1a;大部分是在PyCharm上面写的 解释器装的多 → 环境错乱 → error&#xff1a;没有配置&#xff0c;no model 爬虫可以做什么&#xff1f; 下载数据【文本/二进制数据&#xff08;视频、音频、图片&#xff09;】、自动化脚本【自动抢票、答题、采数据、评论、点…

vue3 响应式 API:ref() 和 reactive()

在 Vue 3 中&#xff0c;响应式系统是其核心特性之一&#xff0c;它使得数据的变化能够自动触发视图的更新。 官方文档&#xff1a; 响应式 API&#xff1a;核心 要更好地了解响应式 API&#xff0c;推荐阅读官方指南中的章节&#xff1a; 响应式基础 (with the API preference…

SX_初识GitLab_1

1、对GitLab的理解&#xff1a; 目前对GitLab的理解是其本质是一个远程代码托管平台&#xff0c;上面托管多个项目&#xff0c;每个项目都有一个master主分支和若干其他分支&#xff0c;远程代码能下载到本机&#xff0c;本机代码也能上传到远程平台 1.分支的作用&#xff1a…

Oracle大师Roger Cornejo的推荐:使用ASH诊断Oracle解析故障

这篇文章被Oracle大师Roger Cornejo在X平台上推荐&#xff08;见下图&#xff09;&#xff0c;英文原文在&#xff1a; Diagnosing Parsing Issue with ASH 解析&#xff0c;尤其是硬解析&#xff0c;是非生产性操作&#xff0c;会消耗大量系统资源&#xff0c;导致库缓存争用…

Meta 如何实现 99.99999999% 的缓存一致性

曾经的故事 Meta&#xff08;Facebook&#xff09; 曾经运行一个简单的技术栈——PHP 和 MySQL。 但随着更多用户的加入&#xff0c;他们面临着可扩展性问题。因此他们建立了一个分布式缓存。 虽然这暂时解决了可扩展性问题&#xff0c;但保持缓存数据的新鲜度变得困难。以下…

SpringBoot 整合 Redis 实现验证码登录功能

一、整合Redis 在pom.xml中添加Redis相关依赖&#xff1b; <!--Spring Data Redis依赖配置--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>…

阿里云账户注册与实名认证详细教程

在开始使用阿里云服务之前&#xff0c;您需要先有一个阿里云账号&#xff0c;拥有阿里云账号后&#xff0c;您可以选购和使用云产品和服务。如果您没有阿里云账号&#xff0c;需要先注册一个阿里云账号。 说明 一个手机号码下最多可以注册6个阿里云账号。如果您的手机号码已经…

一分钟了解VMware虚拟机三种网络模式区别

VMware虚拟机提供了三种主要的网络模式&#xff0c;分别是桥接模式&#xff08;Bridged Mode&#xff09;、网络地址转换模式&#xff08;NAT Mode&#xff09;和仅主机模式&#xff08;Host-Only Mode&#xff09;。这三种模式各有其特点和适用场景&#xff0c;以下是对它们之…