SPI 访问 FLASH (w25q128fv)

2018.9.14 本篇是关于SPI 访问 FLASH的

        SPI 访问 FLASH的代码写完了很久一直没有去写这篇笔记,今天简单记录一下以免以后忘记。

        我这里用到了 SPI的控制器 来进行数据的通信 然后观看FLASH的数据手册发现SPI对FLASH进行访问大致分这样一些步骤:

        在读FLASH的情况下

如图所示是读的操作 command这个数组中0x03是读数据指令,后边三个字节的数据是表示读数据的地址即24位地址

读之前要

1、判断这个FLASH忙不忙 如果忙的话则一直等待着不忙。

2、然后进行才能进行读指令的操作。

 


/***********************************************************************************************************************************************************************************************************************************/

值得注意的是 我所用到的函数接口中有偏移量这一说,而当我们进行读取数据的时候,需要将偏移量设定为指令的长度,本例子中设定的长度为4个字节。

如果不设置准确的偏移量的话,读取出来的数据是不对的。

/***********************************************************************************************************************************************************************************************************************************/

在写操作的时候:

在进行写操作的时候,需要的步骤为

1、写使能

2、擦除数据(可以是扇区,可以是整个芯片)

3、判断状态寄存器忙不忙(忙则阻塞等待不忙)

4、然后写入指令 0x02是写指令,后边跟三个字节的地址,(在写操作的时候函数接口不需要偏移量)

 

 

 

/***********************************************************************************************************************************************************************************************************************************/

接下来贴出来的是完整代码

#include "A60_Upgrade_FPGA.h"

#include <sys/types.h>

#include <sys/socket.h>

typedef struct SpiData{

    uint32_t nID;

    uint8_t *outBuffer;

    uint32_t nOutCount;

    uint8_t *inBuffer;

    uint32_t nInOffset;

    uint32_t nInCount;

}SPI;

static int compare_flag = 0;

static pthread_mutex_t lock;

int Spi_Write_Flash(int fd,int Start_Addr,int End_Addr,int count,int SelectMode );

void Save_Upgreda_File(int sock_fd,int fd);

void Spi_Init_And_RDWR(struct SpiData *spi)

{

    if(true != Pic32IoSpiWriteRead(spi->nID,spi->outBuffer,spi->nOutCount,spi->inBuffer,spi->nInOffset,spi->nInCount)){

        printf("RDWR ERROR !!!\n");

    }    

}

/*********************************

功能:写使能

**********************************/

void Spi_Write_Enable()

{

    SPI spi;

    spi.nID = 0;

    spi.nInOffset = 0;

    uint8_t write_command[1] = {0x06};//写使能

    spi.outBuffer = write_command;

    spi.nOutCount = 1;

    spi.inBuffer = NULL;

    spi.nInCount = 0;

    Spi_Init_And_RDWR(&spi);

 

}

/********************************

功能:擦除整个芯片数据

*********************************/

void Spi_Chip_Erase()

{

    SPI spi;

    spi.nID = 0;

    spi.nInOffset = 0;

    uint8_t    buffer5[1] = {0x60};//擦除整个芯片

    spi.outBuffer = buffer5;

    spi.nOutCount = 1;

    spi.inBuffer = NULL;

    spi.nInCount = 0;

    Spi_Init_And_RDWR(&spi);

    

}

/*******************************

功能:读取并设置状态寄存器2的QE位

      用于使能四路输出

********************************/

void Spi_RDWR_Reg2()

{

 

    SPI spi;

    spi.nID = 0;        

    uint8_t    read_reqister2[2] = {0x35};

    spi.outBuffer = read_reqister2;

    spi.nOutCount = 1;

    spi.nInOffset = 1;

    spi.inBuffer = &read_reqister2[1];

    spi.nInCount = 1;

    Spi_Init_And_RDWR(&spi);

    

    uint8_t    write_reqister2[2] = {0x31};

    write_reqister2[1] = read_reqister2[1]|0x02;

    spi.outBuffer = write_reqister2;

    spi.nOutCount = 2;

    spi.nInOffset = 0;

    spi.inBuffer = NULL;

    spi.nInCount = 0;

    Spi_Init_And_RDWR(&spi);

}

