目录
1、SPI驱动FLASH芯片:思路
2、SPI发送数据和接收数据函数
3、查看SPI传输状态
4、FLASH芯片简介
5、代码
5.1 初始化SPI
5.2 使用SPI发送一个字节,返回收到的数据
5.3?从FLASH指定地址读出指定长度的数据
5.4?从FLASH指定地址写入指定长度的数据
5.5?通过SPI外设给FLASH写数——主函数
1、SPI驱动FLASH芯片:思路
(1)驱动GPIO及端口的时钟 (2)是能SPI外设的时钟 (3)配置SPI外设的模式、地址、速率等参数并使能SPI外设 (4)编写基本SPI按字节收发的函数 (5)编写对FLASH擦除及读写操作的函数
(6)编写main()函数
2、SPI发送数据和接收数据函数
void SPI_I2S_SendData(SPI_TypeDef* SPIx,u16 Data) u16 SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
3、查看SPI传输状态
/* 等待发送区为空,TXE */ /* 等待接收缓存区非空,RXNE*/
SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)
4、FLASH芯片简介
W25Q64:将8M容量分为128个块(Block), 每个块64K字节,每个块分为16个扇区(Sector),每个扇区4K字节 W25Q64的最小擦除单位为一个扇区,每次必须擦除4K个字节 因此需要给W25Q24开辟一个4K缓存区; W25Q64:四线SPI,最大时钟80MHz。
5、代码
5.1 初始化SPI
void SPI_Flash_Init(void)
{
?? ?GPIO_InitTypeDef GPIO_InitStructure;
?? ?RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
?? ?RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI1, ENABLE );
?? ?GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
?? ?GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PA5,6,7 复用推挽输出
?? ?GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
?? ?GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化串口A
?? ?GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);//初始化GPIO
?? ?
?? ?SPI_InitTypeDef SPI_InitStructure;
?? ?SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工
?? ?SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI
?? ?SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收 8 位帧结构
?? ?SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
?? ?SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个跳变沿数据被采样
?? ?SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制
?? ?SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256
?? ?SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
?? ?SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
?? ?SPI_Init(SPI1, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器
?? ?
?? ?SPI_Cmd(SPI1,ENABLE); //使能SPI外设
?? ?
?? ?SPI1_ReadWriteByte(0xff);
}
5.2 使用SPI发送一个字节,返回收到的数据
/* TxData:发送的数? */
/* u8返回值:读取到的字节 */
u8 SPI1_ReadWriteByte(u8 TxData)
{
u8 retry=0;
/* 发送缓存为空 */
while(SPI_I2S_GetFlagStatus(SPI,SPI_I2S_FLAG_TXE)==RESET)
{
retry++;
if(retry>200)return0;
};
SPI_I2S_SendData(SPI1,TxData);//通过外设SPI发送一个数据
retry=0;
/* 接收缓存为非空 */
while(SPI_I2S_GetFlagStatus(SPI,SPI_I2S_FLAG_RXNE)==RESET)
{
retry++;
if(retry>200)return0;
};
return SPI1_ReceiveData(SPI1);//返回SPI1收到的数据
}
5.3?从FLASH指定地址读出指定长度的数据
/* 读取指定长度的数据*/ /* pbuffer:数据存储区*/ /* ReadAddr:开始读取的地址(24bit)*/ /* NumByteToRead:要读取的字节数 (最大65535)*/
void SPI_Flash_Read(u8* pbuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
SPI1_FLASH_CS = 0; //开始CS使能
SPI1_ReadWriteByte(W25X_ReadData);//发送读取命令
SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); //发送 24bit 地址
SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
SPI1_ReadWriteByte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pbuffer[i] = SPI1_ReadWriteByte(0XFF);
}
SPI1_FLASH_CS = 1; //取消CS使能
}
5.4?从FLASH指定地址写入指定长度的数据
void SPI_Flash_Write(u8* pbuffer,u32 WriteAddr,u16 NumByteToRead)
{
u8 SPI_FLASH_BUFFER[4096];
u32 secpos; u16 secoff;
u16 secremain; u16 i;
secpos=WriteAddr/4096; //扇区地址
secoff=WriteAddr%4096; //在扇区内的偏移
secremain=4096-secoff; //扇区剩余空间大小
if(NumBytetoWrite<=secremain)
{
secremain = NumBytetoWrite;
}
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[i]; //复制
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; //下一个扇区可以写完了
}
};
}
5.5?通过SPI外设给FLASH写数——主函数
int main(void)
{
const u8 TEXT_Buffer[] = {"WarShipSTM32 SPI TEST"};
u8 key;
u16 i=0;
u8 datatemp[size];
u32 FLASH_SIZE;
delay_init();
SPI_Flash_Init();
while(1)
{
if(key == WKUP_PRES) //WK_UP按键按下,写入FLASH
{
SPI_Flash_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
}
if(key == KEY0_PRES) //key0按下,读取字符串并显示
{
SPI_Flash_Read(datatemp,FLASH_SIZE-100,SIZE);//从指定地址读 SIZE 字节
}
i++;
delay_ms(10);
}
}
|