OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 20245|回复: 27

【ALIENTEK 战舰STM32开发板例程系列连载+教学】第二十八章 SPI实验

[复制链接]

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2013-3-16 21:08:07 | 显示全部楼层 |阅读模式

第二十八章 SPI 实验

本章我们将向大家介绍STM32SPI功能。在本章中,我们将使用STM32自带的SPI来实现对外部FLASHW25Q64)的读写,并将结果显示在TFTLCD模块上。本章分为如下几个部分:

28.1 SPI 简介

28.2 硬件设计

28.3 软件设计

28.4 下载验证 

28.1 SPI 简介

SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROMFLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32也有SPI接口。

SPI接口一般使用4条线通信:

MISO 主设备数据输入,从设备数据输出。

MOSI 主设备数据输出,从设备数据输入。

SCLK时钟信号,由主设备产生。

CS从设备片选信号,由主设备控制。

SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。

SPI总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。

不同时钟相位下的总线数据传输时序如图28.1.1所示:



28.1.1 不同时钟相位下的总线传输时序(CPHA=0/1

STM32SPI功能很强大,SPI时钟最多可以到18Mhz,支持DMA,可以配置为SPI协议或者I2S协议(仅大容量型号支持,战舰STM32开发板是支持的)。

本章,我们将使用STM32SPI来读取外部SPI FLASH芯片(W25Q64),实现类似上节的功能。这里对SPI我们只简单介绍一下SPI的使用,STM32SPI详细介绍请参考《STM32参考手册》第457页,23节。然后我们再介绍下SPI FLASH芯片。

这节,我们使用STM32SPI2的主模式,下面就来看看SPI2部分的设置步骤吧,STM32的主模式配置步骤如下:

1)配置相关引脚的复用功能,使能SPI2时钟。

我们要用SPI2,第一步就要使能SPI2的时钟,SPI2的时钟通过APB1ENR的第14位来设置。其次要设置SPI2的相关引脚为复用输出,这样才会连接到SPI2上否则这些IO口还是默认的状态,也就是标准输入输出口。这里我们使用的是PB1314153个(SCK.MISOMOSICS使用软件管理方式),所以设置这三个为复用IO

2)设置SPI2工作模式。

这一步全部是通过SPI2_CR1来设置,我们设置SPI2为主机模式,设置数据格式为8位,然后通过CPOLCPHA位来设置SCK时钟极性及采样方式。并设置SPI2的时钟频率(最大18Mhz),以及数据的格式(MSB在前还是LSB在前)。

3)使能SPI2

这一步通过SPI2_CR1bit6来设置,以启动SPI2,在启动之后,我们就可以开始SPI通讯了。

SPI2的使用就介绍到这里,接下来介绍一下W25Q64W25Q64是华邦公司推出的大容量SPI FLASH产品,W25Q64的容量为64Mb,该系列还有W25Q80/16/32等。ALIENTEK所选择的W25Q64容量为64Mb,也就是8M字节。

W25Q648M的容量分为128个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25Q64的最少擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25Q64开辟一个至少4K的缓存区,这样对SRAM要求比较高,要求芯片必须有4K以上SRAM才能很好的操作。

W25Q64的擦写周期多达10W次,具有20年的数据保存期限,支持电压为2.7~3.6VW25Q64支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可以到80Mhz(双输出时相当于160Mhz,四输出时相当于320M),更多的W25Q64的介绍,请参考W25Q64DATASHEET

28.2 硬件设计

本章实验功能简介:开机的时候先检测W25Q64是否存在,然后在主循环里面检测两个按键,其中1个按键(WK_UP)用来执行写入W25Q64的操作,另外一个按键(KEY1)用来执行读出操作,在TFTLCD模块上显示相关信息。同时用DS0提示程序正在运行。

所要用到的硬件资源如下:

1)  指示灯DS0

2)  WK_UPKEY1按键

3) TFTLCD模块

4) SPI

5) W25Q64

这里只介绍W25Q64STM32的连接,板上的W25Q64是直接连在STM32SPI2上的,连接关系如图28.2.1所示:



28.2.1 STM32W25Q64连接电路图

28.3 软件设计

打开上一章的工程,首先在HARDWARE文件夹下新建一个FLASH的文件夹和SPI的文件夹。然后新建一个flash.cflash.h的文件保存在FLASH文件夹下,新建spi.cspi.h的文件,保存在SPI文件夹下,并将这两个文件夹加入头文件包含路径。

