这里用的是野火霸道的板子,搭载了STM32F103ZET6. 目前所学知识尚且较为简单,这里的笔记仅仅记录一下学习过程中的代码,方便以后回头复习以及复制。主要参考资料——《零死角玩转 STM32F103—霸道_V2 开发板》,视频——150集-野火F103霸道/指南者视频教程-中级篇

硬件IIC读写EEPROM

原理参考:IIC完整讲解

STM32 BUG参考:

  1. STM32 I2C控制器使用库函数时卡在CheckEvent的解决建议
  2. STM32_I2C,不稳定,死在I2C_CheckEvent解决办法

所用EEPROM型号为AT24C02,有32页,每页8字节,需8位地址寻址。EEPROM部分的原理图如下所示。图中只有一个从机(即EEPROM),当实际电路中有多个从机时,SCL和SDA两根线上不需在R218、R219的基础上再加入上拉电阻。
EEPROM部分原理图

宏部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define EEPROM_I2Cx                 I2C1
#define EEPROM_I2C_ClockCmd RCC_APB1PeriphClockCmd
#define EEPROM_I2C_CLK RCC_APB1Periph_I2C1
#define EEPROM_I2C_GPIO_ClockCmd RCC_APB2PeriphClockCmd
#define EEPROM_I2C_GPIO_CLK RCC_APB2Periph_GPIOB
#define EEPROM_I2C_SCL_PORT GPIOB
#define EEPROM_I2C_SCL_PIN GPIO_Pin_6
#define EEPROM_I2C_SDA_PORT GPIOB
#define EEPROM_I2C_SDA_PIN GPIO_Pin_7
#define I2C_Speed 400000 // <= 400kbit
#define I2Cx_OWN_ADDR1 0x00 // STM32设备本身的地址
#define I2C_PageSize 8 // 根据AT24C01/02手册调整
#define EEPROM_ADDR 0xA0 // EEPROM设备8位地址
#define I2C_TIMEOUT_LOOP 0x2000 // 超时设置

核心函数

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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
static uint32_t I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;

/**
* @brief 初始化I2C EEPROM
*
*/
void I2C_EEPROM_Init(void){
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
EEPROM_I2C_ClockCmd(EEPROM_I2C_CLK, ENABLE);
EEPROM_I2C_GPIO_ClockCmd(EEPROM_I2C_GPIO_CLK, ENABLE);
// GPIO初始化
// SCL
GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SCL_PIN; // SCL用于发送同步时钟脉冲信号
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出
GPIO_Init(EEPROM_I2C_SCL_PORT, &GPIO_InitStruct);
// SDA
GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SDA_PIN;
GPIO_Init(EEPROM_I2C_SDA_PORT, &GPIO_InitStruct);
// I2C初始化
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // SCL低电平占空比
I2C_InitStruct.I2C_OwnAddress1 = I2Cx_OWN_ADDR1;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = I2C_Speed;
I2C_Init(EEPROM_I2Cx, &I2C_InitStruct);
I2C_Cmd(EEPROM_I2Cx, ENABLE);
}

/**
* @brief 超时自动执行函数
*
* @return (uint8_t) 1
*/
static uint8_t I2C_TIMEOUT_Callback(void){
printf("I2C TIMEOUT!\n");
return 1;
}

/**
* @brief 向EEPROM写入一个字节数据
*
* @param addr 写入地址
* @param data 写入数据
* @return uint8_t 是否发生错误,1-错误 0-正常
*/
uint8_t EEPROM_Byte_Write(u16 addr, u8 data){
// 括号表示从机信号,A表示ACK,N表示NACK
// S - addr - w - (A) - w_addr - (A) - data - (A) - P
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_SendData(EEPROM_I2Cx, addr);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_SendData(EEPROM_I2Cx, data);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR){
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
return 0;
}

/**
* @brief 写入数组数据到EEPROM
*
* @param addr 写入data的起始地址,使用 page write 要求addr % 8 == 0,与count无关。
* @param data 数组地址
* @param count 所用芯片要求count <= 8
* @return uint8_t 是否发生错误,1-错误 0-正常
*/
uint8_t EEPROM_Page_Write(u16 addr, u8 *data, u8 count){
// S - addr - w - (A) - w_addr - (A) - data - (A) - data - (A) - ... - data - (A) - P
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_SendData(EEPROM_I2Cx, addr);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
while (count){
I2C_SendData(EEPROM_I2Cx, *data);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR){
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
count--;
data++;
}
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
return 0;
}

