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