SST25LF020+STM32F103RCT6

记录,分享,进步。
最近在做一个定位的项目,里面用到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.代码
图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();   // 这一行,又是一个大坑啊,醉了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值