打开spi.c文件,输入如下代码:

#include "spi.h"

//SPI口初始化

//这里针是对SPI2的初始化

void SPI2_Init(void)

{    

       RCC->APB2ENR|=1<<3;           //PORTB时钟使能        

       RCC->APB1ENR|=1<<14;        //SPI2时钟使能

       //这里只针对SPI口初始化

       GPIOB->CRH&=0X000FFFFF;

       GPIOB->CRH|=0XBBB00000;     //PB13/14/15复用            

       GPIOB->ODR|=0X7<<13;         //PB13/14/15上拉

       SPI2->CR1|=0<<10;                    //全双工模式 

       SPI2->CR1|=1<<9;                     //软件nss管理

       SPI2->CR1|=1<<8; 

 

       SPI2->CR1|=1<<2;                     //SPI主机

       SPI2->CR1|=0<<11;                    //8bit数据格式     

       SPI2->CR1|=1<<1;                     //空闲模式下SCK1 CPOL=1

       SPI2->CR1|=1<<0;                     //数据采样从第二个时间边沿开始,CPHA=1 

       //SPI2属于APB1的外设.时钟频率最大为36M.

       SPI2->CR1|=3<<3;                     //Fsck=Fpclk1/256

       SPI2->CR1|=0<<7;                     //MSBfirst  

       SPI2->CR1|=1<<6;                     //SPI设备使能

       SPI2_ReadWriteByte(0xff);//启动传输       

}  

//SPI2速度设置函数

//SpeedSet:0~7

//SPI速度=fAPB1/2^(SpeedSet+1)

//APB1时钟一般为36Mhz

void SPI2_SetSpeed(u8 SpeedSet)

{

       SpeedSet&=0X07;                //限制范围

       SPI2->CR1&=0XFFC7;

       SPI2->CR1|=SpeedSet<<3;   //设置SPI2速度 

       SPI2->CR1|=1<<6;              //SPI设备使能       

}

//SPI2 读写一个字节

//TxData:要写入的字节

//返回值:读取到的字节

u8 SPI2_ReadWriteByte(u8 TxData)

{           

       u16 retry=0;                       

       while((SPI2->SR&1<<1)==0)             //等待发送区空    

       {

              retry++;

              if(retry>=0XFFFE)return 0;        //超时退出

       }                    

       SPI2->DR=TxData;                         //发送一个byte

       retry=0;

       while((SPI2->SR&1<<0)==0)            //等待接收完一个byte 

       {

              retry++;

              if(retry>=0XFFFE)return 0;  //超时退出

       }                                                  

       return SPI2->DR;                       //返回收到的数据                          

}

此部分代码主要初始化SPI,这里我们选择的是SPI2,所以在SPI2_Init函数里面,其相关的操作都是针对SPI2的,其初始化步骤和我们上面介绍的一样。在初始化之后,我们就可以开始使用SPI2了,在SPI2_Init函数里面,把SPI2的频率设置成了最低(36Mhz256分频)。在外部函数里面,我们通过SPI2_SetSpeed来设置SPI2的速度,而我们的数据发送和接收则是通过SPI2_ReadWriteByte函数来实现的。

保存spi.c,并把该文件加入HARDWARE组下面,然后我们打开spi.h在里面输入如下代码:

#ifndef __SPI_H

#define __SPI_H

#include "sys.h"

// SPI总线速度设置

#define SPI_SPEED_2              0

#define SPI_SPEED_4              1

#define SPI_SPEED_8              2

#define SPI_SPEED_16             3

#define SPI_SPEED_32              4

#define SPI_SPEED_64              5

#define SPI_SPEED_128            6

#define SPI_SPEED_256            7

void SPI2_Init(void);                       //初始化SPI2

void SPI2_SetSpeed(u8 SpeedSet);     //设置SPI2速度  

u8 SPI2_ReadWriteByte(u8 TxData);   //SPI2总线读写一个字节

#endif

此部分代码我们就不多介绍了,保存spi.h,然后我们打开flash.c,在里面编写与W25Q64操作相关的代码,由于篇幅所限,详细代码,这里就不贴出了。我们仅介绍几个重要的函数,首先是SPI_Flash_Read函数,该函数用于从W25Q64的指定地址读出指定长度的数据。其代码如下:           

//读取SPI FLASH 

//在指定地址开始读取指定长度的数据

//pBuffer:数据存储区

//ReadAddr:开始读取的地址(24bit)

//NumByteToRead:要读取的字节数(最大65535)

