文章目录

  • 一、SPI 协议简介
  • 二、硬件电路
    • 2.1.SPI的连接
    • 2.2.数据的移位
    • 2.3.时序基本单元
      • 2.3.1.起始条件和终止条件
      • 2.3.2.模式 0
      • 2.3.3.模式 1
      • 2.3.4.模式 2
      • 2.3.5.模式 3
    • 2.4.时序
  • 三、软件实现
  • 四、W25Q64
    • 4.1.简介
    • 4.2.硬件电路
    • 4.3.框图
    • 4.4.操作注意事项
  • 五、实验

一、SPI 协议简介

SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线,四根通信线:

  • SCK(Serial Clock)
  • MOSI(Master Output Slave Input)
  • MISO(Master Input Slave Output)
  • SS(Slave Select)

具有时钟线 SCK 是同步通信,发送和接收都分别有单独的一根线 MOSI 和 MISO 是全双工模式,支持总线挂载多设备(一主多从)。

二、硬件电路

2.1.SPI的连接

下图是 SPI 协议下的一主多从模式,一个主机与多个从机相互通信,就需要多根 SS 与从机相连,不需要像 IIC 和 CAN 总线协议一样,IIC 指定从机地址来和指定设备通信,CAN 的仲裁段让总线上的设备来竞选谁先发数据,SPI 就像一位大少爷,想和谁通信,就拉一根线与该设备相连接,这样的方法优点是指定从机方式变简单了,缺点是占用的 IO 口增多。

在这里插入图片描述

SS 线可以配置为推挽输出,由主机决定是否要和指定从机通信;MISO 线配置为浮空或者上拉输入,等待从机发送信息,主机接收;MOSI 线配置为复用推挽输出,主机发信息给从机;SCK 线配置为推挽输出,由主机提供时钟。

2.2.数据的移位

下面是 SPI 数据传输的具体移位图,高位先行,例如:当时钟的上升沿到来时,主机和从机的移位寄存器就把一个数据移出寄存器,时钟的下降沿到来时,就移入对方的移位寄存器中,有 8 个数据,就要循环 8 次的时钟。双方的时钟源由主机的波特发生率来提供。

在这里插入图片描述

如果主机只想发送,主机发送了信息,就一定会收到从机交换来的信息,主机选择不理睬就可以了;如果主机只想接收从机发来的信息,主机需要发送一条无关紧要的数据交换从机发来需要的信息。

2.3.时序基本单元

2.3.1.起始条件和终止条件

数据的传输都要在被选中设备的 SS 线由高电平变为低电平开始,SS 线为低电平期间内传输,SS 线由低电平变为高电平数据传输结束。

在这里插入图片描述

2.3.2.模式 0

SPI 数据传输有四个模式,这四个模式由两个变量组成:CPOL (Clock Polarity) 时钟极性;CPHA (Clock Phase) 时钟相位。CPOL=0:空闲状态时,SCK为低电平;CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据。时钟空闲时为低电平,时钟到来的第一个边沿是上升沿,需要移入数据到数据寄存器,那双方的数据在第一个时钟上升沿到来之前就要将数据移出传输线上面,等待第二个边沿移入,也就是下降沿。

在这里插入图片描述

2.3.3.模式 1

CPOL=0:空闲状态时,SCK为低电平;CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据。时钟空闲时为低电平,第一个边沿为上升沿,需要移出数据到传输线上,第二个边沿是下降沿,下降沿到来时就将数据移入对方的数据寄存器中。

在这里插入图片描述

2.3.4.模式 2

CPOL=1:空闲状态时,SCK为高电平;CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据。时钟空闲时为高电平,第一个边沿到来之前需要将数据移出到传输线上,等待第一个边沿到来移入数据,第一个边沿时下降沿,下降沿移入数据,第二个边沿是上升沿,这时候移入数据。

在这里插入图片描述

2.3.5.模式 3

CPOL=1:空闲状态时,SCK为高电平;CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据。时钟空闲时为高电平,第一个边沿为下升沿,需要移出数据到传输线上,第二个边沿是上降沿,上降沿到来时就将数据移入对方的数据寄存器中。

在这里插入图片描述

