打印
[其他ST产品]

ST32之SPI与W25Qxx实验-1

[复制链接]
508|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
1 SPI底层函数讲解:
1)初始化函数:
//以下是SPI模块的初始化代码,配置成主机模式,访问SD Card/W25Q64/NRF24L01
//MISO根据手册要设置成上拉输入或浮空输入,根据经验设置成推挽复用也可以,所以这里全部设置成推挽复用功能。
void SPI2_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2,  ENABLE );//SPI2时钟使能

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出,按照表格上SPI_MISO应设置成浮空输入或上拉输入,但根据经验设置成复用推挽输出也可以正常工作。
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB

  GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15上拉

//初始化SPI参数,这里的相位和极性要跟W25Q64芯片相匹配。
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256,这里速率设置为最小值。
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器

SPI_Cmd(SPI2, ENABLE); //使能SPI外设

SPI2_ReadWriteByte(0xff);//启动传输  
                                        //SPI是由主机发起,主机向它的SPI寄存器写入一个字节来发起一次传输。所以这里                                             写入一个字节用来启动传输。

}  


使用特权

评论回复
沙发
自动化陈稳|  楼主 | 2021-3-31 23:01 | 只看该作者
2)SPI2的时钟由APB2经过预分频产生,这里分频系数设置成256。速率为最低。
分频系数的有效范围 从2一直到256

使用特权

评论回复
板凳
自动化陈稳|  楼主 | 2021-3-31 23:02 | 只看该作者
//SPI 速度设置函数,这个函数在很多地方会用到,但固件库中没有提供
//SpeedSet:
//SPI_BaudRatePrescaler_2   2分频   
//SPI_BaudRatePrescaler_8   8分频   
//SPI_BaudRatePrescaler_16  16分频  
//SPI_BaudRatePrescaler_256 256分频
  
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
SPI2->CR1&=0XFFC7;
SPI2->CR1|=SPI_BaudRatePrescaler; //设置SPI2速度
SPI_Cmd(SPI2,ENABLE);

}

使用特权

评论回复
地板
自动化陈稳|  楼主 | 2021-3-31 23:03 | 只看该作者
3)SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓                                                                                                              存空标志位
{
retry++;
if(retry>200)return 0;
}  
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
retry=0;

while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受                                                                                                                 缓存非空标志位
{
retry++;
if(retry>200)return 0;
}      
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据   
}

数据都存放在DR寄存器中,发送出去一个字节后就可以直接读取一个数据

使用特权

评论回复
5
自动化陈稳|  楼主 | 2021-3-31 23:06 | 只看该作者
2  SPI flash W25Qxx的操作
     W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节。W25Q64 的最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节

使用特权

评论回复
6
自动化陈稳|  楼主 | 2021-3-31 23:08 | 只看该作者

使用特权

评论回复
7
自动化陈稳|  楼主 | 2021-3-31 23:10 | 只看该作者
在往某个地址写之前必须确保这个地址上的值是0xFF,否则说明这个地址以前被写过数据,还没有被擦除。W25Q64擦除的最小单位是Sector也就是4k个字节,也就是说如果要想往某个地址写一个值,如果这个地址上的值不是0xFF,那么就要把整个扇区都擦除,然后在写。

使用特权

评论回复
8
自动化陈稳|  楼主 | 2021-3-31 23:11 | 只看该作者
给W25Q64开辟一个4k的缓存,比如定义一个4k的数组,然后在写数据之前先判断如果这个地址上的数据不是0xFF,就先把这个地址所在的Sector里的数据全部保存在4k缓存中,再擦除这个扇区,再把缓存中对应的地址上的数据更新,再把这个4k缓存区的所有数据一次性的写入到这个Sector中。

使用特权

评论回复
9
自动化陈稳|  楼主 | 2021-3-31 23:11 | 只看该作者
1)W25Q64芯片的操作

使用特权

评论回复
10
自动化陈稳|  楼主 | 2021-3-31 23:13 | 只看该作者
里面有很多指令。

flash.h头文件中定义了一些指令,对应芯片手册中的指令。

使用特权

评论回复
11
自动化陈稳|  楼主 | 2021-3-31 23:14 | 只看该作者
//指令表
#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

使用特权

评论回复
12
自动化陈稳|  楼主 | 2021-3-31 23:19 | 只看该作者
对于不同容量的芯片读出的ID定义
//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

使用特权

评论回复
13
自动化陈稳|  楼主 | 2021-3-31 23:20 | 只看该作者
/片选线定义
#define SPI_FLASH_CS    PBout(12)  //选中FLASH,电路上片选线接PB12。

使用特权

评论回复
14
自动化陈稳|  楼主 | 2021-3-31 23:21 | 只看该作者
2)底层函数驱动
   1)//初始化SPI FLASH的IO口
void W25QXX_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  // PB12 推挽 ,设置片选线PB12的工作模式
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
  GPIO_SetBits(GPIOB,GPIO_Pin_12);

W25QXX_CS=1; //SPI FLASH不选中,默认这个Flash不被选中,需要让它工作的时候再拉低。

SPI2_Init();   //初始化SPI

SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式,因为读写Flash数据要求速度比高。
W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.  

}  

使用特权

评论回复
15
自动化陈稳|  楼主 | 2021-3-31 23:22 | 只看该作者
2)读取状态寄存器
//读取SPI_FLASH的状态寄存器
//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
u8 SPI_Flash_ReadSR(void)   
{  
u8 byte=0;   
SPI_FLASH_CS=0;                            //使能器件   

SPI2_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令   
byte=SPI2_ReadWriteByte(0Xff);             //发0xff就读取出来数据,读取一个字节  

SPI_FLASH_CS=1;                            //取消片选     
return byte;   
}

使用特权

评论回复
16
自动化陈稳|  楼主 | 2021-3-31 23:22 | 只看该作者
3) 读ID函数

使用特权

评论回复
17
自动化陈稳|  楼主 | 2021-3-31 23:23 | 只看该作者
4)写状态寄存器

使用特权

评论回复
18
自动化陈稳|  楼主 | 2021-3-31 23:24 | 只看该作者
先发指令01H,接着写要写入的字节。

//写SPI_FLASH状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
void SPI_FLASH_Write_SR(u8 sr)   
{   
SPI_FLASH_CS=0;                            //使能器件   
SPI2_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令   
SPI2_ReadWriteByte(sr);               //写入一个字节  
SPI_FLASH_CS=1;                            //取消片选         
}   

使用特权

评论回复
19
自动化陈稳|  楼主 | 2021-3-31 23:24 | 只看该作者
5)SPI_Flash写使能

使用特权

评论回复
20
自动化陈稳|  楼主 | 2021-3-31 23:25 | 只看该作者
指令是06H。

//SPI_FLASH写使能
//将WEL置位   
void SPI_FLASH_Write_Enable(void)   
{
SPI_FLASH_CS=0;                            //使能器件   
    SPI2_ReadWriteByte(W25X_WriteEnable);      //发送写使能  
SPI_FLASH_CS=1;                            //取消片选         
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

82

主题

1200

帖子

1

粉丝