STM32CubeIDE (SPI 読み取りおよび書き込みフラッシュ)

参考: ① 時間厳守の Atomic MINI チュートリアル ②STM32F103 は STM32CubeMX と連携して SPI の読み書きを実装します flash_stm32f103 cube spi_zerfew のブログ - CSDN ブログ

目次

1. 理論的知識

1.SPI特性

2. SPI ブロック図

3. SPI動作モード

4、W25Q64

4.1 NOR FLASHの特徴

4.2 W25Q64 チップのピン図

 4.3 NOR FLASH の動作シーケンス

5. SPI の主な構造

2. STM32Cubeの構成

1. チップセレクトピン PA2

 2. SPIの設定

3. コードの書き方

1. 関数宣言

2. 関数呼び出し

3. 関数の定義


1. 理論的知識

1.SPI特性

SPI とは Serial Peripheral Interface の英語の略称で、その名の通りシリアル ペリフェラル インターフェイスのことです。 SPI 通信プロトコルは、Motorola の MC68HCXX シリーズ プロセッサで最初に定義されました。 SPI インターフェイスは、高速の全二重同期通信バスであり、多くの MCU、メモリ チップ、AD コンバータ、LCD の間で広く使用されています。ほとんどの STM32 には 3 つの SPI インターフェイスがあります。


● 3 線式全二重同期伝送
● 3 番目の双方向データ ラインの有無にかかわらず 2 線式単信同期伝送
● 8 または16 ビット伝送フレーム形式の選択
● マスターまたはスレーブ動作
● マルチマスター モードのサポート
● 8 つのマスター モード ボーレート プリスケーラ係数 (最大 fPCLK/2)
● スレーブ モード周波数 (最大 fPCLK/2)
● マスター モードとスレーブ モード 高速通信< a i=8> ● NSS 管理は、マスター モードとスレーブ モードの両方でソフトウェアまたはハードウェアによって実行できます。マスター/スレーブ動作モードの動的な変更 ● プログラム可能なクロックの極性と位相< a i=10> ● プログラム可能なデータ順序、MSB ファーストまたは LSB ファースト ● 割り込みをトリガーする専用の送信および受信フラグ ● SPI バス ビジー ステータス フラグ ● 1 バイトの送信とDMA 機能をサポートする受信バッファ: 送信リクエストと受信リクエストを生成 ● メイン モードの障害、割り込みをトリガーする可能性のある過負荷および CRC エラー フラグ ─ 自動全二重モードで受信した最後のバイトの CRC チェック ─ 送信モードでは、CRC 値を最後のワードとして使用できます セクション送信 ● 信頼性の高い通信をサポートするハードウェア CRC









2. SPI ブロック図

①SPIピン情報:

MISO (マスターイン/スレーブアウト) マスターデバイスのデータ入力、スレーブデバイスのデータ出力。
MOSI (マスター出力/スレーブイン) マスターデバイスのデータ出力、スレーブデバイスのデータ入力。
マスター デバイスによって生成される SCLK (シリアル クロック) クロック信号。
CS (チップセレクト) マスターデバイスによって生成されるスレーブデバイスのチップ選択信号。

②SPIの動作原理:

マスターとスレーブの両方にシリアル シフト レジスタがあります。マスターは SPI シリアル レジスタにバイトを書き込むことで転送を開始します
。シリアル シフト レジスタは、MOSI 信号ラインを介してスレーブにバイトを送信し、スレーブはまた、MISO 信号ラインを介してシリアル シフト レジスタの内容をマスタに返します。このようにして、2 つのシフト レジスタの の内容が交換されます。ペリフェラルの書き込み操作と読み取り操作は同期して完了します。単なる書き込み操作の場合、ホストは受信したバイトを単純に無視します。逆に、マスターがスレーブからバイトを読み取りたい場合は、ヌル バイトを送信してスレーブに送信を行わせる必要があります。



 

③SPI伝送方式:

