*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
```