龙芯2k1000-pmon(6)- spiflash的底层操作

接着上篇环境变量的代码,想着继续深入一点。就是对flash的读写操作的一个解析。

但是这里有几个必要的知识:(我可能没讲到很详细,大家可以自行百度没懂的部分)

1. spi通信的原理。

2. flash芯片手册,主要是命令的控制

3. spi控制器的部分。

4. pmon的flash代码分析(不需要看原理的,直接到4就好了。)

1. 简要的介绍一下spi通信。我也参考百度吧。

1.1 这几根连接线很重要,作用,必须有个了解。

1.2 spi分主从,主就是控制端,从就是被控制端。

一般单片机这端都是主,其他的设备都是从,比如flash芯片GD25Q64,这个是不可能作主的。只要听别人控制就行,这样就节省了成本。(两个单片机也可以通过spi通信,那一个是主,另一个就设置为从)

主是如何控制的?“主”有嘴巴和手,“从”只有耳朵和手,“主”嘴巴喊(主动发出时钟),手传递数据,“从”用耳朵听(接收时钟),然后根据“主”的时钟,“从”(用手)把数据传出来。

这是很多主从设备的基本模型,iic也是这种方式。主就是主动,从就是被动。

1.3 spi的通信方式。

spi肯定有自己的通信方法,可以对比iic,就是不同的。

spi相当于是个环形缓存,主发一个字节给从的时候,主也会收到从的一个字节(有些情况下这个字节没有意义,就丢了)。

另一种情况,就是主要读从设备一个字节,就得自己先发送一个字节(一般发0),然后去读接收缓存,就收到从设备得数据了。

还有一个片选信号,因为spi是总线方式,片选表示选中某一个从设备访问。所以第一不可能同时选中两个及以上的设备通信,第二,每一个从设备应该使用不同的片选信号。

这是spi的特点,不然你看程序的时候会莫名奇妙。

 spi还有很多特性,这里我只提了最简单的方式,先弄明白这种吧,这次我的pmon的flash也是这种简单的方式。

其实要结合理论和代码更好理解。手册看不懂的时候,就看看代码呗。

2. spi控制器。大神直接跳过

反正是一堆的控制,人都绕晕了,这是初次接触单片机人的困惑。

spi控制器是啥?我先举个例子。

一个公司的老板(相当于cpu),一个清洁工人(相当于某种控制器)。(首先不是职业歧视哈,就是感觉能更形象一点,大家一看都知道)。

2.1 清洁工是听命令做事的,没有命令的时候就空闲,休息。

2.2 老板说,你去打扫一下办公室一的卫生。

2.3 清洁工执行指令,完成相关的打扫操作(拿扫帚,扫地,倒垃圾,拖地等等)。在清洁工完成这个事情的过程中,老板是可以处理其他事情的。

2.4 清洁完毕,清洁工报告老板,已完成,之后空闲。任务完成了,老板也得到了命令执行情况的结果。

2.5 之后老板又可以发起其他命令给清洁工,让他继续工作。

2.6 老板能不能亲自去完成打扫工作呢?当然可以。在没有清洁工的情况下,就得自己去了。这时候老板在打扫卫生的时候,就没法处理其他事情了。(比如51单片机,就没得spi控制器,就得cpu去模拟这个通信,为此就会减低cpu处理其他事情的能力)。

2.7 老板与清洁工的命令或状态交换。人可以通过语言或文字传递信息,但是机器就没法用这个方法。那么它们就使用电平(或者说是寄存器)。cpu把命令写到寄存器中,控制器看到这条命令,就自己开始工作,工作完成后,把工作的状态写到另一个寄存器中,就结束了。cpu再去查询工作的状态就可以了。

实际的原理比这要复杂,但是这是最基本的过程。

2.8 这次龙芯2k1000的cpu是具有spi控制器的。可以查找一下用户手册(pdf)

大家心里有个概念,这几个寄存器,是cpu用来控制spi控制器完成一些事情的,还有事反馈状态的。(相当于那个清洁工了)。

这不是设备,比如flash。 

设备可以理解为办公室1,办公室2,厕所,等等空间吧。

所以cpu控制的是清洁工,而不是办公室。

3. 讲完控制器,来讲这个flash。感觉上面的例子有点不合适了。

这里我要引进三个角色了(注意是重新定义的)。一是老板(cpu),二是后勤主管(控制器),三是清洁工(设备)。

3.1 老板和后勤主管关系比较近,一般说个话就行了(读写寄存器)。