SPI バスには、全二重、単信、半二重の 3 つの伝送モードがあります。
全二重通信とは、いつでもホストとスレーブ間で同時にデータを送受信できることを意味します。
シンプレックス通信は、同時に、送信または受信の一方向のみの送信が存在することを意味します。
半二重通信とは、データを同時に一方向にのみ送信できることを意味します。

3. SPI動作モード

CPOL (特にクロック極性と呼ばれます) はクロック極性です。マスターとスレーブ間でデータ送信がない場合、 はクロック極性です。アイドル状態、
SPI は 4 つのモードに分かれています。最も一般的に使用されるのはモード 0 とモード 3 です。 は立ち上がりエッジです。  は、データが 2 番目のエッジ、つまり偶数エッジからサンプリングされることを意味します。エッジの極性に注意してください。上記の CPHA と同じ =0 同じエッジケース。前のものは、SCL アイドル状態から直接遷移する奇数エッジのサンプリング データで、アイドル状態がハイ レベルの場合は立ち下がりエッジ、それ以外の場合は立ち上がりエッジです。 CPHA=1 は偶数エッジサンプリングなので偶数エッジで判定する必要があり、最初のエッジ、つまり奇数エッジが立ち下がりエッジの場合、そのエッジ極性は偶数番目のエッジCPHA=1 の場合、状況は 2 つあります。1 つは CS イネーブルのエッジで、もう 1 つはデータの前のフレームの最後のクロック エッジです。 CPOL=0。CPHA = 0 の場合は、データが最初のエッジ信号からサンプリングされることを意味します。立ち上がりエッジか立ち下がりエッジかという具体的な問題は、CPOL によって決まります。 CPHA は基本的にデータのサンプリング時間を指します は、特にクロック位相と呼ばれ、クロック位相です。同期通信では、データの変化とサンプリングはクロックエッジで実行されます。各クロックサイクルには立ち上がりエッジと立ち下がりエッジの 2 つのエッジがあり、データ変化とサンプリングはそれぞれ 2 つの異なるエッジに配置されます。データが生成されて安定するまでの時間がかかるため、最初のエッジ信号でデータを出力すると、スレーブは 2 番目のエッジ信号からのみデータをサンプリングできます。
CPHA、アイドル状態がハイ レベルの場合は CPOL=1、アイドル状態がロー レベルの場合は、 CPOL = 0。 SCL ラインのレベル ステータス

モード0:

 CPOL=0&&CPHA=0のタイミングで、上図はシリアルクロックの奇数エッジの立ち上がりエッジでサンプリングしている様子です まず、CPOL=0に設定されているので、データが取り出されるタイミングがわかると思います。が送信されていない、または送信済みの場合、SCL のステータスは Low レベル、CPHA=0 は奇数エッジ取得を意味します。したがって、送信データは奇数エッジの立ち上がりエッジで収集されます。MOSI および MISO データの有効信号は安定した状態を維持し、SCK の奇数エッジでサンプリングされる必要があります。MOSI および MISO の有効信号は、非同期時にのみ変化します。サンプリングの瞬間。

モード 3:

 上の図は CPOL=1&&CPHA=1 の状況です。データが送信されていないのと、データが送信されていることがわかります。SCL の状態
はハイレベルで奇数エッジ、 のエッジ極性は立ち上がりエッジ、偶数番目のエッジは立ち下がりエッジです。
CPHA=1 であるため、データは偶数エッジの立ち上がりエッジでサンプリングされます。 MOSI と MISO は奇数番号のエッジで変化し、偶数番号のエッジで安定します。

SPI 制御レジスタ 1 (SPI_CR1)
SPI ステータス レジスタ (SPI_SR)
SPI データ レジスタ (SPI_DR)

4、W25Q64

4.1 NOR FLASHの特徴