void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)  

{

      u16 i;                                                                       

       SPI_FLASH_CS=0;                       //使能器件   

    SPI2_ReadWriteByte(W25X_ReadData);        //发送读取命令  

    SPI2_ReadWriteByte((u8)((ReadAddr)>>16));         //发送24bit地址   

    SPI2_ReadWriteByte((u8)((ReadAddr)>>8));  

    SPI2_ReadWriteByte((u8)ReadAddr);  

    for(i=0;i<NumByteToRead;i++)

       {

        pBuffer=SPI2_ReadWriteByte(0XFF);   //循环读数 

    }

       SPI_FLASH_CS=1;                                           

}

由于W25Q64支持以任意地址(但是不能超过W25Q64的地址范围)开始读取数据,所以,这个代码相对来说就比较简单了,在发送24位地址之后,程序就可以开始循环读数据了,其地址会自动增加的,不过要注意,不能读的数据超过了W25Q64的地址范围哦!否则读出来的数据,就不是你想要的数据了。

有读的函数,当然就有写的函数了,接下来,我们介绍SPI_Flash_Write这个函数,该函数的作用与SPI_Flash_Read的作用类似,不过是用来写数据到W25Q64里面的,其代码如下:

//SPI FLASH 

//在指定地址开始写入指定长度的数据

//该函数带擦除操作!

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)                                      

//NumByteToWrite:要写入的字节数(最大65535)  

u8 SPI_FLASH_BUFFER[4096];       

void SPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)  

{

       u32 secpos;

       u16 secoff;

       u16 secremain;         

      u16 i;   

       u8 * SPI_FLASH_BUF;        

     SPI_FLASH_BUF=SPI_FLASH_BUFFER;     

      secpos=WriteAddr/4096;//扇区地址 0~511 for w25x16

       secoff=WriteAddr%4096;//在扇区内的偏移

       secremain=4096-secoff;//扇区剩余空间大小  

      //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用

      if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节

       while(1)

       {    

              SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容

              for(i=0;i<secremain;i++)//校验数据

              {

                     if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//需要擦除          

              }

              if(i<secremain)//需要擦除

              {

                     SPI_Flash_Erase_Sector(secpos);//擦除这个扇区

                     for(i=0;i<secremain;i++)          //复制

                     {

                            SPI_FLASH_BUF[i+secoff]=pBuffer;      

                     }

                     SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);

//写入整个扇区 

              }else SPI_Flash_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;                      //下一个扇区可以写完了

              }    

       };   

}

该函数可以在W25Q64的任意地址开始写入任意长度(必须不超过W25Q64的容量)的数据。我们这里简单介绍一下思路:先获得首地址(WriteAddr)所在的扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过,再先看看是否要擦除,如果不要,则直接写入数据即可,如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。当所需要写入的数据长度超过一个扇区的长度的时候,我们先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样的操作,如此循环,直到写入结束。

其他的代码就比较简单了,我们这里不介绍了。保存falsh.c,然后加入到HARDWARE组下面,再打开flahs.h,在该文件里面输入如下代码:

#ifndef __FLASH_H

#define __FLASH_H                      

#include "sys.h"

//W25X系列/Q系列芯片列表       

//W25Q80 ID  0XEF13

//W25Q16 ID  0XEF14

//W25Q32 ID  0XEF15

//W25Q32 ID  0XEF16      

#define W25Q80   0XEF13       

#define W25Q16   0XEF14

#define W25Q32   0XEF15

#define W25Q64   0XEF16

extern u16 SPI_FLASH_TYPE;           //定义我们使用的flash芯片型号                

#define    SPI_FLASH_CS PBout(12)  //选中FLASH     

//指令表

#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

void SPI_Flash_Init(void);

u16  SPI_Flash_ReadID(void);                           //读取FLASH ID

u8    SPI_Flash_ReadSR(void);                  //读取状态寄存器

void SPI_FLASH_Write_SR(u8 sr);                 //写状态寄存器

void SPI_FLASH_Write_Enable(void);             //写使能

void SPI_FLASH_Write_Disable(void);             //写保护

void SPI_Flash_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);

void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);      //读取flash

void SPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);      //写入flash

void SPI_Flash_Erase_Chip(void);                 //整片擦除

void SPI_Flash_Erase_Sector(u32 Dst_Addr);     //扇区擦除

void SPI_Flash_Wait_Busy(void);              //等待空闲

void SPI_Flash_PowerDown(void);              //进入掉电模式

