接着上篇环境变量的代码,想着继续深入一点。就是对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也不是最小值,没有再去试了。