程序配置过程:
1、使能SPIx和IO口时钟:
RCC_AHBxPeriphClockCmd()/RCC_APBxPeriphClockCmd();
2、初始化IO口为复用功能:
void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct);
3、设置引脚复用映射:
GPIO_PinAFConfig();
4、初始化SPIx,设置SPIx工作模式:
void SPI_Init(SPI_TypeDef* SPIx,SPI_InitTypeDef* SPI_InitStruct);
5、使能SPIx:
void SPI_Cmd(SPI_TypeDef* SPIx,FunctionalState NewState);
6、SPI传输数据:
void SPI_I2C_SendData(SPI_TypeDef* SPIx,uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
7、查看SPI传输数据:
SPI_I2C_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE);
使能SPIx和IO口时钟
- void SPI_Init(void)
- {
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
- }
初始化IO口为复用功能
- void SPI_Init(void)
- {
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
- GPIO_InitTypeDef GPIO_InitABC;
- GPIO_InitABC.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
- GPIO_InitABC.GPIO_Mode=GPIO_Mode_AF;//复用功能
- GPIO_InitABC.GPIO_OType=GPIO_OType_PP;//推挽输出
- GPIO_InitABC.GPIO_Speed=GPIO_Speed_100MHz;//100MHz
- GPIO_InitABC.GPIO_PuPd=GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOB,&GPIO_InitABC);
- }
设置引脚复用映射
- void SPI_Init(void)
- {
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);//PB3复用为SPI1
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);//PB4复用为SPI1
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);//PB5复用为SPI1
-
- RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
- RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
- }
初始化SPIx,设置SPIx工作模式
- void SPI_Init(void)
- {
- SPI_InitTypeDef SPI_InitABC;
- SPI_InitABC.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//设置SPI单向或者双向的数据模式
- SPI_InitABC.SPI_Mode=SPI_Mode_Master;//设置SPI工作模式:设置为主SPI
- SPI_InitABC.SPI_DataSize=SPI_DataSize_8b;//设置SPI的数据大小:SPI发送接收8为帧结构
- SPI_InitABC.SPI_CPOL=SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
- SPI_InitABC.SPI_CPHA=SPI_CPHA_2Edge;//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
- SPI_InitABC.SPI_NSS=SPI_NSS_Soft;//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理
- SPI_InitABC.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_256;//定义波特率预分频的值
- SPI_InitABC.SPI_FirstBit=SPI_FirstBit_MSB;//指定数据传输从MSB位开始
- SPI_InitABC.SPI_CRCPolynomial=7;//CRC值计算的多项式
- SPI_Init(SPI1,&SPI_InitABC);
- }
使能SPIx
- void SPI_Init(void)
- {
- SPI_Cmd(SPI1,ENABLE);
- SPI1_ReadWriteByte(0xff);
- }
- //设置SPI1速度
- void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
- {
- assert_param(IS_SPI_BAUDRATEPRESCALER(SPI_BauudRatePrescaler));//判断有效性
- SPI1->CR1&=0XFFC7;//位3~5清零,用来设置波特率
- SPI1->CR1|=SPI_BaudRatePrescaler;//设置SPI1速度
- SPI_Cmd(SPI1,ENABLE);//使能SPI1
- }
SPI传输数据
- u8 SPI1_ReadEriteByte(u8 TxData)
- {
- while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET){}//等待发送区空
- SPI_I2S_SendData(SPI1,TxData);//通过外设SPIx发送一个byte数据
- while(SPI_I2S_GetFlagStatus(SPI1_I2S_FLAG_RXNE)==RESET){}//等待接收完一个byte
- return SPI_I2S_ReceiveData(SPI1);//返回通过SPIx最近接收的数据
- }
程序思路:
写一个地址之前需要先判断是不是被擦除了(比如擦除之后的值为100,则需要将内容与100相比较从而判断是否擦除了),若未擦除则需要擦除,FLASH的最小单位是4K。
W25QXX_Write函数思路:每个sector是4K(等于4096个地址),在写任何一个地址前,如果该地址的值不是0xFF,必须先擦除对应的sector然后再写。
擦除的原理:将整个sector读出来放到一个4k的Buffer中,然后将sector擦除,在Buffer中更新数据,然后将Buffer中的数据写到sector中。(写的数据未超越一个sector)如果写的数据超过一个sector,处理方式相同。
相关代码:
- void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
- {
- u32 secpos;
- u16 secoff;
- u16 i;
- u16 secremain;
- u8* W25QXX_BUF;
- W25QXX_BUF=W25QXX_BUFFER;
- secpos=WriteAddr/4096;//确定扇区地址(也就是sector的地址)
- secoff=WriteAddr%4096;//判断在扇区的偏移(判断sector中数据的位置,是否跨越一个sector)
- secremain=4096-secoff;//扇区剩余空间(sector剩余空间)
- if(NmByteToWrite<=secremain) secremain=NumByteToWrite;//如果写入的数据小于剩余的空间,就把secremain设置为写入的数据大小,这样只要写到了secremain就会终止。
- while(1)//先把扇区的数据读取出来
- {
- W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
- for(i=1;i
//校验数据,是否擦除 - {
- if(W25QXX_BUF[secoff+i]!=oXFF) break;//需要擦除
- }
- if(i
// - {
- W25QXX_Erase_Sector(secpos);//擦除扇区
- for(i=0;i
- {
- W25QXX_BUF[i+secoff]=pBuffer[i];
- }
- W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区
- }else W25QXX_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;//下一个扇区可以写完了
- }
- }
- }