/*******************************************

功能:读取状态寄存器1的S0位和S1位

参数:read_register1 发送读取指令

********************************************/

void Spi_RD_Reg1(uint8_t *read_register1)

{

    SPI spi;

    spi.nID = 0;

    

    spi.outBuffer = read_register1;

    spi.nOutCount = 1;

    spi.nInOffset = 1;

    spi.inBuffer = &read_register1[1];

    spi.nInCount = 1;

    Spi_Init_And_RDWR(&spi);

//    printf("read reqister  :%x\n",read_register1[1]);

}

/*******************************************

功能:写状态寄存器1

参数:read_register1    发送写入指令

*******************************************/

void Spi_Write_Reg1(uint8_t *read_register1)

{

    SPI spi;

    spi.nID = 0;

    read_register1[0] = 0x01;

    spi.outBuffer = read_register1;

    spi.nOutCount = 2;

    spi.nInOffset = 0;

//    spi.outBuffer = &read_register1[1];

    spi.inBuffer = NULL;

    spi.nInCount = 0;

    Spi_Init_And_RDWR(&spi);

//    printf("read reqister  :%x\n",read_register1[1]);

}

/***************************************

功能:用于设置读取和写入的偏移地址

参数:

      buff            发送指令和接收读取缓存

      addr            地址偏移

****************************************/

void Spi_Set_Addr(uint8_t *buff,int *addr)

{    

    buff[1] = ((*addr) >> 16)&0xff;

    buff[2] = ((*addr) >> 8)&0xff;

    buff[3] = (*addr)&0xff;

}

/*****************************************

功能:擦除一个扇区

参数:

        Erase_Addr            擦除扇区地址

*******************************************/

void Spi_Sector_Erase(int Erase_Addr)

{

 

    SPI spi;

    spi.nID = 0;

    spi.nInOffset = 0;

    uint8_t    buffer5[4] = {0x20};//擦除整个芯片

    Spi_Set_Addr(buffer5,&Erase_Addr);

    spi.outBuffer = buffer5;

    spi.nOutCount = 4;

    spi.inBuffer = NULL;

    spi.nInCount = 0;

    printf("buffer5  0x%x     0x%x    0x%x  \n",buffer5[1],buffer5[2],buffer5[3]);

    Spi_Init_And_RDWR(&spi);

}

/*************************************

功能:读取Flash中数据存入文件当中

参数:

        fd            存储数据的文件

        Start_Addr  开始读地址

        End_Addr    结束读地址

        read_flag    准许读标志位

*************************************/

void Spi_RD_Flash(int fd,int Start_Addr,int End_Addr,int read_flag)

{

    SPI spi;

    spi.nID = 0;

    

    int read_addr = Start_Addr;//读flash数据到文件中

    int read_len = 0;

    int offset = 4;

    uint8_t command[4+256] = {0x03,0x00,0x00,0x00};

    printf("in Spi_RD_Flash--------------------\n");

    lseek(fd,Start_Addr,SEEK_SET);

    for(int i = Start_Addr/256;i<End_Addr/256;i++)

    {

        if(read_flag)

        {

            uint8_t    read_register1[2] = {0x05,0x01};

            while(read_register1[1]&0x01)

            {

                Spi_RD_Reg1(read_register1);

            }

 

            read_addr = read_addr + read_len;

            Spi_Set_Addr(command,&read_addr);

            spi.outBuffer = command;

            spi.nOutCount = 4;

            spi.nInOffset = offset;//偏移4个字节

            spi.inBuffer = &command[4];

            spi.nInCount = 256;

        

            Spi_Init_And_RDWR(&spi);

            read_len = write(fd,&command[4],256);

            fsync(fd);

        }

    }

    printf("quit  Spi_RD_Flash------------------\n");

 

}

