<ARM Embedded Architecture and Interface Technology>-Final Review

*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

```






 
   

转载于:https://www.cnblogs.com/HannibalWangLecter/p/7007143.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值