-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.*linux


-----c++


# CH1 嵌入式系統基礎知識git


### 1.1.2 嵌入式定義(P1)github


- (IEEE)控制、監控或者輔助操做機器、裝置、工廠等大規模系統的設備。編程

- (General)嵌入式系統是指以 應用爲中心,以計算機技術爲基礎,軟硬件可剪裁,適應 應用系統 對功能、可靠性、成本、體積、功耗 嚴格要求的 專用計算機系統。安全


### 1.2.1 嵌入式系統的硬件組成服務器


- 嵌入式處理器 (CPU,核心)異步

- 外圍設備ide

  - 存儲器函數

  - 通訊設備

  - 顯示設備


## 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、一、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燈的是第三、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

```

相關文章
相關標籤/搜索