STM32 SPI Flash学习笔记

开发板:野火指南者(STM32F103VE)

STM32库版本:STM32F10x_StdPeriph_Lib_V3.5.0

IDE:KEIL5(代码编写很不方便,只在编译的时候用到)

代码编写工具:Source Insight 4.0(跟读代码、编写代码的最佳工具)

使用到的串口:USART1

使用到的SPI:SPI1

FLASH型号:W25Q64

 

硬件原理图:

 

 

1. 新建user_spi_flash.h、user_spi_flash.c、user_usart.h、user_usart.c、main.c 5个文件,并从

STM32官方库的例子中将stm32f10x_it.c、stm32f10x_it.h、stm32f10x_conf.h拷贝到自己的工程目录下。

 

2. 在user_spi_flash.h中添加如下代码

 1 #ifndef __USER_SPI_FLASH_H
 2 #define __USER_SPI_FLASH_H
 3 
 4 #include "stm32f10x.h"
 5 #include "stdio.h"
 6 
 7 
 8 
 9 #define TIMEOUT        0x00010000        //定义等待时间
10 #define PageSize    256                //FLASH页大小
11 #define DUMMYDATA    0x00            //无意义数据,用于FLASH读取数据用
12 
13 void user_SPI_GPIO_Config(void);
14 void user_SPI_Config(void);
15 void user_SPI_Flash_CS(int status);
16 void user_SPI_Flash_Write_Contrl(int status);
17 uint8_t user_SPI_Flash_Read_Write_Byte(uint8_t data);
18 int user_timeout(uint16_t SPI_I2S_FLAG, int id);
19 void user_SPI_Flash_Write_PageData(uint8_t * data, uint32_t address, int num);
20 void user_SPI_Flash_Write_NData(uint8_t * data, uint32_t address, uint32_t num);
21 void user_SPI_Flash_Erase_Sector(uint32_t address);
22 void user_testData_Config(uint8_t * data, uint32_t num);
23 void user_SPI_Flash_Read_NData(uint8_t * RevData, uint32_t address, uint32_t num);
24 
25 
26 
27 
28 #endif
View Code

 

3. 在user_spi_flash.c中添加如下代码

  1 #include "user_spi_flash.h"
  2 
  3 ////配置SPI GPIO口
  4 void user_SPI_GPIO_Config(void)
  5 {
  6     GPIO_InitTypeDef SPI_MOSI_PA7,SPI_MISO_PA6,SPI_CS_PC0,SPI_CLK_PA5;
  7 
  8     //使能对应PIN所在的GPIO口时钟
  9     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 10     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
 11     
 12     //配置使用到的PIN
 13     SPI_MOSI_PA7.GPIO_Mode = GPIO_Mode_AF_PP;
 14     SPI_MOSI_PA7.GPIO_Pin = GPIO_Pin_7;
 15     SPI_MOSI_PA7.GPIO_Speed = GPIO_Speed_50MHz;
 16 
 17     SPI_MISO_PA6.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 18     SPI_MISO_PA6.GPIO_Pin = GPIO_Pin_6;
 19 
 20     SPI_CS_PC0.GPIO_Mode = GPIO_Mode_Out_PP;
 21     SPI_CS_PC0.GPIO_Pin = GPIO_Pin_0;
 22     SPI_CS_PC0.GPIO_Speed = GPIO_Speed_50MHz;
 23 
 24     SPI_CLK_PA5.GPIO_Mode = GPIO_Mode_AF_PP;
 25     SPI_CLK_PA5.GPIO_Pin = GPIO_Pin_5;
 26     SPI_CLK_PA5.GPIO_Speed = GPIO_Speed_50MHz;
 27     
 28     //初始化GPIO口对应的寄存器
 29     GPIO_Init(GPIOA, &SPI_MOSI_PA7);
 30     GPIO_Init(GPIOA, &SPI_MISO_PA6);
 31     GPIO_Init(GPIOC, &SPI_CS_PC0);
 32     GPIO_Init(GPIOA, &SPI_CLK_PA5);
 33 
 34     //Flash在读写前,要处于非选择状态,需要读写时再打开,
 35     //否则出现SPI无法正常读写,所以最好在初始化GPIO_InitTypeDef结构体时将其处于非选择状态
 36     user_SPI_Flash_CS(DISABLE);        
 37     
 38     
 39 }
 40 
 41 
 42 //配置SPI
 43 void user_SPI_Config(void)
 44 {
 45 
 46     SPI_InitTypeDef SPI_Config;
 47 
 48     //使能对应的SPI时钟
 49     RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
 50 
 51     //配置SPI
 52     SPI_Config.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
 53     SPI_Config.SPI_CPHA = SPI_CPHA_2Edge;
 54     SPI_Config.SPI_CPOL = SPI_CPOL_High;
 55     SPI_Config.SPI_CRCPolynomial = 7;
 56     SPI_Config.SPI_DataSize = SPI_DataSize_8b;
 57     SPI_Config.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
 58     SPI_Config.SPI_FirstBit = SPI_FirstBit_MSB;
 59     SPI_Config.SPI_Mode = SPI_Mode_Master;
 60     SPI_Config.SPI_NSS = SPI_NSS_Soft;
 61 
 62     //初始化SPI
 63     SPI_Init(SPI1, &SPI_Config);
 64     
 65     //使能SPI
 66     SPI_Cmd(SPI1, ENABLE);
 67 
 68 }
 69 
 70 
 71 
 72 //FLASH片选函数,低电平有效
 73 void user_SPI_Flash_CS(int status)
 74 {
 75     if(status == DISABLE)
 76     {
 77         GPIOC->BSRR = GPIO_Pin_0;
 78     }
 79     else
 80     {
 81         GPIOC->BRR = GPIO_Pin_0;
 82     }
 83 }
 84 
 85 
 86 
 87 //FLASH写使能控制函数
 88 void user_SPI_Flash_Write_Contrl(int status)
 89 {
 90     if(status == DISABLE)
 91     {
 92         user_SPI_Flash_Read_Write_Byte(0x04);
 93     }
 94     else
 95     {
 96         user_SPI_Flash_Read_Write_Byte(0x06);
 97     }
 98 }
 99 