3.2 后勤主管和清洁工是控制和被控制关系,清洁工不能主动打扫卫生,除非收到指令。

3.3 以上是前提,开始工作。老板对主管说,把整个公司卫生搞一下。

3.4 主管对清洁工说,办公室1的卫生要扫和拖,开始工作。……办公室2的卫生要扫和拖,开始工作。……厕所要扫并冲水。

3.5 清洁工执行工作,一步一步完成,汇报状态,主管可以随时查看状态。没干完之前,主管一直等待。

3.6 每一个小步,都可以设置不同的状态,老板可以要求主管汇报实时进展情况。

3.7 主管不能完成清洁工的工作,只能等待清洁工完成。老板不能完成清洁工的工作,但是可以完成主管的工作(上述模拟时序的方式)。

3.8 清洁工的工作可以理解为flash的读写擦除等工作。只能由flash芯片本身完成。spi控制器只能发送控制指令,然后等待完成。

3.9 主管和清洁工之间也要交换数据,这个时候就是flash内部的空间了。这里表现为命令的编号。

这里不要混淆了控制器的寄存器和flash的内部寄存器,控制器的寄存器,是用来交换cpu和控制器的数据,一般可以认为在控制器这端。用于操作控制发出时序等操作,还有状态的返回。

控制器和flash也有数据交换,这一部分寄存器在flash内部。用于控制flash的操作(读写擦除)和状态。

cpu能不能直接访问到flash的寄存器呢,一般是不能的。除非模拟时序。使用了控制器之后,必须经过控制器转发(由控制器完成时序),才能读写数据。

4. 前面啰嗦了一大堆,现在开始看代码

4.1 flash的操作,昨天setenv中就有

 一个是擦除flash的操作,一个是编程操作。

flash的特点,不能由0编程为1,只能使用擦除命令,将0改为1。所以每次写入,必须先擦除。

擦除有相关的指令。而且擦除不是擦除一个字节,一次就至少擦除一段。看手册就是一段4k字节

 还可以块擦除和整个芯片擦除。(使用不同的指令)

4.2 找到这两个函数的实现。spi_w.c(Targets/LS2k/dev/)

 两个函数流程一样,中间使用了不同的函数处理写和擦除。

4.2.1 看一下第一个函数 fl_find_map

 返回数组的首地址

 看一下这个数组的内容:

#define    TARGET_FLASH_DEVICES_16 \
    { PHYS_TO_UNCACHED(BONITO_FLASH_BASE), 0x00100000, 1, 1, FL_BUS_8  },    \
    { PHYS_TO_UNCACHED(0x1e000000), 0x02000000, 1, 1, FL_BUS_16  },    \
    { 0x00000000, 0x00000000 }

 

 宏定义展开:

#define    TARGET_FLASH_DEVICES_16 \
    { 0x1fc00000 | 0xa0000000 = 0xbfc000000, 0x00100000, 1, 1, FL_BUS_8  },    \
    { PHYS_TO_UNCACHED(0x1e000000) = 0xbe000000, 0x02000000, 1, 1, FL_BUS_16  },    \
    { 0x00000000, 0x00000000 }

对应结构体中的数据,我只写了第一项。

struct fl_map {
    u_int32_t fl_map_base = 0xbfc000000;    /* Start of flash area in physical map */
    u_int32_t fl_map_size = 0x00100000;    /* Size of flash area in physical map */
    int    fl_map_width = 1 ;    /* Number of bytes to program in one cycle */
    int    fl_map_chips = 1;    /* Number of chips to operate in one cycle */
    int    fl_map_bus = FL_BUS_8;    /* Bus width type, se below */
    int    fl_map_offset = 0;    /* Flash Offset mapped in memory */
    int fl_type = 0;
};

struct fl_map *
fl_find_map(void *base)   //参数正好是0xbfc00000
{
	struct fl_map *map;
	for(map = tgt_flashmap(); map->fl_map_size != 0; map++) {
		if(map->fl_map_base > (u_int32_t)base ||   //第一项正好相等,不大于
		   (map->fl_map_base + map->fl_map_size - 1) < (u_int32_t)base) {  //第二个也不相等
			continue;	/* Not this one */
		}
		else {
			return(map);  //执行else,返回结构体指针
		}
	}
	return((struct fl_map *)NULL);
}

返回了结构体的指针,里面记录了flash的起始地址,大小,以及其他的一些特性。

4.2.2 off = (int)(fl_base - map->fl_map_base) + map->fl_map_offset;,计算一个偏移.

根据上面列出的数据,计算的结果就是base的值0xbfc00000

