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;
}
本篇文章是自己的日志,仅供参考。(本人刚毕业是菜鸟呦,看代码需谨慎!!!)