100 
101 //FLASH单个字节读写函数,其它函数都需要调用此函数,特别注意的是读写需要写在一起,否则无法正常工作,
102 //具体原因不知,不能工作的写法如随后注释的代码
103 uint8_t user_SPI_Flash_Read_Write_Byte(uint8_t data)
104 {
105     if(user_timeout(SPI_I2S_FLAG_TXE, 1) == -1)
106     {
107         return -1;
108     }
109 
110     SPI_I2S_SendData(SPI1, data);
111 
112     if(user_timeout(SPI_I2S_FLAG_RXNE, 2) == -1)
113     {
114         return -1;
115     }
116 
117     return SPI_I2S_ReceiveData(SPI1);
118 }
119 
120 /*
121 
122 int user_SPI_Write_Byte(uint8_t data)
123 {
124 
125     if(timeout(SPI_I2S_FLAG_TXE, 3) == 0)
126     {
127         return 0;
128     }
129 
130 
131 
132     SPI_I2S_SendData(SPI1, data);
133 
134     return 1;
135 
136 }
137 
138 
139 int user_SPI_Read_Byte(uint8_t data)
140 {
141     if(timeout(SPI_I2S_FLAG_RXNE, 4) == 0)
142     {
143         return 0;
144     }
145 
146 
147     return SPI_I2S_ReceiveData(SPI1);
148 
149 }
150 
151 */
152 
153 
154 
155 //读取操作等待时间函数,如超时则进行相应的处理
156 int user_timeout(uint16_t SPI_I2S_FLAG, int id)
157 {
158     int time = TIMEOUT;
159     while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG) == RESET)
160     {
161         if(time == 0)
162         {
163             printf("timeout! the error id is %d\n",id);
164             return -1;
165         }
166         time--;
167     }
168     
169     return 0;
170 }
171 
172 
173 //页数据写入函数
174 void user_SPI_Flash_Write_PageData(uint8_t * data, uint32_t address, int num)
175 {
176     if(num > PageSize)
177     {
178         printf("the data is too match! the max write 256 data!\n");
179         num = PageSize;
180     }
181 
182 
183     user_SPI_Flash_CS(ENABLE);
184     //user_SPI_Flash_Write_Contrl(ENABLE);    //写使能函数,在此并没有什么影响
185     user_SPI_Flash_Read_Write_Byte(0x02);
186 
187     user_SPI_Flash_Read_Write_Byte(address >> 16);
188     user_SPI_Flash_Read_Write_Byte(address >> 8);
189     user_SPI_Flash_Read_Write_Byte(address);
190 
191     while(num--)
192     {
193         user_SPI_Flash_Read_Write_Byte(*data);
194         data++;
195         //address++;
196     }
197 
198     user_SPI_Flash_Read_Write_Byte(0x05);
199     while(0x01 == user_SPI_Flash_Read_Write_Byte(DUMMYDATA))
200     {
201 
202     }
203 
204     //user_SPI_Flash_Write_Contrl(DISABLE);
205     user_SPI_Flash_CS(DISABLE);
206 
207     
208 }
209 
210 //写入N个数据到FLASH,但是这里最大也就只能到256,跟页写入函数一样,
211 //此函数的意义,个人认为主要是进行写地址对齐,当然真的要写入大于256的数据应该也是可以的,但是
212 //应该比较麻烦一点,个人想到的是定义一个二维数组
213 void user_SPI_Flash_Write_NData(uint8_t * data, uint32_t address, uint32_t num)
214 {
215     
216     int count_address = 0, count_pageData = 0, count_morePageData = 0;
217 
218     count_address = address % PageSize;        //不满一页的地址,0为地址已对齐
219     count_pageData = num / PageSize;        //多少整页数据
220     count_morePageData = num % PageSize;    //不满一页的数据
221 
222     //地址是否对齐
223     if(count_address == 0)
224     {    
225         if(count_morePageData == 0)        //是否有不满一页的数据
226         {
227             while(count_pageData--)        //将整页数据先写入
228             {
229                 user_SPI_Flash_Write_PageData(data, address, PageSize);
230                 address += PageSize;
231                 data += PageSize;
232             }
233         }
234         else
235         {
236             if(count_pageData > 0)
237             {
238                 while(count_pageData--)
239                 {
240                     user_SPI_Flash_Write_PageData(data, address, PageSize);
241                     address += PageSize;
242                     data += PageSize;
243                 }
244             }
245             
246             user_SPI_Flash_Write_PageData(data, address, count_morePageData);
247         }
248     }
249     else
250     {
251         user_SPI_Flash_Write_PageData(data, address, PageSize - count_address);
252         address += PageSize - count_address;
253         data += PageSize - count_address;
254         
255         if(count_morePageData == 0)
256         {
257             while(count_pageData--)
258             {
259                 user_SPI_Flash_Write_PageData(data, address, PageSize);
260                 address += PageSize;
261                 data += PageSize;
262             }
263         }
264         else
265         {
266             if(count_pageData > 0)
267             {
268                 while(count_pageData--)
269                 {
270                     user_SPI_Flash_Write_PageData(data, address, PageSize);
271                     address += PageSize;
272                     data += PageSize;
273 
274                 }
275 
276             }
277 
278             user_SPI_Flash_Write_PageData(data, address, count_morePageData - count_address);
279         }
280     }
281     
282     
283 
284 }
285 
286 
287 //从FLASH读取数据,个数不受限制
288 void user_SPI_Flash_Read_NData(uint8_t * RevData, uint32_t address, uint32_t num)
289 {
290     
291     user_SPI_Flash_CS(ENABLE);
292     //user_SPI_Flash_Write_Contrl(ENABLE); 读数据不能打开写使能
293 
294     user_SPI_Flash_Read_Write_Byte(0x03);
295     user_SPI_Flash_Read_Write_Byte(address >> 16);
296     user_SPI_Flash_Read_Write_Byte(address >> 8);
297     user_SPI_Flash_Read_Write_Byte(address);
298 
299     while(num--)
300     {
301         *RevData = user_SPI_Flash_Read_Write_Byte(DUMMYDATA);
302         RevData++;
303         
304     }
305 
306     //user_SPI_Flash_Write_Contrl(DISABLE);    与前面对应,没有打开就没有关闭
307     user_SPI_Flash_CS(DISABLE);
308     
309 }
310 
311 
312 
313 
314 
315 void user_SPI_Flash_Erase_Sector(uint32_t address)
316 {
317     user_SPI_Flash_CS(ENABLE);
318     user_SPI_Flash_Write_Contrl(ENABLE);
319 
320     user_SPI_Flash_Read_Write_Byte(0x20);        //扇区擦除指令
321 
322     user_SPI_Flash_Read_Write_Byte(address >> 16);
323     user_SPI_Flash_Read_Write_Byte(address >> 8);
324     user_SPI_Flash_Read_Write_Byte(address);
325 
326     user_SPI_Flash_Read_Write_Byte(0x05);        //读取状态寄存器指令
327     while(0x01 == user_SPI_Flash_Read_Write_Byte(DUMMYDATA));        //读取FLASH状态寄存器,若为完成则一直循环,等待操作完成
328 
329     user_SPI_Flash_Write_Contrl(DISABLE);
330     user_SPI_Flash_CS(DISABLE);
331     
332 }
View Code

 

