【ARM编程模型】
硬件: 电路原理图
软件: 体系结构,
指令集, 寄存器组
【ARM编程技术】
汇编/C语言 编译, 链接,
烧写和调试
windows: MDK
linux : gcc
【ARM接口编程】
电路原理图
datasheet
------> 裸机程序(不带操作系统,直接操作硬件)
中断技术
初始化程序
【 ARM基础知识】
1.
冯·诺依曼结构特点:
采用二进制表示数据和程序
事先存储程序
利用控制流来驱动程序
五大部件
2. CPU = CU +
ALU
CPU:
解释并执行指令的功能部件
CU:
译码并发出各种控制信号
ALU: 运算
3.
常见存储器
RAM:
(掉电丢失数据)
SRAM: 静态
96KB
DRAM: 动态(SDRAM,
DDRII(256MB))
ROM:
(掉电不丢失数据)
PROM
EPROM
E2PROM
BIOS
flash:
norflash
: 2MB 有地址线, 片上执行(CPU可以直接访问), 读数据速度快,
存储启动程序
nandflash:
256MB 无地址线, 不片上执行, 存储大数据量数据
【ARM体系结构】
1. ARM :
advanced RISC machine
含义:
公司
技术
微处理器
2.编程模型
1)数据和指令类型:
ARM处理器支持:
8bit, 16bit,
32bit数据
Thumb指令集,
ARM指令集
半字对齐: 被2整除 [0]=0
字对齐 : 被4整除
[1:0]=00
例子:
运行地址 机器码
机器码符号化
0x00000000 E3A00001 MOV
R0,#0x00000001
2)处理器工作模式
ARM处理器一般支持
7
种基本工作模式
不同的工作模式对应着不同的寄存器
特殊寄存器: R13(SP), R14(LR), R15(PC),
CPSR, SPSR
程序指针PC (r15): 程序执行到哪里(地址), PC指针指到哪里(地址)
3. 异常处理过程
产生:(硬件过程)
1.
CPSR ---> SPSR_mode
2.
设置CPSR
CPSR[5]
=
0 处理器处于ARM状态
CPSR[4:0]=mode 异常工作模式
CPSR[7:6]=11 根据实际情况
3.
返回地址--->LR_mode
4. PC --->
异常向量表入口地址
5. 保护现场(堆栈)
退出:(软件过程)
1.
SPSR_mode ---> CPSR
2.
LR_mode----> PC
3.
恢复现场(堆栈)
注意:
异常产生,退出时,ARM处理器必须处于ARM工作状态
【指令集】
1. RISC:
精简指令集系统
特点:
机器码是固定长度
指令规整简单
单周期执行,
容易流水线机制
大量寄存器
访问存储设备:
加载,存储, 交换
2. ARM指令格式
操作码
+ 目标寄存器 + OP1 +
OP2....
|---助记符
|---条件码
3. 第二操作数寻址方式
立即数寻址: mov
r1, #1
寄存器寻址: mov r0,
r1
移位寻址 : mov r0, r1,
lsl #2
寄存器间接: ldr r0,
[r1]
基址寻址 : ldr r0, [r1,
#2]
多寄存器 : ldmia r0!,
{r0-r7}
堆栈寻址 : stmfd
sp!, {r0-r12, lr}
相对寻址
: b label
【系统设计】
1. SOC概念:
system
on chip
ARM芯片 = ARM内核 + 片内存储器 +
片内各种电路
S5PC100 cortex-A8
SRAM... 各种控制器
2. 指令流水线
在编译器中,
黄色箭头指向的地址 = 执行PC = R15(PC)
ARM态:
PC值 = 当前执行指令PC +
8
(取指PC)
Thumb态:
PC值 = 当前执行指令PC +
4
(取指PC)
例如:
add
pc, pc, #4 ; PC = PC + 4
; R15 = 取指PC +
4
3. ARM920T = ARM9TDMI + I/D cache + MMU + cp14 +
cp15
【ARM指令集】
1. 条件执行及标志位
cmp: 功能相当于减法运算,不保存结果,
但影响标志位
条件码: 16条, AL
2. 分支指令
B : 相当于C语言goto
label, 不带返回地址
BL: 相当于C语言函数调用, 处理器自动保存下一条指令的返回地址 --->
LR
跳转范围 = 偏移量(机器码低24位) * 4 +
当前PC值(取指PC)
= 0x1 * 4 +
(0x8+8)
=
0x14
例子:
0x00000008 EB000001 BL
0x00000014
长跳转:
ldr pc,
=label
注意: B,BL指令是位置无关指令
3. 数据处理指令
注意: 上述指令只能对寄存器操作,不能针对存储器
ADD SUB AND EOR ORR
CMN CMP TST TEQ BIC
4. 桶型移位器
逻辑移位: lsl
lsr
算术移位: asr
循环移位: ror 左移 = 32 -
右移
5. 立即数
第二个操作数有12位:
低8位: 常数
高4位: 移位次数---> 循环右移 * 2
如何判断常数是否是合法立即数?
1.
利用编译器
例如:
0x00000004
E3A014FF MOV
R1,#0xFF000000
低8位:
0xff
高4位: 4 * 2 =
8(循环右移8位)
2. 规则
1.
找出8位常数
第一个"1"和最后一个"1",
最短距离不能超出8位
2. 常数能否循环右移偶数位
6. 装载32
bit常数
伪指令:
不会生成一一对应的机器码
ldr r0, =const
const: 符合立即数 mov r0, #const
const:
不符合立即数 ldr r0, [pc, #offset]
7. 单寄存器数据传送
ldr: 读 源寄存器在后,
目标寄存器在前
str: 写 源寄存器在前, 目标寄存器在后
前索引: LDR
r0,[r1,#8] 后索引: LDR, R0,[R1],#8
8. 块数据传送
ldm: 读 源寄存器在后,
目标寄存器在前
stm: 写 源寄存器在后, 目标寄存器在前
操作多寄存器访问:
1.
低编号的寄存器对应着低地址数据
2. 读取存储设备数据分别存放在寄存器参数列表中
3.
指针向上/下移动
9. 堆栈
1)堆栈指针移动方向:
向上: 写入数据(压栈),
指针递增
向下: 读取数据(出栈), 指针递减
2) 满堆栈:
堆栈指针指向最后入栈的有效数据的位置
空堆栈:
堆栈指针指向等待下一个入栈的数据的空位置
STMFD (压栈)
LDMFD (出栈)
注意: 运行C语言main函数之前,必须确保堆栈空间已经设置好了!!!
异常处理退出过程: 软件过程中
1.
SPSR--->CPSR
2. LR --->PC
3.
恢复现场
例如:
保护现场:
stmfd sp!, {r0-r12, lr}
恢复现场: ldmfd sp!,
{r0-r12,
pc}^
解释: 堆栈列表中, 如果有"PC"和"^"同时存在, 表示处理器自动将SPSR_mode
---> CPSR, LR_mode--->PC
10. SWP软交换
作用: 原子操作, 不可被外部因素打断.
唯一一条原子操作的访存指令
11. 软件中断
(SWI)
格式:
swi
软中断号
swi是一种用户自定义指令,当执行到swi指令时,
跳转到异常向量表0x8入口地址, 自动切换到SVC工作模式
12. PSR 传送指令
msr/mrs 设置CPSR/SPSR指令
【异常处理过程】
1、产生:(硬件过程)
1. CPSR
---> SPSR_mode
2.
设置CPSR
CPSR[5] =
0 处理器处于ARM状态
CPSR[4:0]=mode 异常工作模式
CPSR[7:6]=11 根据实际情况
3.
返回地址--->LR_mode
4. PC --->
异常向量表入口地址
5. 保护现场(堆栈)
2、退出:(软件过程)
1.
SPSR_mode ---> CPSR
2. LR_mode---->
PC
3. 恢复现场(堆栈)
注意:
异常产生,退出时,ARM处理器必须处于ARM工作状态
【异常返回地址】
前提: 在异常产生的时候内核设置 LR_mode = PC -
4
【搭建linux下ARM开发环境】
1.
将arm-none-eabi-4.2.2.tgz拷贝到ubuntu /usr/local目录,然后解压
2.
将解压之后生成arm目录,改变权限
3. 将交叉开发工具链的路径:
/usr/local/arm/4.2.2-eabi/usr/bin
添加到
/etc/environment
4. 更新工具链路径
source
/etc/environment
arm_project工程有:
start.S
uart.c serial.c
1. 将源文件编译生成
*.o文件 gcc
2.
将*.o文件链接生成*.elf文件 ld
3.
将*.elf文件转换生成*.bin文件 objcopy
4.
将*.elf文件反汇编生成*.dis文件 objdump
【系统时钟】
1. 各种不同的外围设置需要的工作频率:
CPU: 667MHZ
内存: 200MHZ
串口: 115200bps
...
...
2. 设置不同的外围设备需要的工作频率:
如何设置不同的工作频率?
12MHZ
--->
667MHZ工作频率 倍频
最高工作频率--->
各种不同的设备 分频
PLL锁相环
3. 查看用户手册
S5PC100通过3个域管理不同外围设备需要的工作频率:
D0: ARMCLK,
HCLK, PCLK
D1: HCLK,
PCLK
D2: HCLK, PCLK
4. PLL工作原理
1.
设置PLL使能信号
2. 设置PLL
LOCKTIME
3.
设置Fin和Fout之间参数
4.
设置比例关系:
D0: ARMCLK:HCLK:PCLK
=
1:4:8
D1:
MPLLCLK:PCLK = 1:4
5. 系统时钟寄存器
1.
A/M/E/HPLL_MASK: LOCKTIME =
0xe10
2. A/M/E/HPLL_CON
: PLL使能信号, 设置输出工作频率的参数
3.
CLK_SRC0~3 : 选择时钟源, CLK_SRC0 =
0x1111
4. CLK_DIV0~4
: 分频比例关系
6. 编写驱动系统时钟流程:
1.
设置LOCKTIME
2.
设置分频比例关系
3.
使能PLL信号,设置输出工作频率的参数
FOUT
= MDIV X FIN / (PDIV X 2^SDIV)
4.
选择时钟源为输出
【中断控制器】
中断处理过程流程:
1.
建立异常向量表
2. 关闭看门狗, 初始化系统时钟
3. 设置堆栈空间: IRQ_stack
system_stack
4. 开CPSR[I]=0
5.
跳转C语言main函数
6. 正常程序
7.
中断初始化
1)
设置K1(GPH0_1/EINT1)引脚功能为: 中断输入 GPH0CON[7:4] = 0010
WKUP_INT[1]
2)
设置K1(GPH0_1/EINT1)触发方式为: 下降沿触发 WKUP_INT0_7_CON[6:4]=010 Falling
edge
triggered
3)
WKUP_INT0_7_MASK &=
~(0x1<<1);
VIC0INTENABLE |=
(0x1<<1);
VIC0VECTADDR1
= (unsigned long)int_key1; //ISR函数入口地址
8.
产生IRQ信号:
保护现场
调用ISR:
((void
(*)(void))VIC0ADDRESS)(); //强制将VIC0ADDRESS转换成函数指针
LED亮
清中断:
VIC0ADDRESS
= 0;
WKUP_INT0_7_PEND =
(0x1<<1);
恢复现场
调整返回地址
【串口控制器】
1. 使用uart需要设置:
波特率
数据位
奇偶校验位
停止位
流控
2.
uart寄存器
ULCONn: 设置uart传输格式
8N1
UCONn
: 选择时钟源, 传输方式
UTRSTATn: 检测uart发送/接收状态
UTXHn/URXHn:发送/接收数据
UBRDIVn/UDIVSLOTn: 波特率
DIV_VAL
= (PCLK / (bps x 16 ) ) ?1
=
(66.75mhz/(115200*16)) - 1
= 35.2
UBRDIVn
= 35
UDIVSLOT0
= 3
3.
编写uart 程序流程:
1.
设置GPA0_0,1引脚功能: GPA0CON[3:0] = 0010 UART_0_RXD GPA0CON[7:4]
= 0010 UART_0_TXD
2.
设置系统时钟
3.
初始化uart:
波特率
数据位
奇偶校验位
停止位
流控
4、串口出现乱码原因
硬件: 串口线型号
软件: 系统时钟,波特率,串口程序
【PWM timer】
1. PWM
timer操作原理
参考网络解释
2.
timer寄存器
TCFG0: 一级预分频值
TCFG1: 二级5路分频值 Timer
Input Clock Frequency = PCLK / ( {prescaler value + 1} ) / {divider
value}
=
66.75MHZ / 250 / 8
=
33.375KHZ
=
0.03ms
TCON: 启动/停止,
装载方式
TCNTBn: 计数值
TCMPBn: 比较(控制高电平)
3.
编写驱动PWM timer程序流程:
1.
设置GPD1/pwmtout1引脚功能: GPDCON[7:4] = 0010 TOUT_1
2.
初始化系统时钟
3. 初始化pwm
timer1:
设置一级预分频值
设置二级5路分频值
设置计数值
设置比较值
启动timer1(手动装载,
自动)
【nand控制器】
查看用户手册
查看nandflash芯片手册
查看电路原理图
结合分析
编写程序
1.nandflash内部结构
1个nand器件
= 2048块
1块 = 64页
1页
= (2048 + 64)B
2048B: 代表main区,存放正常数据
64B: 代表spare区(OOB区), 存放EDC/ECC校验码
读写nandflash以 页
为单位
擦出nandflash以 块
为单位
2.
nand容量
256MB:
二进制
28bit 表示
寻址:
发 5 次地址
3.
nand寻址
以 页 为单位
A0~A11
: 列地址(页内地址)
A12~A28: 行地址(页地址)
4.
nandflash操作步骤
发命令: 复位:
0xff 读: 0x00 0x30
发地址: 连续发5次地址(发2次列地址,
发3次行地址)
检测nandflash:
读写:
5.
nand寄存器
NFCONF: 选择时钟,
设置时序时间
NFCONT: 使能nand控制器,
片选nandflash芯片
NFCMMD: 命令
NFADDR: 地址
NFDATA: 数据
NFSTAT: 检测nandflash状态
6.
编写nandflash驱动程序流程
1.
nand初始化
1.1
设置GPK0_2,3引脚功能: GPK0CON[11:8]=0011 NFCSn[0] GPK0CON[15:12]=0011
NFCSn[1]
1.2
选择时钟源, 设置时序时间
1.3
使能nand控制器
1.4
nandflash复位:
片选nandflash芯片
发出复位命令: 0xff
检测nand状态
取消nandflash片选
2.
从nandflash拷贝数据到内存中
2.1
定义目标地址ddr_start, 源地址nand_start, 数据大小len
2.2
片选nandflash芯片
2.3
循环读取数据到内存中
for(i=nand_start;
i<(nand_start+len); i+=2048) //页地址
{
发地址准备命令:
0x00
连续发5次地址(发2次列地址,
发3次行地址)
发数据准备命令: 0x30
检测nand状态
for(j=0;j<2048;j++) //页内地址
{
}
}
3.
取消nandflash片选
【A/D转换器】
1.
A/D概念:
将电信号(模拟信号)转换为数字量(数字信号,二进制)
2.
A/D转换原理
采样---> 量化 ---->
编码
3.
A/D编码位数: 12位
Vmax =
3.3V
Vmin = 0
V
4.
A/D转换公式
采样值 = (4096/3.3V) *
采样电压 (4096 = 2 ^ 12,12个输出)
5.
ADC操作方法:
选择输入通道: 采样通道[5:0] 触摸通道[9:6]
ADC初始化
6.
ADC寄存器
ADCCON: 设置输出编码位数,
检测adc转换状态, 设置预分频值, 启动读
ADCDAT0:
采样值
ADCMUX:
输入通道选择 ADCMUX = 0000 AIN0
7.
编写ADC驱动程序流程:
1.
设置系统时钟
2.
初始化ADC
选择输入通道
设置输出编码位数,
检测adc转换状态, 设置预分频值, 启动读
读取采样值
【I2C控制器】
1. I2C寄存器
I2CCONn: 使能ACK信号,
选择时钟源, 使能传输方式
I2CSTATn: 设置主机发送/接收模式,
启动/停止, 使能输出, ACK信号能否被接收
I2CDSn: 发送/接收数据
2.
编写I2C驱动程序流程:(根据LM75时序图)
设置GPD3,4引脚功能: GPDCON[15:12]
= 0010 I2C0_SDA GPDCON[19:16] = 0010
I2C0_SCL
第一阶段:
1.
装载LM75地址: 0x90
2.
设置主机发送模式, 使能ACK信号能被接收, 使能数据输出, 设置开始条件
3.
使能ACK信号, 选择时钟源, 使能传输方式
4.
等待
5.
配置LM75模式: 温度模式 0x0
6.
使能ACK信号, 选择时钟源, 使能传输方式
第二阶段:
1.
装载LM75地址: 0x90
2.
设置主机接收模式, 使能ACK信号能被接收, 使能数据输出, 设置开始条件
3.
使能ACK信号, 选择时钟源, 使能传输方式
4.
等待
5.
使能ACK信号能被接收, 接收第一个字节数据(温度整数部分)
6.
关闭ACK信号被接收 , 接收第二个字节数据(温度小数部分)
7.
设置停止条件
@成鹏致远 | 2013-08-30