void SPI_Flash_WAKEUP(void);                     //唤醒

#endif

这里面就定义了一些与W25Q64操作相关的命令(部分省略了),这些命令在W25Q64的数据手册上都有详细的介绍,感兴趣的读者可以参考该数据手册,其他的就没啥好说的了。保存此部分代码。最后,我们在test.c里面,修改main函数如下:

//要写入到W25Q64的字符串数组

const u8 TEXT_Buffer[]={"WarShipSTM32 SPI TEST"};

#define SIZE sizeof(TEXT_Buffer)     

int main(void)

{           

       u8 key;

       u16 i=0;

       u8 datatemp[SIZE];

       u32 FLASH_SIZE;

      Stm32_Clock_Init(9);    //系统时钟设置

       uart_init(72,9600);      //串口初始化为9600

       delay_init(72);                  //延时初始化

       LED_Init();                 //初始化与LED连接的硬件接口

       LCD_Init();                  //初始化LCD

       usmart_dev.init(72);      //初始化USMART       

       KEY_Init();                  //按键初始化             

       SPI_Flash_Init();                //SPI FLASH 初始化      

      POINT_COLOR=RED;//设置字体为红色

       LCD_ShowString(60,50,200,16,16,"WarShip STM32");   

       LCD_ShowString(60,70,200,16,16,"SPI TEST");      

       LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");

       LCD_ShowString(60,110,200,16,16,"2012/9/9");      

       LCD_ShowString(60,130,200,16,16,"WKUP:Write  KEY1:Read"); //显示提示信息                   while(SPI_Flash_ReadID()!=W25Q64)                                                 //检测不到W25Q64

       {

              LCD_ShowString(60,150,200,16,16,"25Q64 Check Failed!");

              delay_ms(500);

              LCD_ShowString(60,150,200,16,16,"Please Check!      ");

              delay_ms(500);

              LED0=!LED0;//DS0闪烁

       }

       LCD_ShowString(60,150,200,16,16,"25Q64 Ready!");

       FLASH_SIZE=8*1024*1024;       //FLASH 大小为8M字节

      POINT_COLOR=BLUE;              //设置字体为蓝色   

       while(1)

       {

              key=KEY_Scan(0);

              if(key==KEY_UP)//KEY_UP按下,写入W25Q64

              {

                     LCD_Fill(0,170,239,319,WHITE);//清除半屏   

                    LCD_ShowString(60,170,200,16,16,"Start Write W25Q64....");

                     SPI_Flash_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);             

//从倒数第100个地址处开始,写入SIZE长度的数据

                     LCD_ShowString(60,170,200,16,16,"W25Q64 Write Finished!");//提示传送完成

              }

              if(key==KEY_DOWN)//KEY_DOWN按下,读取字符串并显示

              {

                    LCD_ShowString(60,170,200,16,16,"Start Read W25Q64.... ");

                     SPI_Flash_Read(datatemp,FLASH_SIZE-100,SIZE);                     

//从倒数第100个地址处开始,读出SIZE个字节

                     LCD_ShowString(60,170,200,16,16,"The Data Readed Is:  ");//提示传送完成

                     LCD_ShowString(60,190,200,16,16,datatemp);//显示读到的字符串

              }

              i++;

              delay_ms(10);

              if(i==20)

              {

                     LED0=!LED0;//提示系统正在运行    

                     i=0;

              }              

       }

}

这部分代码和IIC实验那部分代码大同小异,我们就不多说了,实现的功能就和IIC差不多,不过此次写入和读出的是SPI FLASH,而不是EEPROM

28.4 下载验证

在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,通过先按WK_UP按键写入数据,然后按KEY1读取数据,得到如图28.4.1所示:



28.4.1 SPI实验程序运行效果图

伴随DS0的不停闪烁,提示程序在运行。程序在开机的时候会检测W25Q64是否存在,如果不存在则会在TFTLCD模块上显示错误信息,同时DS0慢闪。大家可以通过跳线帽把PB12PB13短接就可以看到报错了。

 

实验23 SPI实验.rar

124.62 KB, 下载次数: 1910

《STM32开发指南》第二十八章 SPI实验.rar

