STM32—SPI協議

文章目錄

  • 一、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…