/*******************************************

功能:升级后对flash数据进行读取比较线程

参数:

        read_flag        读标志位

********************************************/

void* RD_Upgrade_Flash(void *flag)

{

    SPI spi;

    spi.nID = 0;

    int fd = open("FPGA.hex",O_RDWR);

    lseek(fd,0,SEEK_SET);

    int len = lseek(fd,0,SEEK_END);

    close(fd);

    if(len == 16*1024*1024)

    {

        Pic32IoSpiInitial(spi.nID);

        for(int i =0;i<3;i++){

        if(compare_flag == 0)

        {

            char recvbuff[10] = "";

            int log_fd = open("log.txt",O_RDWR,0777);

            if(log_fd < 0){

                perror("log file open");

            }

            else

            {

                read(log_fd,recvbuff,sizeof(recvbuff));

                close(log_fd);

                printf("in sretor Erase-------------------\n");

        

                int Start_Addr = 0,count = 0;

                int End_Addr = 16*1024*1024;

                if((atoi(recvbuff)<65535)&&(atoi(recvbuff)>45000)){

                    if(strlen(recvbuff)>0){

                        count = atoi(recvbuff)+1;

                        Start_Addr = (count*256);

                        Start_Addr = Start_Addr/4096*4096;//算出写到第几个扇区上一回并得到起始地址

                        count = Start_Addr/256;

                    }

                    printf("Start_Addr --------------------%x\n",Start_Addr);

                    printf("Count --------------------%d\n",count);

                    int fd = open("FPGA.hex",O_RDWR);

                    if(fd < 0)

                    {

                        perror("open");

                        exit(-1);

                    }

                    Spi_Write_Flash(fd,Start_Addr,End_Addr,count,1);

                    close(fd);

                }

                else if(atoi(recvbuff)<=45000){

                        Spi_Write_Enable();

                        Spi_Chip_Erase();

                        int fd = open("FPGA.hex",O_RDWR);

                        if(fd < 0)

                        {

                            perror("open");

                            exit(-1);

                        }

                        Spi_Write_Flash(fd,Start_Addr,End_Addr,count,0);

                    }

                else{

                        system("sync");

                        //system("reboot");

                        break;

                    }

             }

        }

        }

    }

}

/*********************************************

功能:读取文档中的数据并发送到Flash中

参数:

        fd                读取文件描述符

        Start_Addr        开始写的地址

        End_Addr        结束写地址

        count            写入日志文档数据,用于续传

*********************************************/

int Spi_Write_Flash(int fd,int Start_Addr,int End_Addr,int count,int SelectMode )

