单片机学习小组
直播中

陈霞

7年用户 936经验值
私信 关注

怎样用device ID来验证一下spi3和flash之间的通信呢

flash有哪几个关键参数呢?
怎样用device ID来验证一下spi3和flash之间的通信呢?

回帖(1)

温洁

2022-2-17 11:13:07
记录,分享,进步。
最近在做一个定位的项目,里面用到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%srn", datatemp);
                memset(datatemp, 0, sizeof(datatemp));    // 擦除缓冲数组


                if (BytePro_SPIFlash(0x5F20, (u8 *)TEXT_Buffer, SIZE))                // 从第0x5F20个地址处开始,写入SIZE长度的数据
                {
                        printf("write failedrn");
                }
                printf("Write Finished!rn");        // 提示传送完成


                Read_SPIFlash(0x5F20, datatemp, SIZE);                                // 从第0x5F20个地址处开始,读出SIZE个字节
                printf("read2:%srn", 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();   // 这一行,又是一个大坑啊,醉了
举报

更多回帖

发帖
×
20
完善资料,
赚取积分