2.4.时序

  • 指定地址写:向SS指定的设备,发送写指令(0x02),随后在指定地址(Address[23:0])下,写入指定数据(Data):

在这里插入图片描述

  • 指定地址读:向SS指定的设备,发送读指令(0x03),随后在指定地址(Address[23:0])下,读取从机数据(Data):

在这里插入图片描述

三、软件实现

初始化 SPI,PA4 为 SS,PA5 为时钟输出,PA7 为主机接收,PA6 为主机输出:

#include "stm32f10x.h"                  // Device header/*** 函    数:SPI写SS引脚电平,SS仍由软件模拟* 参    数:BitValue 协议层传入的当前需要写入SS的电平,范围0~1* 返 回 值:无* 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SS为低电平,当BitValue为1时,需要置SS为高电平*/
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);		//根据BitValue,设置SS引脚的电平
}/*** 函    数:SPI初始化* 参    数:无* 返 回 值:无*/
void MySPI_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);	//开启SPI1的时钟/*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);					//将PA4引脚初始化为推挽输出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);					//将PA5和PA7引脚初始化为复用推挽输出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);					//将PA6引脚初始化为上拉输入/*SPI初始化*/SPI_InitTypeDef SPI_InitStructure;						//定义结构体变量SPI_InitStructure.SPI_Mode = SPI_Mode_Master;			//模式,选择为SPI主模式SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//方向,选择2线全双工SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//数据宽度,选择为8位SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;		//先行位,选择高位先行SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;	//波特率分频,选择128分频SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;				//SPI极性,选择低极性SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;			//SPI相位,选择第一个时钟边沿采样,极性和相位决定选择SPI模式0SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;				//NSS,选择由软件控制SPI_InitStructure.SPI_CRCPolynomial = 7;				//CRC多项式,暂时用不到,给默认值7SPI_Init(SPI1, &SPI_InitStructure);						//将结构体变量交给SPI_Init,配置SPI1/*SPI使能*/SPI_Cmd(SPI1, ENABLE);									//使能SPI1,开始运行/*设置默认电平*/MySPI_W_SS(1);											//SS默认高电平
}/*** 函    数:SPI起始* 参    数:无* 返 回 值:无*/
void MySPI_Start(void)
{MySPI_W_SS(0);				//拉低SS,开始时序
}/*** 函    数:SPI终止* 参    数:无* 返 回 值:无*/
void MySPI_Stop(void)
{MySPI_W_SS(1);				//拉高SS,终止时序
}/*** 函    数:SPI交换传输一个字节,使用SPI模式0* 参    数:ByteSend 要发送的一个字节* 返 回 值:接收的一个字节*/
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

4.1.简介

W25Qxx 系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景。存储介质:Nor Flash(闪存),时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)。存储容量:

在这里插入图片描述

4.2.硬件电路

该存储芯片一共有 8 个引脚,除了 SPI 必要的四根线,还有自己的电源线、地线,还有写保护和锁定数据,方便被中断打断之后,还能继续发送。

在这里插入图片描述

由下面硬件电路图可知,CS 也是 SS,低电平有效,HOLD 数据锁定和 WP 写保护也是低电平有效。

在这里插入图片描述

4.3.框图

W25Q64 的内存分布如下图所示,该芯片一共有 64M Bite / 8M Byte 大小,存储器以字节为单位,每一字节都有唯一地址。地址由左下角的 0 字节开始自增到 7FFFFF,W25Q64 的地址宽度是 24 位,3 个字节,因此 24 位地址最大的寻址范围是 16MB。每 64KB 分为一个块区,8MB 可以分为 128 个块区,每个块区又可以细分,每 4KB 分为一个扇区,可以分为 16 份。每 4KB 的扇区又可以细分为页,每一页有 256 个字节,4KB * 1024 = 4096 Byte, 4096 Byte / 256 = 16页,每一个扇区又可以分为 16 页:
在这里插入图片描述

4.4.操作注意事项

写入操作时:

  • 写入操作前,必须先进行写使能
  • 每个数据位只能由 1 改写为 0,不能由 0 改写为 1
  • 写入数据前必须先擦除,擦除后,所有数据位变为 1
  • 擦除必须按最小擦除单元进行(页)
  • 连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
  • 写入操作结束后,芯片进入忙状态,不响应新的读写操作