601.08 KB, 下载次数: 1838

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-3-16 21:21:17 | 显示全部楼层
建议版主能同时上传一个库函数版本的
目前看的库函数的多一些,当然寄存器也会通读几遍功能
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-3-16 21:47:29 | 显示全部楼层
我也想,不过由于帖子字数限制,库函数的大家还是直接看我们的《STM32开发指南》-库函数版本吧。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-3-21 10:56:43 | 显示全部楼层
回复【3楼】正点原子:
---------------------------------
库函数pdf的不好复制摘录
而且库函数版本 pdf写的比这里详细我觉得
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-3-21 11:01:59 | 显示全部楼层
回复【4楼】wwjdwy:
---------------------------------
那看库函数的文档就好了.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-5-3 07:26:10 | 显示全部楼层
在某个地址写入前,必须先erase所在sector?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-5-3 11:24:30 | 显示全部楼层
如果是0XFF就不需要擦除。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

3

主题

15

帖子

0

精华

新手上路

积分
47
金钱
47
注册时间
2013-5-17
在线时间
0 小时
发表于 2013-7-11 20:09:44 | 显示全部楼层
回复【7楼】正点原子:
---------------------------------
能不能说下,W25Q64的地址范围?
 FLASH_SIZE=8*1024*1024;
SPI_Flash_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
就能读写正常,地址换成别的100,150就不能读出来,换到10000又可以了,是不是有些地址不让用呢?
用串口显示读出的数据,居然是空格,定义的数据是
const u8 TEXT_Buffer[]={"STM32 W25Q64 TEST"};
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-7-12 00:16:19 | 显示全部楼层
回复【8楼】流星飞雪shmily:
---------------------------------
读写都要改才可以。任意地址都可以的。
地址范围是0~8*1024*1024.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

3

主题

15

帖子

0

精华

新手上路

积分
47
金钱
47
注册时间
2013-5-17
在线时间
0 小时
发表于 2013-7-12 08:54:57 | 显示全部楼层
回复【9楼】正点原子:
---------------------------------
读写的地址一样的,都是FLASH_SIZE-100,只是初始化的时候,把这个值改成大于0 的,从0--4096*2,试了10几个地址。都读不出来。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-7-12 10:20:19 | 显示全部楼层
贴个你读不出来的代码看看.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

11

主题

71

帖子

0

精华

初级会员

Rank: 2

积分
135
金钱
135
注册时间
2014-1-3
在线时间
0 小时
发表于 2014-1-7 10:39:17 | 显示全部楼层
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
{
retry++;
if(retry>200)return 0;
}     原子哥,这段程序的意思是:等待 总线为不空闲的时候吧?如果总线一直为高电平(不能接发数据),retry++,等到retry到200,返回0是什么意思呢?这个200是怎么确定的呢?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2014-1-7 15:13:36 | 显示全部楼层
回复【12楼】jidian0177:
---------------------------------
返回0,是SPI通信存在问题的时候返回的,这个0没有特别意义,你可以设置为其他值,只要不影响你其他代码使用即可.
而200也是随意设置,原则是最大限度的进行延时,提高容错能力.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

11

主题

71

帖子

0

精华

初级会员

Rank: 2

积分
135
金钱
135
注册时间
2014-1-3
在线时间
0 小时
发表于 2014-1-7 15:45:35 | 显示全部楼层
回复【13楼】正点原子:
---------------------------------
谢谢原子哥啊
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2014-1-29
在线时间
3 小时
发表于 2014-2-27 10:41:36 | 显示全部楼层
原子站长,在函数u8 SPI2_ReadWriteByte(u8 TxData)中,while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) 这句话是用来接收从flash发送的数据的吗?如果是的话,似乎是先发送一个数据,接着又接收数据,因为全双工的原因?
u8 SPI2_ReadWriteByte(u8 TxData)
{
u8 retry=0;  
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //
{
retry++;
if(retry>200)return 0;
}   
SPI_I2S_SendData(SPI2, TxData); //
retry=0;

while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //
{
retry++;
if(retry>200)return 0;
}        
return SPI_I2S_ReceiveData(SPI2); //     
}
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2014-1-29
在线时间
3 小时
发表于 2014-2-27 10:43:01 | 显示全部楼层
回复【15楼】southernsky:

原子站长,在函数u8 SPI2_ReadWriteByte(u8 TxData)中,while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) 这句话是用来接收从flash发送的数据的吗?如果是的话,似乎是先发送一个数据,接着又接收数据,因为全双工的原因?
u8 SPI2_ReadWriteByte(u8 TxData)
{
u8 retry=0;  
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //
{
retry++;
if(retry>200)return 0;
}   
SPI_I2S_SendData(SPI2, TxData); //
retry=0;
while
......
---------------------------------
这个时候接收到的数据有什么意义?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2014-2-27 10:48:04 | 显示全部楼层
回复【15楼】southernsky:
---------------------------------
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) 
是等待接收完成.