W25Q64 は、8M の大容量 SPI フラッシュ製品です。 8M バイトの容量を 128 のブロック (Block) に分割し、各ブロックのサイズは 64K バイト、各ブロックは 16 のセクター (セクター) に分割され、各セクターは 16 ページ、1 つあたり 256 バイトです。ページ、つまりセクターあたり 4K バイトです。

W25Q64 の最小消去単位は 1 セクターです。つまり、毎回 4K バイトを消去する必要があります
。このように、SRAM要件が比較的高いW25Q64では少なくとも4Kのキャッシュ領域を開く必要があり、チップが正常に動作するには4K以上のSRAMが必要です。 W25Q64 は、消去および書き込みサイクルが最大 10W、データ保持期間が 20 年、電圧 2.7 ~ 3.6V をサポートし、標準 SPI に加えて、デュアル出力/クワッド出力 SPI もサポートします。クロックは 80Mhz に達します (デュアル出力時は 160Mhz に相当、クワッド出力時は 320Mhz に相当)。

フラッシュの読み取りは非常に簡単で、spi に対応する命令を実行するだけです。
フラッシュの書き込みはさらに複雑です。まず、フラッシュは上書きをサポートしていないことを知っておく必要があります。フラッシュの書き込み操作は 1 を 0 に変更することしかできず、0 を 1 に変更することはできないためです。0 を 1 に変更したい場合は、消去操作しか使用できません。フラッシュに書き込む場合、対応する位置の値がすべて 1 ではない場合は、最初に消去操作を実行してから書き込み操作を実行する必要があります。

W25Q64 の最小消去単位は 1 セクタです。つまり、毎回 4K バイトを消去する必要があります。したがって、消去するセクタの内容を保存するには、プログラム内で 4K 以上の配列変数を定義してください。

4.2 W25Q64 チップのピン図

チップ ピンは次のように接続されています: CS はチップ選択信号入力で、ロー レベルでアクティブです。DO は MISO ピンで、CLK ピンの立ち下がりエッジでデータを出力します。
; WP は書き込み保護ピン、高レベルは読み取りと書き込みが可能、低レベルは読み取りのみ可能; DI は MOSI ピン、
ホストによって送信されるデータ、アドレス、およびコマンドはSI ピンからチップへの入力 内部的に、キャプチャ データは CLK ピンの立ち上がりエッジでキャプチャされます。
CLK はシリアル クロック ピンであり、入出力にクロック パルスを提供します。HOLD はホールドピン、ローレベルでアクティブ。
STM32F103 は、SPI バス経由で W25Q64 の対応するピンに接続することでデータ送信を開始できます。

 4.3 NOR FLASH の動作シーケンス

①リード動作タイミング

 データ読み取りコマンドは 03H で、1 バイトまたは複数バイトを読み取ることができます。読み取り動作を開始するときは、まず CS チップ選択ピンを Low に引き下げ、次に MOSI ピンを介してチップに 03H を送信し、次に読み取られる 24 ビット アドレスを送信します。この 一部のデータCLK の立ち上がりエッジでサンプリングされます。チップが 24 ビット アドレスを受信すると、CLK ピンの立ち下がりエッジで MISO ピンから対応するアドレスのデータを送信します。この図から、CLK が常に動作している限り、チップ記憶領域全体のデータを read 命令によって読み出すことができることがわかります。ホストが CS ピンを High にすると、データ送信が停止します



②ページ書き込みタイミング

 ページ書き込みコマンドを送信する前に、まず「書き込み可能」コマンドを送信する必要があります。次に、ホストは CS ピンを Low にし、
MOSI ピンを介して 02H をチップに送信し、次に 24 ビット アドレスを送信します。最後に、書き込む必要があるバイト データを送信できます。
をチップに接続します。データの書き込みが完了したら、CS ピンを High にしてデータ送信を停止する必要があります。

