|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区
您需要 登录 才可以下载或查看,没有账号?立即注册
×
STM32F429外部SPI下载算法
一、使用CubeMX新建工程
二、Flash驱动
三、在上项目基础上 制作FLM下载算法
四、修改代码
五、注意问题
六、工程下载
感谢安富莱及正点原子的开源教程
使用正点原子STM32F429开发板
一、使用CubeMX新建工程
SPI在CubeMX中配置如下
时钟可选外部内部都行。
按照以上设置生成代码
二、Flash驱动
按下以下步骤创建文件及文件夹放置在工程目录中
w25q256_spi.c中代码(此代码参考正点原子F429开发板)
#include "w25q256_spi.h"
//#include "stm32f4xx_hal_spi.h"
#define W25Q256_CS_HIGH() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_6,GPIO_PIN_SET);
#define W25Q256_CS_LOW() HAL_GPIO_WritePin(GPIOF,GPIO_PIN_6,GPIO_PIN_RESET);
static void BSP_SPI5_GPIO_Init(void);
static void BSP_SPI5_Init(void);
static uint8_t W25Q256_ReadWriteByte(uint8_t TxData);
static uint8_t W25Q256_ReadSR(uint8_t regno);
static void W25Q256_Write_SR(uint8_t regno,uint8_t sr);
static void W25Q256_Write_Enable(void);
static void W25Q256_Write_Disable(void);
static void W25Q256_Wait_Busy(void);
SPI_HandleTypeDef SPI5_Handler;
static void BSP_SPI5_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* SPI5 clock enable */
__HAL_RCC_SPI5_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
/**SPI5 GPIO Configuration
PF6 ------> SPI5_CS
PF7 ------> SPI5_SCK
PF8 ------> SPI5_MISO
PF9 ------> SPI5_MOSI
*/
GPIO_InitStruct.Pin=GPIO_PIN_6;
GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull=GPIO_PULLUP;
GPIO_InitStruct.Speed=GPIO_SPEED_FAST;
HAL_GPIO_Init(GPIOF,&GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI5;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}
static void BSP_SPI5_Init(void)
{
BSP_SPI5_GPIO_Init();
SPI5_Handler.Instance = SPI5;
SPI5_Handler.Init.Mode = SPI_MODE_MASTER;
SPI5_Handler.Init.Direction = SPI_DIRECTION_2LINES;
SPI5_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
SPI5_Handler.Init.CLKPolarity = SPI_POLARITY_HIGH;
SPI5_Handler.Init.CLKPhase = SPI_PHASE_2EDGE;
SPI5_Handler.Init.NSS = SPI_NSS_SOFT;
SPI5_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
SPI5_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
SPI5_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
SPI5_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
SPI5_Handler.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&SPI5_Handler) != HAL_OK)
{
// Error_Handler();
}
}
//SPI5 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
static uint8_t W25Q256_ReadWriteByte(uint8_t TxData)
{
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&SPI5_Handler,&TxData,&Rxdata,1, 1000);
return Rxdata; //返回收到的数据
}
void W25Q256_Init(void)
{
BSP_SPI5_Init();
W25Q256_CS_LOW();
W25Q256_ReadWriteByte(W25X_Enable4ByteAddr);//发送进入4字节地址模式指令
W25Q256_CS_HIGH();
}
//读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
//0XEF18,表示芯片型号为W25Q256
uint16_t W25Q256_ReadID(void)
{
uint16_t Temp = 0;
W25Q256_CS_LOW();
W25Q256_ReadWriteByte(0x90);//发送读取ID命令
W25Q256_ReadWriteByte(0x00);
W25Q256_ReadWriteByte(0x00);
W25Q256_ReadWriteByte(0x00);
Temp|=W25Q256_ReadWriteByte(0xFF)<<8;
Temp|=W25Q256_ReadWriteByte(0xFF);
W25Q256_CS_HIGH();
return Temp;
}
//读取W25Q256的状态寄存器,W25Q256一共有3个状态寄存器
//状态寄存器1:
//BIT7 6 5 4 3 2 1 0
//SPR RV TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
//状态寄存器2:
//BIT7 6 5 4 3 2 1 0
//SUS CMP LB3 LB2 LB1 (R) QE SRP1
//状态寄存器3:
//BIT7 6 5 4 3 2 1 0
//HOLD/RST DRV1 DRV0 (R) (R) WPS ADP ADS
//regno:状态寄存器号,范:1~3
//返回值:状态寄存器值
static uint8_t W25Q256_ReadSR(uint8_t regno)
{
uint8_t byte=0,command=0;
switch(regno)
{
case 1:
command=W25X_ReadStatusReg1; //读状态寄存器1指令
break;
case 2:
command=W25X_ReadStatusReg2; //读状态寄存器2指令
break;
case 3:
command=W25X_ReadStatusReg3; //读状态寄存器3指令
break;
default:
command=W25X_ReadStatusReg1;
break;
}
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(command); //发送读取状态寄存器命令
byte=W25Q256_ReadWriteByte(0Xff); //读取一个字节
W25Q256_CS_HIGH(); //取消片选
return byte;
}
//写W25Q256状态寄存器
static void W25Q256_Write_SR(uint8_t regno,uint8_t sr)
{
uint8_t command=0;
switch(regno)
{
case 1:
command=W25X_WriteStatusReg1; //写状态寄存器1指令
break;
case 2:
command=W25X_WriteStatusReg2; //写状态寄存器2指令
break;
case 3:
command=W25X_WriteStatusReg3; //写状态寄存器3指令
break;
default:
command=W25X_WriteStatusReg1;
break;
}
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(command); //发送写取状态寄存器命令
W25Q256_ReadWriteByte(sr); //写入一个字节
W25Q256_CS_HIGH(); //取消片选
}
//W25Q256写使能
//将WEL置位
static void W25Q256_Write_Enable(void)
{
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_WriteEnable); //发送写使能
W25Q256_CS_HIGH(); //取消片选
}
//W25Q256写禁止
//将WEL清零
static void W25Q256_Write_Disable(void)
{
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令
W25Q256_CS_HIGH(); //取消片选
}
//等待空闲
static void W25Q256_Wait_Busy(void)
{
while((W25Q256_ReadSR(1)&0x01)==0x01); // 等待BUSY位清空
}
//擦除整个芯片
//等待时间超长...
void W25Q256_Erase_Chip(void)
{
W25Q256_Write_Enable(); //SET WEL
W25Q256_Wait_Busy();
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
W25Q256_CS_HIGH(); //取消片选
W25Q256_Wait_Busy(); //等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个扇区的最少时间:150ms
void W25Q256_Erase_Sector(uint32_t Dst_Addr)
{
//监视falsh擦除情况,测试用
//printf("fe:%x\r\n",Dst_Addr);
Dst_Addr*=4096;
W25Q256_Write_Enable(); //SET WEL
W25Q256_Wait_Busy();
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令
W25Q256_ReadWriteByte((uint8_t)((Dst_Addr)>>24));
W25Q256_ReadWriteByte((uint8_t)((Dst_Addr)>>16)); //发送24bit地址
W25Q256_ReadWriteByte((uint8_t)((Dst_Addr)>>8));
W25Q256_ReadWriteByte((uint8_t)Dst_Addr);
W25Q256_CS_HIGH(); //取消片选
W25Q256_Wait_Busy(); //等待擦除完成
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25Q256_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{
uint16_t i;
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_ReadData); //发送读取命令
W25Q256_ReadWriteByte((uint8_t)((ReadAddr)>>24));
W25Q256_ReadWriteByte((uint8_t)((ReadAddr)>>16)); //发送24bit地址
W25Q256_ReadWriteByte((uint8_t)((ReadAddr)>>8));
W25Q256_ReadWriteByte((uint8_t)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer=W25Q256_ReadWriteByte(0XFF); //循环读数
}
W25Q256_CS_HIGH();
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25Q256_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t i;
W25Q256_Write_Enable(); //SET WEL
W25Q256_CS_LOW(); //使能器件
W25Q256_ReadWriteByte(W25X_PageProgram); //发送写页命令
W25Q256_ReadWriteByte((uint8_t)((WriteAddr)>>24));
W25Q256_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
W25Q256_ReadWriteByte((uint8_t)((WriteAddr)>>8));
W25Q256_ReadWriteByte((uint8_t)WriteAddr);
for(i=0;i<NumByteToWrite;i++)W25Q256_ReadWriteByte(pBuffer);//循环写数
W25Q256_CS_HIGH(); //取消片选
W25Q256_Wait_Busy(); //等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25Q256_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain=256-WriteAddr%256; //单页剩余的字节数
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节
while(1)
{
W25Q256_Write_Page(pBuffer,WriteAddr,pageremain);
if(NumByteToWrite==pageremain)break;//写入结束了
else //NumByteToWrite>pageremain
{
pBuffer+=pageremain;
WriteAddr+=pageremain;
NumByteToWrite-=pageremain; //减去已经写入了的字节数
if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节
else pageremain=NumByteToWrite; //不够256个字节了
}
}
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
uint8_t W25Q256_BUFFER[4096];
void W25Q256_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t * W25Q256_BUF;
W25Q256_BUF=W25Q256_BUFFER;
secpos=WriteAddr/4096;//扇区地址
secoff=WriteAddr%4096;//在扇区内的偏移
secremain=4096-secoff;//扇区剩余空间大小
//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节
while(1)
{
W25Q256_Read(W25Q256_BUF,secpos*4096,4096);//读出整个扇区的内容
for(i=0;i<secremain;i++)//校验数据
{
if(W25Q256_BUF[secoff+i]!=0XFF)break;//需要擦除
}
if(i<secremain)//需要擦除
{
W25Q256_Erase_Sector(secpos);//擦除这个扇区
for(i=0;i<secremain;i++) //复制
{
W25Q256_BUF[i+secoff]=pBuffer;
}
W25Q256_Write_NoCheck(W25Q256_BUF,secpos*4096,4096);//写入整个扇区
}
else W25Q256_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.
if(NumByteToWrite==secremain)break;//写入结束了
else//写入未结束
{
secpos++;//扇区地址增1
secoff=0;//偏移位置为0
pBuffer+=secremain; //指针偏移
WriteAddr+=secremain;//写地址偏移
NumByteToWrite-=secremain; //字节数递减
if(NumByteToWrite>4096)secremain=4096; //下一个扇区还是写不完
else secremain=NumByteToWrite; //下一个扇区可以写完了
}
}
}
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
w25q256_spi.h中代码
#ifndef __W25Q256_SPI_H
#define __W25Q256_SPI_H
//#include "stm32f429xx.h"
//#include "stm32f429xx.h"
#include "stm32f4xx_hal.h"
//W25X系列/Q系列芯片列表
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17
#define W25Q256 0XEF18
//指令表
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg1 0x05
#define W25X_ReadStatusReg2 0x35
#define W25X_ReadStatusReg3 0x15
#define W25X_WriteStatusReg1 0x01
#define W25X_WriteStatusReg2 0x31
#define W25X_WriteStatusReg3 0x11
#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 W25X_Enable4ByteAddr 0xB7
#define W25X_Exit4ByteAddr 0xE9
void W25Q256_Init(void);
uint16_t W25Q256_ReadID(void);
void W25Q256_Erase_Chip(void);
void W25Q256_Erase_Sector(uint32_t Dst_Addr);
void W25Q256_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);
void W25Q256_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25Q256_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25Q256_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
#endif /*__ w25q256_spi_H */
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
以上完成外部Flash w25q256 spi的驱动
三、在上项目基础上 制作FLM下载算法
将D:\Keil_v5\Packs\ARM\CMSIS\5.7.0\Device 下 文件夹 _Template_Flash 拷贝至 工程目录
打开 E:\STM32F429_CubeMX6.0.1\Demo9_SPI-FLM_V1.25.0_MDK5_Template_Flash下MDK工程
添加分组及文件
选择芯片型号
配置工程
按照驱动代码配置此选项
四、修改代码
以下参考安富莱教程完成
FlashDev.c
struct FlashDevice const FlashDevice = {
FLASH_DRV_VERS, /* 驱动版本,勿修改,这个是MDK定的 */
"STM32F429_SPI_W25Q256", /* 算法名,添加算法到MDK安装目录会显示此名字 */
EXTSPI, /* 设备类型 */
0xC0000000, /* Flash起始地址 */
32 * 1024 * 1024, /* Flash大小,32MB */ 1024, // Programming Page Size
4096, /* 编程页大小 */
0xFF, /* 擦除后的数值 */
6000, /* 页编程等待时间 */
6000, /* 扇区擦除等待时间 */
// Specify Size and Address of Sectors
4 * 1024, 0x000000, /* 扇区大小,扇区地址 */
SECTOR_END
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FlashPrg.c
#include "FlashOS.h" // FlashOS Structures
#include "stm32f4xx_hal.h"
#include "w25q256_spi.h"
#define SPI_FLASH_MEM_ADDR 0xC0000000
int SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
HAL_RCC_DeInit();
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
// RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
// RCC_OscInitStruct.HSIState = RCC_HSI_ON;
// RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
// RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
// RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
// RCC_OscInitStruct.PLL.PLLM = 8;
// RCC_OscInitStruct.PLL.PLLN = 180;
// RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
// RCC_OscInitStruct.PLL.PLLQ = 4;
//
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 360;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 8;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
return 1;
}
/** Activate the Over-Drive mode
*/
if (HAL_PWREx_EnableOverDrive() != HAL_OK)
{
return 1;
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
return 1;
}
return 0;
}
//时钟设置函数
//Fvco=Fs*(plln/pllm);
//Fsys=Fvco/pllp=Fs*(plln/(pllm*pllp));
//Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq));
//Fvco:VCO频率
//Fsys:系统时钟频率
//Fusb:USB,SDIO,RNG等的时钟频率
//FsLL输入时钟频率,可以是HSI,HSE等.
//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.
//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
//外部晶振为25M的时候,推荐值:plln=360,pllm=25,pllp=2,pllq=8.
//得到:Fvco=25*(360/25)=360Mhz
// Fsys=360/2=180Mhz
// Fusb=360/8=45Mhz(使用USB时,需设置plln=384,即可得到48Mhz频率)
//返回值:0,成功;1,失败。
int Sys_Clock_Set(uint32_t plln,uint32_t pllm,uint32_t pllp,uint32_t pllq)
{
uint16_t retry=0;
uint8_t status=0;
RCC->CR|=1<<16; //HSE 开启
while(((RCC->CR&(1<<17))==0)&&(retry<0X1FFF))retry++;//等待HSE RDY
if(retry==0X1FFF)status=1; //HSE无法就绪
else
{
RCC->APB1ENR|=1<<28; //电源接口时钟使能
PWR->CR|=3<<14; //高性能模式,时钟可到180Mhz
RCC->CFGR|=(0<<4)|(5<<10)|(4<<13);//HCLK 不分频;APB1 4分频;APB2 2分频.
RCC->CR&=~(1<<24); //关闭主PLL
RCC->LLCFGR=pllm|(plln<<6)|(((pllp>>1)-1)<<16)|(pllq<<24)|(1<<22);//配置主PLL,PLL时钟源来自HSE
RCC->CR|=1<<24; //打开主PLL
while((RCC->CR&(1<<25))==0);//等待PLL准备好
FLASH->ACR|=1<<8; //指令预取使能.
FLASH->ACR|=1<<9; //指令cache使能.
FLASH->ACR|=1<<10; //数据cache使能.
FLASH->ACR|=5<<0; //5个CPU等待周期.
RCC->CFGR&=~(3<<0); //清零
RCC->CFGR|=2<<0; //选择主PLL作为系统时钟
while((RCC->CFGR&(3<<2))!=(2<<2));//等待主PLL作为系统时钟成功.
}
return status;
}
//系统时钟初始化函数
//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.
//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
int Stm32_Clock_Init(uint32_t plln,uint32_t pllm,uint32_t pllp,uint32_t pllq)
{
RCC->CR|=0x00000001; //设置HISON,开启内部高速RC振荡
RCC->CFGR=0x00000000; //CFGR清零
RCC->CR&=0xFEF6FFFF; //HSEON,CSSON,PLLON清零
RCC->LLCFGR=0x24003010; //PLLCFGR恢复复位值
RCC->CR&=~(1<<18); //HSEBYP清零,外部晶振不旁路
RCC->CIR=0x00000000; //禁止RCC时钟中断
return Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟
}
/*
* Initialize Flash Programming Functions
* Parameter: adr: Device Base Address
* clk: Clock Frequency (Hz)
* fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
/* Add your Code */
int result = 0;
//
// /* 系统初始化 */
SystemInit();
/* 时钟初始化 */
result = SystemClock_Config();
// result = Stm32_Clock_Init(360,25,2,8);
if (result != 0)
{
return 1;
}
W25Q256_Init();
return 0;
}
/*
* De-Initialize Flash Programming Functions
* Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int UnInit (unsigned long fnc) {
/* Add your Code */
return (0); // Finished without Errors
}
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip (void) {
/* Add your Code */
W25Q256_Erase_Chip();
return (0); // Finished without Errors
}
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
int EraseSector (unsigned long adr) {
/* Add your Code */
adr -= SPI_FLASH_MEM_ADDR;
W25Q256_Erase_Sector(adr);
return (0); // Finished without Errors
}
/*
* Program Page in Flash Memory
* Parameter: adr: Page Start Address
* sz: Page Size
* buf: Page Data
* Return Value: 0 - OK, 1 - Failed
*/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {
/* Add your Code */
adr -= SPI_FLASH_MEM_ADDR;
W25Q256_Write(buf, adr, sz);
// W25Q256_Write_NoCheck(buf, adr, sz);
return (0); // Finished without Errors
}
unsigned char aux_buf[4096];
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)
{
int i;
adr -= SPI_FLASH_MEM_ADDR;
W25Q256_Read(aux_buf, adr, sz);
for (i = 0; i< sz; i++)
{
if (aux_buf != buf)
return (adr+i); /* 校验失败 */
}
adr += SPI_FLASH_MEM_ADDR;
return (adr+sz); /* 校验成功 */
}
int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat)
{
return 0;
}
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
244
245
246
247
248
249
250
251
五、注意问题
一:时钟初始化问题
方案一:可以选择正点原子教程基于寄存器版本的时钟初始化
方案二:选择CubeMX生成的初始化方案但需注意以下问题
所有调用函数均屏蔽参数
uint32_t tickstart;
1
以及获取时间的部分例如
1
/* Check the HSE State */
if((RCC_OscInitStruct->HSEState) != RCC_HSE_OFF)
{
/* Get Start Tick */
// tickstart = HAL_GetTick();
/* Wait till HSE is ready */
while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)
{
// if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)
// {
// return HAL_TIMEOUT;
// }
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SPI调用库函数也需屏蔽超时部分
六、工程下载
一、驱动下载地址:https://download.csdn.net/download/qq992035949/14045332.
二、FLM下载算法工程地址:https://download.csdn.net/download/qq992035949/14045346.
————————————————
版权声明:本文为CSDN博主「不哭的大熊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq992035949/article/details/112358428 |
|