{

    SPI spi;

    spi.nID = 0;

    int addr = Start_Addr;//读文件中的数据写如flash中

    int len = 0,k =count,sector_flag = 0 ;

    char log[10] = "";

    lseek(fd,Start_Addr,SEEK_SET);

    printf("in Spi_Write_Flash------------------------\n");

    for(int i = Start_Addr/256;i<End_Addr/256;i++)

    {

        if((sector_flag%16 == 0)&&(SelectMode == 1))

        {

        /*    uint8_t    read_register1[2] = {0x05,0x01};//0x05用于读取寄存器1判断当前flash忙不忙S0 是不是可写入状态S1

            Spi_RD_Reg1(read_register1);

            read_register1[1] = read_register1[1]&0x00;

            

            Spi_Write_Enable();

            Spi_Write_Reg1(read_register1);

            read_register1[0] = 0x05;

            Spi_RD_Reg1(read_register1);

            printf("read reg1 :0x%x \n",read_register1[1]);*/

            Spi_Write_Enable();

            Spi_Sector_Erase(Start_Addr);

        }

        

        {

        sector_flag++;    

        memset(log,0,sizeof(log));    

        uint8_t send_buff[260] = {0x02};

        uint8_t    read_register1[2] = {0x05,0x01};//0x05用于读取寄存器1判断当前flash忙不忙S0 是不是可写入状态S1

        while((read_register1[1]&0x01)||!(read_register1[1]&0x02))

    //    while(read_register1[1] != 0x7e)

        {

 

            Spi_Write_Enable();

            Spi_RD_Reg1(read_register1);

//            printf("read_register            %x\n",read_register1[1]);

        }

        Spi_Set_Addr(send_buff,&addr);

        len = read(fd,&send_buff[4],256);

        addr = addr +256 ;

        spi.outBuffer = send_buff;

        spi.nOutCount = 260;

        spi.nInOffset = 0;

        spi.inBuffer = NULL;

        spi.nInCount = 0;

        Spi_Init_And_RDWR(&spi);

 

        int comparefd = open("fpgacompare.hex",O_RDWR|O_TRUNC|O_CREAT,0777);

        if(comparefd < 0){

            perror("fpgacompare open");

            return -1;

        }

        Spi_RD_Flash(comparefd,addr-256,addr,1);

        

        lseek(comparefd,addr-256,SEEK_SET);

        char compare_buff[256] = "";

        read(comparefd,compare_buff,sizeof(compare_buff));

        close(comparefd);

        if(memcmp(compare_buff,send_buff+4,256)!=0)

        {

            printf("Flash RDWR No Same!!!!!\n");

            return -1;

        }

        sprintf(log,"%d",k);

        k++;

        int log_fd = open("log.txt",O_TRUNC|O_RDWR|O_CREAT,0777);

        if(log_fd < 0)

        {

            perror("log_fd open");

            return -1;

        }

        write(log_fd,log,sizeof(log));

        close(log_fd);

        }

    }

    printf("quit  Spi_Write_Flash------------------\n");

}

/***********************************

功能:读取升级文件并发送给客户端数据

参数:

      sock_fd        TCP连接的套接字

      fd            升级文件描述符

************************************/

void Send_Upgreda_File(int sock_fd,int fd)

{

    int i =0;

    char getvol [20] = "";

    lseek(fd,0,SEEK_SET);

    int filesize = lseek(fd,0,SEEK_END);

    lseek(fd,0,SEEK_SET);

    printf("file size  :  %d\n",filesize);

    sprintf(getvol,"%d",filesize);

    send(sock_fd,getvol,sizeof(getvol),MSG_NOSIGNAL);

    while(1)

    {

        char recv_buff[2] = "";

        int len = recv(sock_fd,recv_buff,sizeof(recv_buff),MSG_NOSIGNAL);

        printf("if str %s\n",recv_buff);

        if(len == 0)

        {

            printf("close sock");

            close(sock_fd);

            break;

        }

        if(strcmp("ok",recv_buff)==0)

        {

            printf("send   file============ %d\n",i);

            i++;

            char send_buff[1024*32] = "";//5K

            len = read(fd,send_buff,sizeof(send_buff));

            printf("len======%d\n",len);

            send(sock_fd,send_buff,len,MSG_NOSIGNAL);

            if(len <= 0 )

            {

                break;

            }

        }

    }

}

/*******************************************

功能:保存上位机发送的升级文件到FPGA.hex中

参数:

      sock_fd            TCP连接套接字

      fd                保存文件描述符

*******************************************/

void Save_Upgreda_File(int sock_fd,int fd)