另外,这个SPI是全双工,所有时钟都是由主机产生,所以要接收从机数据,必定要主机发送一个数据(目的就是产生时钟),从而读取(同时)来自从机的数据.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
30
金钱
30
注册时间
2014-1-29
在线时间
3 小时
发表于 2014-2-27 11:00:45 | 显示全部楼层
回复【17楼】正点原子:

回复【15楼】southernsky:
---------------------------------
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) 
是等待接收完成.
另外,这个SPI是全双工,所有时钟都是由主机产生,所以要接收从机数据,必定要主机发送一个数据(目的就是产生时钟),从而读取(同时)来自从机的数据.

---------------------------------
这个时候读取到的来自从机数据的意义是什么呢?在flash.c中的void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   中,
void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   

  u16 i;         
SPI_FLASH_CS=0;                            //
    SPI2_ReadWriteByte(W25X_ReadData);         //
    SPI2_ReadWriteByte((u8)((ReadAddr)>>16));  //   
    SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
    SPI2_ReadWriteByte((u8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)

        pBuffer=SPI2_ReadWriteByte(0XFF);   //  
    }
SPI_FLASH_CS=1;                            //            
}  
SPI2_ReadWriteByte(0XFF)这句话又是什么意思;前面几句已经设定命令和地址了,为什么在这里还要写入0xFF?>
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2014-2-27 19:11:19 | 显示全部楼层
回复【18楼】southernsky:
---------------------------------
前面说了,SPI是主设备产生时钟,你不给从机时钟,他能输出数据么?
都是时序逻辑啊...


时钟多没有,你读啥数据???
你也没看到W25Q64要带个晶振吧?

既然没有晶振,必须有时钟,它才能工作啊.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2014-1-24
在线时间
0 小时
发表于 2014-9-24 20:56:04 | 显示全部楼层
回复【19楼】正点原子:
---------------------------------
原子哥我想问下怎么从一个给定的起始地址还有一个结束地址擦除呢?!
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2014-9-24 23:00:45 | 显示全部楼层
回复【20楼】wsx05:
---------------------------------
不要这么做。
以扇区为单位擦除的
无法以字节为单位擦除。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

初级会员

Rank: 2

积分
52
金钱
52
注册时间
2013-9-18
在线时间
6 小时
发表于 2014-12-5 21:02:03 | 显示全部楼层
你好,原子哥:

问题:        
       GPIOB->CRH|=0XBBB00000;     //PB13/14/15复用            
     GPIOB->ODR|=0X7<<13;         //PB13/14/15上拉
     B13/14/15已经设置为了复用推挽输出,规格书上显示上拉/下拉禁止了,那你这里将PB13/14/15设为上拉,是什么意思?

     谢谢!!
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2014-12-5 22:48:00 | 显示全部楼层
回复【22楼】chengong510:
---------------------------------
 这是习惯性操作,实际没影响,去掉即可。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

4

主题

11

帖子

0

精华

新手上路

积分
47
金钱
47
注册时间
2015-8-5
在线时间
0 小时
发表于 2015-8-6 16:02:43 | 显示全部楼层
原子哥,我使用的是你的代码,为什么我的只可以读,不能写入呢!
希望大家能相互帮助下
回复 支持 反对

使用道具 举报

4

主题

11

帖子

0

精华

新手上路

积分
47
金钱
47
注册时间
2015-8-5
在线时间
0 小时
发表于 2015-8-6 16:03:04 | 显示全部楼层
回复【23楼】正点原子:
---------------------------------
原子哥,我使用的是你的代码,为什么我的只可以读,不能写入呢!
希望大家能相互帮助下
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2015-8-6 21:35:50 | 显示全部楼层
回复【25楼】记忆:
---------------------------------
用的是我们的开发板?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

头像被屏蔽

12

主题

26

帖子

0

精华

禁止发言

积分
112
金钱
112
注册时间
2013-3-16
在线时间
29 小时
发表于 2016-1-20 17:19:35 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
129
金钱
129
注册时间
2017-4-11
在线时间
31 小时
发表于 2017-5-3 13:50:11 | 显示全部楼层
请问下这个程序里有带均衡擦写和垃圾回收吗,我看了代码,有点像又觉得不是,
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-8-21 17:53

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表