4. 在user_usart.h中添加如下代码

 1 #ifndef __USER_USART_H
 2 #define __USER_USART_H
 3 
 4 #include "stdio.h"
 5 #include "stm32f10x.h"
 6 
 7 void user_USART_GPIO_Config(void);
 8 void user_USART_Config(void);
 9 int fputc(int data, FILE * f);        //重写fputc函数,以支持printf函数
10 int fgetc(FILE * f);                //重写fgetc函数,以支持printf函数
11 
12 
13 
14 #endif
View Code

 

5. 在user_usart.c中添加如下代码

 1 #include "user_usart.h"
 2 
 3 //配置USART GPIO口
 4 void user_USART_GPIO_Config(void)
 5 {
 6     GPIO_InitTypeDef USART_TX_PA9,USART_RX_PA10;
 7 
 8     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
 9 
10     USART_TX_PA9.GPIO_Mode = GPIO_Mode_AF_PP;
11     USART_TX_PA9.GPIO_Pin = GPIO_Pin_9;
12     USART_TX_PA9.GPIO_Speed = GPIO_Speed_50MHz;
13 
14     USART_RX_PA10.GPIO_Mode = GPIO_Mode_IN_FLOATING;
15     USART_RX_PA10.GPIO_Pin = GPIO_Pin_10;
16 
17     GPIO_Init(GPIOA, &USART_TX_PA9);
18     GPIO_Init(GPIOA, &USART_RX_PA10);
19 }
20 
21 
22 //配置USART
23 void user_USART_Config(void)
24 {
25     USART_InitTypeDef USART_Config;
26 
27     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
28 
29     USART_Config.USART_BaudRate = 115200;
30     USART_Config.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
31     USART_Config.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
32     USART_Config.USART_Parity = USART_Parity_No;
33     USART_Config.USART_StopBits = USART_StopBits_1;
34     USART_Config.USART_WordLength = USART_WordLength_8b;
35 
36     USART_Init(USART1, &USART_Config);
37 
38     USART_Cmd(USART1, ENABLE);
39     
40 
41 }
42 
43 //重写fputc函数,以支持printf函数
44 int fputc(int data, FILE * f)
45 {
46     USART_SendData(USART1, (uint8_t)data);
47 
48     while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
49     {
50         
51     }
52 
53     return data;
54 }
55 
56 
57 
58 //重写fgetc函数,以支持printf函数
59 int fgetc(FILE * f)
60 {
61     while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)
62     {
63 
64     }
65 
66     return (int)USART_ReceiveData(USART1);
67 }
View Code

 

