1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
#include "./flash/bsp_spi_flash.h"
SPI_HandleTypeDef SpiHandle;
/* MSP:开时钟 + 配 GPIO(SCK/MOSI/MISO 复用,CS 普通推挽输出) */
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
GPIO_InitTypeDef GPIO_InitStruct;
SPIx_SCK_GPIO_CLK_ENABLE();
SPIx_MISO_GPIO_CLK_ENABLE();
SPIx_MOSI_GPIO_CLK_ENABLE();
SPIx_CS_GPIO_CLK_ENABLE();
SPIx_CLK_ENABLE();
GPIO_InitStruct.Pin = SPIx_SCK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SPIx_MISO_PIN; // MISO 同样复用
HAL_GPIO_Init(SPIx_MISO_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SPIx_MOSI_PIN;
HAL_GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = FLASH_CS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // CS 用软件普通 GPIO 控制
HAL_GPIO_Init(FLASH_CS_GPIO_PORT, &GPIO_InitStruct);
}
/* SPI 参数配置:主模式、8 位、MSB 先行、模式 3、软件 NSS、4 分频 */
void SPI_FLASH_Init(void)
{
SpiHandle.Instance = SPIx;
SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // 72M/4=18MHz
SpiHandle.Init.Direction = SPI_DIRECTION_2LINES; // 全双工
SpiHandle.Init.CLKPhase = SPI_PHASE_2EDGE; // CPHA=1
SpiHandle.Init.CLKPolarity = SPI_POLARITY_HIGH; // CPOL=1(模式3)
SpiHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
SpiHandle.Init.CRCPolynomial = 7;
SpiHandle.Init.DataSize = SPI_DATASIZE_8BIT;
SpiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
SpiHandle.Init.NSS = SPI_NSS_SOFT; // 软件管理 CS
SpiHandle.Init.TIMode = SPI_TIMODE_DISABLE;
SpiHandle.Init.Mode = SPI_MODE_MASTER;
HAL_SPI_Init(&SpiHandle);
__HAL_SPI_ENABLE(&SpiHandle);
}
/* 向 Flash 发送 1 字节,同时读出 1 字节(SPI 全双工特性) */
uint8_t SPI_FLASH_SendByte(uint8_t byte)
{
/* 等待发送缓冲区空 */
while (__HAL_SPI_GET_FLAG(&SpiHandle, SPI_FLAG_TXE) == RESET);
WRITE_REG(SpiHandle.Instance->DR, byte); // 写入数据寄存器
/* 等待接收缓冲区非空 */
while (__HAL_SPI_GET_FLAG(&SpiHandle, SPI_FLAG_RXNE) == RESET);
return READ_REG(SpiHandle.Instance->DR); // 读出收到的数据
}
/* 读 JEDEC ID:发 0x9F 后连续收 3 字节 */
uint32_t SPI_FLASH_ReadID(void)
{
uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
SPI_FLASH_CS_LOW();
SPI_FLASH_SendByte(W25X_JedecDeviceID);
Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
SPI_FLASH_CS_HIGH();
Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2; // 组合成 24 位 ID
return Temp;
}
/* 扇区擦除(4KB):写使能 -> 发指令+3字节地址 -> 等待写完 */
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
SPI_FLASH_WriteEnable();
SPI_FLASH_WaitForWriteEnd();
SPI_FLASH_CS_LOW();
SPI_FLASH_SendByte(W25X_SectorErase);
SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16); // 地址高字节
SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8); // 地址中字节
SPI_FLASH_SendByte(SectorAddr & 0xFF); // 地址低字节
SPI_FLASH_CS_HIGH();
SPI_FLASH_WaitForWriteEnd();
}
/* 按页写:最多写一页 256 字节,写完等待内部编程结束 */
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
SPI_FLASH_WriteEnable();
SPI_FLASH_CS_LOW();
SPI_FLASH_SendByte(W25X_PageProgram);
SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
SPI_FLASH_SendByte(WriteAddr & 0xFF);
if (NumByteToWrite > SPI_FLASH_PerWritePageSize)
NumByteToWrite = SPI_FLASH_PerWritePageSize; // 超页则截断
while (NumByteToWrite--)
SPI_FLASH_SendByte(*pBuffer++); // 逐字节写入
SPI_FLASH_CS_HIGH();
SPI_FLASH_WaitForWriteEnd();
}
/* 缓冲区写:自动拆分跨页,内部调用 PageWrite */
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
Addr = WriteAddr % SPI_FLASH_PageSize; // 起始地址在页内的偏移
count = SPI_FLASH_PageSize - Addr; // 当前页还能写多少
NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
if (Addr == 0) // 地址正好页对齐
{
if (NumOfPage == 0) // 不到一页
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
else
{
while (NumOfPage--) // 整页挨个写
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
WriteAddr += SPI_FLASH_PageSize;
pBuffer += SPI_FLASH_PageSize;
}
if (NumOfSingle != 0) // 尾巴不足一页
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
}
}
else // 地址未对齐
{
if (NumOfPage == 0)
{
if (NumOfSingle > count) // 当前页放不下,拆成两段
{
temp = NumOfSingle - count;
SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
SPI_FLASH_PageWrite(pBuffer + count, WriteAddr + count, temp);
}
else
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
}
else // 多页情况,先填满当前页再整页写
{
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
WriteAddr += count; pBuffer += count;
while (NumOfPage--)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize;
}
if (NumOfSingle != 0)
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
}
}
}
/* 读数据:发 0x03 + 3 字节地址,随后逐字节收 */
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
SPI_FLASH_CS_LOW();
SPI_FLASH_SendByte(W25X_ReadData);
SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
SPI_FLASH_SendByte(ReadAddr & 0xFF);
while (NumByteToRead--)
*pBuffer++ = SPI_FLASH_SendByte(Dummy_Byte); // 每发 1 个哑字节,收 1 个真实数据
SPI_FLASH_CS_HIGH();
}
|