*This document is edited by Dr.Hannibal Lecter (undergraduate student in School of Mathematics & Computer Science,Anhui Normal University) and is licensed under GNU General Public License Version 3 or later.*
-----
# CH1 嵌入式系统基础知识
### 1.1.2 嵌入式定义(P1)
- (IEEE)控制、监控或者辅助操作机器、装置、工厂等大规模系统的设备。
- (General)嵌入式系统是指以 应用为中心,以计算机技术为基础,软硬件可剪裁,适应 应用系统 对功能、可靠性、成本、体积、功耗 严格要求的 专用计算机系统。
### 1.2.1 嵌入式系统的硬件组成
- 嵌入式处理器 (CPU,核心)
- 外围设备
- 存储器
- 通信设备
- 显示设备
## 1.4.1 交叉编译(P9)
在一个平台上生成可以 在另一个平台上执行的代码.
如同翻译:把相同程序的代码翻译成不同的CPU对应语言.
不同CPU需要不同的编译器
# CH2 ARM技术概述
### 2.1.2 ARM处理器特点(P15)
- 体积小、低功耗、低成本、高性能
- 支持Thumb(16位)/ARM(32位)双指令集,能很好地兼容8位/16位器件
- 大量使用寄存器,执行速度更快
- 大多数 数据操作 都在寄存器内完成
- 地址方式灵活简单,执行效率高
- 指令长度固定
### 2.2.8 Cortex处理器系列(P21)
Cortex-A:面向应用,能运行Linux,Windows CE,Symbian系统
Cortex-R:实时控制应用,汽车电子等
Cortex-M:费用低,高性能,向上兼容
## 2.3 ARM体系结构主要特征(P23)
- 大量寄存器,他们都可以用于多种用途
- Load/Store 体系结构
- 每条指令都 条件执行
- 多寄存器的Load/Store 指令
- 能够在 单时钟周期 执行的 单条指令内 完成一项普通的 移位操作
- 通过 协处理器指令集 来扩展 ARM指令集,在编程模式中 增加了 新的寄存器 和数据类型
- 如果把Thumb指令集也作为ARM体系结构的一部分,那么还可以加上在Thumb体系结构中以高密度16位 压缩形式表示指令集
#### 2.4.2.6 中断控制器(P26)
ARM内核只提供了快速中断(FIQ)和标准中断(IRQ)两个中断向量(区别:高优先级、低优先级),但各个半导体厂家在设计芯片时会加入自己定义的中断控制器,并且可以选择上升沿、下降沿、高电平、低电平 4种 中断方式
### 2.6.1 ARM的基本数据类型(P28)
- Byte:字节,8位
- HalfWord:半字,16位(必须与2字节边界对齐)
- Word:字,32位(字必须与4字节边界对齐)
- DoubleWord(Cortex-A支持):双字,64位(字必须与8字节边界对齐)
## 2.7 Cortex-A8 内核工作模式(P30)
1. 用户模式usr: ARM处理器正常的程序执行状态
2. 快速中断模式fiq: 高速数据传输/通道处理
3. 外部中断模式irq: 通用中断处理
4. 管理模式/特权模式svc : 操作系统使用的 保护模式
5. 数据访问种植模式abt:当数据或指令预取终止时进去该模式,可用于虚拟存储及存储保护
6. 系统模式sys:运行具有特权的OS任务
7. 未定义指令中止模式und:当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真
8. 监控模式mon:安全/非安全模式 转换
- 特权模式:2~8
- 异常模式:2,3,4,5,7
### 2.8.2 存储管理单元 (MMU)(K,Cho)(P33)
关键服务:**使各个任务作为各自独立的程序在其私有存储空间中运行**
在带有MMU的OS控制下,运行的任务无需知道其它与之无关的任务的存储情况,简化了各个任务的设计.
提供了一些资源可以允许使用虚拟存储器
### 2.9.2 3级流水线的ARM组织(K,X)(P35)
1. 取指令:从寄存器装载一条指令
2. 译码:识别被执行的指令,为下一个周期准备数据通路的控制信号
3. 执行:处理指令并将结果写回寄存器
### 2.9.3 影响流水线性能的因素(P36)
- 互锁:一条指令的结果被用锁下一条指令的操作数
- 跳转指令:后续指令的取指令步骤收到跳转目标计算的影响
### 2.11 程序状态寄存器(CPSR)(P39)
可以在任何处理器模式下被访问
### 包含内容
- ALU状态标识的备份
- 当前的处理器模式
- 中断使能的标识
- 设置处理器的状态(ARM/Thumb状态)
![CPSR](./CPSR.PNG)
# CH3 ARM 指令的集合
## 3.1 数据操作指令(P44)
```assembly
MOV R0,R0
MOV R0,R0,LSL#3 ;R0=R0*8
AND R0,R0,#3 ;按位逻辑与:保留第0,1位,其余舍弃
AND R2,R1,R3 ;R2=R1&R3
EOR R1,R1,#0xF ;按位异或:将R1的4位取反
SUB R0,R2,R3,LSL#1 ;R0=R2-(R3<<1)
ADD R0,R2,R3,LSL#1 ;R0=R2+(R3<<1)
CMP ;(比较):EQ相等,NE不相等,GE有符号大于等于,LE小于等于,GT大于,LT小于
ORR R0,R0,#0xF ;逻辑或
BIC R0,R0,#0x1011 ;位清零:将0、1、3清零,其余不变
```
## Load/Store 指令(P54)
```assembly
LDR R3,=0x20009000
LDR R0,[R3] ;从内存将一个32位的字读取到制定寄存器
STR R2,[R3] ;32位字写入到制定内存单元
```
### 3.1.4 跳转指令 (star)(课后习题4)(P59)(??)
B:分支指令 pc<-label
BL:带返回的分支指令 Lr<-PC-4 ;PC<-label
BX:带状态切换的分支指令 PC<-Rm(某一个寄存器) 切换处理器状态(CPSR的T位)(32位转到16位,可以把目标地址的代码当作32/16位来解释)(P61)
### 3.1.5 状态操作指令(P62)
#### 3.1.5.3 程序状态寄存器指令的应用(P63)
##### 开中断(IRQ)
```assembly
MRS R0,CPSR
BIC R0,R0,0x80 ;第7位清零
MSR CPSR,R0
MOV PC,LR
```
##### 关中断(IRQ)
```assembly
MSR R0,CPSR
ORR R0,R0,0x80 ;第7位置逻辑或
MSR CPSR,R0
MOV PC,LR
```
##### 堆栈指令初始化(??)
```assembly
MOV R0,LR ;保存返回地址
MSR CPSR,0xD3 ;设置管理模式堆栈:11010011 SWV
LDR SP,STACKSVC;设置中断模式堆栈 11010010 IRQ
MSR CPSR,0xD2
LDR SP,STACKSVC
```
### 3.1.7 异常产生指令(P65)
| | | |
| ---- | ------ | ---------------------------------------- |
| SWI | 软中断指令 | 产生中断,用户模式 -> 管理模式 |
| BKPT | 断点中断指令 | 产生一个预取异常,常被用来设置软件断点,在调试程序时十分有用.系统中存在调试硬件时,该指令被忽略.处理器产生软件中断点 |
## 3.2 ARM指令的寻址方式(P67)
| 寻址方式 | 用例 | 说明 |
| :-----: | :--------------------------------------: | :--------------------------------: |
| 寄存器寻址 | MOV R0,R0 | |
| 立即寻址 | MOV R0,#FF00 | |
| 寄存器移位寻址 | MOV R0,R0,LSL#3 | |
| 寄存器间接寻址 | LDR R0,[R2] ;STR R0,[R2] | |
| 基址变址寻址 | LDR R2,[R2,#0x0C] | 读取R3+0xOC的内容,放入R2 |
| 多寄存器寻址 | LDMIA R1!,{R2-R7}; STMIA R0!,{R2-R7} | 将R1中的相应数据读到R2~R7中,R1自动加4 ; (??) |
| 堆栈寻址 | | |
| 相对寻址 | B; BL | |
| 块拷贝寻址 | STMIA R0!,{R1-R7}; STMIB R0!,{R1-R7} | 将R1~R7中的数保存到R0指向的内存单元中,R0在保持 之后增加 |
##### 课后习题 如果R0>0x50,则将
# CH4 GNU汇编伪指令集(Expr)
### 4.3.3 过程调用该标准ATPCS规范(K,F,X)(P83)
1. 子程序通过寄存器R0~R3来传递参数,如果参数多于4个,多出部分用堆栈来传递,R0~R3不用恢复
2. R4~R11:保存局部变量,须将用来的寄存器保存,子程序退出时要恢复这些寄存器
3. R12:保存SP(在函数返回时使用该寄存器出栈)
4. R13:堆栈指针(SP)
5. R14:连接寄存器(LR):保存子程序的返回地址
6. R15-用户程序计数器(PC),不用作其它用途
7. 函数只有1个返回值,一般保存在R0中
### 4.4.1 GNU内联汇编(P85)
#### 概念
在 C/C++ 代码中嵌入的汇编代码
#### 作用
- 提高效率
- 实现C语言无法实现的部分
#### 使用情景(P85)
- 饱和算数运算(当*运算*结果大于一个上限或小于一个下限时,结果就等于上限或是下限)
- 对 协处理器 操作
- 在C中完成对CPSR的操作
# CH5 ARM集成开发环境的搭建(Expr)
## 配置IP
```
set ipaddr 192.168.1.1
set serverip 192.168.1.32
save !保存
md 20008000 !查看第20008000个字节
```
# CH6 GPIO编程(Expr)
(See Below)
# CH7 ARM系统时钟及编程(Expr)
## 时钟产生过程
外部时钟 -> 各级锁相环 输出 高频时钟 -> 提供给各种设备
## 设置PLL基本配置步骤(K,X)
### 打开PLL
```
PLL_CON[31]=1
wait_lock_time
PLL_SEL=1 !??
```
### 关闭PLL
```
PLL_SEC=0 //取消...(锁相环不输出)
PLL_SEL[31]=0
```
# CH8 ARM异常处理及编程
### 8.1.1 中断 的 概念(K,F)(P111)
CPU在正常执行程序的过程中,遇到外部/内部的紧急事件需要处理,暂时中断当前程序的执行,而转去为事件服务,待服务完毕,再返回到暂停出继续执行原来的程序.
## 8.2 ARM体系 异常种类(P113)
| 异常类型 | 处理器模式 |
| :-------------------------: | :-----: |
| 复位异常 | 特权模式 |
| 未定义指令异常 | 未定义指令中止 |
| 软中断异常SWI | 特权 |
| 预取异常(处理器试图去取一条被标记为预取无效的指令时) | 指令访问中止 |
| 数据异常 | 数据访问中止 |
| 外部中断请求 | 外部中断请求 |
| 内部中断请求 | 快速中断请求 |
## 8.3 ARM异常优先级(P117)
复位异常 > 数据异常 > 快速中断请求 > 外部中断请求 > 预取异常 > 软中断 > 未定义指令
`用户模式` 和`系统模式`是 仅有的不通过异常进入的 2种模式.which means:要进去这2种模式,必须通过 编程改变 `CPSR` .
### 8.5.1 中断响应的步骤(P118)
1. 保护断点:保存下一条即将执行的指令地址(把这个地址入栈)
2. 寻找中断入口:根据不同的中断源所产生的中断,查找不同的入口地址
3. 执行中断处理程序
4. 中断返回
## 8.5.3 从异常处理程序中返回(JD,K,X)(P119)
1. 恢复通用寄存器
2. 恢复`CPSR`
3. 恢复PC指针
2,3 两条对应指令(??)
```assembly
MOVS PC,LR ;MOVS总是会影响CPSR, 包括N,Z,C标志位,执行MOVS pc, lr时,CPSR会被SPSR覆盖(内核态,USER和SYSTEM模式下没有SPSR)
SUBS PC,LR,#4
LDMFD SP!,{PC}^ ; ^:指令在执行时,同时完成从SPSR到CPSR的赋值,达到恢复状态寄存器的目的
```
### 8.5.2 对异常响应的步骤(K,F)(P118)(??)
1. 将下一条指令的地址相应LR
2. `CPSR`复制到 `SPSR`
3. 根据异常类型,设置CPSR模式位
4. 强制PC从异常向量地址下一条指令执行,跳到相应异常处理程序
### 8.5.3 从异常处理程序中返回(JD?)(P119)
1. 通用寄存器的恢复 LR->PC
2. 状态寄存器的恢复 SPSR->CPSR
3. PC指针的恢复
# CH9 串行通信接口
### 9.1.2 异步串行方式特点(K,F)(P134)
1. 以`字符`为单位 传送信息
2. 相邻 2字符 间 间隔任意长
3. 1个字符中bit位长度有效,需要接受/发送时钟 只要相近即可
4. 字符间异步,字符内部各位同步
### 9.1.3 异步串行方式的数据格式(P134)
...
### 9.2.1 S5PC串口控制器特点(K,F,X)(P137)(??)
- 每个UART通道包含`2个` `64字节` 的收发FIFO
1. `4组`收发通道,同时支持`中断模式`,`DMA操作` (??)
2. 通道`0,1,2,带红外3通道`,都支持`64字节FIFO`
3. 通道1,3 支持 高速操作模式
4. 支持 握手模式 的发送接收
# CH10 PWM定时器(Expr)
## 10.1.1 概述(工作过程)(P143)
### 在S5PC100中(K,X,F)(??)
一共有`5个32位`的定时器,这些定时器可发送 中断信号 给ARM子系统.
定时器`0,1,2`包含了`脉冲宽度调制(PWM)`,并可驱动其扩展的I/O.
PWM对`定时器0` 有可选的`dead-zone`功能.
### 10.1.3 PWM定时器的寄存器(P148)
#### 1.定时器配置寄存器0(TFCG0)(cloz)
定时器输入时钟频率=PCLK/{prescaler value +1}/{divider value} <-> (预分频器+1)/分频器
加1是因为防止除数为0
### 如何启动定时器(K,X)
#### 开中断
1. 初始化引脚:分频器,选择器,GPD0CON
2. 初始化 TCNTB(定时器n计数缓冲寄存器),TCMPB(定时器比较缓冲寄存器) (P151)
3. 停止 auto-reload; 使能 manual-update
4. 使能 auto-reload; 停止 manual-update
5. 启动定时器
### 10.2.3 看门狗软件程序设计流程(K,X)(P156)
1. 设置看门狗 中断操作,包括全局中断和看门狗中断的使能及看门狗中断向量的定义
2. 对看门狗控制寄存器(WTCON)设置
3. WTDAT,WTCNT(计数)设置
4. 启动看门狗定时器
```C++
//1. 寄存器定义
typedef struct {
unsigned int WTCON;
unsigned int WTDAT;
unsigned int WTCNT;
unsigned int WTCLRINT;
} wdt;
#DEFINE WDT(*(VOLATILE WDT *) 0xEA200000)
//2. 寄存器初始化
void wdt_init() { //(??)
wdt.WTCNT=0x277E;//指定的超时时间(重载数值寄存器)
wdt.WTCON=(1<<0) |(1<<2) |(3<<3) |(1<<5) |(255<<8);//打开看门狗产生复位信号, 时钟分频值为128, 时钟使能, 预分频值255
//计算方法:66MHz(见P166页:例如PCLK为66MHz) 预分频255 得到255824Hz,再进行128分频得到f=2022Hz ; data* (1/f)=5 延时5s得到data=0x277E
}
//3. 主程序
#include "S5PC100.h"
int main() {
int i;
GPG3.GPG3CON=(~ (0xF<<4) & GPG3.GPG3CON | (0x1<<4);//第1位引脚输出
GPG3.GPG3DAT=0x2;//00000010:第1个引脚输出高电平
wdt_init();
while(1) {
delay(1);
}
}
```
----
*This Expr part is integrated by WH via Feng's car*
# Expr 1 开发环境的搭建
### 已知Uboot启动后,printf函数存放在地址为0x2fd17b18处.编译调试下面程序hello.c,并加载到开发板0x20008000处执行
```c++
__start(void){
int(*my_printf)(char*,...)=(int(*)(char*,...))0x2fd18b18;//定义函数指针
my_printf("hello world!\n");
while(1);
}
```
```
编写源程序,保存为hello.c
arm-linux-gcc -c source -o destination //编译成目标文件 nostdlib:不使用库函数
arm-linux-ld -Ttext addr -o destination sources //Ttext指定程序的链接地址(运行地址)
arm-linux-objcopy -O binary -S source destination //生成纯二进制文件(.bin)
将hello.bin复制到tftp服务器目录下,启动tftp服务器
连接好开发板,启动uboot
tftp 20008000 hello.bin
go 20008000
```
# Expr 2 ARM指令
R4~R7在计算时也可以用,用堆栈的情况仅限 参数传递
##### Ex3.将sy2程序下载到0x20007000处,编写汇编程序sy3.S,调用sy2,并能正确返回
```assembly
.text
.global _start
_start:
LDR R0,=0x20007000
MOV PC,R0
.END
```
##### Ex4.开中断,禁止快速中断
```assembly
MRS R0,CPSR
BIC R0,R0,#0x80
ORR R0,R0,#0x40
MSR CPSR,R0
MOV PC,LR
```
##### Ex5.读取内存地址为0x20008000处的值入R0,修改R0低8位为0x1F,其它位保持不变.并将R0的值存入内存单元0x20009000处.
```assembly
LDR R1,=0x20008000
LDR R0,[R1]
ORR R0,,R0,#0x1F
LDR R2,=0x20009000
STR R0,[R2]
MOV PC,LR
.END
```
##### Ex7. 1+(1+2)+(1+2+3)+...+(1+2+...+20),结果存入0x20009000.要编写子程序addn计算1+2+...+m.(函数之间必须用R0传值) (??)
```assembly
.text
.global _start
start:
MOV R4,#20;最大加数
MOV R5,#1;初始加数
MOV R6,#0;初始和
PUSH {LR}
LOOP2:
CMP R5,R4 ;一组和的最大元素是否超过最大的加数
BGT SAVE ;超过说明计算完成,转向存取步骤
MOV R0,R5 ;
BL FN
ADD R6,R6,R0
ADD R5,R5,#1;R5++
B LOOP2
FN:
MOV R1,#1
MOV R2,#0
LOOP:
CMP R1,R0
BGT FNend
ADD R2,R2,R1
ADD R1,R1,#1
B LOOP
FNend:
MOV R0,R2
MOV PC,LR
SAVE:
LDR R4,=0x20009000
STR R6,[R4]
POP {LR}
MOV PC,LR
```
# Expr 3 GNU汇编(1)
##### Ex1.返回n!.调用jc,计算5!,结果存入0x20009000,程序运行结束后能正确返回到uboot.
```assembly
;汇编调用C语言
.global _start
_start:
MOV R0,#5 ;MaxNum
PUSH {LR} ;执行到的主程序位置入栈
BL JC ;调用C语言求和
LDR R2,=0x20009000 ;存储内存地址
STR R0,[R2] ;计算结果存入R2所指内存单元
POP {LR} ;执行到的主程序内存地址出栈
MOV PC,LR ;返回主程序
.END
~
int jc(int n) {
int i,s=0;
for(i=0; i<=n; i++)
s*=i;
return s;
}
```
##### Ex2.c程序改写成等价的汇编程序:for(;i<=100;i++){s+=i}; (*int*)0x40009000=s;
```assembly
.GLOBAL _start
_start:
MOV R0,#0;存储累加和
MOV R1,#1;初始加数
PUSH {LR};主程序
MOV LR,PC;PC记录当前代码执行的地址. LR记录函数调用位置下一条指令的地址
LOOP:
MOV R2,#100 ;MaxNum
CMP R1,R2 ;最大的加数是否超过100
BGT STARTEND ;大于等于 代表运算结束,进入存储步骤
ADD R0,R0,R1 ;累加和+=加数
ADD R1,R1,#1 ;加数++
B LOOP
STARTEND:
LDR R3,=0x40009000
STR R0,[R3]
POP {LR}
MOV PC,LR ;返回主程序
.END
```
##### Ex3.printf函数位于内存地址0x2fd17b18,计算1+3+5_...+99,调用printf函数输出结果,输出格式为1+3+5+...+99=xxx.
```assembly
.global _start
_start:
PUSH {LR}
LDR R0,=STR
LDR R3,=0x2FD17B18
MOV LR,PC
MOV R1,#0
MOV R2,#1
LOOP:
CMP R2,#99
BGT LOOPEND
ADD R1,R1,R2
ADD R2,R2,#2
B LOOP
LOOPEND:
MOV PC,R3
POP {LR}
MOV PC,LR
str:
.string "1+3+5+....+99=%d"
.end
```
##### Ex4.把程序补充完整,以便得到a+b的值,该值最后存于0x40009000.
```assembly
void _start(void){
int a=30;
int b=4-;
int sum=0;
__asm__?__volatile__(
"MOV R1,%2\n"
"MOV R2,%2\n"
"ADD %0,R2,R2\N"
:"=R"(sum)
:"r"(a),"r"(b)
);
*((int *)0x40009000)=sum;
}
```
# Expr 4 GNU汇编(2)
##### Ex1.用C编写函数,返回a+b+c+d+e+f. 汇编语言调用sixadd,计算,并将计算结果保存到内存0x20009000处,sixadd函数的参数值由汇编程序传入.
```assembly
.global _start
_start:
PUSH {LR}
MOV R0,#1
MOV R1,#2
MOV R2,#3
MOV R3,#4
MOV R4,#5
MOV R5,#6
PUSH {R4-R5};超过4个寄存器的参变量必须入栈
MOV LR,PC
BL SIXADD
PUSH {R6}
LDR R6,=0x20009000
STR R0,[R6];保存 计算结果R0
POP {R6}
POP {R4-R5};释放栈中元素
POP {LR}
MOV PC,LR
.END
///sixadd.c
int six add(int a,int b,int c,int d,int e,int f) {
int s=0;
s=a+b+c+d+e+f;
return s;
}
;运行结果:0x15
```
##### Ex2.汇编程序 int addn(int n)返回1+2+...+n. 编写C 调用汇编中的addn,计算addn(10),并将结果保存到0x40008000处.
```assembly
.global _addn
_addn:
MOV R1,#1
MOV R2,#0 ;累加和
PUSH {LR}
LOOP:
CMP R1,R0
BGT END
ADD R2,R2,R1 ;sum+=Ri
ADD R1,R1,#1
B LOOP
END:
MOV R0,R2
POP {LR}
MOV PC,LR
.END
//Sy4_2c.c
int addn(int n);
void _start(void) {
int s;
s=_addn(10);
*(int*)0x40009000=s;??为什么格式
}
;运行结果:0x37
```
# Expr 5 GPIO编程(1)
##### Uboot修改内存命令mw 实现 点亮实验板的一个LED灯
```
MW 0xe0200060 0x11000 !初始化
MW 0xe200064 0x18 !点亮2个灯
```
##### 轮流点亮实验板上4个LED灯(K,X)
```c++
#define gpc2con *(volatile unsigned *)0xe0200060
#define gpc0dat *(volatile unsigned *)0xe0200064
void led_init (void);
void led_on(char n);
void delay(int time);
void _start(void) {
int i = 1;
led_init();
while(1) {
i=i%3;
if(i==0)
led_on(0);
if(i==1)
led_on(0x8);
if(i==2)
led_on(0x18);
delay(1000000);
i++;
}
}
void led_init(void) {
gpc2con = gpc2con & (~0xFFFFF)|0x11000;//负责LED灯的是第3、4位,设置为输出(??)
}
void led_on(char n) {
gpc0dat = gpc0dat &(~0x18)|(n&0x18);//(??)
}
void delay(int time) {
int i;
for (i=0; i<time; i++);
}
```
##### 用GPIO的控制寄存器4个分别控制四个灯从7个引脚**,**4个灯从GPG3CON**[**0**]~**GPG3CON**[**3**]**途中占用4个引脚. pin4**~**6 **->**input.8种状态.要求:P6~P4(??)(K,F)
| P6 | P5 | P4 | |
| :--: | :--: | :--: | :---: |
| 0 | 0 | 1 | 前2灯 |
| 0 | 1 | 0 | 后2灯 |
| 0 | 1 | 1 | 前后右侧灯 |
| 1 | 0 | 0 | 前后左侧灯 |
```C++
#include "S5PC100.h"
(())
//user-mode为用户打开的模式
int main(void) {
GPG3.GPG3CON=(~0xF) &GPG3.GPG3CON| (0x1<<4);//冯老师给这4句打了问号
GPG3.GPG3CON=(~0xF<<1) &GPG3.GPG3CON| (0x1<<4);
GPG3.GPG3CON=(~0xF<<2) &GPG3.GPG3CON| (0x1<<4);
GPG3.GPG3CON=(~0xF<<3) &GPG3.GPG3CON| (0x1<<4);
//将GPG3CON[0]~[3]设为输出
int i,tu;
while(1) {
user_mode=read_mode();//在后面,用GPG3CON[4]~[6]
swtich(user_mode) {
case 1: {
GPG3.GPG3DAT=0x3;
for (i=0; i<=1000000; i++) {
GPG3.GPG3DAT=0x0;
for (i=0; i<=1000000; i++)
}
}
case 2: {
GPG3.GPG3DAT=0x0C;
for (i=0; i<=1000000; i++) {
GPG3.GPG3DAT=0x0;
for (i=0; i<=1000000; i++)
}
}
case 3: {
GPG3.GPG3DAT=0x0A;
for (i=0; i<=1000000; i++) {
GPG3.GPG3DAT=0x0;
for (i=0; i<=1000000; i++)
}
}
case 4: {
GPG3.GPG3DAT=0x5;//00000101:2个灯亮
for (i=0; i<=1000000; i++) {
GPG3.GPG3DAT=0x0;
}
for (i=0; i<=1000000; i++)
}
case 5: {
GPG3.GPG3DAT=0x0;
}
}
}
}
```
# Expr 9 片外设备中断
##### 按键中断实验
按"向上方向键"点亮第一个LED灯,同时输出"up-key pressed!",....,主程序调用相关初始化参数
```C++
//X解法
//LED灯
#define gpc0con *(volatile unsigned *(0xe0200060
/*.....*/
int on;
int (*printf)(char*,...)(int(*)(char*,...))0x2fd18b18
void led_init(void);
void led_on(char on); //根据参数决定LED灯的 状态
void key_init(void); //根据电路图初始化按键
void out_int_init(void);//初始化片外设备中断控制器
void int_init(void); //初始化中断控制器,并 使能CPU IRQ中断
void vic0_init(void);
void clear_init(void);
void cpu_int_on(void);
void __attribute__((interrupt)) isr(void);
void start(void) {
led_init();
key_init();
out_int_init();
vic0_init();
cpu_int_on();
}
void out_int_init(void) {
GPH0CON=GPH0CON|0xFFFF;//设置为外部中断
}
void led_init(void) {
GPC0CON=GPC0CON & (0xFF000)|0x11000;//???
GPJ1CON=GPJ1CON & (0xF<<20)|0x1<<20;
GPD0CON=GPD0CON & (0xF<<8)|0x1<<8;
}
void led_on(int on) {
GPD0DAT=GPD0DAT & (0x1<<2)|( (on & 0x1) <<2 );//???
GPJ1DAT=GPJ1DAT & (0x1<<5)|( (on & 0x2) >>1 );
GPC0DAT=GPC0DAT & (0x3<<3)|( (on & 0xC) >>2 );
}
void vic0_init(void) {
VIC0INTSELECT=VIC0INTSELECT & ~(0xF); //0~3
VICINTENABLE =VIC0INTENABLE |(0xF);
VIC0VECTADDR0(volatile unsigned int) isr;//??
VIC0VECTADDR1(volatile unsigned int) isr;
VIC0VECTADDR2(volatile unsigned int) isr;
VIC0VECTADDR3(volatile unsigned int) isr;
__asm__ __volatile__(
"MRS R0,CPSR \n"
"BIC R0,R0,#0x80 \n"
"MSR CPSR,R0 \n"
);
}
void clear_init(void) {
VIC0ADDRESS = 1;
VIC1ADDRESS = 1;
VIC2ADDRESS = 1;
VIC3ADDRESS = 1;
ext_int_0_pend = ext_int_0_pend;//??
}
void __attribute__((interrupt)) isr(void) {
on=ext_int_0_pend;//判断哪1个按键按下
if(on==0x1) {
printf("up_key pressed\n");
};
if(on==0x2) {
printf("down_key pressed\n");
};
if(on==0x4) {
printf("right_key pressed\n");
};
if(on==0x8) {
printf("left_key pressed\n");
};
led_on(on);
clear_init();
}
```
```assembly
//Assembly Code
.global _start , cpu_int_on
cpu_int_on:
MSR R0,CPSR
BIC R0,R0,#0x80
MSR CPSR,R0
MOV PC,LR
.END
```
# Expr 10 PWM实验
##### Ex1.编程实现输出占空比为2**:**1**,**波形周期为9ms的PWM波形
```c++
/*
流程
(1)定义GPD_1为输出:保持引脚状态不变
(2)定义GPD_1的输出频率:让其为输出
(3)进入死循环
*/
//1.寄存器定义
typedef struct {
unsigned int GPDCON;
unsigned int GPDDAT;
unsigned int GPDPULL;//下面4个定义偏置电阻
unsigned int GPDDRV;
unsigned int GPDPDNCON;
unsigned int GPDNPULL;
} GPD;
#DEFINE GPD(*(VOLATILE GPD *) 0xE030080)
typedef struct {
unsigned int TCFG0;
unsigned int TCFG1;
unsigned int TCON;
} timer-type;
#define TIMER1(*(volatile timer1_type *) 0xEA000018)
//...略
//2.定时器的初始化
void PWM_init() {
GPD.GPDCON=GPD.GPDCON &(~0Xe0) | (0X2<<4); //设置IO功能为TOUT1输出
TIMER.TCFG0=(TIMER.TCFG0 &(~0XFF)+ 0xFF; //配置预分频值为255
TIMER.TCFG1=(TIMER.TCFG1 &(~0XF0)+ 4<<4; //分频值为1/16分频
TIMER1.TCNTB1=91022; //设置缓冲器的值
TIMER1.TCNPB1=9102213; //比较缓冲器的值
TIMER1.TCON=0x0E<<8; //(针对Timer3:手动更新,使缓冲器的值到计数器里面,双缓冲机制(P150)
TIMER1.TCON=0x0D<<8; //清除手动更新位,并启动定时器
}
//3.主程序
#include "S5PC100.h"
int main(void) {
PWM_init();
while(1);
}
```
# Expr 11 A/D 转换器
##### 编程实现利用S5PC100ND控制器的A/D通道采集**,**一个范围在0**~**3.3V的电压(example,K,F)(P167)(WJP觉得会考)
```C++
//1.相关寄存器定义
#DEFINE ADCCON(*(volatile unsigned) 0x58000000)
typedef struct {
unsigned int ADCCON;
unsigned int ADCTSC;
unsigned int ADCDLY;
unsigned int ADCDAT0;
unsigned int ADCDAT1;
unsigned int ADCUPDN;
unsigned int ADCLRINT;
unsigned int ADCMUX;
unsigned int ADCPNDCLR;
} ADC;
#DEFINE ADC (* (VOLATILE ADC * ) 0xF3000024)
//2.相关测试程序
#include "S5PC100.h"
#include "uart.h"
unsigned int table[10]= {'0','1','2','3','4','5','6','7','8','9'};//数字 -> 字符 (查表)
int main(void) {
unsigned int temp=0;//读取数值
unsigned int a;
unsigned char bit4,bit3,bit2,bit1;//用作存储数字
unsigned int count;
uart0_init();//异步串口通信初始化
ADC.ADCMUX=0;//多路复用为 0
ADC.ADCCON=(1<<16 |1<<14 |0xFF<<6 |0<<2 | 1<<1);//控制模式:12bit输出,允许预分频,多路控制,正常模式,允许sort-by-read
temp=ADC.ADCDAT0 & 0xFFF;//0000111111111111:低电平等待中断,正常A/D转换顺序,无操作模式
while(1) {
while(!(ADC.ADCCON & 0x8000));//见P165:15位为1,其余都是0:A/D转换结束
temp=ADC.ADCDAT0 & 0xFFF;//0000111111111111还处于待机状态
temp=3.3*1000*temp/ 0xFFF ;//对读出数据进行数值转换:char型,除出来以后自动取整
bit4=temp/1000;
putc(table[bit4]);//打印出第一位数字,后面依次打印
bit3=(temp%1000)/100;
putc(table[bit3]);
bit2=((temp%1000)%100)/10;
putc(table[bit2]);
bit1=temp%10;
putc(table[bit1]);
putc("mv\n");
for(count=1000000;count!=0;count--);//延时
}
return 0;
}
```
# CH 14 存储器接口
##### 14.3.13 NAND读操作 流程图
```flow
st=>start: 开始
e=>end: 页大小读完成
write=>operation: 写 0x00
write_address=>operation: 写地址(按照表 写5次)
write_30H=>operation: 写30H
read_data=>operation: 读数据(向好事者"要")
ECC_prod=>operation: ECC产生链
ECC_check=>condition: 校验ECC
clear_error=>operation: 清除错误
st->write
write->write_address->write_30H
write_30H->read_data
read_data->ECC_prod->ECC_check
ECC_check(no,left)->clear_error
ECC_check(yes)->e
```
##### 14.3.2 NAND擦除操作 流程图
```flow
st=>start: 开始
e=>end: 擦除完成
write=>operation: 写 60H
write_address=>operation: 写入块地址
write1=>operation: 写D0H
read_reg=>operation: 读状态寄存器
judge1=>condition: I/O 6=1? or R/B=1?
judge2=>condition: I/O 0=0
error=>operation: 错误
clear=>operation: 清除错误
st->write->write_address->write1->read_reg->judge1
judge1(no,right)->judge1
judge1(yes)->judge2
judge2(no,right)->error
judge2(yes)->e
```
##### 14.3.3 NAND写操作 流程图
```flow
st=>start: 开始
write=>operation: 写80H
write_address=>operation: 写地址
write_data=>operation: 写数据
write1=>operation: 写10H
read_reg=>operation: 读状态寄存器
judge1=>condition: I/O 6=1? or R/B=1?
judge2=>condition: I/O 0=0
error=>operation: 编程错误
e=>end: 编程完成
st->write->write_address->write_data->write1->read_reg->judge1
judge1(no,right)->judge1
judge1(yes)->judge2
judge2(no,right)->error
judge2(yes)->e
```