4.2.3 spi_erase_area 和 spi_write_area先跳过

4.2.4 spi_initr();

void spi_initr()
{
  	SET_SPI(PARAM, 0x47);             //espr:0100
}

展开这些宏,

*(volatile char*)(0xffffffffa0000000 | 0x1fff0220 + addr) = val 
*(volatile char*)(0xffffffffa0000000 | 0x1fff0220 + 0x4) = 0x47 
*(volatile char*)(0xffffffffbfff0224) = 0x47    //0100 0111

 这个标红的地址正好是spi控制的地址,,经过转换,转为虚拟地址。(可以参考mips地址空间)

设置了读模式。 

也就是说,不管是擦除还是编程,结束的时候,都把控制器设置为读模式了。

4.3 spi_write_area函数

int spi_write_area(int flashaddr,char *buffer,int size)
{
	int j;
	spi_initw();    //spi控制设置为写模式
	SET_SPI(0x5,0x10); //spi控制器,设置片选,输出低,低有效
	write_sr(0x00);  //写flash的状态寄存器,不是控制器的
    for(j=0;size > 0;flashaddr++,size--,j++)
    {
        spi_write_byte(flashaddr,buffer[j]);   //写入数据,一个字节一个字节
    	dotik(32, 0);			//延时
    }

	SET_SPI(SOFTCS,0x11); //取消片选,之前写入的数据是先到flash的缓存,然后才会编程到flash中,这样速度快一些
	delay(10);	//延时,结束,写入数据之后,flash会忙一阵子(把缓存的数据编程到flash中去)
	return 0;
}

4.3.1 spi_initw函数,看设置为写模式。

void spi_initw()
{ 
  	SET_SPI(SPSR, 0xc0);   
  	SET_SPI(PARAM, 0x40);    //这里没有读使能了         //espr:0100
 	SET_SPI(SPER, 0x05); //spre:01 
  	SET_SPI(PARAM2,0x01); 
  	SET_SPI(SPCR, 0x50);
}

SET_SPI 前面已经展开了,参考之前(4.2.4的截图中有)的操作。

这里设置了时钟分频系数等,可以参考ls2k1000用户手册,我就不一一截图了。

4.3.2 SET_SPI(0x5,0x10);片选,其实这个要看一下原理图,看看片选是哪个引脚。

 

原理图上,CE# ,# 号表示低电平有效。

按照手册(下图)的说明,似乎程序有点问题。低4位中要设置1,才能用高4位控制输出电平。!!!如果要输出低电平,应该设置的值是0x01

 4.3.3 写状态寄存器,为0

///write status reg///
int write_sr(char val)
{
	int res;
	
	set_wren();	//flash写使能操作
	res = read_sr();  //读flash状态寄存器
	while(res&0x01 == 1)  //忙则继续等
	{
		res = read_sr();
	}
	
	SET_SPI(SOFTCS,0x01);  //片选
	SET_SPI(TXFIFO,0x01);  //发出命令0x1,这里是写发送缓存,写入发送缓存的数据,就会发送给flash
       	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){ //读控制器的状态,读缓存为空吗?没收完整就是空     			
	}				//发送是串行的,数据写入缓存,到发送完是有个时间的。
	GET_SPI(RXFIFO);  //读接收缓存,数据丢掉

	SET_SPI(TXFIFO,val);  //再发送值,由参数传入
       	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){   //等待发送完    			
	}
	GET_SPI(RXFIFO);  //读接收缓存,数据丢掉
	SET_SPI(SOFTCS,0x11);  //取消片选
	
	return 1;	
}

写使能这个比较特殊,擦除,编程,写状态寄存器都需要先设置写使能,否则就用不了

set write enable//
int set_wren(void)
{
	int res;
	
	res = read_sr();  //读flash状态寄存器
	while(res&0x01 == 1)  //忙则继续等
	{
		res = read_sr();  
	}
	
	SET_SPI(SOFTCS,0x01);  //片选
	
	SET_SPI(TXFIFO,0x6);    //发出命令0x6
       	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){  //等待发送接收
	}
	GET_SPI(RXFIFO);  //读接收缓存,数据丢掉

	SET_SPI(SOFTCS,0x11);   //取消片选

	return 1;
}

 4.3.4 读flash的状态寄存器read_sr,返回了状态寄存器的值。

这里要特别说明一下,发送0x5的时候,同时会收到一个无效的数据,因为前面讲了,主发送8个位的时候,同时也会收到从的8个位。