读取操作时:直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取。

五、实验

使用 SPI 协议将 STM32 最小系统板和 W25Q64 进行信息交流,将写入的数据读出来,下面是对 W25Q64 的代码封装和定义:

#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
#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"/*** 函    数:W25Q64初始化* 参    数:无* 返 回 值:无*/
void W25Q64_Init(void)
{MySPI_Init();					//先初始化底层的SPI
}/*** 函    数:W25Q64读取ID号* 参    数:MID 工厂ID,使用输出参数的形式返回* 参    数:DID 设备ID,使用输出参数的形式返回* 返 回 值:无*/
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_JEDEC_ID);			//交换发送读取ID的指令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收MID,通过输出参数返回*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收DID高8位*DID <<= 8;									//高8位移到高位*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//或上交换接收DID的低8位,通过输出参数返回MySPI_Stop();								//SPI终止
}/*** 函    数:W25Q64写使能* 参    数:无* 返 回 值:无*/
void W25Q64_WriteEnable(void)
{MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_WRITE_ENABLE);		//交换发送写使能的指令MySPI_Stop();								//SPI终止
}/*** 函    数:W25Q64等待忙* 参    数:无* 返 回 值:无*/
void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);				//交换发送读状态寄存器1的指令Timeout = 100000;							//给定超时计数时间while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//循环等待忙标志位{Timeout --;								//等待时,计数值自减if (Timeout == 0)						//自减到0后,等待超时{/*超时的错误处理代码,可以添加到此处*/break;								//跳出等待,不等了}}MySPI_Stop();								//SPI终止
}/*** 函    数:W25Q64页编程* 参    数:Address 页编程的起始地址,范围:0x000000~0x7FFFFF* 参    数:DataArray	用于写入数据的数组* 参    数:Count 要写入数据的数量,范围:0~256* 返 回 值:无* 注意事项:写入的地址范围不能跨页*/
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();						//写使能MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_PAGE_PROGRAM);		//交换发送页编程的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位for (i = 0; i < Count; i ++)				//循环Count次{MySPI_SwapByte(DataArray[i]);			//依次在起始地址后写入数据}MySPI_Stop();								//SPI终止W25Q64_WaitBusy();							//等待忙
}/*** 函    数:W25Q64扇区擦除(4KB)* 参    数:Address 指定扇区的地址,范围:0x000000~0x7FFFFF* 返 回 值:无*/
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();						//写使能MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);	//交换发送扇区擦除的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位MySPI_Stop();								//SPI终止W25Q64_WaitBusy();							//等待忙
}/*** 函    数:W25Q64读取数据* 参    数:Address 读取数据的起始地址,范围:0x000000~0x7FFFFF* 参    数:DataArray 用于接收读取数据的数组,通过输出参数返回* 参    数:Count 要读取数据的数量,范围:0~0x800000* 返 回 值:无*/
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start();								//SPI起始MySPI_SwapByte(W25Q64_READ_DATA);			//交换发送读取数据的指令MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位MySPI_SwapByte(Address);					//交换发送地址7~0位for (i = 0; i < Count; i ++)				//循环Count次{DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//依次在起始地址后读取数据}MySPI_Stop();								//SPI终止
}

下面是主程序 main.c 代码实现:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"uint8_t MID;							//定义用于存放MID号的变量
uint16_t DID;							//定义用于存放DID号的变量uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};	//定义要写入数据的测试数组
uint8_t ArrayRead[4];								//定义要读取数据的测试数组int main(void)
{/*模块初始化*/OLED_Init();						//OLED初始化W25Q64_Init();						//W25Q64初始化/*显示静态字符串*/OLED_ShowString(1, 1, "MID:   DID:");OLED_ShowString(2, 1, "W:");OLED_ShowString(3, 1, "R:");/*显示ID号*/W25Q64_ReadID(&MID, &DID);			//获取W25Q64的ID号OLED_ShowHexNum(1, 5, MID, 2);		//显示MIDOLED_ShowHexNum(1, 12, DID, 4);		//显示DID/*W25Q64功能函数测试*/W25Q64_SectorErase(0x000000);					//扇区擦除W25Q64_PageProgram(0x000000, ArrayWrite, 4);	//将写入数据的测试数组写入到W25Q64中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){}
}

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

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

