首先吐槽新浪博客,粘代码上去提示我非法字符,转战优快云。
使用的主芯片是efm32wg256,spiflash和主芯片的接口情况如下:
首先进行spi通信的配置:
/* Enable clock for USART2 */
CMU_ClockEnable(cmuClock_USART2, true);
extern void USART2_enter_DefaultMode_from_RESET(void)
{
// $[USART_InitAsync]
// [USART_InitAsync]$
// $[USART_InitSync]
USART_InitSync_TypeDef initsync = USART_INITSYNC_DEFAULT;
initsync.baudrate = 20000000;
initsync.databits = usartDatabits8;
initsync.master = 1;
initsync.msbf = 1;
initsync.clockMode = usartClockMode0;
#if defined( USART_INPUT_RXPRS ) && defined( USART_TRIGCTRL_AUTOTXTEN )
initsync.prsRxEnable = 0;
initsync.prsRxCh = 0;
initsync.autoTx = 0;
#endif
USART_InitSync(USART2, &initsync);
// [USART_InitSync]$
// $[USART_InitPrsTrigger]
USART_PrsTriggerInit_TypeDef initprs = USART_INITPRSTRIGGER_DEFAULT;
initprs.rxTriggerEnable = 0;
initprs.txTriggerEnable = 0;
initprs.prsTriggerChannel = usartPrsTriggerCh0;
USART_InitPrsTrigger(USART2, &initprs);
// [USART_InitPrsTrigger]$
}
/* Pin PB3 is configured to Push-pull with alt. drive strength */
GPIO->P[1].DOUT |= (1 << 3);
GPIO->P[1].MODEL = (GPIO->P[1].MODEL & ~_GPIO_P_MODEL_MODE3_MASK)
| GPIO_P_MODEL_MODE3_PUSHPULLDRIVE;
/* Pin PB4 is configured to Input enabled*/
GPIO->P[1].MODEL = (GPIO->P[1].MODEL & ~_GPIO_P_MODEL_MODE4_MASK)
| GPIO_P_MODEL_MODE4_INPUT;
/* Pin PB5 is configured to Push-pull with alt. drive strength */
GPIO->P[1].DOUT |= (1 << 5);
GPIO->P[1].MODEL = (GPIO->P[1].MODEL & ~_GPIO_P_MODEL_MODE5_MASK)
| GPIO_P_MODEL_MODE5_PUSHPULLDRIVE;
/* Pin PB6 is configured to Push-pull with alt. drive strength */
GPIO->P[1].DOUT |= (1 << 6);
GPIO->P[1].MODEL = (GPIO->P[1].MODEL & ~_GPIO_P_MODEL_MODE6_MASK)
| GPIO_P_MODEL_MODE6_PUSHPULLDRIVE;
配置完通信开始编写spiflash的驱动文件:bsp_spiflash.c
首先是初始化函数:
#define W25QXX_CS(x) x?(GPIO_PinOutSet(gpioPortB,6)):(GPIO_PinOutClear(gpioPortB,6)); //W25QXX的片选信号
void W25QXX_Init(void)
{
W25QXX_CS(1); //SPI FLASH不选中
//复位SPI 到原始状态,防止上次命令死锁导致初始化失败
uint32_t i;
W25QXX_Wait_Busy();
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
W25QXX_CS(0);
SPI1_ReadWriteByte(W25X_ResetEnable);
W25QXX_CS(1);
i=3904;
while (i-- > 0) __asm__("nop");
W25QXX_CS(0);
SPI1_ReadWriteByte(W25X_Reset);
W25QXX_CS(1);
i=11904;
while (i-- > 0) __asm__("nop");
//此时的等待是必须的
W25QXX_Wait_Busy();
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
W25QXX_CS(0);
/* Send "Write SR3 " instruction */
SPI1_ReadWriteByte(0x11);
/* Enable 100% OUT PUT DRIVER STRENGTH 3 byte地址模式 */
SPI1_ReadWriteByte(0x00);
W25QXX_CS(1);
//此时的等待是必须的,设置w25qXX为4byte模式
W25QXX_Wait_Busy();
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
W25QXX_CS(0);
SPI1_ReadWriteByte(W25X_4byteEnable);
W25QXX_CS(1);
}
之后使用了原子的spiflash的读写擦除等一系列函数,只是将读写地址改为4byte模式,重定义了spi发送指令的函数
//SPI 读写一个字节
//Tx Data:要写入的字节
//返回值:读取到的字节
uint8_t SPI1_ReadWriteByte(uint8_t data)
{
return USART_SpiTransfer(USART2,data);
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(32bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
W25QXX_CS(0); //使能器件
SPI1_ReadWriteByte(W25X_ReadData); //发送读取命令
SPI1_ReadWriteByte((u8)((ReadAddr)>>24)); //发送32bit地址
SPI1_ReadWriteByte((u8)((ReadAddr)>>16));
SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
SPI1_ReadWriteByte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=SPI1_ReadWriteByte(0XFF); //循环读数
}
W25QXX_CS(1);
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(32bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
W25QXX_Write_Enable(); //SET WEL
W25QXX_CS(0); //使能器件
SPI1_ReadWriteByte(W25X_PageProgram); //发送写页命令
SPI1_ReadWriteByte((u8)((WriteAddr)>>24)); //发送32bit地址
SPI1_ReadWriteByte((u8)((WriteAddr)>>16));
SPI1_ReadWriteByte((u8)((WriteAddr)>>8));
SPI1_ReadWriteByte((u8)WriteAddr);
for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer[i]);//循环写数
W25QXX_CS(1); //取消片选
W25QXX_Wait_Busy(); //等待写入结束
}
原子的spiflash写函数自带4k区擦除功能,使用很方便。
现在公司要求管理w25q256,每秒向里面写入1帧解析后的GPS数据,掉电后从上次写入的地址继续往下写。我暂定每帧数据为64字节,实现的逻辑是:每次写入1帧数据之后,将本次写入的地址保存在flash最后的4k区内,每个地址占4字节。每次写入数据之前,先从最后的4k区查询到上次写入的地址,然后地址偏移继续往后写。查询上次地址的方法为,从最后4k区第一个地址往后查询,查询到第一个空的帧,往前偏移1帧的地址,即为上次flash写入的地址,当最后4k区写满之后,进行一次扇区擦除。
//SPIflash写GPS数据的任务
void add_data_to_flash (void *p)
{
static uint32_t last_time=1;
if (last_time == (time_speed.time)) return;
uint8_t data[64];
uint8_t addr[4];
uint32_t address=0;
uint32_t i;
//处理4k地址区
for(i=0;i<4096;i+=4)
{
W25QXX_Read(addr,SPIFLASH_SIZE-4096+i,4);//从地址4K区读取地址
if ((addr[0] == 0xFF)&&(addr[1] == 0xFF)&&(addr[2] == 0xFF)&&(addr[3] == 0xFF))//读取到本次需要写入的4K地址区地址
{
if(i==0)//如果4K地址区为空
{
address = 0;
addr[0] = 0x00;
addr[1] = 0x00;
addr[2] = 0x00;
addr[3] = 0x00;
break;
}
else
{
i-=4;
W25QXX_Read(addr,SPIFLASH_SIZE-4096+i,4);//读取到上次flash写到的地址
address = (((uint32_t)addr[0]))+(((uint32_t)(addr[1]))<<8)+(((uint32_t)(addr[2]))<<16)+(((uint32_t)(addr[3]))<<24);
i+=4;
break;
}
}
else if(i == 4092)
address = (((uint32_t)addr[0]))+(((uint32_t)(addr[1]))<<8)+(((uint32_t)(addr[2]))<<16)+(((uint32_t)(addr[3]))<<24);
}
//处理得到的上次写入的地址
if (i > 0)
{
address = address+(flash_frame_size);
addr[0] = (uint8_t)(address);
addr[1] = (uint8_t)(address>>8);
addr[2] = (uint8_t)(address>>16);
addr[3] = (uint8_t)(address>>24);
}
if (address >= SPIFLASH_SIZE-4096)//如果数据区已写满
{
address = 0;
addr[0] = 0;
addr[1] = 0;
addr[2] = 0;
addr[3] = 0;
}
//把新数据写进下一帧地址
sprintf (data,"%d %d %d %d %d %d %d\r\n"
,(uint32_t)(pGPS_Data.Hour+8)*10000+(uint32_t)pGPS_Data.Min*100+(uint32_t)pGPS_Data.Sec
,(uint32_t)(time_speed.speed*100)
,time_speed.latitude_int
,time_speed.longitude_int
,time_speed.count_satellite
,pGPS_Data.PUBX_data.avg_cno
,hr);
W25QXX_Write(data,address,flash_frame_size);
last_time = time_speed.time;
if(i>=4092)//如果4K地址区已经写满
{
W25QXX_Erase_Sector((SPIFLASH_SIZE-4096)/4096);//擦除4k地址扇区
i=0;
}
W25QXX_Write(addr,SPIFLASH_SIZE-4096+i,4);//将本次地址写入4K地址区
}
这个函数在主函数中以时间片的形式调用,周期为300ms。使用RTOS的话加入系统延时函数即可。