如果要收到0x5这个命令执行的结果,就还需要发送8个位(一般是0),这时又会收到8个位,这个才是真正的结果。

///read status reg /

int read_sr(void)
{
	int val;
	
	SET_SPI(SOFTCS,0x01);  //设置片选
	SET_SPI(TXFIFO,0x05);  //发送命令
	while((GET_SPI(SPSR))&RFEMPTY);  //等待发送结束

	val = GET_SPI(RXFIFO);  //读缓存
	SET_SPI(TXFIFO,0x00);	//写数据0,是为了读取一个数据

	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY); //等待发送结束

	val = GET_SPI(RXFIFO); //读缓存
	
	SET_SPI(SOFTCS,0x11);  //取消片选
      
	return val;
}

 4.3.5 spi_write_byte(unsigned int addr,unsigned char data) 写数据函数。

void spi_write_byte(unsigned int addr,unsigned char data)
{
    /*byte_program,CE 0, cmd 0x2,addr2,addr1,addr0,data in,CE 1*/
	unsigned char addr2,addr1,addr0;
        unsigned char val;
	addr2 = (addr & 0xff0000)>>16;
    	addr1 = (addr & 0x00ff00)>>8;
	addr0 = (addr & 0x0000ff);
        set_wren();
        val = read_sr();
        while(val&0x01 == 1)
        {
            val = read_sr();
        }
	SET_SPI(SOFTCS,0x01);/*CE 0*/

        SET_SPI(TXFIFO,0x2);/*byte_program */
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);
        
        /*send addr2*/
        SET_SPI(TXFIFO,addr2);     
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);
        
        /*send addr1*/
        SET_SPI(TXFIFO,addr1);
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);
        
        /*send addr0*/
        SET_SPI(TXFIFO,addr0);
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);

        /*send data(one byte)*/
       	SET_SPI(TXFIFO,data);
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);
        
        /*CE 1*/
	SET_SPI(SOFTCS,0x11);

}

一次写入一个字节,效率不高啊!!!好在不怎用这个函数。

5. 因为这个实在是没看了。我后来修改了以下代码,我贴在下面吧。后面就不解释了。

我开启了宏#define NEW_SPI_ZZ

#include <stdio.h>
//#include "include/fcr.h"
#include <stdlib.h>
#include <ctype.h>
#undef _KERNEL
#include <errno.h>
#include <pmon.h>
//#include <include/types.h>
#include <pflash.h>

#define SPI_BASE  0x1fff0220
#define PMON_ADDR 0xa1000000
#define FLASH_ADDR 0x000000

#define SPCR      0x0
#define SPSR      0x1
#define TXFIFO    0x2
#define RXFIFO    0x2
#define SPER      0x3
#define PARAM     0x4
#define SOFTCS    0x5
#define PARAM2    0x6

#define WFFULL (1<<3)   //发送缓存满
#define RFEMPTY 1
#define KSEG1_STORE8(addr,val)	 *(volatile char *)(0xffffffffa0000000 | addr) = val
#define KSEG1_LOAD8(addr)	 *(volatile char *)(0xffffffffa0000000 | addr) 

#define SET_SPI(addr,val)        KSEG1_STORE8(SPI_BASE+addr,val)
#define GET_SPI(addr)            KSEG1_LOAD8(SPI_BASE+addr)
#define NEW_SPI_ZZ


int write_sr(char val);
void spi_initw()
{ 
  	SET_SPI(SPSR, 0xc0);   
  	SET_SPI(PARAM, 0x40);    //这里没有读使能了         //espr:0100
 	SET_SPI(SPER, 0x05); //spre:01 
  	SET_SPI(PARAM2,0x01); 
  	SET_SPI(SPCR, 0x50);
}

void spi_initr()
{
  	SET_SPI(PARAM, 0x47);             //espr:0100
}


#ifdef NEW_SPI_ZZ

//发送数据,需配合写使能,片选操作。
static unsigned char spi_send_byte(unsigned char val)
{	
	while((GET_SPI(SPSR))&WFFULL);  //发送缓存满!!,等待
	SET_SPI(TXFIFO,val);
	while((GET_SPI(SPSR))&RFEMPTY); //等待发送结束
	return GET_SPI(RXFIFO); //读缓存
}
#endif


///read status reg /

