韦东山学习笔记

1:地址空间的分配2:开发板上一般都用SDRAM做内存
,flash(nor、nand)来当做ROM。其中nand flash没有地址线,一次至少要读一页(512B).
其他两个有地址线3:nandflash不用来运行代码,只用来存储代码,NORflash,SDRAM可以直接运行代码)
4:s3c2440总共有8个内存banks  6个内存bank可以当作ROM或者SRAM来使用  留下的2个bank除了当作ROM 或者SRAM,还可以用SDRAM(各种内存的读写方式不一样)  
7个bank的起始地址是固定的,还有一个灵活的bank的内存地址,并且bank大小也可以改变5:s3c2440支持两种启动模式:NAND和非NAND(这里是nor flash)
。具体采用的方式取决于OM0、OM1两个引脚OM[1:0所决定的启动方式OM[1:0]=00时,处理器从NAND Flash启动OM[1:0]=01时,处理器从16位宽度的ROM启动OM[1:0]=10时
,处理器从32位宽度的ROM启动。OM[1:0]=11时,处理器从Test Mode启动。

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

本文来自 lyd1587202069 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/lyd1587202069/article/details/52318085?utm_source=copy 
linux 命令行
pwd :当前所在的目录
ls: 显示当前目录下的文件
cd :切换路径
 cd .. 返回到上一级路径
mkdir :新建目录
touch:新建文件
cp:复制文件
cat :将文件内容显示在终端中
rmdir:删除目录
rm:删除文件
clear:清屏


默认路径。由环境变量path决定
命令:可执行程序

在shell中输入字符串并回车的过程
1.shell根据第一个字符串去环境变量中PATH所指定的目录中找到同名的应用程序
2,然后执行它

我们可以设置Path环境变量添加我们的目录:
export PATH=$PATH:/my/dir(我们自己的路径)

Permission denied 没有可执行权限

command not found的原因:
1.PATH所确定的目录中无此程序
2.它不是可执行的程序
 
echo:输出
显示指定环境变量 (一般是 echo $PATH)
通过export设置新的环境变量

cd ~ 当前用户的家目录
cd .. 切换到上一级目录
cd ../.. 切换到上上级路径
cd / 切换到根目录
cd - 切换到上一次的路径

cd /home 可以在任意目录使用
cd home只能在你在/目录的时候使用

相对路径 一般以当前路径 一般以./..来构成

ls和cd 差不多一样
ls  显示目录文件的所有文件
ls -lh  将文件大小以K(KB),M(MB),G(GB)来表示

rmdir 目录名
注意:rmdir不能删除非空目录(非空目录:该目录下面有子目录或者文件 rm  -r 可以)

touch:新建文件
同一目录无法创建同名的文件
touch 文件名

cat 可以接多个文件
cat file1 file2

gedit图形应用程序的编辑器

clear :刷新屏幕 保留历史命令操作记录
reset:全部删除

linux 帮助命令
man 提供命令,API ,概念 ,配置文件等帮助信息
常用选项为-l ,-a,-h


--help 提供命令帮助信息
info 和man有很多交集,能更完整GUN工具

man man  查看man手册的说明
man ls 当没有指定使用那一页 默认为第一页
man 1(数字) ls 第一页查看 (ls)在第一册中

info -ls

ls --help

man手册一共有9册 每一册专注一个方面
当同时在几个手册里包含时,需要指定哪本手册 以确定具体的含义
            名称                     说明
第一册  可执行程序或shell命令     用户可操作的命令
第二册  系统调用                  内核提供的函数(查头文件)
第三册  库调用                     常用的函数库
。。。。
第九册  内核相关                     Linux内核相关文件
man 1 gcc gcc是一个应用程序 
open/write/read/close 等等都是系统调用
man 2 open

man 手册
段名
NAME                            命令,数据名称的简要说明
SYNOPSIS                        简短的命令语句说明
DESCRIPTION                    最为权威和全面的使用说明
EXAMPLES                       使用本命令或数据的一些参考示例
AUTHOR
REPORTING BUGS
COPYRIGHT
SEE ALSO


vi:
查找字符串,使用/加上要查找的字符串

在一般模式下 hjkl这四个按键就可以移动光标
h(左)
j(下)
k(上)
l(右)

快速的定位到某一行
文件头,文件尾,指定某一行
ngg光标移至第n行的行首(n为数字)
G转至文件结尾

在某一行如何快速定位到某一列(一般模式下)
0(数字0)光标移至当前行行首
$光标移至当前行行末
fx搜索当前行中下一个出现字母x的地方

按u键来撤销上一步操作

查找和替换
/pattern 从光标开始处向文件尾搜索 ,后按下n或N
注意:
n在同一个方向重复上一次搜索命令g
N 在反方向重复上一次的搜索命令

注意:搜索是从光标开始处开始搜索 所以最好搜索之前,先跳到第一行

:%s/p1/p2/g将文件中所有的p1均用p2替换
:%s/p1/p2/gc替换是需要确认

s:substitute 替换
g:global 全局
c:confirm 确认

查找文件
find:
格式:find 目录名 选项 查找条件
fine 查找的路径 -name(文件名的选项) “test1.txt(文件名)”或“*.txt”  *和F表示 通配符
* - 通配符,代表任意字符(0到多个)
? - 通配符,代表一个字符
可以查找目录是否存在
如果没有指定目录 就默认为·当前路径

2)find还有一些高级用法,如查找最近时间内有变动的文件
find /home -mtime -2 //查找/home目录下两天内有变动的文件
   grep:在指定文件中搜索特定的内容,并将含有这些内容的行标准输出
   
  grep -rn "字符串" 文件名
  r:递归查找
  n:显示目标位置的行号
  字符串:要查找的目标文件,如果是*则表示查找当前目录下的所有文件和目录
  举例:
  grep -n "abc" test1.txt 在test11.txt中查找字符串abc
  grep -rn "abc" *  在当前目录目标递归查找字符串abc
  可以在-w 全字匹配
  
  file
  目的:识别文件类型
  格式:file 文件名
  file ~/图片 为directory 表明这是一个目录
  file /bin/pwd 出现 ELF 64-bit LSB executable,即为ELF格式的可执行文件
  file /dev/* 出现character special(字符设备文件),block special(块设备文件)等
  
  which 和 whereis
  which
  目的:查找命令或应用程序的所在位置
  格式:which 命令名/应用程序名
  
  在终端上执行pwd实际上是去执行了/bin/pwd
  举例:
  which pwd
  
  whereis pwd查找到可执行程序的位置/bin/pwd和手册页的位置/usr/share/man/man1/pwd.1.gz
  
  gzip的常用选项(压缩和解压二个功能)//针对单个文件 ,并不适合于目录
  -l 列出信息
  -k 在压缩时或解压时,保留输入文件
  -d 将压缩文件进行解压缩

1)gzip -kd 压缩文件 //解压文件

如果gzip不在任何选项,此时为压缩,压缩完该文件生成为后缀为.gz的压缩文件
并删除原有的文件 所以说 推荐使用gzip -k 来压缩源文件  

tar 对目录文件进行打包或解包
tar -czvf 压缩打包后文件名 目录名(需要打包压缩的名称)
—C <指定目录> 解压到指定目录
-t 可以查看压缩的文件
tar -tvf 压缩文件名

2240:
1.使用串口(USB串口)观察信息
2.使用JTAG口(USB烧录器)烧写程序
3.如果板上程序支持USB下载 ,板子的usb Device 连接 pc USB口
COM1 板载USB转串口连接ARM
ARM芯片 USB DEVICE

easyopenjtag(简称eop) 
openjtag(op)

U-Boot的工作模式有启动加载模式和下载模式。
启动加载模式是Bootloader的正常工作模式,
嵌入式产品发布时,Bootloader必须工作在这种模式下,
Bootloader将嵌入式操作系统从FLASH中加载到SDRAM中运行,
整个过程是自动的。
下载模式就是Bootloader通过某些通信手段将内核映像或根文件系统映像等从PC机中下载到目标板的FLASH中。
用户可以利用Bootloader提供的一些命令接口来完成自己想要的操作。

软件:oflash.exe 能烧写所有的bin文件
bin文件包括u-boot.bin leds_bin
u-boot.bin 可以烧录进Nor,Nand
leds_bin 可以烧录进Nand
lcd_bin

flash(快闪存储器):电子式可清除程序化只读存储器的形式
NorFlash(2M):NOR Flash需要很长的时间进行抹写,但是它提供完整的寻址与数据总线,并允许随机存取存储器上的任何区域
NandFlash(256M):要在NandFlash上面读写数据,要外部加主控和电路设计。。NAND Flash具有较快的抹写时间, 而且每个存储单元的面积也较小
u-boot.bin 可以烧录进Nor,Nand
leds_bin 可以烧录进Nand
lcd_bin
从0地址开始烧录

使用Uboot烧写裸板程序
1.使用op/eop把u-boot,bin 烧到nor flash
2.开发板设置为nor启动,上电后马上在串口输入空格键,使板子进入UBOOT而不是启动板子上的内核
3.连接PC---------开发板的usb device口;安装驱动
4 在UBOOT的串口菜单中输入n(表示接收USB文件并烧写到NAND)
5.使用dnw_100ask.exe发送bin文件
6.uboot即会自动接收,烧写bin文件
7.断电,设为NAND启动,上电;运行nand上烧好的程序

烧录和下载的区别:
1、连接接口有区别
烧写多使用JTAG、USB、网络、串口连接(也有使用SD卡、U盘上的映像文件烧写的);
下载一般用的是网络连接(少数用串口)。
2、文件有区别
烧写的文件一般为烧写软件可以识别的、有一定格式的映像文件(如boot、kernel映像文件);下载的文件一般是操作系统可以识别的二进制文件、文本文件、其它数据文件等,映像文件只是其中一种。
3、存储目标不同
烧写多是指烧写软件将映像文件一个字节一个字节地物理地写到FLASH上的一定位置上;
下载是操作系统将收到的文件保存到本地文件系统上(或映射到本地的文件系统上),不关心文件的物理存储位置。


   
   oflash.exe可烧写所有的目录
   
 1.  使用Uboot烧写裸板程序:
   1:使用eop把u-boot.bin烧到nor flash;
   2:开发板设为nor启动,上电后马上在串口输入空格键,使板子进入UBOOT而不是启动板子上的内核。
   。。。。。
   
   linux kernel:linux 内核
  
2.开发板开发ping问题
开发板运行于u-boot时,用set ipaddr xxx.xx.xx.xxx 设置ip
开发板启动到Linux时,用ifconfig eth0 xxx.xx.xx.xx 设置ip
开发板运行于U-boot时的IP,启动到Linux时就无效了,在LINUX需要重设ip
   
必须设置开发板和pc机 的ip处于同一网段
* pc上多个网卡A,B,C...不能设为同一网段
开发板烧写新的u-boot

win下面用ipconfig查看ip
在u-boot下 开发板能ping pc机 pc ping 开发板就 不行
ipaddr是开发板的IP地址
serverip是服务器的ip

3.裸机开发步骤简介
gcc -v 查看gcc的版本
交叉编译模式:
通常编译嵌入式程序的平台成为宿主机(如:pc的ubuntu系统,cpu架构为x86结构),
运行嵌入式程序的平台成为目标机(如:某款ARM开发板,cpu架构为ARM架构)
x86平台编辑和编译器arm-linux-gcc编译ARM架构的程序,
两者属于不同的架构平台,从而属于交叉编译模式中(下载方式:JTAG,USB,SD卡,网络等)

编译器:
推荐使用win平台source insight和notepad

编译器()
推荐使用arm-linux-gcc编译ARM架构的程序
是基于linux平台的arm编译器。他是开源免费的编译器。

source insight:c 语言 h文件 ARM汇编文件
notepad:其他文件,如Makefile(简单理解为:指明了要编译哪些源文件,指定编译后的输出文件名)

4.点亮LED:
二极管:正向流入导通 反向则不行(发光二极管)
三极管:电压大于一个阈值 就导通(NPN PNP)

LED:
n_LED1 :同名net表示连接在一起
n:表示低电平有效(低电平灯亮)

    指针4个字节
    int a =123
    int *pa =&a
    指针pa储存着int a的起始地址(第一个字节)
    所有变量在内存中都有一块区域
    可以通过①变量②指针操作内存
    t=TYPE *p = val1
    *p=va12          这两个表达式的意思是:把va12写入地址va11的内存,写入(sizeof(TYPE))字节
    单片机(SCM) 微控制器(MCU) 系统级芯片(So c)
   
   怎么让GPIO F4输出1/0
   1.配置为输出引脚
   2.设置状态
   设置GPFCON [9:8]=ob01 , GPF4配置为输出
   ob为二进制
   
   端口配置为功能引脚,将读取到未定义值。功能引脚的意思是外部中断
   GPIO:General Purpose I/O
   机器掉电后,SRAM(4K)的信息不可以保存,而DRAM的信息丢失
   RAM和ROM分别对应电脑的内存和硬盘
   FLASH存储器又称闪存,它结合了ROM和RAM的长处,不仅具备电子可擦除可编程(EEPROM)的性能,还不会断电丢失数据同时可以快速读取数据(NVRAM的优势)
  
  S3C2440框架与启动过程:
  片内RAM指知道SRAM
   nor flash与 hand flash
   nor 启动:Nor Flash 基地址为0时 片内RAM地址为0x4000,0000
   cpu 读出NOR上第一个指令(前4字节),执行cpu 继续读出其它指令执行
   
   Nand 启动:片内4K RAM基地址为0 当执行为Hand时 NOR Flash不可访问
   上电后,2440硬件自动把Nand前4k内容复制到片内RAM(SRAM)
   然后cpu从0地址取出第1条指令执行
   一般程序要找到内存所存储的数据或者下一个指令,都要通过段地址+偏移地址的形式来确定所需要的东西所在内存的位置,以便读取
   再回来说,一个程序由数据段,代码段,堆栈段,附加段四个主要段组成。(即一个程序被运行后,所占据内存就是给这个四个段使用
   而你说的基地址其实就是每个段的起始地址,又称段地址
   
   几条汇编代码:
   1.LDR(load):读内存,LDR R0,[R1](格式) 地址为R1,读取地址R1上的数据(4字节),保存到R0中
   2.STR (store):写内存命令,STR R0,[R1](格式), 地址
   
   是R1,把R0的值写到地址R1(4字节)
   str r3,[fp,#-16]
   r3存入 fp的地址-16(十进制)的地方  fp-16最后得出来是基地址,然后存放四个字节
   
   3.B:跳转
   4.move : move R0 R1 把R1的值赋给R0,R0=R1
     MOV R0,#ox100,R0=0x100
     mov r0,#256(十进制转为十六进制(0x100)) ;0x100
     
   5.LDR R0,=0x12345678: R0=0x12345678
   伪指令,他会被拆分为几条真正的RAM指令
   所以引入伪指令
   LDR R0,=任意值
   
   c: e59f100c ldr r1,[pc,#12]//pc表示当前的地址+8 总地址等于0xc+8+12=32(十进制)
  最后的.bin文件就是机器码
  
  晶体管只有2个状态;off 0
                     on  1
    数据使用多个晶体管表示
    二进制:1位表示一个bit
    八进制:1位表示3个bit
    十六制:1位表示4个bit
  c语言:
  int a=96;(十)
  int a=0140(八)
  int a=0x(十六)
  
  置位:
  int a=0x12345678
  把bit 7,8置位
  int b=a|(1<<7)|(1<<8)
  
  清位:
  int a=0x12345678
  把bit 7,8清除
  int b=(a&~(1<<7))&(~1<<8)
  
  c语言的内部机制:
  sub r0,r1,#4  :r0=r1-4
  sub(减) ro,r1,r2  :r0=r1-r2
  add(加) r0,r1,#4 :r0=r1+4
  bl(branch and Link):
  eg:
  bl *** ----1.跳到***
             2.把返回地址(下一条指令的地址)保存在lr寄存器中
  
  ldm(m-many)---读内存,写入(存放)多个寄存器
  stm---------把多个寄存器的值写入内存
  
  sp:基地址
  
   ldmia
   stmdb
   ia,db:其他形式简单的描述指令的行为,
   意思分别是过后增加(Increment After)、
   预先增加(Increment Before)、过后减少(Decrement After)、预先减少(Decrement Before)
   eg:stmdb sp!,{fp,ip,lr,pc}
     sp!=最终的,被修改的sp值 (加!的作用)
      db(看上面两个单词的缩写):先减后存(先减指的是:先当前地址的值减去4,然后在上一个地址(未先减之前的地址)中存放寄存器(pc,lr)的值)
      高编号寄存器存在高地址(先存pc的值,依次:pc(R15),lr(R14),ip,fp)(见ARM指令集,寄存器和处理器模式)
      
   ldmia sp,{fp,sp,pc}
    高编号寄存器存在高地址 又因为的是逐渐累加的,先读fp,sp,pc
   ia:先读(读内存里面保存的值保存在寄存器里面(fp,sp,pc)) 后增(内存增加)
   sp在先读这里被修改
   但是 sp无!sp修改后的值(地址)不存入sp中
   
   ldmia和stmdb的意思就是要么把内存里面的值写入寄存器,要么就是把寄存器的值写入内存
   
   
   
   Start.S
   几个作用:
   1.设置栈
   2.调用main 并把返回地址保存在lr中
   LED.c
   作用:
   1.定义二个局部变量
   2.设置变量
   3.return 0
   
   栈:sp所指向的内存
   
   为何要设置栈:
   因为c函数要用
   怎么使用栈
   a.保存局部变量
   b.保存lr等寄存器
   3. 调用者如何传参数给被调用者
   被调用者如何传返回值给调用者
   
   调用者(通过r0,r1,r2,r3保存参数或者返回值)被调用者
   在函数中,r4-r11可能被使用,所以在入口保存它们,在出口恢复它们
   
   如果分辨是nor/nand启动
   nor flash类似硬盘,只能读,但是写的时候,非要以一定的格式写,不然写不成功。
   用这样的特性,
   写0到0地址,在读出来
   如果得到0,表示0地址上的内容被修改了,它对应ram,这就是nand启动
   否则就是nor
   
   4.gcc,arm-linux-gcc
   gcc -v:查看gcc的版本,和gcc编译过程
   方式1:
   gcc 文件名 生成一个a.out可执行文件
   预处理 编译 汇编 链接
   以“#”开头的命令被称为预处理命令,生成.i文件
   gcc -E 文件名 -o 生成文件名
       -S
       
    链接就是将汇编生成的OBJ文件,系统库的OBJ文件,库文件链接起来,最终生成可以在特定平台运行的可执行程序
    -lc:链接libc库文件,其中libc库文件中就实现了printf等函数
    
     目标文件又称为OBJ文件。
     反汇编是指机器代码转换为汇编代码
     
     -nostdlib 不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。这个选项常用于编译内核,bootloader等程序,他们不需要启动文件,标准库文件
     一般应用程序才需要系统标准启动文件和标准库文件
     
     -ldd 可执行文件 可以看他链接动态库库
     
     
     4.Makefile的引入及规则
     make +[目标] 
     若无没有目标,就是make
     执行第一个的命令行
     若有。
     就执行目标文件的命令行
     b.假象目标:.PHONY:
     
     c.即时变量,延时变量,export
     简单变量(即时变量)
     A:=xxx #(注释符) A的值即刻确定,在定义时确认
     B=xxx #B的值使用时才确认
     := #即时变量
     = #延时变量
     ?= #延时变量,如果是第一次定义才起效,如果在前面该变量已定义则忽略这句
     += #附加,它是即时变量还是延时变量取决于前面的定义
     
     echo 表示输出的意思,想使用变量时,用$来引用,$(变量)
     
     Makefile函数
     a.$(foreach var,list,text)
     b.$(filter pattern...,text) 在text中取出符合patten格式的值
       $(filter-out pattern...,text) 在text中取出不符合patten格式的值
       
     c.$(wildcard pattern)    #pattern定义了文件名的格式
                              #wildcard取出其中存在的文件
                              
     d.$(patsubst pattern,replacement,$(var)) #从列表中取出每一个值
                                              #如果符合pattern
                                              #则替换为replacement
                                             
     
    例子:    
    走在Makefile文件中
    A=a b c
    B=$(foreach f,$(A),$(f).o)
    
    c=a b c d/
    D=$(filter %/,$(C))
    E=$(filter-out %/,$(C))
    files=$(wildcard *.c)
    
    files2=a.c b.c c.c d.c e.c
    files3=$(wildcard $(files2))
    dep_files=$(patsubst %.c,%.d,$(files2))
    all:
        @echo B=$(B)
        @echo D=$(D)
        @echo E=$(E)
        @echo files = $(files)
        @echo files3=$(files3)
        @echo dep_files = $(dep_files)
        
最后执行make
 结果:
 B = a.o b.o c.o
 D = d/
 E = a b c
 files= a.c c.c b.c
 files3= a.c b.c c.c//为什么是这三个,是因为当前目录下 只存在这三个真实的文件
 dep_files=a.d b.d c.d d.d e.d
 
 a.改进:支持头文件依赖
 
 gcc -M c.c//打印出依赖
 gcc -M -MF c.d c.c //把依赖写入文件c.d
 gcc -c -o c.o c.c -MD -MF c.d //编译c.o 把依赖写入文件c.d
 
 掌握ARM芯片时钟体系
 时钟总线:
 AHB bus(总线):用于高性能、高时钟频率的系统结构
 h:high(高速总线)
 APB BUS :低速总线,主要用于外设
 三种时钟:
 ARM:Fclk 最高400M
 AHB:HCLK 最高136M
 APB:PclK 最高68M
 Mpll=(2*m*Fin)/(p*2^S)
 NRESET:复位引脚:等待电源稳定,才输出高电平
 
 MMU1_SetAsyncBusMode
MRC  p15, 0,  r0,  c1,  c0,  0 (c:表示协处理器,r:寄存器,)mrc:从协处理器中读取某个值,放入寄存器中
ORR  r0,  r0,  #R1_nF:OR:R1_iA
MCR  p15, 0,  r0,  c1,  c0,  0   mcr:是把寄存器里面的值写到协处理器中

 时钟源:12M晶振 通过PLL(锁相环)配置三个时钟的频率
 OM[3:2]:外部时钟选择器(决定选择哪一个时钟源)默认为00
 外部晶振(XTIpll)或外部时钟(EXTCLK)
 有二个PLL 1.MPLL 2.UPLL
                                                           FCLK
 晶振--|----———— ~                MPLL(main PLL 锁相环)----|----cpu
       |                                                     |
       |_UPLL(U PLL)-USB                                     | 
                                                             |_  HDIV(分频)________HCLK_____AHB总线----nand 等等
                                                             |
                                                             |
                                                             |__PDIV(分频)————————PCLK——————APB总线-----外设
                   
                   
                                                    ---MPLLCON(PLL控制寄存器(配置PLL频率的))
                   怎么写程序:控制MPLL,HPTV,PDTV---
                                                    ---CLKDIVN(时钟分频控制寄存器(配置HCLK,PCLK的))
                   
    步骤一:1.复位芯片等待电源稳定,才输出高电平
            2.根据OM[3:2]的值,FcLK=晶振--|----——
            3.PLL锁存OM[3:2]的值
            4.设置PLL
            5.PLL工作
            6.FCLK=PLL输出的新时钟,cpu运行
    
                串口:
            波特率:每一秒传送多少个位
            格式:数据位,停止位,校验位,流量控制
            
            波特率的发生:
            UBRDIVn=(int)(UART时钟/(波特率*16))-1
            
            逻辑电平:
            在TTL/CMOS逻辑电平下,超过某个电压时,为1,电压小于某个电压时为0,电压过低不适合在长距离传输
            所以,引入了RS-232逻辑电平
              在TxD和RxD上:
            逻辑1(MARK)=-3V~-15V
            逻辑0(SPACE)=+3~+15V
            
            ARM(TTL电平) PC(串口RS232) 启用电平转换芯片或者接USB串口芯片
            
            串口上逐位发送,串行发送(移位器)
            过程是:缓冲寄存器把数据(从内存来的数据)发送到移位器里面,移位器并行一位一位把数据发送出去
            接受数据最后是接受寄存器中把数据放入内存中
            
            串口步骤:
            引脚用于串口
            设置波特率
            设置数据格式
            
            使用FIFO,可以传送64个字节
            
            不使用,可以传送1个字节
            
            
            printf()的声明
            int printf(const char * format,...);
            format:固定参数
            ....:可变参数

    printf-----:
            printf中的格式字符:
            可变参数:
            1)代码:手工确定可变参数
            2)代码:自动确定可变参数
            
            x86平台,函数调用时参数传递是使用堆栈来实现的
            所以:int push_test(const char * format,...)可变参数
            参数:内存地址是物理上相邻
            
            这里...代表的是任意类型的形参 必须是... 三个点 ,这样编译可以通过
            _attribute_((packed)) 让所作用的结构体取消在编译过程中的优化对齐,按照实际占用字节数进行对齐
            _attribute((aligned(n))),让所作用的结构体成员对齐在n字节边界上。如果结构体中有成员变量的字节长度大于n,则按照最大成员变量的字节长度来对齐
            
            自动确定可变参数:
            头文件:#include<stdarg.h>
            (表达式1,表达式2)
            先求解表达式1的值,在求解表达式2的值,然后返回表达式2的值
            
            
内存控制器与SDRAM:
                addr              根据addr选择不同的模块
            cpu------内存控制器-----------------------------(发送或接受数据)模块(各种模块的寄存器)
            
    
            
            
            
            
            
            
            cpu--内存控制器--模块之间有addr(地址)线,片选信号,data(数据线)
            
            1.GPIO/门电路
            2.协议类接口:UART,I2C,SPI
            1,2.cpu通过地址给内存控制器,间接控制模块控制器,然后在控制外设
            
            3内存接口:nor 网卡 SDRAM(nand flash不算) cpu通过内存控制器直接控制内存型设备
            
            
            
            cpu与内存接口,不只有地址总线,还有数据总线,片选线
            
            怎么互不干扰(访问其中一个内存模块时,怎么让其他的内存模块不去干扰他):(内存控制器根据不同的地址范围,发送不同的片选引脚,
            只有被选中模块,才会工作)
            根据cs(片選)引脚chip select:决定使用哪个模块,而且都是nGCSn(前面的n表示低电平有效,后面的n表示第几个引脚)
            只有2440让某个片选引脚为低电平的时候,对应的模块才会开始工作,才会接受和发送数据和cpu之间
            cpu把addr给内存控制器,然后内存控制器根据地址决定选择哪个片选信号
            举例:当0<=addr<0x0x08000000 时让nGCS0为低
            
            
            每一个片选信号可选择的地址空间是:128
            十六进制的每一个“1”代表一个字节
            
            每一个片选信号可选择的地址空间是:128M=2e7*2e20=2e27 至少需要27条地址线(addr)来选定一个片选(一条地址线表示一个二进制地址位的位数)
            
            
            各种模块控制器都属于cpu统一编址,nand flash不参与cpu的统一编址,nand flash 是nand flash控制器中片选信号(nFCE)
            片内就芯片本身所带的flash,片外是你经过总线扩展芯片之外的flash
            
                                                                           1发送出片选信号
            让cpu发送32位的addr给内存控制器发送(cpu去访问片外内存设备)---                           
                                                                           2发出addr0---addr26----外设
                 
            cpu通过引脚的设置表示读/写数据
        
不同位宽设备的连接:
            nbit ROM:表示对外能提供nbit的数据,且数据是以nbit储存的
            
            8bitROM就是里面的数据是以8bit为最小单位保存的(cpu从A0开始,)
            16bitROM     16bit为最小单位保存(cpu从A1开始)
            32bitROM     32bit为最小单位保存(cpu从A2开始)

            假设cpu执行:
            mov R0,#3      去地址3读一个字节
            LDRB R1,[R0]
            cpu A0=1 A1=1 A2~A26=0
            过程:cpu 送出地址 |ROM收到地址  ROM返回数据  内存控制器挑选数据----返回给cpu
            8bit       000011   000011           8bit (3)           8bit给cpu
            16bit      000011   000001           16bit (1)        根据A0=1挑出给CPU
            32bit      000010   000000           32bit  ( 0)     根据A1A0=11挑出数据给cpu
            
            
            mov R0,#4
            LDR R1,[R0]  去地址4 读取4个字节
            过程:  cpu发出地址    内存控制器转发给R0M    ROM返回数据给cpu                 内存控制器组装好了给cpu
                                    000100
                8bit                000101                从地址4到7的字节                  组装好成4byte给cpu
                                    000110
                                    000111
                                    
                16bit              00010                  从2个(第二个开始的刚好是地址4的字节)
                                                         到3个的16byte数据
                                   00011                
            
            
                   32bit              00001                 得到第一个32位的数据
            数据都是从0个开始计数的
            cpu----内存控制器-------ROM
            
            
            
            怎么确定芯片访问地址:
            1;根据片选信号确定基址
            2:根据芯片所接地址线来确定范围
            nor:要加上A0 (刚好)
            范围:base+0b00...000~base+0b0000....000
            =0~0x1fffff(刚好是2M空间)
            
            硬件上的cs信号都是根据地址自动拉低的
            软件上的cs信号先后可以自行拉低
            
            
SDRAM:
1.cpu发送读写的命令(LDR R0,=0x3000 0000,LDR R1 [R0])---发给内存控制器----1发出nGCS6
                                                                             2根据类型(SDRAM)拆分地址
                                                                             a.BANk地址
                                                                             b.行地址--------怎么拆分----行地址几条线--列地址几条线
                                                                             c.列地址
                                                                             3读数据
寄存器:Trcd:行地址到列地址的时间延迟
刷新周期:64ms refresh peried(8K Cycle)周期:64/8192=7.8ms
                                                                             
                                                                             
硬件--时序图分析:            
            nOE读信号
            nwe写信号
            Taa:发出addr后多长时间data才有效
            Tce:在发出片选信号之后data才有效
            Toe:      读     
            Toh:     数据保持的时间
            Tdf:     在这段时间不能访问其他芯片
            Trc:     读所花费的时间(最快是70ns)
            
            为简单,让CE#,OE#,ADDR同时发出,同时保持低电平70ns
            
异步通信(UART)指两个互不同步的设备通过计时机制或其他技术进行数据传输。
异步通信中两个字符之间的时间间隔是不固定的,而在一个字符内各位的时间间隔是固定的。
基本上,发送方可以随时传输数据,而接收方必须在信息到达时准备好接收。相反,同步传输是一个精确同步的位流,其中字符的起始是由计时机制来定位的

SDRAM的设置:
LDR R0,=0x3000,0000
LDR R1,[R0]
过程:发给内存控制器---内存控制器根据地址发送nGCS6
                       根据类型(SDRAM)拆分地址
                       a.BANk地址
                       b.行地址
                       c.列地址
                       怎么拆分
                       
                       行地址几条线
                       列地址几条线
                       设置内存控制器的寄存器

                       
一.IIC  
        单片机的端口按通信方式分,可以分成哪两种?
        答:【串行通信、并行通信】
        串行通信又分为异步传送和同步传送两种基本方式
        
        由于数据块传递开始要用同步字符来指示,同时要求由时钟来实现发送端与接收端之间的同步,故硬件较复杂。
        同步传输方式比异步传输方式速度快,这是它的优势。但同步传输方式也有其缺点,即它必须要用一个时钟来协调收发器的工作,所以它的设备也较复杂。
        
        start信号 地址 方向 传输 回应 结束信号
        主从结构:一主多从
        每一个从芯片都有一个设备地址
        
        写:
        start---设备地址方向(是读还是写)---回应(从)--(从设备)传输数据(回应)(从)--p信号结束
        读:
        start---设备地址方向--回应(从)---数据(从)--回应--p信号结束
        后面没加的都表示主
        
        设备地址(7位)方向一位,逐8位传输,从最高位开始传输(MSB)
        用9个clk传8位数据,第9clk是回应
        scL低电平时 SDA可变化 ,高电平 SDA不变
        
        
        
        如何在SDA上实现双向传输:
        1.主设备发送时,从设备不发送,可以通过SCL区分
        2.主设备发送时,从设备的“发送引脚”不影响数据
        不想影响SDA,就不驱动三极管
        想输出高电平,不驱动
        想输出低电平:驱动
        使用开极电路:
        
        主机(传输8bit)给从机
        1.前8个clk,从设备不要影响,从设备不驱动三极管,主设备决定数据
        2.第9个clk,由从设备决定设备,主设备不驱动三极管,SDA高:NO Ack     SDA低:ACK
        
         I2c协议规定如何传数据,数据含义由设备决定
         
         
         
         
         
         重定位:
         cpu不能直接访问nand,所以Nand不能直接运行。(SDRAM,Nor可以直接运行)
         Nand启动时,
         nand启动时,前4k复制sram
         cpu从0运行
         如果程序>4k时,前4k的代码需要把整个程序读出来,放到SDRAM里面(重定位)
         
         nor:像内存一样的读,不像内存可以直接写-------
         BSS:代码段
         data:数据段
         test:代码段
         ** bss段/commen 不保存在bin中
         
        nor可读不可写,所以程序中含有需要写的变量、全局/静态,他们在bin中,写在NOR上,直接修改变量,无效,
        所以要重定位,放到SDRAM中
        
        在nor里面在,把数据段放在SDRAM,这样会造成bin文件内存过大(因为代码段从地址0开始,而代码段从0x30000000(sdram的起始地址)开始),
        因为,解决文件过大的问题以下:
        方法一:1.在bin文件中,让全局变量放在代码段的上面(这样就能解决文本过大的问题了)
                 2.烧写进nor 
                 3.运行的时候把全局变量复制到0x3000 0000(重定位)
        方法二:
                 1.代码段从0x300 0000 开始
                 2,烧写进nor(从nor的0地址开始)
                 3.运行时候,代码段和数据段复制到0x3000 0000
                 
                 
        AT ( ldadr )
                表达ldadr下面的AT关键字指定段的加载地址。默认(如果不使用 AT关键字)是使加载地址与重定位地址相同
                
                .data 0x30000000 : AT(0x800)  { *(.data) }//表示在bin文件中,把数据段从0x3000 0000移到0x800段 来达到减少文件内存大小的办法,
                然后main函数会以地址0x3000 0000来访问

        
        SECTIONS
        {
        ....
        secname start BLOCK(align) (NOLOAD):AT(ldadr)
        {
        contents//内容
        }>region:phdr=fill
        ...}
        
        eg:SECTIONS{
        .text 0 :{*(.text)}
        .rodata :{*(.radata)}
        .data 0x30000000 :AT(0x800) 
        {
        data_load_addr = LOADADDR(.data);
        data_start = .;
        *(.data)
        data_end = .;
        }
        .bss : {*(.bss) *(.COMMON)}
        }

        
        contents(里面的格式有):
        1..o文件
        2.*(.text)
        3..o文件和*(.text)(不只是代码段)
        
                 
        SECTIONS
        {
        secname(段名) start(起始地址:运行时地址(runtime addr)或者重定位地址(relocate addr)) :AT(ldadr)(load,Addr:加载地址(data段在bin文件中的地址),)
        }
        //不写AT时,Load Addr=runtime addr()
        
        对于elf文件
        1.链接得到elf文件,含有地址信息(Load ADDR)
        2.使用加载器(对于裸板,是JTAG调试工具)对于应用APP.加载器也是APP
        
        使用加载器把elf文件读入内存(读到Load Addr(加载地址))
        
        3.运行程序
        
        4,如果load Addr !=Runtime ADDR 程序本身要重定位  
        1,2,3,4的核心//核心:程序运行时,应该位于runtime ADDR(Relocate Addr)
        
        
         对于bin文件
         1 elf---bin
         2 硬件机制启动
         3 如果bin所在位置!=run time ADDR
         程序本身实现重定位
         
         eg:SECTIONS{
        .text 0 :{*(.text)}// Load Addr = Runtime Addr = 0
        .rodata :{*(.radata)}
        //一二行不需要重定位
        .data 0x30000000 :AT(0x800) //Load Addr=0x800 Runtime Addr=0x3000,0000  //重定位:0x800地方的代码复制到0x300 0000中
        //前面的代码段来实现这个复制,这个重定位的功能(就是重定位)//load Addr=0x800 Runtime Addr=0x30000000
        {
        data_load_addr = LOADADDR(.data);
        data_start = .;
        *(.data)和
        data_end = .;
        }
        .bss : {*(.bss) *(.COMMON)}//运行时这一段地址紧接着data段摆放// 在elf文件和bin都不存bss段(所以这么说 ROM里面没有bss段(给存储器节约内存),RAM里面有bass段(运行后代码段执行代码直接清零))
        //程序运行时,把bss段对应空间清零
        }

        4.拷贝代码和链接脚本的改进
        a.拷贝代码要改进
        b,链接脚本要改进
        拷贝代码就是把代码从flash中复制进sdram中
         
        
        1.把全部的程序(由nor里面的代码段的代码将全部的代码复制进SDRAM中,实现
        重定位整个程序:start.S里面的cpy和clean)复制进SDRAM(当有的单片机没有NOR,有的只有内存卡等等,这时就需要把代码全复制到SDRAM中,而不是分开的存放的)
        1.在链接脚本中指定Runtime Addr为SDRAM地址
        2.重定位之前的代码,为位置无关,用位置无关码写成
        
        mov r1,#0//nor启动时对应0地址,nand启动时对应片内的内存的0地址
        ldr r2, =data_start
        ldr r3, =data_end
         重点:在dis文件中
              B/BL 0x3*******(只是起方便查看作用,不是跳到这个地址)
        具体跳转地址是是当前pc+offset(也就是sdram_init的位置)去执行
        如果(从0开始运行)那么就是pc=0
        
        所以以后我们要想写出与链接地址无关的代码时,跳转命令必须用bl/b命令,所以用的是偏移地址,而不是绝对地址
        
              怎么写位置无关的程序:
              位置无关码:放在任何地址都能运行,不需要放入运行地址即可
              1.使用相对跳转命令
              
              B/BL
              
              使用位置无关码,不使用绝对地址,最根本的办法是看反汇编
              1.使用BL命令相对跳转,程序依然在NOR/SDRM(片内ram)执行
              
              2.重定位之前,不可使用绝对地址,不可访问全局变量/静态变量(全局变量和静态变量会以运行地址(绝对地址来访问)来访问),不可访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问)
              
              3 重定位之后,使用ldr pc,=xxx 跳转到Runthe Addr(SDRAM里面)
        
          
              
              结论1.c程序不保存lds文件中的变量(借助symbol table 保存lds的变量)
                  2.万一要使用呢,编译程序时,有一个symbol table(符号表),用symbol table 保存lds变量,
                  使用方法:
                  1.对于g_i,使用&g_i得到ADDR
                  为了保持代码一致
                  对于lds中的a1,使用&a1
                  在链接时确定
                                                                                       指令不对
                  按键------
                  定时器------ 中断控制器(事件发送给中断控制器)-----cpu-------|      数据访问有问题
                                                                                |----
                  网络数据-----                                                        Reset
                  ----------------------------------------------                -----------------------
                                                                                  
                                 中断 (中断属于一种异常)                                   异常
                  
                 
    ARM对异常(中断)的使用过程:
        中断与异常:
        中断也属于一种异常
    1.初始化:a.设置中断源,让它可以产生中断
              b.设置中断控制器(屏蔽(某个中断),优先级)
              c.设置cpu总开关      
        
    2.执行程序
    
    3.产生中断:按下按键---
    例子:按下按键(中断信号发给)----中断控制器(发送信号)-----cpu-----
    
    cpu每执行完一条指令,都会检查有无中断,异常产生
    
    4处理
    发现有异常、中断产生,开始处理----对于不同的异常,跳去不同的地址(这些地址叫异常向量,一般来说这些地址是排在一起)执行程序
    这些地址,只是一条跳转指令,跳去执行其他函数
    
    //在这上面几个步骤都是硬件做的
    
    ------这些函数做以下的事情:
    代码去做下面的事情:
    保存现场(各种寄存器)
    处理异常(中断):(先分辨不同的中断源,在调用不同的函数)                                               
    恢复现场 :                                                                                                           

    b   reset
    ldr  pc,_.....
    ldr pc, _irq(假设是这条指令)
    ldr pc, _fip
    发生中断时,cpu强制跳到这个地址执行该指令
    我们先在0x18放ldr pc ,_irp这个指令
    于是cpu跳去执行_irq这个地方的代码:
    代码做以下这些事情:
    保存现场(各种寄存器)
    处理异常(中断):在调用不同的函数                                               
    恢复现场 :  
    
    //这些是软件做的
                                                                                              
                                                                                                                                             |------------------保护现场 
                                                                                                                                             |                            |---- 1.函数先分辨中断源
                                                                                                              执行完一条指令跳转到其他函数   |                            |
    中断程序()//怎么被调用?//cpu--(强制跳到)----0x18(这是中断的地址,在这之前是由硬件完成,后面由软件完成)----------------------------------------------调用函数---|
                                                                                                       (0x18处放了一个跳转指令)             |                            |
                                                                                                                                             |                            |------2.调用对应函数
cpu模式(Mode)_状态(State)与寄存器                                                                                                         |----------            |-----恢复现场

7种Mode
1.usr(正常)(用户模式):
 
2.sys(兴奋)(系统模式):
3.异常模式:---1.und:未定义指令模式  (当cpu执行时,当碰到某条指令cpu不认识,就会进入这种模式)
               2.svc:管理模式  3.abt:中止模式(1.指令预取中止  2数据访问中止)
               4.IRQ:中断模式    5.FIQ快中断模式(快速处理)

               2和3(6种中断,系统和用户模式)处于特权模式:可以编程操作CPSR寄存器直接进入其他模式
               1模式就是用户模式(usr)不可直接进入其他模式
               出现哪种异常就进去哪个异常模式,在这个异常模式下,更容易处理这些异常情况
               
               模式下寄存器中,打了三角符号的,表示这个是在这个模式下专属的寄存器。
               r13用作sp(栈)   r14用作是LR(保存发生异常时,指令地址)
               
state:

  cpu state---
  1.ARM state:ARM指令集 每个指令占据4btye,就是一条指令变成机器码时,是占据4btye
  
  
  2.Thumb state:Thumb指令集 每个指令占据2byte,就是一条指令变成机器码时,是占据2btye
  ARM920T 支持字节(8 位)、半字(16 位)和字(32 位)的数据类型。字必须按 4 字节对齐边界,半字必须按 2 字节对齐边界
  
  保存现场:保存被中断模式下的寄存器
  
  处理异常:
  
  恢复现场:
        
  CPSR/SPSR:()
  ARM每种工作模式除R0~R15共16个寄存器外,还有第17个寄存器CPSR,
  叫做 “当前程序状态寄存器”,CPSR中一些位被用于标识各种状态,一些位被用于标识当前处于什么工作模式
  SPSR:程序状态保存寄存器:发生异常时,用来保存“被中断模式下的cpsr里面的值”  
      cmp R0,R1:影响Z位 if R0==R1,z=1
    beq xxx  :使用z位 if z==1 则跳转

  异常处理流程(硬件)如下:
                                                 |----pc+4
  1.LR_异常(某一种模式下) = 下一条指令的地址---|             (lr_und(在und模式下,lr寄存器)保存有被中断模式中的下一条即将执行的指令的地址)
                               --------------    |-----pc+8
                 被中断模式下的下一条指令的地址             
  2.SPSR_异常=cpsr  (spsr_und 保存有被中断模式的cpsr)
  3.修改cpsr的M4~M0,进入异常模式(CPSR中的M4~M0被设置为1101,进入und模式)
  4.跳到向量表(跳到0x4的地方执行程序)//(括号后面的是一个示例,进入und模式下)
   
  第二步(软件): 
  设置栈(因为每个模式下栈是不一样的)
   第三歩:
  保存现场:保存被中断模式下的寄存器,也要保存lr寄存器(lr是异常处理完后的返回地址,也要保存)
  
  处理异常:
  
  恢复现场:从栈中恢复被中断模式下的寄存器,把spsr的值恢复到cpsr里面
   
   
  离开异常的操作:
  1.pc=LR_异常减去offset
  2.CPSR=SPSR_异常    
  3.清中断 
  
  
  不重要_Thumb指令集程序示例
  
  
  und异常模式程序示例
  
 SWI异常(软件中断):
 app想访问硬件,必须切换Mode
 
 怎么切换----发生异常----中断
                         und
                         swi #val 使用软中断
  
 按键中断程序示例:
 1.设置中断源,让它能够发出中断信号
 2.设置中断控制器,让它能够发出中断给cpu
 3.设置cpu,cpsr有I位,他是总开关
 1,2,3是初始化
 相对于异常多了以下几个步骤:
 处理时,要分辨中断源
 处理完清中断
 
 定时器中断程序示例:
 使用定时器来点灯实现计数
 1.每来一个clk,TCNTn减一
 2.当TCNTn==TCMPn时
 3.TCNTn继续减1
   当TCNTn==0时,可以产生中断,pwm引脚再次翻转
 4.TCNTn==0时,可自动加载初值
   TCMPn,TCNTn初值来自TCMPBn,TCNTBn
 定时器:
  1.设置时钟
  2.设置初值
  3.加载初值,启动Timer
  4.设置为自动加载
  5.中断相关
  
          nor              nand
  接口:RAM--LIKE        引脚少,复用
  容量: 小                  大
  读:   简单              复杂
  写    发出特定命令(慢)  一样(快)
        无位反转 无坏块      位反转 有坏块
 
 使用&a1
 u-boot   
 使用UBOOT体验nor flash 的操作(开发板设为nor,进入UBOOT)
 1.使用openjtag烧写uboot到nor flash
 1.读数据
 md.b 0
 2.读id
 2440的A1接到nor的A0,所以2440发出(555h<<1)(为什么要向左移一位,因为cpu的发的地址线是A1发给nor的A0),nor才能收到555h这个地址
 退出读id状态:
   
 3.nor有两种规范,jedec,cfi(common flash interface)
  读取cfi信息
  
  进入cfi模式 往55H写入98H
  读数据:    读27H得到容量
  退出cfi模式  往任意地址写入F0
   
   第二节:NorFlash编程_识别
   1.编译程序时加上:-march=armv4
   否则
   volatile unsigned short *p=xxx;
   *p = val;
   2.把timer中断去掉
   否则:测试nor时进入cfi等模式时,
   如果发生了中断,cpu必定读NOR,那么读不到正确的指令,导致程序崩溃
   
    NAND FLASH 是一个存储芯片
    1.原理图上NAND FLASH和s3c2440之间只有数据线,怎么传输地址
      在DATA0~DATA7上既传输数据,又传输地址。
      当ALE为高电平时传输的是地址,
      当ALE为低电平时传输的是数据
     2.从NAND FLASH 芯片手册可知,要操作NAND FLASH需要先发出命令
     怎么传入命令?
      在DATA0~DATA7上既传输数据,又传输地址,也传输命令
      当ALE为高电平时传输的是地址,
      当CLE为高电平时传输的是命令,
      当ALE和CLE都为低电平时传输的是数据
     3.数据线既接到NAND FLASH ,也接到nor flash ,还接到SDRAM,DM9000等等
      那么怎么避免干扰
      这些设备,要访问必须“选中”,没有选中的芯片不会工作,相当于没接一样(片选引脚CE)
     4.假设烧写NAND FLASH ,把命令,地址,数据发给它之后
      NAND FLASH 肯定不可能瞬间完成烧写的,怎么判断烧写完成
      通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙
     5.怎么操作NAND FLASH呢
         根据NAND FLASH的芯片手册,一般的过程是:
        发出命令
        发出地址
        发出数据/读数据
      
           NAND FLASH(NAND的角度)            S3C2440(cpu的角度,只需要以下命令,nand的步骤他自己完成)
发命令      选中芯片
           CLE设为高电平
           在DATA0~DATA7上输出命令值             NFCMD=命令值(NFCMD是寄存器,只要给这个寄存器赋值,就可以完成写命令)           
           发出一个写脉冲           
           
发地址      选中芯片
            ALE设为高电平
            在DATA0~DATA7上输出地址值            NFADDR=地址值(同上)            
            发出一个写脉冲      
            
发数据       选中芯片
             AlE,CLE设为低电平                     NFDATA=数据值
             在DATA0~DATA7上输出数据值
             发出一个写脉冲                          
      
读数据      选中芯片
            发出读脉冲
            读DATA0~DATA7的数据                          val=NFDATA             
      
 用UBOOT来体验NAND Flash的操作:
 1.读ID 
                                S3C2440                 U-BOOT    
 选中(片选引脚)             NFCONT的bit1设为0      0x4E000004    (md.w 0x4E000004 1)----读出这个地址的二个字节 
                                                      (md.l(long)以四字节 .b(byte)以一字节)(md.[b,w,l] addr value(读的格式) 后面的value表示读几次) mw.[b,w,l] addr value表示写的命令
 发出命令0x90                  NFCMMD=0X90            寄存器的地址
 发出地址0x00                  NFADDR=0X00             同上
 读数据得到0xEC                VAL=NFDATA              同上
 读数据得到device code         VAL=NFDATA              同上
  退出读id的状态               NFCMMD=0XFF             同上
            
*每执行一个命令完了,必须退出该命令,然后才能进入下一个命令。        
2.读内容:读0地址的数据
  使用UBOOT命令:
  nand内存为256M
  一条地址线有二种可能(0,1)所以一个128M的nand flash 至少需要28条线  128M=2^28 因为位宽为8,一次只能交换8位。所以在256Mnand flash里面数据线(nand 里面地址线和数据线共用一根)是8条,至少需要
  4个地址周期
  
  存储芯片的编程
  1.初始化  主控芯片的nand flash 控制器
  2. 识别    读取ID
  3.读        一次读一个页(page)
  4.写        一次写一个页(page)
  5.擦除      一次擦除一个块(block)
  
 小知识 :一般前面加n 上面有一个—— 就表示低电平有效 这种写法就表示nWE -- 低电平有效 这两种写法等效
                                 WE
    
                                                                      
                                                                      

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值