/**
* @brief 读取EEPROM
*
* @param addr 读取地址
* @param data 接收数据所用数组
* @param count 接收数据量
* @return uint8_t 是否发生错误,1-错误 0-正常
*/
uint8_t EEPROM_Read(u16 addr, u8 *data, u16 count){
// S - addr - w - (A) - w_addr - (A) - \n
// S - addr - r - (A) - (data) - N - P
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_SendData(EEPROM_I2Cx, addr);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
// 二次起始信号
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Receiver);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR){ //EV6
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}

while(count){
if (count == 1){ // 待读取的最后一个
I2C_AcknowledgeConfig(EEPROM_I2Cx, DISABLE); // 产生NACK信号
}
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR){ // EV7
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
*data = I2C_ReceiveData(EEPROM_I2Cx);
data++;
count--;
}

I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
I2C_AcknowledgeConfig(EEPROM_I2Cx, ENABLE); // 再设置回去,自动产生ACK信号
return 0;
}

/**
* @brief 等待EEPROM内部写入完成。
* @return uint8_t 是否发生错误,1-错误 0-正常
*/
uint8_t EEPROM_Wait_For_Writing(void){
do{
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_SB) == RESET){ // EV5
// while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR); // 卡死
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
}while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_ADDR) == RESET); // EV6
// while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR); // 卡死
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
return 0;
}

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(void){
u32 i = 0;
u8 readData[10] = {0};
u8 writeData[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02};
USART_Config();
I2C_EEPROM_Init();
printf("Writing...\n");
// EEPROM_Byte_Write(11, 0xff);
// EEPROM_WAIT_Done();
EEPROM_Page_Write(0, writeData, 8);
EEPROM_Wait_For_Writing();
EEPROM_Read(0, readData, 8);
printf("Reading...\n");
for (i = 0; i < 8; i++){
printf("%x ", readData[i]);
}
while(1);
}

SPI读写FLASH

SPI与IIC较为类似。所用FLASH为W25Q64。FLASH部分的原理图如下。
FLASH部分的原理图

宏部分

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
// Basic
#define FLASH_SPIx SPI1
#define FLASH_SPI_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_CLK RCC_APB2Periph_SPI1
// CS(Chip select)/SS/NSS (GPIO)
#define FLASH_SPI_CS_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_CS_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_CS_PORT GPIOA
#define FLASH_SPI_CS_PIN GPIO_Pin_4
// SCK
#define FLASH_SPI_SCK_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_SCK_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_SCK_PORT GPIOA
#define FLASH_SPI_SCK_PIN GPIO_Pin_5
// MISO
#define FLASH_SPI_MISO_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_MISO_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_MISO_PORT GPIOA
#define FLASH_SPI_MISO_PIN GPIO_Pin_6
// MOSI
#define FLASH_SPI_MOSI_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_MOSI_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_MOSI_PORT GPIOA
#define FLASH_SPI_MOSI_PIN GPIO_Pin_7

#define FLASH_SPI_S GPIO_ResetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
#define FLASH_SPI_P GPIO_SetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)

#define SPI_FLAG_TIMEOUT 0x1000
#define FLASH_SPI_PageSize 256

// FLASH W25X CMD
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F
#define Dummy_Byte 0xFF

#define W25X_FLAG_BSY 0x01 // BUSY 位

核心部分

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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
static uint32_t SPI_TIMEOUT;

void FLASH_SPI_Init(void){
SPI_InitTypeDef SPI_struct;
GPIO_InitTypeDef GPIO_struct;
FLASH_SPI_ClockCmd(FLASH_SPI_CLK, ENABLE);
FLASH_SPI_CS_ClockCmd(FLASH_SPI_CS_CLK, ENABLE);
FLASH_SPI_CS_ClockCmd(FLASH_SPI_SCK_CLK, ENABLE);
FLASH_SPI_CS_ClockCmd(FLASH_SPI_MISO_CLK, ENABLE);
FLASH_SPI_CS_ClockCmd(FLASH_SPI_MOSI_CLK, ENABLE);
// CS config
GPIO_struct.GPIO_Pin = FLASH_SPI_CS_PIN;
GPIO_struct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_struct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_struct);
// SCK
GPIO_struct.GPIO_Pin = FLASH_SPI_SCK_PIN;
GPIO_struct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_struct);
// MOSI
GPIO_struct.GPIO_Pin = FLASH_SPI_MOSI_PIN;
GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_struct);
// MISO
GPIO_struct.GPIO_Pin = FLASH_SPI_MISO_PIN;
GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_struct);
FLASH_SPI_P;
SPI_struct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_struct.SPI_Mode = SPI_Mode_Master;
SPI_struct.SPI_DataSize = SPI_DataSize_8b;
SPI_struct.SPI_CPOL = SPI_CPOL_High; // clock polarity 极性. 空闲时SCK为高电平
SPI_struct.SPI_CPHA = SPI_CPHA_2Edge; // clock phase 相位. 数据捕获于第二个时钟沿
SPI_struct.SPI_NSS = SPI_NSS_Soft; // NSS引脚软件控制
SPI_struct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // SPI时钟2分频
SPI_struct.SPI_FirstBit = SPI_FirstBit_MSB; // MSB先行 数据高位先发送
SPI_struct.SPI_CRCPolynomial = 7; // CRC校验成员值 若要启用,还要用SPI_TransmitCRC和SPI_CalculateCRC函数来启用发送CRC功能
SPI_Init(FLASH_SPIx, &SPI_struct);
SPI_Cmd(FLASH_SPIx, ENABLE);
}