int read_sr(void)
{
	int val;
	
	SET_SPI(SOFTCS,0x01);  //设置片选

#ifdef NEW_SPI_ZZ
	spi_send_byte(0x05);
	val = spi_send_byte(0x00);
#else
	SET_SPI(TXFIFO,0x05);  //发送命令
	while((GET_SPI(SPSR))&RFEMPTY);  //等待发送结束

	val = GET_SPI(RXFIFO);  //读缓存
	SET_SPI(TXFIFO,0x00);	//写数据0,是为了读取一个数据

	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY); //等待发送结束

	val = GET_SPI(RXFIFO); //读缓存
#endif	
	SET_SPI(SOFTCS,0x11);  //取消片选
      
	return val;
}

#ifdef NEW_SPI_ZZ
static void spi_flash_check_busy(void)
{
	unsigned char res;

	do{
		res = read_sr();  //读flash状态寄存器
	}while((res&0x01));  //忙则继续等
}
#endif


set write enable//
int set_wren(void)
{
	int res;

#ifdef NEW_SPI_ZZ
	//spi_flash_check_busy();
	SET_SPI(SOFTCS,0x01);  //片选
	spi_send_byte(0x06);  //写使能	
	SET_SPI(SOFTCS,0x11);   //取消片选
	spi_flash_check_busy();
	return 1;
#else	
	res = read_sr();  //读flash状态寄存器
	while(res&0x01 == 1)  //忙则继续等
	{
		res = read_sr();  
	}
	
	SET_SPI(SOFTCS,0x01);  //片选
	
	SET_SPI(TXFIFO,0x6);    //发出命令0x6
       	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){  //等待发送接收
	}
	GET_SPI(RXFIFO);  //读接收缓存,数据丢掉

	SET_SPI(SOFTCS,0x11);   //取消片选

	return 1;
#endif
}

///write status reg///
int write_sr(char val)
{
	int res;
#ifdef NEW_SPI_ZZ
	set_wren(); //flash写使能操作

	//spi_flash_check_busy();
	SET_SPI(SOFTCS,0x01);  //片选
	spi_send_byte(0x01);  //写状态寄存器	
	spi_send_byte(val);  //写入值
#else	
	set_wren();	//flash写使能操作
	res = read_sr();  //读flash状态寄存器
	while(res&0x01 == 1)  //忙则继续等
	{
		res = read_sr();
	}
	
	SET_SPI(SOFTCS,0x01);  //片选
	SET_SPI(TXFIFO,0x01);  //发出命令0x1,这里是写发送缓存,写入发送缓存的数据,就会发送给flash
       	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){ //读控制器的状态,读缓存为空吗?没收完整就是空     			
	}				//发送是串行的,数据写入缓存,到发送完是有个时间的。
	GET_SPI(RXFIFO);  //读接收缓存,数据丢掉

	SET_SPI(TXFIFO,val);  //再发送值,由参数传入
       	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){   //等待发送完    			
	}
	GET_SPI(RXFIFO);  //读接收缓存,数据丢掉
#endif
	SET_SPI(SOFTCS,0x11);  //取消片选
	
	return 1;	
}

///erase all memory/
int erase_all(void)
{
	int res;
	int i=1;

	spi_initw();
	set_wren();
	res = read_sr();
	while(res&0x01 == 1)
	{
		res = read_sr();
	}
	SET_SPI(SOFTCS,0x1);
	
	SET_SPI(TXFIFO,0xC7);
       	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){      			
	}
	GET_SPI(RXFIFO);
	
	SET_SPI(SOFTCS,0x11);
    while(i++){
        if(read_sr() & 0x1 == 0x1){
            if(i % 10000 == 0)
                printf(".");
        }else{
            printf("done...\n");
            break;
        }   
    }
	return 1;
}



void spi_read_id(void)
{
    unsigned char val;
    spi_initw();
    val = read_sr();
    while(val&0x01 == 1)
    {
        val = read_sr();
    }
    /*CE 0*/
    SET_SPI(SOFTCS,0x01);
    /*READ ID CMD*/
    SET_SPI(TXFIFO,0x9f);
    while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

    }
    GET_SPI(RXFIFO);

    /*Manufacturer’s ID*/
    SET_SPI(TXFIFO,0x00);
    while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

    }
    val = GET_SPI(RXFIFO);
    printf("Manufacturer's ID:         %x\n",val);

    /*Device ID:Memory Type*/
    SET_SPI(TXFIFO,0x00);
    while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

    }
    val = GET_SPI(RXFIFO);
    printf("Device ID-memory_type:     %x\n",val);
    
    /*Device ID:Memory Capacity*/
    SET_SPI(TXFIFO,0x00);
    while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

    }
    val = GET_SPI(RXFIFO);
    printf("Device ID-memory_capacity: %x\n",val);
    
    /*CE 1*/
    SET_SPI(SOFTCS,0x11);

}