相关文章

Qt中的QWebEngineView

第1章 本地目录结构1.1 自己写的两个网页(html)mermaid.html &#xff08;自己写的网页界面&#xff09;WebTest.html (自己写的网页界面)qwebchannel.js (Qt下载安装之后&#xff0c;会在安装目录下有这个文件&#xff0c;需要将安装目录下的改文件拷贝…

Flutter 应用国际化 (i18n) 与本地化 (l10n) 完整指南

Flutter 国际化 (i18n) 完全指南&#xff1a;从入门到精通 在现代移动应用开发中&#xff0c;支持多语言是触达全球用户的基本要求。Flutter 提供了强大且灵活的国际化 (i18n) 和本地化 (l10n) 支持。本文将带你从零开始&#xff0c;一步步深入掌握在 Flutter 中实现国际化的几…

计算机视觉与深度学习 | 计算机视觉中线特征提取与匹配算法综述

文章目录 一、线特征提取算法原理 1.1 Hough变换及其优化 1.2 LSD算法 1.3 EDLines算法 二、核心数学公式 2.1 直线表示与误差计算 2.2 LSD算法关键公式 三、线特征匹配算法 3.1 LBD描述符 3.2 匹配策略 四、代码实现 4.1 LSD线段检测(Python) 4.2 LBD特征匹配(C++) 五、算…

Transformer 模型:Attention is All You Need 的真正含义

2017 年&#xff0c;Google Brain 发布了一篇具有里程碑意义的论文——《Attention Is All You Need》&#xff0c;这篇论文不仅首次提出了 Transformer 模型&#xff0c;更重要的是&#xff0c;它宣称“注意机制&#xff08;Attention Mechanism&#xff09;就足以构建强大的模…

数据库约束表的设计

数据库约束概念&#xff1a;数据库约束是关系型数据库的一个重要功能&#xff0c;主要是保证数据的完整性&#xff0c;也可理解为数据的正确性&#xff08;数据本身是否正确&#xff0c;关联关系是否正确&#xff09;&#xff08;一般是用在指定列上&#xff09;常见的约束类型…

【案例分享】TeeChart 助力 Softdrill 提升油气钻井数据可视化能力

在钻井与地质工程领域&#xff0c;数据可视化是核心环节。图表不仅需要精确与高效&#xff0c;还需符合行业习惯并支持交互与定制。Softdrill 自 2012 年起在核心产品中集成了TeeChart 图表库&#xff0c;将复杂的井下数据转化为直观的工程图表&#xff0c;极大提升了钻井工程师…

【Flink】Flink Runtime 架构设计

Flink Runtime 架构设计 整体架构 ┌─────────────────────────────────────────────────────────────────┐ │ Flink Runtime │ ├─────────…

Git 命令教程

Git介绍 分布式版本控制系统。 Git命令 初始化/全局配置git init初始化一个Git仓库&#xff08;会创建一个.git的目录&#xff09;git config --global user.name “name”设置提交时的用户名git config user.name查看设置的用户名git config --global user.email “youemail.c…

git config --global user.name指令报错时的解决方案

问题分析 %HOMEDRIVE%%HOMEPATH%/.gitconfig 是Windows环境变量的表示方式&#xff1a; %HOMEDRIVE% 通常是 C:%HOMEPATH% 通常是 \Users\你的用户名完整路径应该是&#xff1a;C:\Users\你的用户名\.gitconfig 但这里环境变量没有被正确解析&#xff0c;显示的是字面意思。 …

websocket和socket io的区别

好的&#xff0c;这是一个更具体也更常见的问题。WebSocket 是一种协议&#xff0c;而 Socket.IO 是一个库&#xff0c;它使用了 WebSocket 但提供了多得多的功能。 简单比喻&#xff1a; WebSocket 就像是给你提供了一条高效的“快递专线”&#xff08;双向通信通道&#xff…

Nginx反向代理与负载均衡部署