{

    int num = 0,read_flag = 0;

    int ack_num = 0,OACK = 0;

    char ACK[6] = "";

    int recv_ack = 0;

//    send(sock_fd,"ok",strlen("ok"),0);

    int r_len = 0;

    int t_len = 0;

    char recv_buff[1024*32]= {0};

    lseek(fd,0,SEEK_SET);

    while(1)

    {

        memset(ACK,0,sizeof(ACK));

 

        if( 0 == recv(sock_fd,&recv_ack,sizeof(int),MSG_NOSIGNAL))    break;

        else{

            //printf("%d %d %d %d %d %d\n",ACK[0],ACK[1],ACK[2],ACK[3],ACK[4],ACK[5]);

            printf("r_ack = %d\n",recv_ack);

        }

        if(recv_ack == ack_num)

        {

            while(1)

            {

                r_len = recv(sock_fd,recv_buff + t_len,sizeof(recv_buff)-t_len,MSG_NOSIGNAL);

                t_len += r_len;

                if(t_len >= sizeof(recv_buff))    break;

            }

            printf("t_len ====%d\n",t_len);

            write(fd,&recv_buff[0],sizeof(recv_buff));

            fsync(fd);

            t_len = 0;

            r_len = 0;

            ack_num++;

            send(sock_fd,&recv_ack,sizeof(int),MSG_NOSIGNAL);

            printf("245 R_ACK   ====%d\n",recv_ack);

        }

        else{

            printf("ACK   ==== %d  ack_num  == %d     \n",recv_ack,ack_num);

            printf("[ERROR]:Recv flag\n");

            break;

        }

    }

}

/*************************************

功能:用于升级FPGA

参数:

      select_command    选择选项命令

      sock_fd            TCP连接套接字

**************************************/

int Upgrade_FPGA(int select_command,int sock_fd)

{

    SPI spi;

    spi.nID = 0;

//    system("./reset");

    Pic32IoSpiInitial(spi.nID);

    pthread_mutex_init(&lock,NULL);

    if(select_command == 0x02)    //开发者

    {    

        int fd = open("FPGA.hex",O_RDWR,0777);

        if(fd < 0)

        {

            perror("open");

            return -1;

        }

        Spi_RD_Flash(fd,0,16*1024*1024,1);

        Send_Upgreda_File(sock_fd,fd);

        close(fd);

    }

    else if(select_command == 0x03)//客户

    {

            pthread_mutex_lock(&lock);

            compare_flag = 1;

            pthread_mutex_unlock(&lock);

            Spi_Write_Enable();

            Spi_Chip_Erase();

            int Start_Addr = 0,End_Addr = 16*1024*1024,count = 0;

            int fd = open("FPGA.hex",O_RDWR|O_TRUNC|O_CREAT,0777);

            if(fd < 0)

            {

                perror("open");

                return -1;

            }

            Save_Upgreda_File(sock_fd,fd);

            Spi_Write_Flash(fd,Start_Addr,End_Addr,count,0);

            close(fd);

            

            close(sock_fd);

            system("sync");

            system("reboot");

            pthread_mutex_lock(&lock);

            compare_flag = 0;

            pthread_mutex_unlock(&lock);

    }

    return 0;    

}

 

/***********************************************************************************************************************************************************************************************************************************/

 

 

SPI通讯接口函数

 

 

/***********************************************************************************************************************************************************************************************************************************/

#include "IoSpiDrv.h"

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <fcntl.h>

#include <string.h>

#include <sys/ioctl.h>

#include <linux/types.h>

#include <linux/spi/spidev.h>

#define    MAX_SPI0_TR_LEN    2048

#define    MAX_SPI1_TR_LEN    1024

 

 

int spi_fd0 = 0;

int spi_fd1 = 0;

uint8_t spi0_tx[MAX_SPI0_TR_LEN] = {0};

uint8_t spi0_rx[MAX_SPI0_TR_LEN] = {0};

uint8_t spi1_tx[MAX_SPI1_TR_LEN] = {0};

uint8_t spi1_rx[MAX_SPI1_TR_LEN] = {0};

 

//IOSPI模块初始化

bool    Pic32IoSpiInitial(int nID)