#ifdef NEW_SPI_ZZ
#define PAGE_SIZE 0x100  //# 256B
//返回写入的字节数
static int spi_write_pagebytes(unsigned int addr,unsigned char *data,int len)
{
	unsigned int i = 0;

//	printf("1 addr = %#x i = %u, len = %d data[0] = %hhx\n",addr,i,len,data[0]);
	
	if(len > PAGE_SIZE)
		len = PAGE_SIZE;   //最多一次编程1page

	i = addr & (0xff);  //起始地址是不是256的整数倍
	if(len + i > PAGE_SIZE) //页内有偏移,从写入的位置开始,到结束不能超过页的边界
		len = PAGE_SIZE - i; //写入页内字节数
	
//	printf("addr = %#x i = %u, len = %d data[0] = %hhx\n",addr,i,len,data[0]);
	//1. 写使能
	set_wren();

	//2 .片选,页编程命令
	SET_SPI(SOFTCS,0x01);/*CE 0*/
	spi_send_byte(0x02);  //写页编程指令

	//3. 发送地址
	spi_send_byte((addr)>>16);  //写地址
	spi_send_byte((addr)>>8);  //写地址
	spi_send_byte(addr);  //写地址

	//4. 发送数据
	for(i=0;i<len;i++)
	{
		spi_send_byte(data[i]);  //写地址
	}
	
	//5.取消片选  /*CE 1*/
	SET_SPI(SOFTCS,0x11);  //取消片选

	spi_flash_check_busy(); //等待数据写入完成

	return len;   //返回实际写入的字节数
}

//写入数据
static void spi_write_bytes(unsigned int addr,unsigned char *data,int len)
{
	int ret = 0;

	while(len>0)
	{
		delay(3000);    //必须延时,否则写失败
		ret = spi_write_pagebytes(addr,data,len);  //返回写入了多少个字节
	//	printf("spi_write_bytes ret = %d\n",ret);
		addr+=ret;  //指针向后移动
		data+=ret;  //指针向后移动
		len -= ret;
	//	udelay(10000);    //必须延时,否则写失败
		dotik(32, 0);			//显示旋转的字符
	}	
}
#endif

void spi_write_byte(unsigned int addr,unsigned char data)
{
#ifdef NEW_SPI_ZZ
	spi_write_pagebytes(addr,&data,1);
#else
    /*byte_program,CE 0, cmd 0x2,addr2,addr1,addr0,data in,CE 1*/
	unsigned char addr2,addr1,addr0;
        unsigned char val;
	addr2 = (addr & 0xff0000)>>16;
    	addr1 = (addr & 0x00ff00)>>8;
	addr0 = (addr & 0x0000ff);
        set_wren();
        val = read_sr();
        while(val&0x01 == 1)
        {
            val = read_sr();
        }
		SET_SPI(SOFTCS,0x01);/*CE 0*/

        SET_SPI(TXFIFO,0x2);/*byte_program */
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);
        
        /*send addr2*/
        SET_SPI(TXFIFO,addr2);     
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);
        
        /*send addr1*/
        SET_SPI(TXFIFO,addr1);
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);
        
        /*send addr0*/
        SET_SPI(TXFIFO,addr0);
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);

        /*send data(one byte)*/
       	SET_SPI(TXFIFO,data);
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        val = GET_SPI(RXFIFO);
        
        /*CE 1*/
	SET_SPI(SOFTCS,0x11);
#endif
}
int write_pmon_byte(int argc,char ** argv)
{
    unsigned int addr;
   unsigned char val; 
    if(argc != 3){
        printf("\nuse: write_pmon_byte  dst(flash addr) data\n");
        return -1;
    }
    addr = strtoul(argv[1],0,0);
    val = strtoul(argv[2],0,0);
    spi_write_byte(addr,val);
return 0;

}