static uint8_t SPI_TIMEOUT_Callback(u8 code){
printf("SPI_ERROR, Code: %d\n", code);
return 1;
}


static void FLASH_ERROR_LOG(char* info){
printf("FLASH Error log: %s\n", info);
}


/**
* @brief 使用SPI发送1byte数据
*
* @param data uint8_t
* @return uint8_t 接收到的数据
*/
uint8_t FLASH_SPI_Send_Byte(u8 data){
SPI_TIMEOUT = SPI_FLAG_TIMEOUT;
while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET){
if ((SPI_TIMEOUT--) == 0) return SPI_TIMEOUT_Callback(0);
}
SPI_I2S_SendData(FLASH_SPIx, data);
SPI_TIMEOUT = SPI_FLAG_TIMEOUT;
while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET){
if ((SPI_TIMEOUT--) == 0) return SPI_TIMEOUT_Callback(1);
}
return SPI_I2S_ReceiveData(FLASH_SPIx);
}


/**
* @brief 读取一个字节的数据
*
* @return uint8_t
*/
uint8_t FLASH_SPI_Read_Byte(void){
// STM32必须要发送数据才能使时钟变化,从而接收数据
return FLASH_SPI_Send_Byte(Dummy_Byte);
}


void FLASH_SPI_WriteEnable(void){
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_WriteEnable);
FLASH_SPI_P;
}


/**
* @brief 读取FLASH的ID
*
* @return uint32_t
*/
uint32_t FLASH_SPI_ReadID(void){
u32 ID = 0;
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_JedecDeviceID);
ID = FLASH_SPI_Send_Byte(Dummy_Byte); // 00 00 00 AA
ID <<= 8; // 00 00 00 AA << 8 -> 00 00 AA 00
ID |= FLASH_SPI_Send_Byte(Dummy_Byte); // 00 00 AA 00 | 00 00 00 BB
ID <<= 8; // 00 00 AA BB << 8 -> 00 AA BB 00
ID |= FLASH_SPI_Send_Byte(Dummy_Byte); // 00 AA BB 00 | 00 00 00 CC
FLASH_SPI_P;
return ID; // 00 AA BB CC
}


/**
* @brief 等待FLASH写入完成
*
*/
void FLASH_SPI_Wait_For_Writing(void){
u8 Flash_status = 0;
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_ReadStatusReg);
do{
Flash_status = FLASH_SPI_Send_Byte(Dummy_Byte);
}while((Flash_status & W25X_FLAG_BSY) == SET);
FLASH_SPI_P;
}


/**
* @brief 擦除扇区
*
* @param SectorAddr 扇区地址 (需对齐到4KB)
*/
void FLASH_SPI_SectorErase(u32 SectorAddr){
FLASH_SPI_WriteEnable();
FLASH_SPI_Wait_For_Writing();
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_SectorErase);
// 从高位到低位发送擦除扇区地址
FLASH_SPI_Send_Byte((SectorAddr & 0xFF0000) >> 16);
FLASH_SPI_Send_Byte((SectorAddr & 0xFF00) >> 8);
FLASH_SPI_Send_Byte(SectorAddr & 0xFF);
FLASH_SPI_P;
FLASH_SPI_Wait_For_Writing();
}