Nginx反向代理与负载均衡部署实战指南前言一、规划部署负载均衡和反向代理二、部署Nginx负载均衡器2.1. 准备基础环境2.2. 创建Nginx运行用户2.3. 编译安装Nginx2.4. 配置Nginx系统服务2.5. 验证Nginx安装三、部署后端2台Tomcat应用服务器3.1. 安装JDK3.2. 部署Tomcat实例13.3.…

从源码和设计模式深挖AQS(AbstractQueuedSynchronizer)

AQS 概念 AbstractQueuedSynchronizer&#xff08;AQS&#xff09; 是 Java 并发包 (java.util.concurrent.locks) 的核心基础框架&#xff0c;它的实现关键是先进先出 (FIFO) 等待队列和一个用volatile修饰的锁状态status。具体实现有 : ReentrantLock、Semaphore、CountDownL…

Dart → `.exe`:Flutter 桌面与纯命令行双轨编译完全指南

Dart → .exe&#xff1a;Flutter 桌面与纯命令行双轨编译完全指南 关键词&#xff1a;Dart、Flutter、Windows、可执行文件、桌面端、CLI、交叉编译 1. 前言 很多开发者以为 Dart 只能跑在 AOT 移动端或 Web 端&#xff0c;其实 官方工具链早已支持一键输出 Windows 原生 .ex…

互联网接入网中PPPoE和PPP协议

<摘要> PPPoE和PPP是宽带接入网络中至关重要的协议组合&#xff0c;其中PPP提供通用的点对点链路层解决方案&#xff0c;而PPPoE则是在以太网架构上扩展PPP应用的技术桥梁。本文从技术演进视角系统解析了两者的内在关联与本质区别&#xff1a;PPP作为成熟链路层协议&…

详细解析SparkStreaming和Kafka集成的两种方式的区别和优劣

spark streaming是基于微批处理的流式计算引擎&#xff0c;通常是利用spark core或者spark core与spark sql一起来处理数据。在企业实时处理架构中&#xff0c;通常将spark streaming和kafka集成作为整个大数据处理架构的核心环节之一。 针对不同的spark、kafka版本&#xff0…

Kite Compositor for Mac v2.1.2 安装教程|DMG文件安装步骤(Mac用户必看)

Kite Compositor​ 是一款专为 ​macOS​ 设计的 ​轻量级界面设计 & 动画制作工具&#xff0c;它可以让你像拼图一样直观地 ​创建、编辑和预览用户界面&#xff08;UI&#xff09;以及动画效果。 一、下载文件 首先&#xff0c;你得先把这个 ​Kite Compositor for Mac …

【逆向】Android程序静态+动态分析——去壳

对提供的 CrackmeTest.apk 进行逆向分析&#xff0c;程序含有反调试机制&#xff08;加壳&#xff09;&#xff0c;通过静态补丁反反调试&#xff08;去壳&#xff09;&#xff0c;再动态调试获取其中密码。 目录 环境 基础 实验内容 静态分析 动态分析 反反调试 再动态…

Rust 开发环境安装与 crates.io 国内源配置(Windows / macOS / Linux 全流程)

Rust 这几年在系统编程、WebAssembly、区块链、后端服务领域越来越火&#xff0c;很多开发者都在尝试用它做一些新项目。 但是国内安装 Rust 开发环境时&#xff0c;经常遇到 安装慢、依赖拉不下来、crates.io 超时 等问题。本文结合个人踩坑经验&#xff0c;整理了一份 跨平台…

Nginx SSL/TLS 配置

Nginx SSL/TLS 配置指南&#xff1a;从入门到安全强化前言一、环境准备&#xff1a;Nginx安装配置1.1. **EPEL仓库配置**&#xff1a;1.2. **Nginx安装**&#xff1a;1.3. **服务启停管理**&#xff1a;1.4. **服务状态验证**&#xff1a;二、SSL/TLS证书获取方案方案A&#xf…

Java ReentrantLock和synchronized的相同点与区别

1. 核心概念与定位synchronized&#xff1a;Java 内置的关键字&#xff0c;属于 JVM 层面的隐式锁。通过在方法或代码块上声明&#xff0c;自动实现锁的获取与释放&#xff0c;无需手动操作。设计目标是提供简单易用的基础同步能力&#xff0c;适合大多数常规同步场景。Reentra…