③セクタ消去タイミング

 セクタ消去とはセクタを消去することを指し、W25Q64 のセクタサイズは 4K バイトです。セクタが消去されると、セクタ内のすべてのビットが 1 に設定されます。つまり、セクタ バイトは FFh になります。同様に、セクタ消去を実行する前に、最初にライトイネーブルコマンドを実行する必要があります。ここで注意する必要があるのは、現在の SPI バスのステータスです。バスのステータスが BUSY の場合、このセクタ消去は無効です。したがって、CS ピンを Low にしてデータ送信の準備をする前に、SPI バスのステータスを確認する必要があります。ステータス レジスタの BUSY ビットを読み取るには、ステータス レジスタ読み出し命令を実行する必要があります。消去作業を実行する前に、BUSY ビットが 0 になるまで待つ必要があります。次に、タイミング図分析によると、ホストは最初に CS ピンを Low にプルし、次に MOSI ピンを介して命令コード 20h をチップに送信し、次に 24 ビットのセクター アドレスをチップに送信します。 CS ピンを High にしてレジスタを読み出し、ステータスはセクタ消去動作が完了するのを待っています。
さらに、チップ全体を消去する操作があります。セクタ消去よりもタイミングが簡単です。24 ビットのアドレスを送信する必要はありません。 のみ命令コード C7h を送信する必要があり、チップに到達することで消去できます。 W25Q64 マニュアルには読み取り/書き込み/消去操作の多くの方法が記載されています。W25Q64 マニュアルを参照してください。

5. SPI の主な構造

typedef struct __SPI_HandleTypeDef
{
SPI_TypeDef *Instance; /* SPI 寄存器基地址 */
SPI_InitTypeDef Init; /* SPI 通信参数 */
uint8_t *pTxBuffPtr; /* SPI 的发送缓存 */
uint16_t TxXferSize; /* SPI 的发送数据大小 */
__IO uint16_t TxXferCount; /* SPI 发送端计数器 */
uint8_t *pRxBuffPtr; /* SPI 的接收缓存 */
uint16_t RxXferSize; /* SPI 的接收数据大小 */
__IO uint16_t RxXferCount; /* SPI 接收端计数器 */
void (*RxISR)(struct __SPI_HandleTypeDef *hspi); /* SPI 的接收端中断服务函数 */
void (*TxISR)(struct __SPI_HandleTypeDef *hspi); /* SPI 的发送端中断服务函数 */
DMA_HandleTypeDef *hdmatx; /* SPI 发送参数设置(DMA) */
DMA_HandleTypeDef *hdmarx; /* SPI 接收参数设置(DMA) */
HAL_LockTypeDef Lock; /* SPI 锁对象 */
__IO HAL_SPI_StateTypeDefState; /* SPI 传输状态 */
__IO uint32_t ErrorCode; /* SPI 操作错误代码 */
} SPI_HandleTypeDef;


typedef struct
{
uint32_t Mode; /* 模式:主(SPI_MODE_MASTER)从(SPI_MODE_SLAVE) */
uint32_t Direction; /* 方向:只接收模式 单线双向通信数据模式 全双工 */
uint32_t DataSize; /* 数据帧格式: 8 位/16 位 */
uint32_t CLKPolarity; /* 时钟极性 CPOL 高/低电平 */
uint32_t CLKPhase; /* 时钟相位 奇/偶数边沿采集 */
uint32_t NSS; /* SS 信号由硬件(NSS)管脚控制还是软件控制 */
uint32_t BaudRatePrescaler; /* 设置 SPI 波特率预分频值*/
uint32_t FirstBit; /* 起始位是 MSB 还是 LSB */
uint32_t TIMode; /* 帧格式 SPI motorola 模式还是 TI 模式 */
uint32_t CRCCalculation; /* 硬件 CRC 是否使能 */
uint32_t CRCPolynomial; /* 设置 CRC 多项式*/
} SPI_InitTypeDef;

2. STM32Cubeの構成

1. チップセレクトピン PA2

オプションのプルアップ モード、リセット状態はロー レベル状態 CS チップ セレクトは有効

 2. SPIの設定