6. 在main.c中添加如下代码

 1 #include "stm32f10x.h"
 2 #include "user_spi_flash.h"
 3 #include "user_usart.h"
 4 
 5 #define Buffsize    250        //数据个数
 6 
 7 
 8 
 9 int main(void)
10 {
11     uint8_t w_Buff[Buffsize],r_Buff[Buffsize];    //定义数组存放待写入和读取到的数据
12     uint32_t i;
13 
14     user_USART_GPIO_Config();    //配置USART GPIO口
15     user_USART_Config();        //配置USART
16     user_SPI_GPIO_Config();        //配置SPI GPIO口
17     user_SPI_Config();            //配置SPI
18 
19 
20     //赋值给待写入数组
21     for(i = 0; i < Buffsize; i++)
22     {
23         w_Buff[i] = i + 1;
24     }
25 
26     user_SPI_Flash_Erase_Sector(0);        //擦除要写入的内存区域,要写入必须先擦除,否则写入失败,因为FLASH写入0有效,
27     user_SPI_Flash_Write_NData(w_Buff, 0, Buffsize);    //写入数据到FLASH
28     user_SPI_Flash_Read_NData(r_Buff, 0, Buffsize);        //从FLASH读取数据
29 
30     
31 
32     printf("===================Start===================\n");
33     printf("w_Buff:\n");
34     for(i = 0; i < Buffsize; i++)
35     {
36         printf("w_Buff[%d] = %d\n", i, w_Buff[i]);
37     }
38 
39     printf("r_Buff:\n");
40     for(i = 0; i < Buffsize; i++)
41     {
42         printf("r_Buff[%d] = %d\n", i, r_Buff[i]);
43     }
44     
45 
46 
47     return 0;
48 }
View Code

 

总结:

1. Flash在读写前,要处于非选择状态,需要读写时再打开,否则出现SPI无法正常读写,所以最好在初始化GPIO_InitTypeDef结构体时将其处于非选择状态

2. 字节读写操作不能分开封装,不然会SPI出错,无法正常使用,具体原因不明白?

3. 写入数据前,需要先对写入区域做擦除动作,否则写入的数据异常,这与FLASH的内部硬件实现方式相关,这个是很硬件的问题!

 

实验代码:

链接:https://pan.baidu.com/s/1IELyatFQFpcXPUoVxBqYAg
提取码:vbtv

 

posted @ 2019-04-09 22:52  秋水寒林  阅读(1926)  评论(0编辑  收藏  举报