存储设备:
处理器中必须额外接存储设备
RAM(内存,掉电数据丢失):
原理分类:
1.SRAM
静态RAM,线性存储的结构,不会刷数据,每增加个数据,就会在末尾处加一块内存,这样存取数据会快一些,同时也导致SRAM的面积大一些
小的SRAM集成到CPU上(小的4~8k,大的几十k)
2.DRAM
动态RAM(常用),特点就是便宜,会每间隔ms就刷一次内存数据,这段间隔时间内存储器时无法使用的,这是个缺点,但DRAM面积小一些
嵌入式处理器,外接DRAM
处理器中有控制器,然后根据接口控制外接DRAM(SDRAM和DDRAM),外接DRAM必须一开机初始化才能使用,但是开机的一刹那,程序如果是在DRAM上运行,则会导致程序无法运行,所以就需要SRAM,把DRAM的初始化工作放到SRAM中,等到他初始化完成之后,再执行DRAM中的程序
本身具有随机访问的能力
FLASH(代替电脑的硬盘):
NandFlash
同与(N and),与DRAM优点差不多,就是便宜,NandFlash没有地址线,所以不能直接执行程序,操作系统把其中的内容读出来放到内存中,然后才能被执行,操作Nandflash要按块去操作,不可以按字节去操作
最小结构叫做页
可以存储启动代码
处理器中内置nandflash控制器
CPU利用nandflash控制器从nandflash读出数据后放到DRAM后,再由处理器从内存中取指执行
我们编写程序主要还是对nandflash控制器编程
NorFlash
同或(N or),NorFlash与SRAM的处理器接口是完全一样,NorFlash中的程序可以直接运行,本身具有随机访问的能力
Nandflash:
分析原理
需要单独学习nandflash的datasheet(三星的nandflash datasheet)
先弄清楚管脚的定义,共48根管脚其中N.C(not connect)是未连接的
IO0~7:数据输入输出的8根管脚,因此从NandFlash中分批次的读写数据,就是8位数据,作为输出管脚的话,把数据传递给CPU,作为输入管脚的话,把数据/命令/地址从CPU读进flash里边,nandflash没有地址线,所以CPU就不能直接寻址,对nandflash的操作就是操作命令(读,写,擦除命令)
CPU给Nandflash发送数据可以是数据,地址,命令,那么怎么区分,就引入了下述两根管脚,比如发送0x30
CLE(command latch enable):命令锁存使能管脚,CLE拉高那么ALE就不会拉高,拉高就表示IO中传递的是谁,当两者都拉低的时候IO传递就是数据
ALE(address latch enable):地址锁存使能管脚,同上
CE(chip enable):芯片使能管脚,flash内部分块的,片选就是选择一个内存块,比如3G,分成3个块,片选一个就可以用一个G,片选信号一般都是低电平有效
CE1同上
CE2同上
这里有个小标记,在datasheet中,CE上边有小横线,就代表是低电平有效,CE/CE1/CE2都有小横线,所以都是低电平有效,有的时候#/n都是代表低电平有效
RE(read enable):读使能,一根信号线,低电平有效,激活的话就是nandflash要读数据,数据流向处理器,就是处理器读
WE(write enable):写使能,一根信号线,低电平有效,激活的话就是nandflash要写数据,数据流向存储器,处理器写
WP(write protect):写保护,只读不写,不可以写,只可以读
RnB(ready busy):B低电平有效,R高电平有效,他俩复用,B是忙的状态,R是准备状态,这个就是通知Nandflash是否是忙状态
Vcc:点烟
GND:地
待续??
看原理图
flash的IO连接处理器的数据线的低八位
片选使能,使能命令管脚,传递命令,使能地址所存,传递地址,读/写使能,传输数据,这些都是nandflash控制器硬件完成
看datasheet
找到与nandflash相关的部分
写入数据,要先写校验,然后再写数据,读也需要先校验,暂不考虑校验
NFCONF寄存器:
1位:AddrCycle,对nandflash操作需要传递地址周期,地址是分周期传递的,这个周期是看页大小,地址周期?页大小?从哪知道周期?
地址周期:nandflash没有地址线,CPU如何操作nandflash的地址,处理器地址和设备地址(nandflash地址),处理器的地址空间0~4G,是ARM最大寻址空间,是处理器地址,是处理器可以直接访问,通过ldr与str可以直接访问,但是ARM不能直接访问nandflash的地址(设备地址),我们要把5存到0x10000(nandflash的地址)上(处理器上也会有这个地址0x10000),我们要操作的设备地址,理论上16个G的nandflash,需要34位的数,但实际处理器与nandflash只有8根线,所以传递一个地址,需要多次传递过去,这个次数就是地址周期,所以需要5个周期来传递地址,这个周期还需要页的大小是谁,周期为5时,页大小是2k(这个可以从nandflash的datasheet中得到)
每个页式基本单位,每个页后边跟一块存储区,是校验用的,校验方法:硬件校验与软件校验,写入之前要校验,校验完成,写的数据放到2k中,校验数据放到64字节中,读取的时候,2k与64字节都需要寻址,所以寻址一个页需要12个位数据来寻址,若干个页构成一个块,三星的这款NandFlash是64个页组成的块,对Flash擦除是整块擦除,还有个特点,在写到Flash前要先擦除,默认的全是F,写数据是把1变成0可以,要把0变成1,只能擦除整个块,若干个块构成一个device(8192个块)
2位:PageSize,页大小,要看MLCFlash是多少,来决定这个位的值代表的页大小
3位:MLCFlash,FLASH的类型,SLC(单层的,1~2个G)与MLC(多层的,比较大的)这两种flash
4~15:TWRPH1,TWRPH0,TACLS
这几位的配置需要看时序图,HCLK是时钟用来驱动nandflash的时钟,差不多是130M左右,PCLK是外设时钟,串口控制器,GPIO控制器,SPI控制器都会用,HCLK对速度要求高点的设备用,nandflash就是用这个时钟,H2CLK高速设备,没来一个周期,控制器做一件事,来一个周期,CLE与ALE先变化,都先拉高,从这里可以知道ALE和CLE先拉高,然后数据会变化,数据先往上放,数据会持续一段时间会在8个IO上,然后WE再变化,此时是写使能稳定一段时间之后,拉低,在写使能拉低的过程中,会将数据发送出去,其他的那些都是准备时间
TACLS(time address command):地址或命令锁存建立时间,这个时间应该越短越好,但肯定是要有的,因为要使能地址和命令锁存器时间是Duration = HCLK(时钟周期) x TACLS,Duration是已知的,HCLK由uboot决定,从而计算出TACLS
TWRPH0(time write RPH0):写使能持续时间,Duration = HCLK x ( TWRPH0 + 1 ),取值同上
TWRPH1(time write RPH1):写使能持续时间,Duration = HCLK x ( TWRPH1 + 1 ),取值同上
23~24:ECCType0,ECC校验类型
MsgLength:与校验相关的
NFCONT寄存器:(与中断相关)
0位:MODE,是nandflash控制器的模式
NFCMMD寄存器(命令寄存器):
8位
NFADDR寄存器(地址寄存器):
8位
NFDATA寄存器(数据寄存器):
可以当做32位/16位/8位,一次性可以读8位,如果读一个页,需要读2k次,如果一次传32位,则需要循环512次,nandflash控制器中肯定是有缓冲,缓存32位后一下发到寄存器中,所以CPU读这个寄存器可以读到32位,决定读多少位是咱们告诉CPU的,采用int型的就是按4字节读,采用short是采用2字节读,采用char就是按1字节读
以上三个寄存器都是通过8根IO来控制,nandflash控制器把传递的功能映射到3个寄存器(NFCMMD,NFADDR,NFDATA)上,然后由处理器来处理
U盘就是USB+FLASH
实现从nandflash中读ID的操作:
读nandflash的datasheet,可以看到read ID operation的时序
1.使能片选信号,CPU发送片选信号给nandflash
2.然后命令锁存寄存器使能,同时写寄存器使能,这就代表发送命令,CPU发送命令给nandflash
3.然后地址锁存寄存器使能,同时写寄存器使能,这就代表发送地址,CPU发送地址给nandflash
4.读寄存器使能,这个时候IO不断有数据出来,是CPU从nandflash中读出来
读出来数据是EC DC 35 75 75,这几个值读对了,就代表是正确的
读ID是确定nandflash是否正常,是否好用,所以驱动都先是读ID
读flash代码
start.s
.global start@为了让.c看得到
start:
bl uart_run @没有赋值,说明uart_run没有参数
bl
b . @b .是死循环
uart.c
char shell_buffer[256];
void uart_init(void){
配置寄存器,就是操作地址
}
char getc(void){
volatile int *b = (int *)0xe2900010;
//1.查看状态
while(!(*b&0x1)){
//2.读出字符
b = (int *)0xe2900024;
return (char)*b;
}
}
void putc(char a){
volatile int *b = (int *)0xe2900010;
//1.查看状态
while(!(*b&0x2)){
//2.写入字符
b = (int *)0xe2900024;
(char)*b = a;
}
}
void puts(char* s){
}
void gets(void){
int i = 0;
while(1){
if((shell_buffer[i] != '\r')&&(i<256)){ //输入回车结束,'\r'是回车
shell_buffer[i] = getc(); //获取输入的指令
putc(shell_buffer[i]); //这里是为了在终端显示
i++;
}else{
if(shell_buffer[i] == '\r'){
shell_buffer[i] = '\0';
}else{
/*do nothing*/
}
break;
}
}
}
void nand_init(void){
//配置寄存器,nandflash,config寄存器
//控制寄存器,nandflash,contrl寄存器
}
void nand_read(){
//操作命令寄存器
//操作地址寄存器
//操作数据寄存器
//itoa具体实现
//puts
}
void do_nand_id(){
//1.nandflash初始化
nand_init();
//2.读id
nand_read();
}
void parse_cmd(void){
char cmd_buffer[5] = "test";
char cmd_buffer1[9] = "donandID";
for(int i=0;((shell_buffer[i] != '\0')&&(i<5));i++){
if(shell_buffer[i] != cmd_buffer[i]){
puts("cmd is error");
break;
}
else if(shell_buffer[i] == cmd_buffer1[i]){
do_nand_id();
}
else{
do_test_cmd();//硬盘上的一段程序,通常需要shell进程去做这个函数的处理
}
}
}
void uart_run(void){
uart_init();
while(1){
char a = getc();
putc(a);
puts("##");
gets();
parse_cmd();
}
}
32位地址怎么变成5周期地址,5个8位地址有CPU上的nandflash控制器传递地址给nandflash
通常这个位置(32位整数)变成5周期的地址
5周期地址分成两部分:2周期是列地址(页内部地址,一个页内的地址),3周期是行地址(页偏移地址,是一个块内的页的地址)
我操作的一个页是2k的,我想读0x100000的地址,怎么读?
0x100000除以2k就是页偏移地址,一个周期只能使用8根IO传递数据,需要24位数来传递,所以需要3个周期
余数就是页内地址需要11位,就需要2个周期
按页读取的话,地址一定是2k页的整数倍,所以页内便宜一定是0
R/B信号(ready/busy):就是在读大的数据量的时候,显示busy状态,这个时候就是准备过程,等到,ready状态后,CPU可以读出数据,R/B寄存器只会由0变1,但是由1变0不可以,所以操作R/B状态时,需要在读操作之前先给它写0,然后再检测什么时候被拉高变1,变1说明是进入ready状态