全二重ホスト モードを選択し、CPOL CPHA を設定し、通常は SPI 動作モード 0 および 3 を使用し、要件に応じて CRC チェックを有効にします。

3. コードの書き方

1. 関数宣言

/* USER CODE BEGIN PFP */
void flash_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t write_len);
void flash_write_page(uint8_t *pbuffer, uint32_t write_addr, uint16_t write_len);
void flash_erase(uint32_t fan_addr);
void spi_read_flash(uint8_t *pbuffer, uint32_t read_addr, uint16_t read_len);
void spi_write_flash(uint8_t *pbuffer, uint32_t write_addr, uint16_t write_len);
/* USER CODE END PFP */

2. 関数呼び出し

	/* USER CODE BEGIN 2 */
	uint8_t writeMsg[100] = "1111111111";
	uint8_t readMsg[100] = "";
	uint32_t flash_size = 8*1024*1024;

	spi_write_flash(writeMsg, flash_size - 1000, sizeof(writeMsg));
	spi_read_flash(readMsg, flash_size - 1000, sizeof(readMsg));
	/* USER CODE END 2 */

3. 関数の定義

/* USER CODE BEGIN 4 */
void flash_write(uint8_t *pbuffer, uint32_t write_addr, uint16_t write_len)
{
	uint16_t page_remain;  // 当前页面剩余可写字节数

	// 计算当前页剩余可写字节数
	page_remain = 256 - write_addr % 256;

	// 如果要写入的数据长度小于等于当前页剩余可写字节数,直接写入当前页
	if (write_len <= page_remain)
	{
		page_remain = write_len;
		flash_write_page(pbuffer, write_addr, page_remain);
	} else
	{
		// 如果要写入的数据长度超过当前页剩余可写字节数,需要进行分页写入
		while (1)
		{
			flash_write_page(pbuffer, write_addr, page_remain); // 写入当前页数据
			if (write_len == page_remain)
			{
				break; // 数据已全部写入
			} else
			{
				// 更新缓冲区指针、写入地址和剩余长度
				pbuffer += page_remain;
				write_addr += page_remain;
				write_len -= page_remain;

				// 根据剩余长度判断下一页要写入的字节数
				if (write_len > 256)
				{
					page_remain = 256;
				} else
				{
					page_remain = write_len;
				}
			}
		}
	}
}

void flash_write_page(uint8_t *pbuffer, uint32_t write_addr, uint16_t write_len)
{
	uint16_t i = 0;
	uint8_t send_cmd = 0;        // 发送指令
	uint8_t recv_cmd = 0;        // 接收指令

	// 写使能
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
	send_cmd = 0x06;             // 写使能指令
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0);
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);

	// 写页数据
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
	send_cmd = 0x02;             // 写页指令
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0);

	send_cmd = (uint8_t) (write_addr >> 16); // 获取写入地址的高位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址高位并接收响应
	send_cmd = (uint8_t) (write_addr >> 8);  // 获取写入地址的中间位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址中间位并接收响应
	send_cmd = (uint8_t) write_addr;          // 获取写入地址的低位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址低位并接收响应

	for (i = 0; i < write_len; i++)
	{
		send_cmd = pbuffer[i]; // 获取要写入的数据
		HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送数据并接收响应
	}

	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // 禁用片选信号(CS)

	while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY)
		; // 等待 SPI 完成操作
}

void flash_erase(uint32_t fan_addr)
{
	uint8_t send_cmd = 0;   // 发送指令
	uint8_t recv_cmd = 0;   // 接收指令
	fan_addr *= 4096;       // 将扇区地址转换为字节地址(每个扇区大小为 4096 字节)

	// 写使能
	send_cmd = 0x06;        // 写使能指令
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0);
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
	while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY){}

	// 扇区擦除
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
	send_cmd = 0x20;        // 扇区擦除指令
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0);

	//8M 23字节?
	send_cmd = (uint8_t) (fan_addr >> 16); // 获取擦除地址的高位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址高位并接收响应
	send_cmd = (uint8_t) (fan_addr >> 8);  // 获取擦除地址的中间位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址中间位并接收响应
	send_cmd = (uint8_t) fan_addr;          // 获取擦除地址的低位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址低位并接收响应

	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // 禁用片选信号(CS)
	while (HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY)
		;
}