int write_pmon(int argc,char **argv)
{
	long int j=0;
        unsigned char val;
        unsigned int ramaddr,flashaddr,size;
	if(argc != 4){
        printf("\nuse: write_pmon src(ram addr) dst(flash addr) size\n");
        return -1;
    }

    ramaddr = strtoul(argv[1],0,0);
    flashaddr = strtoul(argv[2],0,0);
    size = strtoul(argv[3],0,0);
        
	spi_initw();
    write_sr(0);
// read flash id command
    spi_read_id();
	val = GET_SPI(SPSR);
	printf("====spsr value:%x\n",val);
	
	SET_SPI(0x5,0x10);
// erase the flash     
	write_sr(0x00);
//	erase_all();
    printf("\nfrom ram 0x%08x  to flash 0x%08x size 0x%08x \n\nprogramming            ",ramaddr,flashaddr,size);
    for(j=0;size > 0;flashaddr++,ramaddr++,size--,j++)
    {
        spi_write_byte(flashaddr,*((unsigned char*)ramaddr));
        if(j % 0x1000 == 0)
            printf("\b\b\b\b\b\b\b\b\b\b0x%08x",j);
    }
    printf("\b\b\b\b\b\b\b\b\b\b0x%08x end...\n",j);

    SET_SPI(0x5,0x11);
	return 1;
}

int read_pmon_byte(unsigned int addr,unsigned int num)
{
        unsigned char val,data;
	val = read_sr();
	while(val&0x01 == 1)
	{
		val = read_sr();
	}
	
	SET_SPI(0x5,0x01);
// read flash command 
	SET_SPI(TXFIFO,0x03);
	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
	}
	GET_SPI(RXFIFO);
	
// addr
	SET_SPI(TXFIFO,0x00);
	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
	}
        GET_SPI(RXFIFO);
	
	SET_SPI(TXFIFO,0x00);
	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
	}
	GET_SPI(RXFIFO);
	
	SET_SPI(TXFIFO,0x00);
	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
	}
	GET_SPI(RXFIFO);
	
        
        SET_SPI(TXFIFO,0x00);
        while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){

        }
        data = GET_SPI(RXFIFO);
	SET_SPI(0x5,0x11);
        return data;
}

int read_pmon(int argc,char **argv)
{
	unsigned char addr2,addr1,addr0;
	unsigned char data;
	int val,base=0;
	int addr;
	int i;
        if(argc != 3)
        {
            printf("\nuse: read_pmon addr(flash) size\n");
            return -1;
        }
        addr = strtoul(argv[1],0,0);
        i = strtoul(argv[2],0,0);
	spi_initw();
	val = read_sr();
	while(val&0x01 == 1)
	{
		val = read_sr();
	}
	
	SET_SPI(0x5,0x01);
// read flash command 
	SET_SPI(TXFIFO,0x03);
	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
	}
	GET_SPI(RXFIFO);
	
// addr
	SET_SPI(TXFIFO,((addr >> 16)&0xff));
	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
	}
        GET_SPI(RXFIFO);
	
	SET_SPI(TXFIFO,((addr >> 8)&0xff));
	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
	}
	GET_SPI(RXFIFO);
	
	SET_SPI(TXFIFO,(addr & 0xff));
	while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
	}
	GET_SPI(RXFIFO);
// addr end
	
        
        printf("\n");
        while(i--)
	{
		SET_SPI(TXFIFO,0x00);
		while((GET_SPI(SPSR))&RFEMPTY == RFEMPTY){
      			
		}
	        data = GET_SPI(RXFIFO);
                if(base % 16 == 0 ){
                    printf("0x%08x    ",base);
                }
                printf("%02x ",data);
                if(base % 16 == 7)
                    printf("  ");
                if(base % 16 == 15)
                    printf("\n");
		base++;	
	}
        printf("\n");
	return 1;
	
}

int spi_erase_area(unsigned int saddr,unsigned int eaddr,unsigned sectorsize)
{
	unsigned int addr;
       	spi_initw(); 

	for(addr=saddr;addr<eaddr;addr+=sectorsize)
	{

	SET_SPI(SOFTCS,0x11);

	set_wren();

	write_sr(0x00);

	while(read_sr()&1);

	set_wren();

	SET_SPI(SOFTCS,0x01);

        /* 
         * 0x20 erase 4kbyte of memory array
         * 0x52 erase 32kbyte of memory array
         * 0xd8 erase 64kbyte of memory array                                                                                                           
         */
	SET_SPI(TXFIFO,0x20);
       	while((GET_SPI(SPSR))&RFEMPTY);
	GET_SPI(RXFIFO);
	SET_SPI(TXFIFO,addr >> 16);

       	while((GET_SPI(SPSR))&RFEMPTY);
	GET_SPI(RXFIFO);

	SET_SPI(TXFIFO,addr >> 8);
       	while((GET_SPI(SPSR))&RFEMPTY);
	GET_SPI(RXFIFO);

	SET_SPI(TXFIFO,addr);
	
       	while((GET_SPI(SPSR))&RFEMPTY);
	GET_SPI(RXFIFO);
	
	SET_SPI(SOFTCS,0x11);

	while(read_sr()&1);
	}
	SET_SPI(SOFTCS,0x11);
	delay(10);

	return 0;
}

