记录,分享,进步。
最近在做一个定位的项目,里面用到flash是超捷的SST25LF020,资料很少啊。遇到不少问题,现在将遇到的两个问题进行分析。
0.遇到的问题:
a.正确的读出数据:gaobaoqing SPI_FLASH TEST,但是从flash内读出来的数据不完全正确,之间有问号。
b.无论是整个sector擦除后读取数据,还是写入后读取数据,第一个数据总是不对,read1是sector erase后,read2是写入数据后,读出的数据,它俩都存在一个问题,在数据的前面总是有个未知的符号。
…过了很久…过了很久…过了很久…我才找见问题出在哪里。
对于a问题,这个flash每写入一个字节数据(Byte program)之前,都要取消写保护,而且还要等待这个取消命令完成后才能进行写入操作。见后面函数BytePro_SPIFlash()里面。(厂家难道不怕写入速度太慢了有人投诉他吗)
对于b问题,这个flash读数据之前,要先读一个空操作。见后面的函数Read_SPIFlash()里面。(厂家难道不怕这个额外的读操作会有人投诉他吗)
好啦好啦,既然这两个棘手的问题解决了,我就能愉快的和大家分享我的代码啦,啦啦啦~。
1.先介绍一下这个flash的几个关键参数。
a.这个flash容量有2Mbit
b.Single 3.0-3.6V Read and Write Operations
c. Uniform 4 KByte sectors,Uniform 32(or 64)KByte overlay blocks
d.Software Write Protection(在这个上面吃了亏)。
2.电气连接
先介绍一下这颗flash的引脚,好绘制他的原理图似乎所有的flash的原理图都是和下面这样设计。
3.代码
因为板子刚刚打回来,不清楚spi3的配置好坏,所以只能先读一下device ID来验证一下spi3和flash之间的通信。
下面是有关SPI3的所有函数。
// 下面的代码均在spi.c中
//SPI3_Init初始化函数
void SPI3_Init(void)
{
RCC->APB2ENR |= 0x01 << 3; //PORTB时钟使能
RCC->APB1ENR |= 1 << 15; //SPI3时钟使能
RCC->APB2ENR |= 1 << 0; //开启辅助时钟
JTAG_Set(SWD_ENABLE); // 因为SPI3和JTAG冲突,所以要把JTAG关闭,改为swd下载。
//这里只针对SPI口初始化
GPIOB->CRL &= 0XFF000FFF;
GPIOB->CRL |= 0X00BBB000; //PB3,4,5复用
GPIOB->ODR |= 0X7 << 3; //PB3,4,5上拉
SPI3->CR1 |= 0 << 10;//全双工模式
SPI3->CR1 |= 1 << 9; //软件nss管理
SPI3->CR1 |= 1 << 8;
SPI3->CR1 |= 1 << 2; //SPI主机
SPI3->CR1 |= 0 << 11;//8bit数据格式
// SPI3->CR1 |= 1 << 1; //空闲模式下SCK为1 CPOL=1
// SPI3->CR1 |= 1 << 0; //数据采样从第二个时间边沿开始,CPHA=1
SPI3->CR1 &= ~(1 << 1); //空闲模式下SCK为0 CPOL=0
SPI3->CR1 &= ~(1 << 0); //数据采样从第一个时间边沿开始,CPHA=0
SPI3->CR1 |= 7 << 3; //Fsck=Fcpu/256
SPI3->CR1 |= 0 << 7; //MSBfirst
SPI3->CR1 |= 1 << 6; //SPI设备使能
//SPI3_ReadWriteByte(0xff); //启动传输(主要作用:维持MOSI为高)
}
//SPI3 速度设置函数
//SpeedSet:0~7
//SPI速度=fAPB2/2^(SpeedSet+1)
//APB2时钟一般为72Mhz
void SPI3_SetSpeed(u8 SpeedSet)
{
SpeedSet &= 0X07; //限制范围
SPI3->CR1 &= 0XFFC7;
SPI3->CR1 |= SpeedSet<<3; //设置SPI3速度
SPI3->CR1 |= 1<<6; //SPI设备使能
}
//SPI3 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI3_ReadWriteByte(u8 TxData)
{
u16 retry = 0;
while((SPI3->SR & 1 << 1) == 0)//等待发送区空
{
retry++;
if(retry>0XFFFE)return 0;
}
SPI3->DR = TxData; //发送一个byte
retry = 0;
while((SPI3->SR&1 << 0) == 0) //等待接收完一个byte
{
retry++;
if(retry>0XFFFE)return 0;
}
return SPI3->DR; //返回收到的数据
}
下面这段代码在main.c中,整段代码的逻辑是:验证了flash的device id,待验证成功后,先擦除对应的块地址,然后读一下,验证擦除是否成功,然后使用Byte program命令,对flash进行字符串写入,写成功后再用read命令读一次flash,查看写入的数据是否正确。
// 下面的函数均在spiFlash.c中
// 待写入flash里的数据
const u8 TEXT_Buffer[]={"gaobaoqing SPI_FLASH TEST"};
#define SIZE sizeof(TEXT_Buffer)
u8 datatemp[SIZE] = {0,}; // 数组初始化
while(Read_Flash_ID() != SPI_FLASH_TYPE) //检测不到SST25LF020
{
printf("25LF020 Check Failed!");
delay_ms(1000);
}
delay_ms(100);
SecErase_SPIFlash(0x5); // 擦除第6个sector,从0开始
delay_ms(500); // 擦写后,延时500ms
Read_SPIFlash(0x5F20, datatemp, sizeof(datatemp)); // 从第0x5F20个地址处开始,读出SIZE个字节
printf("read1%s\r\n", datatemp);
memset(datatemp, 0, sizeof(datatemp)); // 擦除缓冲数组
if (BytePro_SPIFlash(0x5F20, (u8 *)TEXT_Buffer, SIZE)) // 从第0x5F20个地址处开始,写入SIZE长度的数据
{
printf("write failed\r\n");
}
printf("Write Finished!\r\n"); // 提示传送完成
Read_SPIFlash(0x5F20, datatemp, SIZE); // 从第0x5F20个地址处开始,读出SIZE个字节
printf("read2:%s\r\n", datatemp);
memset(datatemp, 0, sizeof(datatemp)); // 擦除缓冲数组
delay_ms(1000);
// 根据上面的逻辑,这里是读ID
unsigned int Read_Flash_ID(void)
{
unsigned short temp = 0;
SPI_FLASH_ENABLE(); /* 片选SPI Flash */
Send_Byte(SPI_FLASH_RDID); /* 发送读device ID命令 */
Send_Byte(0x00); // device id 地址
Send_Byte(0x00);
Send_Byte(0x01);
temp |= Get_Byte() << 16; /* 读取ID */
temp |= Get_Byte() << 8;
temp |= Get_Byte();
SPI_FLASH_DISABLE(); /* 禁止SPI Flash */
return temp;
}
/***********************************************
** 函数名称 : SecErase_SPIFlash
** 函数功能 : 扇区擦除Flash
** 入口参数 : iDes:该扇区所在的地址
** 出口参数 : TRUE/FALSE
**********************************/
unsigned char SecErase_SPIFlash(unsigned int iDes)
{
unsigned char state;
unsigned int iDes_addr = iDes * 4096; // 一个sector 是4096个字节。
WriteStatReg(0x00); // 关闭写保护
WirteEnable();
SPI_Flash_Wait_Busy();
SPI_FLASH_ENABLE();
// 写出命令
Send_Byte(SPI_FLASH_ERASE_SECTOR);
// 写出地址
Send_Byte((unsigned char)((iDes_addr >>16) & 0xff)); /* 高地址先发送 */
Send_Byte((unsigned char)((iDes_addr >> 8) & 0xff));
Send_Byte((unsigned char)((iDes_addr >> 0) & 0xff));
SPI_FLASH_DISABLE();
SPI_Flash_Wait_Busy();
return 0;
}
/*flash读函数*/
unsigned char Read_SPIFlash(unsigned int iDes, unsigned char *pucData, unsigned int iLen)
{
int i = 0;
if(pucData == NULL)
return 1;
SPI_FLASH_ENABLE();
Send_Byte(SPI_FLASH_READ); // 发送读命令
// 写出地址
Send_Byte((unsigned char)((iDes>>16) & 0xff)); /* 高地址先发送 */
Send_Byte((unsigned char)((iDes>> 8) & 0xff));
Send_Byte((unsigned char)((iDes>> 0) & 0xff));
Get_Byte(); // 在循环读之前先读一次,解决第一个地址出现一个多余的数据。
while(iLen--) {
*pucData++ = Get_Byte();
}
SPI_FLASH_DISABLE();
return 0;
}
// 一个字节一个字节的发送
unsigned char BytePro_SPIFlash(unsigned int iDes, unsigned char *pucData, unsigned int iLen)
{
unsigned char state;
if(pucData == NULL)
return 1;
while(iLen > 0)
{
WriteStatReg(0x00); // 关闭写保护 这里是我踩得第一个坑。稍后给大家分析一下
WirteEnable(); /* 使能状态寄存器的写使能 */
SPI_FLASH_ENABLE();
Send_Byte(SPI_FLASH_PROGRAM_BYTE); /* 选中从设备 */
// 写出地址
Send_Byte((unsigned char)((iDes >>16) & 0xff)); /* 高地址先发送 */
Send_Byte((unsigned char)((iDes >> 8) & 0xff));
Send_Byte((unsigned char)((iDes >> 0) & 0xff));
Send_Byte(*pucData++);
SPI_FLASH_DISABLE(); /* CS为高电平 */
iDes++; /* 地址增加 */
iLen--;
SPI_Flash_Wait_Busy();
}
return 0;
}
void SSP0_Init(void)
{
RCC->APB2ENR |= 1 << 5; //PORTD时钟使能
GPIOD->CRL &= 0XFFFFF0FF;
GPIOD->CRL |= 0X00000300; //PD2 推挽
GPIOD->ODR |= 0X1 << 2; //PD2
SPI3_Init(); //初始化SPI3
SPI3_SetSpeed(SPI_SPEED_4); //设置为18M时钟,高速模式
Send_Byte(0xff); // 维持mosi为高
}
unsigned char Send_Byte(unsigned char data)
{
u16 retry = 0;
SPI3->DR = data; //发送一个byte
while((SPI3->SR & 1 << 1) == 0)//等待发送区空
{
retry++;
if(retry > 0XFFFE)return 0;
}
return 0;
}
unsigned char Get_Byte(void)
{
return SPI3_ReadWriteByte(0xff);
}
void SPI_FLASH_ENABLE(void)
{SPI_FLASH_CS=0;}
void SPI_FLASH_DISABLE(void)
{SPI_FLASH_CS=1;}
//其余的就是一些宏定义了
spiflash.h:
#define SPI_FLASH_READ 0x03
#define SPI_FLASH_ERASE_SECTOR 0x20
#define SPI_FLASH_PROGRAM_BYTE 0x02
#define SPI_FLASH_Read_Status 0x05
#define SPI_FLASH_EWSR 0x50
#define SPI_FLASH_WRSR 0x01
#define SPI_FLASH_WREN 0x06
#define SPI_FLASH_WRDI 0x04
#define SPI_FLASH_RDID 0x90
#define SPI_FLASH_TYPE 0x43bf
#define SPI_FLASH_CS PDout(2)
spiflash.c :
#define WirteEnable() SPI_FLASH_ENABLE(); \
Send_Byte(SPI_FLASH_WREN); \
SPI_FLASH_DISABLE()
#define WirteDisable() SPI_FLASH_ENABLE(); \
Send_Byte(SPI_FLASH_WRDI); \
SPI_FLASH_DISABLE()
#define ReadStatReg(state) SPI_FLASH_ENABLE(); \
Send_Byte(SPI_FLASH_Read_Status); \
state = Get_Byte(); \
SPI_FLASH_DISABLE()
#define WriteStatReg(state) SPI_FLASH_ENABLE();\
Send_Byte(SPI_FLASH_EWSR);\
SPI_FLASH_DISABLE();\
SPI_FLASH_ENABLE();\
Send_Byte(SPI_FLASH_WRSR);\
Send_Byte(state);\
SPI_FLASH_DISABLE();\
SPI_Flash_Wait_Busy(); // 这一行,又是一个大坑啊,醉了