/**
* @brief 按页写入数据 (调用前应先擦除扇区)
*
* @param pBuffer 数据指针
* @param WriteAddr 地址
* @param ByteCount 长度 不可超过页大小
*/
void FLASH_SPI_Page_Write(u8* pBuffer, u32 WriteAddr, u16 ByteCount){
FLASH_SPI_WriteEnable();
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_PageProgram);
FLASH_SPI_Send_Byte((WriteAddr & 0xFF0000) >> 16);
FLASH_SPI_Send_Byte((WriteAddr & 0xFF00) >> 8);
FLASH_SPI_Send_Byte(WriteAddr & 0xFF);
if (ByteCount > FLASH_SPI_PageSize){ // 写入256个字节限制
ByteCount = FLASH_SPI_PageSize;
FLASH_ERROR_LOG("FUNC: FLASH_SPI_Page_Write: ByteCount > FLASH_SPI_PageSize !");
}
while (ByteCount--){
FLASH_SPI_Send_Byte(*pBuffer);
pBuffer++;
}
FLASH_SPI_P;
FLASH_SPI_Wait_For_Writing();
}


/**
* @brief 不定量数据写入 (调用前应先擦除扇区)
*
* @param pBuffer 数据指针
* @param WriteAddr 地址
* @param ByteCount 长度 (无限制)
*/
void FLASH_SPI_Buffer_Write(u8* pBuffer, u32 WriteAddr, u16 ByteCount){
u8 NPages = 0, Nsingle = 0, Addr = 0, count = 0, tmp = 0;
Addr = WriteAddr % FLASH_SPI_PageSize;
count = FLASH_SPI_PageSize - Addr;
NPages = ByteCount / FLASH_SPI_PageSize;
Nsingle = ByteCount % FLASH_SPI_PageSize;
if (Addr == 0){ // 地址对齐
if (NPages == 0){ // 单页
FLASH_SPI_Page_Write(pBuffer, WriteAddr, ByteCount);
}else{ // 多页写入
while (NPages--){
FLASH_SPI_Page_Write(pBuffer, WriteAddr, FLASH_SPI_PageSize);
WriteAddr += FLASH_SPI_PageSize;
pBuffer += FLASH_SPI_PageSize;
}
}
}else{ // 不对齐
if (NPages == 0){
if (Nsingle > count){
tmp = Nsingle - count;
FLASH_SPI_Page_Write(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
FLASH_SPI_Page_Write(pBuffer, WriteAddr, tmp);
}else{
FLASH_SPI_Page_Write(pBuffer, WriteAddr, ByteCount);
}
}else{
ByteCount -= count;
NPages = ByteCount / FLASH_SPI_PageSize;
Nsingle = ByteCount % FLASH_SPI_PageSize;
FLASH_SPI_Page_Write(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
while (NPages--){
FLASH_SPI_Page_Write(pBuffer, WriteAddr, FLASH_SPI_PageSize);
WriteAddr += FLASH_SPI_PageSize;
pBuffer += FLASH_SPI_PageSize;
}
if (Nsingle != 0){ // 写完多余的页
FLASH_SPI_Page_Write(pBuffer, WriteAddr, Nsingle);
}
}
}
}



/**
* @brief 读取FLASH
*
* @param pBuffer 存储数据指针
* @param ReadAddr 读取地址
* @param ByteCount 读取长度
*/
void FLASH_SPI_Buffer_Read(u8* pBuffer, u32 ReadAddr, u16 ByteCount){
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_ReadData);
FLASH_SPI_Send_Byte((ReadAddr & 0xFF0000) >> 16);
FLASH_SPI_Send_Byte((ReadAddr & 0xFF00) >> 8);
FLASH_SPI_Send_Byte(ReadAddr & 0xFF);
while (ByteCount--){
*pBuffer = FLASH_SPI_Send_Byte(Dummy_Byte);
pBuffer++;
}
FLASH_SPI_P;
}

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(void){
u32 FLASH_ID;
u8 TX_Buffer[64] = "Hello world.";
u8 RX_Buffer[64] = {0};
USART_Config();
printf("start...\n");
FLASH_SPI_Init();
FLASH_ID = FLASH_SPI_ReadID();
printf("Device ID: %x\n", FLASH_ID);
if (FLASH_ID == 0xEF4017){
printf("w25Q64 Detected.\n");
FLASH_SPI_SectorErase(0);
FLASH_SPI_Buffer_Write(TX_Buffer, 0, 13);
printf("Write: %s\n", TX_Buffer);
FLASH_SPI_Buffer_Read(RX_Buffer, 0, 13);
printf("Read: %s\n", RX_Buffer);
}else{
printf("Error !\n");
}
printf("end...\n");
}

注:根据本FLASH芯片手册知,每个扇区大小为4kB,所以扇区0的地址为0,扇区1的地址为4096,以此类推。