int spi_write_area(int flashaddr,char *buffer,int size)
{
	int j;
	spi_initw();    //spi控制设置为写模式
//	SET_SPI(0x5,0x10); //spi控制器,设置片选,输出低,低有效
	write_sr(0x00);  //写flash的状态寄存器,不是控制器的
#ifdef NEW_SPI_ZZ
	spi_write_bytes(flashaddr,buffer,size);
#else
    for(j=0;size > 0;flashaddr++,size--,j++)
    {
        spi_write_byte(flashaddr,buffer[j]);   //写入数据,一个字节一个字节
    	dotik(32, 0);			//延时
    }
#endif
//	SET_SPI(SOFTCS,0x11); //取消片选,之前写入的数据是先到flash的缓存,然后才会编程到flash中,这样速度快一些
	delay(10);	//延时,结束,写入数据之后,flash会忙一阵子(把缓存的数据编程到flash中去)
	return 0;
}


int spi_read_area(int flashaddr,char *buffer,int size)
{
	int i;
	spi_initw();

	SET_SPI(SOFTCS,0x01);

	SET_SPI(TXFIFO,0x03);

        while((GET_SPI(SPSR))&RFEMPTY);
        GET_SPI(RXFIFO);
        
        SET_SPI(TXFIFO,flashaddr>>16);     
        while((GET_SPI(SPSR))&RFEMPTY);
        GET_SPI(RXFIFO);

        SET_SPI(TXFIFO,flashaddr>>8);     
        while((GET_SPI(SPSR))&RFEMPTY);
        GET_SPI(RXFIFO);

        SET_SPI(TXFIFO,flashaddr);     
        while((GET_SPI(SPSR))&RFEMPTY);
        GET_SPI(RXFIFO);
        

        for(i=0;i<size;i++)
        {
        SET_SPI(TXFIFO,0);     
        while((GET_SPI(SPSR))&RFEMPTY);
        buffer[i] = GET_SPI(RXFIFO);
        }

        SET_SPI(SOFTCS,0x11);
	delay(10);
	return 0;
}

struct fl_device myflash = {
	.fl_name="spiflash",
	.fl_size=0x100000,
	.fl_secsize=0x10000,
};

struct fl_device *fl_devident(void *base, struct fl_map **m)
{
	if(m)
	*m = fl_find_map(base);
	return &myflash;
}

int fl_program_device(void *fl_base, void *data_base, int data_size, int verbose)
{
	struct fl_map *map;
	int off;
	map = fl_find_map(fl_base);
	off = (int)(fl_base - map->fl_map_base) + map->fl_map_offset;
	spi_write_area(off,data_base,data_size);
	spi_initr();
	return 0;
}


int fl_erase_device(void *fl_base, int size, int verbose)
{
	struct fl_map *map;
	int off;
	map = fl_find_map(fl_base);
	off = (int)(fl_base - map->fl_map_base) + map->fl_map_offset;
	spi_erase_area(off,off+size,0x1000);
	spi_initr();
	return 0;
}

static const Cmd Cmds[] =
{
	{"MyCmds"},
	{"spi_initw","",0,"spi_initw(sst25vf080b)",spi_initw,0,99,CMD_REPEAT},
	{"read_pmon","",0,"read_pmon(sst25vf080b)",read_pmon,0,99,CMD_REPEAT},
	{"write_pmon","",0,"write_pmon(sst25vf080b)",write_pmon,0,99,CMD_REPEAT},
	{"erase_all","",0,"erase_all(sst25vf080b)",erase_all,0,99,CMD_REPEAT},
	{"write_pmon_byte","",0,"write_pmon_byte(sst25vf080b)",write_pmon_byte,0,99,CMD_REPEAT},
	{"read_flash_id","",0,"read_flash_id(sst25vf080b)",spi_read_id,0,99,CMD_REPEAT},
	{0,0}
};

static void init_cmd __P((void)) __attribute__ ((constructor));

static void
init_cmd()
{
	cmdlist_expand(Cmds,1);
}

开始写完之后,调试就是不行,写入的数据读出来是错的,校验就不能成功。

后来加了打印语句之后,莫名发现功能又是正常的。只好加上了延时函数delay(3000),这个数字可能跟具体的flash芯片有关,我的是GD25Q64,可能3000也不是最小值,没有再去试了。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大智兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值