{

    uint8_t mode = 0;

    if(0 == nID)

    {

        spi_fd0 = open("/dev/spidev0.0",O_RDWR);

        if(spi_fd0 < 0)

        {

            printf("Error:in func %s\n",__func__);

            perror("open");

        }

        ioctl(spi_fd0, SPI_IOC_WR_MODE, &mode);

 

    }

    else if(1 == nID)

    {

        spi_fd1 = open("/dev/spidev1.0",O_RDWR);

        if(spi_fd1 < 0)

        {

            printf("Error:in func %s\n",__func__);

            perror("open");

        }

        ioctl(spi_fd1, SPI_IOC_WR_MODE, &mode);

 

    }

    else

        return false;

    return true;

}

 

/*function

********************************************************************************

<PRE>

函数名   :

功能     : SPI的写数据读据操作

参数     :    nID :spi 设备号 0 -1

            *outBuffer为写入的8位数据,NULL的时候不写

            nOutCount为写入的个数

            *inBuffer 为读入的数据,NULL的时候不读

            nInOffset 读取的数据起始位置偏移量,从0开始

            nInCount 读取的字节数目

            

返回值   : 1:SPI数据发送成功0 失败

抛出异常 :

--------------------------------------------------------------------------------

备注     :

    

典型用法 :

    //example1:

    // only write to cs0

    char buffer[8];

    Pic32IoSpiWriteRead(0,buffer,8,NULL,0,0);

 

    //example2:

    // only read to cs0

    char buffer[8];

    Pic32IoSpiWriteRead(0,NULL,0,buffer,0,8);

 

    //example1:

    // write to cs0 2 bytes, and then read 6 bytes from spidi

    char buffer[8];

    Pic32IoSpiWriteRead(0,buffer,2,&buffer[2],2,6);

 

*******************************************************************************/

bool Pic32IoSpiWriteRead(uint32_t nID, uint8_t *outBuffer, uint32_t nOutCount, uint8_t *inBuffer, uint32_t nInOffset, uint32_t nInCount)

{

    uint32_t i,tmp;

    uint32_t byteCount;

    uint8_t byTemp = 0;

    uint32_t nTempBuffer = 0;

 

    

    //先计算spi需要传输的byte数量

    if((NULL == outBuffer) && (NULL != inBuffer))    byteCount = nInOffset + nInCount;

    else if ((NULL == inBuffer) && (NULL != outBuffer))        byteCount = nOutCount;

    else if ((NULL != inBuffer) && (NULL != outBuffer))

    {

        if(nInOffset + nInCount > nOutCount)    byteCount = nInOffset + nInCount;

        else byteCount = nOutCount;

    }

    else

        return false;

 

    if(nID == 0)    {

        memcpy(spi0_tx,outBuffer,nOutCount);

        struct spi_ioc_transfer tr = {0};

        tr.tx_buf = (unsigned long)spi0_tx,

        tr.rx_buf = (unsigned long)spi0_rx,

        tr.len = byteCount,

        tr.delay_usecs = 0,

        tr.bits_per_word = 8,

        

        ioctl(spi_fd0,SPI_IOC_MESSAGE(1),&tr);

        //if(ret < 1)

        //    printf("can't send spi message");

        if(NULL != inBuffer)

            memcpy(inBuffer,spi0_rx+nInOffset,nInCount);

    }

    else if(1 == nID)

    {

        memcpy(spi1_tx,outBuffer,nOutCount);

        struct spi_ioc_transfer tr = {0};

        tr.tx_buf = (unsigned long)spi1_tx,

        tr.rx_buf = (unsigned long)spi1_rx,

        tr.len = byteCount,

        tr.delay_usecs = 0,

        tr.bits_per_word = 8,

        

        

        ioctl(spi_fd1,SPI_IOC_MESSAGE(1),&tr);

        //if(ret < 1)

        //    printf("can't send spi message");

        if(NULL != inBuffer)

            memcpy(inBuffer,spi1_rx+nInOffset,nInCount);

 

    }

    return true;

}

本篇文章是自己的日志,仅供参考。(本人刚毕业是菜鸟呦,看代码需谨慎!!!)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值