void spi_read_flash(uint8_t *pbuffer, uint32_t read_addr, uint16_t read_len)
{
	uint8_t send_cmd;   // 发送指令
	uint8_t recv_cmd;   // 接收指令
	uint16_t i;

	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // 选择片选信号(CS)低电平使能
	send_cmd = 0x03;   // 读数据指令

	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送读指令并接收响应

	//8M 23位? (24位)
	send_cmd = (uint8_t) (read_addr >> 16); // 获取读取地址的高位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址高位并接收响应
	send_cmd = (uint8_t) (read_addr >> 8);  // 获取读取地址的中间位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址中间位并接收响应
	send_cmd = (uint8_t) read_addr;          // 获取读取地址的低位字节
	HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送地址低位并接收响应

	send_cmd = 0xff;    // 发送空数据以接收闪存中的数据
	for (i = 0; i < read_len; i++)
	{
		HAL_SPI_TransmitReceive(&hspi1, &send_cmd, &recv_cmd, 1, 0); // 发送空数据并接收闪存数据
		pbuffer[i] = recv_cmd;  // 将接收到的数据存储在缓冲区中
	}

	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // 禁用片选信号(CS)
}

uint8_t flash_buffer[4096];  // 用于缓存闪存数据的缓冲区
void spi_write_flash(uint8_t *pbuffer, uint32_t write_addr, uint16_t write_len)
{
	uint32_t sector_index;      // 扇区索引
	uint16_t sector_offset;     // 扇区内的偏移
	uint16_t sector_remain;     // 扇区内剩余可写字节数
	uint16_t i = 0;             // 循环计数器

	sector_index = write_addr / 4096;    // 计算扇区索引
	sector_offset = write_addr % 4096;   // 计算扇区内偏移
	sector_remain = 4096 - sector_offset; // 计算扇区内剩余可写字节数

	// 如果要写入的数据长度小于等于扇区内剩余可写字节数,直接写入扇区
	if (write_len <= sector_remain)
	{
		sector_remain = write_len;
		flash_write(pbuffer, write_addr, sector_remain);
	} else
	{
		// 如果要写入的数据长度超过扇区内剩余可写字节数,需要分多次写入
		while (1)
		{
			spi_read_flash(flash_buffer, sector_index * 4096, 4096); // 读取整个扇区的数据

			// 检查扇区内是否有非擦除状态的数据
			for (i = 0; i < sector_remain; i++)
			{
				if (flash_buffer[sector_offset + i] != 0xFF)
				{
					break; // 如果存在非擦除状态的数据,跳出循环
				}
			}

			if (i < sector_remain)
			{
				flash_erase(sector_index); // 擦除整个扇区

				// 将新数据写入缓冲区
				for (i = 0; i < sector_remain; i++)
				{
					flash_buffer[sector_offset + i] = pbuffer[i];
				}

				flash_write(flash_buffer, sector_index * 4096, 4096); // 将缓冲区数据写入扇区
			} else
			{
				flash_write(pbuffer, write_addr, sector_remain); // 直接写入扇区
			}

			if (write_len == sector_remain)
			{
				break; // 数据已全部写入
			} else
			{
				sector_index++;
				sector_offset = 0;

				pbuffer += sector_remain;
				write_addr += sector_remain;
				write_len -= sector_remain;

				if (write_len > 4096)
				{
					sector_remain = 4096;
				} else
				{
					sector_remain = write_len;
				}
			}
		}
	}
}
/* USER CODE END 4 */

Supongo que te gusta

Origin blog.csdn.net/qq_57594025/article/details/132411070
Recomendado
Clasificación