[實驗一 新手上路] 初學者編寫的第一個程序通常是控制XF引腳的變化,然後用示波器測量XF腳波形或觀察與相接的LED。這個程序也常常用來測度一下DSP能否正常工作。
實驗1.1 最簡單的程序:控制XF引腳週期性變化
實驗目的:通過簡單的程序瞭解DSP程序的結構,熟悉CCS開發環境。
*************************************************************
*最簡單的程序:TestXF1.asm
*循環對XF位置1和清0,用示波器可以在XF腳檢測到電平高低週期性變化
*常用於檢測DSP是否工作。
*************************************************************
.mmregs ;預定義的寄存器
.def CodeStart ;定義程序入口標記
.text ;程序區
CodeStart: ;程序入口
SSBX XF ;XF置1
RPT #999 ;重複執行1000次空指令產生延時
NOP
RSBX XF ;XF清0
RPT #999 ;重複執行1000次空指令產生延時
NOP
B CodeStart ;跳轉到程序開頭循環執行
.end
NOP指令執行時間為一個時鐘週期,設DSP工作頻率是50MHz,可以估算出XF引腳電平的變化頻率約為:50M/2000=25kHz
在沒有示波器的情況下,就要將程序1.1稍作改進,增加延時,用一個延時子程序將XF腳電平變化頻率降到肉眼可分辨的程度,就可以用LED來顯示電平的變化,程序如下:
實驗1.2 子程序調用
實驗目的:學習子程序的調用
*************************************************************
*TestXF2.asm
*對TestXF1.asm稍作改進,用延時子程序設置較長的延時,
*可以用試驗板上的LED看到XF引腳電平的變化
*************************************************************
.mmregs ;預定義的寄存器
.def CodeStart ;定義程序入口標記
.text ;程序區
CodeStart: ;程序入口
SSBX XF ;XF置1
CALL Delay ;調用延時程序
RSBX XF ;XF清0
CALL Delay ;調用延時程序
B CodeStart ;跳轉到程序開頭循環執行
**************************************************************
*延時子程序:Delay
*用兩級減一計數器來延時。調整AR1和AR2的大小LED閃爍的頻率不同
**************************************************************
Delay:
STM #999,AR1 ;循環次數1000
LOOP1: STM #4999, AR2 ;循環次數5000
LOOP2: BANZ LOOP2,*AR2- ;如果AR2不等於0,AR2減1,再判斷
BANZ LOOP1,*AR1- ;如果AR1不等於0,AR1減1,跳轉到LOOP1
RET
.end
**************************************************************
*注意這種延時方法並不精確,需要精確定時必須用定時器。
*按此法延時的近似公式為:4*(AR2+1)*(AR1+1)*時鐘週期
*當DSP工作在50MHz(時鐘週期20ns),AR1=999, AR2=4999時
*延時約為400ms,則LED閃爍的週期為800ms,頻率1.25Hz
**************************************************************
設計指導:
1.源代碼書寫格式
源代碼的書寫有一定的格式,初學者往往容易忽視。簡單歸納如下:
1.每一行代碼分為三個區:標號區、指令區和註釋區。標號區必須頂格寫,主要是定義變量、常量、程序標籤時的名稱。指令區位於標號區之後,以空格或TAB格開。如果沒有標號,也必須在指令前面加上空格或TAB,不能頂格。註釋區在標號區、程序區之後,以分號開始。註釋區前面可以沒有標號區或程序區。另外還有專門的註釋行,以*打頭,必須頂格開始。
2.一般區分大小寫,除非加編譯參數忽略大小寫。
3.標點符號有時不注意會打成中文全角字符導致錯誤。
書寫格式的要求在很多DSP書裡都沒有提,初學者往往只把書上的代碼輸入進去,編譯時得到錯誤的提示,而不知所措。其中最容易犯的錯誤指令頂格寫,不過一般經提示後不會犯第二次。
有些格式CCS並沒有做要求,但注意養成良好的代碼書寫風格,增加代碼的可讀性。以上兩個例子的書寫風格可作參考,但不是硬性規定:
1.標號區佔3個TAB的間隔,即12個字符
2.指令中的指令碼佔兩個TAB間隔,然後是操作數。
3.每一行的尾注能對齊的儘量對齊
4.標明一段程序功能的註釋以*號打頭頂格寫,如果功能說明的註釋較多,用分格線框起來。
此外其它編程語言的編程風格也可以借用過來,比如標示符命名規則、程序說明的要求等。如果項目組有規定,則按規定執行。
本書的代碼儘量保持一定的風格,不過讀者可以發現前面的代碼註釋較多,後面隨著學習的深入,一般不會對每一條指令加註釋,只註明程序段的功能。另外代碼貼到word裡後,格式有些錯位,無法一一糾正。
2.鏈接配置文件
一個完整的DSP程序至少包含三個部分:程序代碼、中斷向量表、鏈接配置文件(*.cmd)。這裡介紹一下鏈接配置文件文件,對本次試驗影響不大的中斷向量表將在後文介紹。
連接配置文件的確定了程序鏈接成最終可執行代碼時的選項,其中有很多條目,實現不同方面的選項,其中最常用的也是必須的有兩條:1.存貯器的分配
2.標明程序入口。以本次實驗為例,下面的簡單的鏈接配置文件就夠用了:
/* TestXF.cmd */
-e CodeStart /*程序入口,必須在程序中定義相應的標號*/
MEMORY {
page 0:
PRAM: org=0100h len=0F00h /*定義程序存貯區,起始0100H,長度0F00H*/
}
SECTIONS{
.text:>PRAM page 0 /*將.text段映射到page0的param區*/
}
由於每個程序都需要一個鏈接配置文件,可以編寫一個滿足通常需要的鏈接配置文件。作為本手冊通用的鏈接配置文件如下,可以滿足本書大部分程序的需要。在未特別指明的情況下使用這個通用的鏈接配置文件:
/* 5402.cmd */
-e CodeStart /*程序入口,必須在程序中定義相應的標號*/
-m map.map /*生成存儲器映射報告文件 */
MEMORY {
PAGE 0:
VECT: org=0080h len=0080h /*中斷向量表*/
PARAM: org=100h len=0F00h /*代碼區*/
PAGE 1:
DARAM: org=1000h len=1000h /*數據區*/
}
SECTIONS {
.text :> PARAM PAGE 0 /*代碼段*/
.vectors :> VECT PAGE 0 /*中斷向量表*/
STACK :> DARAM PAGE 1 /*堆棧*/
.bss :> DARAM PAGE 1 /*未命名段*/
.data :> DARAM PAGE 1 /*數據段*/
}
更多參考:
1.關於代碼書寫格式:SPRU102: TMS320C54x Assembly Language Tools User's Guide,3.5
Source Statement Format
2.關於鏈接配置文件:SPRU102: TMS320C54x Assembly Language Tools User's Guide,7.5
Linker Command Files,7.7 The MEMORY Directive,7.8 The SECTIONS Directive
練習:
1、試一下不按規定格式書寫代碼會產生什麼樣的編譯錯誤。
2、試一下將鏈接配置文件中的MEMORY,SECTIONS改成小寫會出現什麼樣的編譯錯誤。
3.修改程序1.2中AR1,AR2的值,觀察LED閃爍頻率
實驗二 基本運算
<本節選自為HK-DSP實驗箱寫的實驗指導書,有待整理>
DSP指令數量最多的是:算術指令、邏輯指令和數據加載與傳送指令。數據加載與傳送指令由於處處要用,所以不單獨列為實驗。算術與邏輯指令也是數量繁多,無法一一舉例,這裡簡單舉一個加法和除法的例子,乘法和乘加指令在FIR用得比較多,稍後一併介紹。其它指令有興趣可以對照指令表的說明,試驗一下各指令運行的結果。
實驗2.1 加減法計算
************************************************
*計算z=x+y-w。
************************************************
.mmregs
.def CodeStart
Data_DP: ;數據段指針
x: .word 10 ;初始化變量
y: .word 26
w: .word 23
z: .word 0 .text
CodeStart:
LD #Data_DP,DP ;裝載數據指針DP
STM #STACK+10H,SP
SUMB: LD x,A ;A=x
ADD y,A ;A=A+y
SUB w,A ;A=A-w
STL A,z ;z=A
END: B END
計算結果數據存儲器地址存儲內容十進制
x1010H000aH10
y1011H001aH26
w1012H0017H23
z1013H000dH13
技巧提示:試驗算術指令由於不需要外部資源,可以不需要仿真器和實驗箱。同學們可以平時自己用軟件仿真,多多實驗。但是複雜的算法最好還是在線仿真,因為程序是流水線執行,軟件仿真有時與實際硬件執行結果有所不同。
實驗2.2 除法計算
DSP並沒有除法指令,回想一下我們用在稿紙上演算除法列的豎式,實際是一種移位減法,DSP中也是通過做多次減法的辦法來做除法。下面例子是把用除以10的辦法二進制數轉成BCD碼例子:
*********************************
*16進制轉BCD碼
*********************************
.mmregs
.global CodeStart
.data
x: .word 1234 ;待轉換的數字
y: .word 10 ;除數
z: .word 0Fh,0Fh,0Fh,0Fh,0Fh;結果區,每位BCD存一個字,
;初始化為F因為實驗板的數碼管不顯示F
.text
CodeStart:
LD #x,DP ;設置DP
LD x,A ;被除數
STM #z,AR1 ;結果區指針
loop:
RPT #15 ;執行完16次減法後,A的高16位是餘數
SUBC y,A ;低16位是商
STH A,*AR1+ ;餘數保存到Z
AND #0FFFFH,A ;掩蓋掉高16位,保留商值
BC loop,ANEQ ;繼續做除法直到商為0
end: B end
練習:
練習其他算術指令
其它參考:
spru172c:TMS320C54x DSP Reference Set Volume 2: Mnemonic Instruction
Set,2.1 Arithmetic Operations
這個資料對每個指令都有詳細說明。也可以在Help中的TMS320C54x DSP Mnemonic Instruction
Set中查詢或搜索相關指令。
實驗三 中斷
中斷的概念應該不陌生,指的是當某個事件發生時,暫停當前的操作,轉向中斷服務程序,執行完後再返回繼續原來的操作。這使得DSP能夠處理多個任務。
DSP有許多中斷源,可以設置中斷控制寄存器來確定響應哪些中斷而不理會哪些中斷。本實驗介紹最常用的定時器中斷和外部中斷的使用方法,並介紹中斷向量表和中斷向量指針。 實驗3.1 定時器中斷:方波發生器
實驗目的:學習定時器中斷的設計方法
回顧一下實驗一控制LED的閃爍實際就是一個簡單的方波發生器。但不足的是延時的方法定時不精確,另外還有一個缺點是在執行延時的過程中DSP就無法執行其它指令,這時就可以用定時器來改進。
使用定時器首先要對它初始化,基本步驟如下:
1.關掉中斷
2.停止定時器運行。
3.設定時器的定時長度
4.允許定時器中斷
5.運行定時器
6.打開中斷
現以簡單的方波程序為例:
;==============================================
; fangbo1.asm
; 利用定時器Timer0在XF腳產生週期2ms的的方波
;==============================================
.title "fangbo1.asm"
.mmregs
.def codestart ;程序入口
.def TINT0_ISR ;Timer0中斷服務程序
STACK .usect "STACK",10H ;分配堆棧空間
;設定定時器0控制寄存器的內容
K_TCR_SOFT .set 0B<<11 ;TCR第11位soft=0
K_TCR_FREE .set 0B<<10 ;TCR第10位free=0
K_TCR_PSC .set 0B<<6 ;TCR第9-6位,可設TDDR一樣,也可不設自動加載
K_TCR_TRB .set 1B<<5 ;TCR第5位TRB=1此位置1,PSC會自動加載的
K_TCR_TSS .set 0B<<4 ;TCR第4位TSS=0
K_TCR_TDDR .set 1001B<<0 ;TCR第3-0位TDDR=1001B
K_TCR .set
K_TCR_SOFT|K_TCR_FREE|K_TCR_PSC|K_TCR_TRB|K_TCR_TSS|K_TCR_TDDR
K_TCR_STOP .set 1B<<4 ;TSS=1時計數器停止
.data
DATA_DP: ;數據區指針
XF_Flag: .word 1 ;當前XF的電平標誌,如果XF_Flag=1,則XF=1
;================================================
;主程序:
;================================================
.text
CodeStart:
STM #STACK+10H,SP ;設堆棧指針SP
LD #DATA_DP,DP ;設數據地址DP
STM #XF_Flag,AR2 ;AR指向XF標誌
;改變中斷向量表位置
K_IPTR .set 0080h ;指向0080H,默認是FF80
LDM PMST,A
AND #7FH,A ;保留低7位,清掉高位
OR #K_IPTR,A ;
STLM A,PMST
*初始化定時器0
*f=50MHz,定時2ms時:
*根據定時器長度計算公式:Tt=T*(1+TDDR)*(1+PRD)
*給定TDDR=9,PRD=9999,CLKOUT主頻f=40MHz,T=25ns
*Tt=20ns*(1+9)*(1+9999)=2000us=2ms
*f=100Mhz,定時最大是:10ns*2^4*2^16=10ms,
PERIOD .set 9999 ;定義計數週期
STM K_TCR_STOP,TCR ;停止計數器0
; STM #PERIOD,TIM ;可設成跟PRD一樣,也可不設自動加載
STM #PERIOD,PRD ;設定計數週期
STM #K_TCR,TCR ;開始Timer0
stm #0008h,IMR ;允許Timer0中斷
STM #0008h,IFR ;清除掛起的中斷
rsbx intm ;開中斷
end: nop
B end
;================================================
;Timer0中斷服務程序:TIN0_ISR
;================================================
TINT0_ISR:
PSHM ST0 ;本中斷程序影響TC,位於ST0中
;判斷當前XF狀態並作電平變化
BITF *AR2,#1 ;IF XF_Flag=1 then TC=1 else TC=0
BC ResetXF,TC ;IF TC=1 then XF=0 else XF=1
setXF:
SSBX XF ;置XF為高電平
ST #1,*AR2 ;相應修改標誌
B Next
ResetXF:
RSBX XF ;;置XF為高電平
ST #0,*AR2 ;相應修改標誌
Next:
POPM ST0
RETE
.end
有時定時的長度不能滿足需要,比如DSP工作頻率50Mhz時,定時最大值是:20ns*2^4*2^16=20ms。如果需要更長的定時,就要在定時器中斷子程序中再加一個計數器,直到產生一定次數的定時中斷後再執行相應的操作。程序只需要稍作修改,見附盤的fangbo2.asm
技巧提示:寄存器的不同位通常有不同的含義,初始化時單獨設定寄存器的每一位可以增加程序可讀性,容易讓其它人看懂具體每一位設置的含義,並且易於修改。如果代碼太長可以自己寫一個初始化的子程序,需要時修改一下調定時再調用。當然如果對寄存器各個位的含義相當熟悉,直接整個初始化也行。可以自行選擇這些不同的編程風格。
設計指導:
1.中斷向量表
中斷向量表是DSP程序的重要組成部分,當有中斷髮生並且處於允許狀態時,程序指針跳轉到中斷向量表中對應的中斷地址。由於中斷服務程序一般較長,通常中斷向量表存放的是一個跳轉指令,指向實際的中斷服務程序。下面是5402中斷向量表的一個範例,可以作為模板,使用時稍作修改就行:
*****************************************************************
*5402Vectors.asm
*完整的5402中斷向量表示例
*5402共有30個中斷向量,每個向量佔4個字的空間。
*使用向量一般用一條跳轉指令轉到相應中斷服務子程序,其餘空位用NOP填充
*未使用的向量直接用RETE返回,是為了防止意外進入未用中斷。
*****************************************************************
.sect ".vectors" ;開始命名段.vecotrs
.global CodeStart ;引用程序入口的全局符號定義
;。。。引用其它中斷程序入口的全局符號定義
.align 0x80 ; 中斷向量表必須對齊128字的頁邊界
RESET: B CodeStart ; Reset中斷向量,跳轉到程序入口
NOP ;用NOP填充表中其餘空字
NOP ;B指令佔了兩個字,所以要填兩個NOP
NMI: RETE ;不可屏蔽中斷
NOP
NOP
NOP
; 軟件中斷
SINT17 .space 4*16 ;軟件中斷使用較少,簡單起見用0填充
SINT18 .space 4*16
SINT19 .space 4*16
SINT20 .space 4*16
SINT21 .space 4*16
SINT22 .space 4*16
SINT23 .space 4*16
SINT24 .space 4*16
SINT25 .space 4*16
SINT26 .space 4*16
SINT27 .space 4*16
SINT28 .space 4*16
SINT29 .space 4*16
SINT30 .space 4*16
INT0: RETE ;外部中斷INT0
NOP
NOP
NOP
INT1: RETE ;外部中斷INT1
NOP
NOP
NOP
INT2: RETE ;外部中斷INT2
NOP
NOP
NOP
TINT: RETE ;Timer0中斷
NOP
NOP
NOP
BRINT0: RETE ;McBSP #0 接收中斷
NOP
NOP
NOP
BXINT0: RETE ;McBSP #0 發送中斷
NOP
NOP
NOP
DMAC0: RETE ;無定義(默認)DMA0中斷
NOP
NOP
NOP
TINT1: RETE ;Timer1中斷(默認)或DMA1中斷.
NOP
NOP
NOP
INT3: RETE ;外部中斷3
NOP
NOP
NOP
HPINT: RETE ;HPI中斷
NOP
NOP
NOP
BRINT1: RETE ;McBSP #1接收中斷(默認)或DMA2中斷
NOP
NOP
NOP
BXINT1: RETE ;McBSP #1發送中斷(默認)或DMA3中斷
NOP
NOP
NOP
DMAC4: RETE ;DMA4中斷
NOP
NOP
NOP
DMAC5: RETE ;DMA5中斷
.end
在本實驗中只要把在開頭加上中斷子程序標號的引用,並在中斷表的TINT部分換成跳轉指令就行了:
*******************************************************
*vectors.asm for 方波發生器
*******************************************************
.sect ".vectors" ;開始命名段.vecotrs
.global CodeStart ;引用程序入口的全局符號定義
.global TINT0_ISR ;引用Timer0中斷子程序
<節省篇幅,中間省略>
TINT: B TINT0_ISR ;Timer0中斷
NOP
NOP
BRINT0: RETE ;McBSP #0 receive interrupt
<節省篇幅,下略>
技巧提示:只有第一個中斷(Reset中斷)是每個程序都應該有的,在不需要其它中斷的情況下,可以只用這一部分,後面可以省略。如果只需要部分中斷也可以按需設置,但必須保證所用中斷在中斷向量表的位置不變。不熟悉中斷向量表的情況下最好還是用這個完整中斷向量表樣例。
另外C5400系列中不同型號DSP的中斷向量數量和在中斷向量表中的位置有所不同,程序移植時需要查相應datasheet確認。
2.中斷向量指針
中斷向量表的位置並沒有強制的位置,可以在內部存貯器,也可以在外部存貯器。但有一個要求:中斷量表必須放在80H字長存貯塊的起始處,即中斷向量表的首地址的低7位必須全為0。DSP
的寄存器PMST的高9位是中斷向量表的指針IPTR。其上電時默認是在FF80H處,這是為了運行固化在內部ROM的上電加載程序(見實驗八的程序加載部分)。由於FF80H是只讀的,加載用戶自定義的中斷向量表時會報錯。這樣需要重新設置IPTR的值,本書一般把它重定義到0080H(也可以用自定義的地址),並在程序開頭重新設置一下IPTR的值:
;改變中斷向量表位置
K_IPTR .set 0080h ;指向0080H,默認是FF80
LDM PMST,A
AND #7FH,A ;保留低7位,清掉高位
OR #K_IPTR,A ;將新值傳到高9位
STLM A,PMST ;修改PMST寄存器
技巧指示:由於這段代碼幾乎每個程序都需要,可以單獨存成一個文件:IPTR0080H.asm,然後在程序需要的地方用.copy或.include指令:
.copy 「IPTR0080H.asm」
或: .include 「IPTR0080H.asm」
編譯時就會自動把這段代碼嵌到相應位置。稍微要注意的是由於這一小段代碼要用到累加器A,所以最好保證執行這段代碼之前不要使用累加器A。
其它還有一些經常重複的代碼,如初始化SP、DP、IPTR的代碼都可以寫在一個文件裡include/copy進來。
注1:.copy和.inlucde指令效果是一樣的,只是在生成程序列表時,.copy會把代碼複製過來,而.include不會。
注2:文件名可以用路徑,如果不用,則編譯器會按下面的循序搜索:當前目錄、編譯選項指定的目錄、環境變量指定的目錄。
更多參考:
1.關於中斷:SPRU131 TMS320C54x DSP Reference Set, Volume 1: CPU and
Peripherals,6.10 Interrupts
2.關於定時器:SPRU131 TMS320C54x DSP Reference Set, Volume 1: CPU and
Peripherals,8.4 Timer
實驗3.2 外部中斷:頻率計
DSP有4個外部中斷INT0-INT3,下降沿觸發,實驗箱的頻率計使用的是INT3。
頻率計的設計原理是:在設定時間下計外部中斷INT3的次數,除以定時器的定時週期(也就是乘以定時器中斷的觸發頻率),就得到外部脈衝頻率。實驗箱上配有1.024k-262.144k共8檔頻率源,也可以外接頻率源。用跳線冒選擇頻率源,並接到INT3上。下面的例程是定時器定時1s,在INT3中斷服務子程序中計脈衝個數,到時則關閉中斷。脈衝計數結果顯示到數碼管上,即為以單位為Hz的頻率值
**********************************************
*頻率計
**********************************************
.mmregs
.global CodeStart
.global TINT1_ISR
.global INT3_ISR
.include "../DefineIO.asm"
.data
DATA_DP:
PulseCounter: .word 0 ;脈衝計數器
Display: .word 0FH,0FH,0FH,0FH,0FH,0FH;存放數據管顯示值,值F在數碼管上不顯示
DotData: .word 000000B ;數碼管的dot point
Number10: .word 10 ;十六進制轉BCD所除的10
.text
CodeStart:
.copy "../SP_DP_IPTR.asm" ;初始化SP、DP和IPTR的代碼段
STM #99,AR1 ;10ms計數後再100分頻
STM #Display,AR3 ;定義數據管顯示存貯區指針
LD #0,A ;A用來計脈衝數
SSBX INTM ;關中斷
CALL Timer1Init ;初始化Timer1
STM #110000000B,IMR ;允許Timer1和INT3中斷
STM #0FFH,IFR ;清除掛起的中斷
RSBX INTM ;開中斷
wait:
B wait;
***************************************
*外部中斷子程序
***************************************
INT3_ISR:
ADD #1,A ;計中斷次數
RETE
***************************************
*定時器中斷子程序
***************************************
TINT1_ISR:
BANZ GoOnCount,*AR1- ;測量次數計數器減1,次數為0就中止計數數,
;結束計數
STM #0,IMR ;取消所有中斷
HEX2BCD: ;把計數結果轉成BCD碼
RPT #15
SUBC Number10,A
STH A,*AR3+
AND #0FFFFH,A
BC HEX2BCD,ANEQ
;在數碼管上顯示結果
STM #Display,AR3
PORTW *AR3+,Digital0
PORTW *AR3+,Digital1
PORTW *AR3+,Digital2
PORTW *AR3+,Digital3
PORTW *AR3+,Digital4
PORTW *AR3+,Digital5
PORTW DotData,DotPoint
RETE
GoOnCount: ;繼續計數
STM #1100001B,IFR ;清除掛起的中斷
RETE
***************************************
*定時器初始化
***************************************
Timer1Init:
;定時器1的寄存器地址
TIM1 .set 0030h ;減1計數器
PRD1 .set 0031h ;存放定時時間常數
TCR1 .set 0032h ;定時器狀態及控制寄存器
;F=50MHz, T=20ns*(1+15)*(1+3124)=20ns*16*31250=10ms
STM #010,TCR1 ;TSS置位停止Timer
STM #31249,PRD1
STM #2FH,TCR1
RET
.end
簡單起見本例只能測一次,可以做一些改進,比如每隔1-2S自動重新測量,或者用按鍵來觸發測量。
實驗4.1 數碼管及LED顯示接口實驗
實驗箱說明部分已經介紹了數碼管的控制原理,下面的程序DigitalLED.asm簡單的演示了對數碼管和LED控制的指令,可以在顯示預設的數字和LED狀態。複雜的程序可以見附盤的流水燈程序,DigitalLED2.asm
;=========================================================
;DigitalLED.asm
;實驗用DSP控制實驗板數碼管
;DSP用I/O指令對CPLD地址1000-10005寫數據,分別對應Digtal0-5
;=========================================================
.mmregs
.def main ;主程序入口
.ref Timer0Init ;Timer0初始化子程序
;數據管地址
Digital0 .set 1000H ;數據管1
Digital1 .set 1001H ;數據管2
Digital2 .set 1002H ;數據管3
Digital3 .set 1003H ;數據管4
Digital4 .set 1004H ;數據管5
Digital5 .set 1005H ;數據管6
DotPoint .set 1006H ;小數點
LED .set 1007H ;LED
STACK .usect "STACK",10H ;分配堆棧空間
.data
DATA: .word 1,2,3,4,5,6 ;測試數據
Dot_DATA: .word 010101b;
LED_DATA: .word 0101010b
.text
main:
STM #STACK+10H,SP ;設堆棧指針SP
STM #K_SWWSR,SWWSR
SSBX INTM ;關中斷
LD #DATA,DP ;設數據地址DP
STM #DATA,AR1
*寫數據
PORTW *AR1+,Digital0
PORTW *AR1+,Digital1
PORTW *AR1+,Digital2
PORTW *AR1+,Digital3
PORTW *AR1+,Digital4
PORTW *AR1+,Digital5
PORTW Dot_DATA,DotPoint
PORTW LED_DATA,LED
END: B END
.end
技巧提示:數碼管、LED的IO地址的定義也可以單獨存到一個文件中,在需要它的程序中用.include/.copy指令。
練習:修改預設值重新運行觀察結果。
實驗4.2 鍵盤接口實驗
實驗板上有四個按鍵,當有鍵按下時,會觸發DSP的INT1中斷,在INT1的中斷服務程序中讀入鍵碼,判斷哪一個鍵被按下,然後執行相應的操作。各鍵對應的二進制和十六進制鍵碼分別為:
按鍵1: 0001B 1H
按鍵2: 0010B 2H
按鍵3: 0100B 4H
按鍵4: 1000B 8H 下面有一個小例子:
******************************************************
*keyboardTest.asm
*測試按鍵的功能,響應按鍵中斷,讀取鍵值,
*並對不同鍵按鍵次數計數
******************************************************
.mmregs
.global CodeStart
.global INT1_ISR
.include "../DefineIO.asm"
.data
DATA_DP:
Counter1: .word 0 ;按鍵1計數器
Counter2: .word 0 ;按鍵2計數器
Counter3: .word 0 ;按鍵3計數器
Counter4: .word 0 ;按鍵4計數器
KeyValue: .space 30H*16 ;按鍵歷史緩衝區
.text
CodeStart:
.copy "../SP_DP_IPTR.asm" ;初始化SP、DP和IPTR代碼段
;初始化變量
STM #KeyValue,AR2
ST #0,Counter1
ST #0,Counter2
ST #0,Counter3
ST #0,Counter4
SSBX INTM ;關中斷
STM #00000010B,IMR ;允許INT1中斷
STM #0FFH,IFR ;清除掛起的中斷
RSBX INTM ;開中斷
wait:
B wait;
*******************************************************
*鍵盤中斷子程序
*******************************************************
INT1_ISR:
PORTR #Keyboard,*AR2 ;讀取鍵碼
ANDM #0FH,*AR2 ;Keyvalue只有低四位有效
BITF *AR2,#01H ;如果鍵碼為1,跳轉到FuncKey1
BC FuncKey1,TC
BITF *AR2,#02H ;如果鍵碼為2,跳轉到FuncKey2
BC FuncKey2,TC
BITF *AR2,#04H ;如果鍵碼為3,跳轉到FuncKey3
BC FuncKey3,TC
BITF *AR2,#08H ;如果鍵碼為4,跳轉到FuncKey4
BC FuncKey4,TC
B FuncKeyEnd ;
FuncKey1:
ADDM #1,Counter1 ;按鍵1計數器+1
B FuncKeyEnd
FuncKey2:
ADDM #1,Counter2 ;按鍵2計數器+1
B FuncKeyEnd
FuncKey3:
ADDM #1,Counter3 ;按鍵3計數器+1
B FuncKeyEnd
FuncKey4:
ADDM #1,Counter4 ;按鍵4計數器+1
B FuncKeyEnd
FuncKeyEnd:
PORTW *AR2+,Digital0 ;當前鍵碼顯示到數碼管上
STM #0FFH,IFR ;清除掛起的中斷
RETE
實驗六 DMA實驗
實驗目的:學習DMA的原理的使用方法
實驗內容:用DMA方法接收McBSP接口語音芯片的數據 DMA是直接存儲器存取,是一種傳送不佔用CPU處理時間的大批量數據傳送的有效方式。我們用以下實例來說明它的應用:
如果我們要做一個音頻處理系統,需要連續用McBSP接口的語音芯片採集若干個樣本進行處理,比如頻譜分析、音頻壓縮等。本例假設要每採集256個樣本進行一次處理。上例用的是查詢方式,佔用了所有CPU資源。可以用中斷方式,結合前面的實驗不難做到,請同學們自行完成。在這個實驗中我們將介紹一個更有效的DMA傳送方式。我們比較一下用中斷方式和DMA方式的效率有何不同:
一、中斷方式:每當中緩衝串口接收一個16bit樣本的數據,觸發一次串口接收中斷,將數據轉移到一個256
word的數據接收緩衝區並計數。當計數達到256個,即緩衝區滿時,將256個數據轉移到數據處理存儲區,並通知主程序進行處理。
二、DMA方式:我們使用一個通道自動接收McBSP傳來的數據並存入接收緩衝區,當緩衝區滿時觸發DMA中斷,將256個數據傳送到數據處理存儲區,傳送完畢觸發通知主程序進行處理。
由上比較可見,每接收一批樣本,用中斷方式將觸發256次中斷,也就是主程被打斷256次去接收數據。而用DMA方式,只在全部256個樣本全部接收完畢時發生一次中斷,這時主程序應該已經處理完上一批的數據。
進一步考慮,當數據處理完畢後還需要將數據送走,這時又可以採用另一個DMA通道完成這個任務,將CPU釋放出來等待進行下一批樣本的處理。
事實上DMA傳送並非比用CPU直接處理快,例如在內部存貯器之間傳送時,用CPU需要2cycle/word,而用DMA要4cycle/word。DMA的優勢在是把CPU解放出來做其它的事。
以下是兩個DMA通道與CPU協調工作的情況(陰影部分表示空閒)。
DMA0①①①
CPU②③ ②③ ②
DMA1 ④ ④
①從McBSP接收數據
②DMA中斷,將數據從接收緩衝區轉移到數據處理存儲區
③對對數據進行處理
④將處理完的數據送走
估計一下各步的時間,設採樣頻率是8kHz,CPU時鐘頻率100MHz。因此一個處理週期為1/8kHz*256=32ms。
②傳送256個點至少需要256word*2cycle/word=512cycle=5.12us。
假設處理完後數據量不變,④需要256word*4cycle/word=1024 cycle=10.24us。
③所需要的時鐘週期取決於算法的複雜度了。
計算好各步所需要的時鐘週期,就可以根據情況靈活選擇如何使用DMA,例如如果CPU有足夠的空閒時間送走數據就不必要④;如果CPU仍然不足,就需要再增加個一個DMA來做②的任務。如果數據的輸出也是從McBSP輸出,還要用一個DMA通道進行McBSP的發送。
總之要合適地使用DMA通道,使用不當也會使程序變得更加複雜,例如多個DMA通道優先級的問題等等。
C54x系列有6個DMA通道,但不同型號C54x系列DSP
DMA通道的使用不全相同,如C5402只能將DMA通道用於內部數據存貯器之間傳送、McBSP和HPI接口,而C5410可用於內部、外部數據、程序存貯器之間傳送。詳細介紹請參閱SPRU302
TMS320C54x DSP Reference Set, Volume 5: Enhanced
Peripherals和各DSP的數據手冊。
實驗7.1 FIR
;=============================================================
; fir4.asm
;用用循環緩衝區和雙操作數尋址方法實現FIR濾波器
;先用matlab,選擇80點漢明窗設計一個截止頻率為0.2π的低通濾波器
; 本例與前不同的是係數直接引用程序存儲器的係數表
;N=5 y(n)=h0*x(n)+h1*x(n-1)+h2*x(n-2)+h3*x(n-3)+h4*x(n-4)
;=============================================================
.title "fir4.asm"
.mmregs
.def start
;分配數據存儲區
.bss y,1 ;y
xn .usect "xn",80 ;xn
h .usect "h",80 ;h
PA0 .set 0000H ;數據輸出端口
PA1 .set 0001H ;數據輸入端口
;參數表
.data
table: .word -7,-18,-24,-22,-9,11,33,48
;已在Matlab中轉成十六進制的小數
.word 46,20,-24,-73,-104,-97,-43,49
.word 146,204,187,81,-91,-268,-371,-337
.word -144,162,476,661,603,261,-297,-894
.word -1283,-1222,-562,697,2373,4142,5618,6456
.word 6456,5618,4142,2373,697,-562,-1222,-1283
.word -894,-297,261,603,661,476,162,-144
.word -337,-371,-268,-91,81,187,204,146
.word 49,-43,-97,-104,-73,-24,20,46
.word 48,33,11,-9,-22,-24,-18,-7 start: SSBX FRCT ;小數乘法
;把參數表複製到數據存儲區
STM #h,AR1
RPT #79
MVPD #table,*AR1+
;把x(n)-x(n-79)賦始值0
STM #xn,AR1
RPT #79
ST #0,*AR1+
STM #xn+79,AR3 ;x(n-79)---AR3
STM #h+79,AR4 ;h(n-79)---AR4
STM #80,BK ;循環緩衝區大小80
STM #-1,AR0 ;指針調整值-1
LD #xn,DP ;DP指向xn所在頁
PORTR PA1,@xn ;輸入數據
LD #y,DP ;DP指向y所在頁
FIR: RPTZ A,#79 ;進行一次FIR運算
MAC *AR3+0%,*AR4+0%,A;A=(AR3)*(AR4)+A,
AR3=AR3+AR0,AR4=AR4+AR0
STH A,@y ;保存計算結果
PORTW @y,PA0 ;輸出結果
BD FIR ;讀入下一個數據並進行下一次計算
PORTR PA1,*AR3+0% ;新數據覆蓋了最舊的數據
.end
實驗7.2 IIR
.mmregs
.global codestart
K_DATA_SIZE .set 256 ;輸入數據個數
K_BUFFER_SIZE .set 8 ;緩衝大小,需是2的整數次冪,並大於a、b的個數
K_STACK_SIZE .set 256 ;堆棧大小
K_A .set 3 ;a向量個數
K_B .set 4 ;b向量的個數
K_CIR .set 4 ;>=a、b的長度,也可以設為K_BUFFER_SIZE-1
STACK .usect "stack",K_STACK_SIZE
SYSTEM_STACK .set K_STACK_SIZE+STACK
.data
DATA_DP:
.align K_BUFFER_SIZE
bufferdatax: .space K_BUFFER_SIZE*16 ;size in bits
bufferdatay: .space K_BUFFER_SIZE*16 ;size in bits
inputdata: .word 0
filterdata: .word 0
.text
.asg AR2, ORIGIN
.asg AR3, INPUT
.asg AR4, FILTER
.asg AR5, OUTPUT
codestart:
SSBX FRCT
SSBX INTM
LD #DATA_DP,DP
STM #SYSTEM_STACK, SP
CALL filter_start
NOP
NOP
NOP
LOOP:
B LOOP
.def b0,b1,b2,b3,a1,a2,a3;
.def filter_start
b0 .set 1456H ;b1=0.1589 *2^15
b1 .set 3D07H ;b2=0.4768
b2 .set 3D07H ;b3=0.4768
b3 .set 1456H ;b4=0.1589
a1 .set -103AH ;a1=-0.1268
a2 .set 430FH ;a2=0.5239
a3 .set -1016H ;a3=-0.1257
;=================================================================
;濾波子程序:filter_start
;=================================================================
.text
filter_start:
STM #K_CIR,BK ;設置環形buffer的大小
STM #1,AR0 ;和步長
STM #inputdata,ORIGIN ;AR2
STM #bufferdatax,INPUT ;AR3
STM #bufferdatay,FILTER ;AR4
STM #filterdata,OUTPUT ;AR5
;初始化
RPT #K_B-1-1 ;
ST #0,*INPUT+0% ;x(-1)、x(-2)、x(-3)設為0
RPT #K_A-1
ST 0,*FILTER+% ;y(-1)、y(-2)、y(-3)設為0
STM #bufferdatay,FILTER
STM #K_DATA_SIZE-1,BRC ;塊循環次數,頭三個值已經直接通過了
RPTB filter_end-1 ;塊循環結束位置
;可以把塊循環改成中斷調用,有新數據就中斷一次。
nop ;數據從件導入點,加nop保證數據在使用前導入
nop
MVDD *ORIGIN,*INPUT ;新數據
MAR *+INPUT(-K_B+1)%
MPY *INPUT+0%,#b3,B ;B=x(n-3)*b3, i=i+1
LD B,A
MPY *INPUT+0%,#b2,B ;B=x(n-2)*b2, i=i+1
ADD B,A
MPY *INPUT+0%,#b1,B ;B=x(n-1)*b1, i=i+1
ADD B,A
MPY *INPUT+0%,#b0,B ;B=x(n)*b0, i=i+1
ADD B,A
MPY *FILTER+0%,#a3,B ;B=y(n-3)*a3, j=j+1 j=n-3為y的指針
ADD B,A
MPY *FILTER+0%,#a2,B ;B=y(n-2)*a2, j=j+1
ADD B,A
MPY *FILTER+0%,#a1,B ;B=y(n-1)*a1, j=j+1
ADD B,A
STH A,*FILTER ;傳送y(n)至y區, ;16位小數相乘得到的是32位小數
STH A,*OUTPUT ;傳送y(n)至結果區 ;取前16位就行了
MAR *+FILTER(-K_A+1)%
nop
nop ;數據文件導出點,加nop保證數據在導出前已更新
filter_end: NOP ;循環結束
RET
.end
實驗八 程序加載
C5000 DSP沒有內部提供掉電保存程序的ROM/EPROM/Flash,上電時需要從外部加載應用程序。C5000
DSP提供了多種程序加載方式,滿足不同應用的需要:串行加載、並行加載、HPI加載等,實際應用最多的是並行加載,本實驗主要介紹8位並行存貯器加載。
加載過程:DSP上電時,如果MP/MC引腳為低電平,則跳轉到內部ROM的FF80中斷向量表的Reset中斷,該處有一個跳轉指令轉到稱為Bootloader的加載程序執行,該程序的功能是按照一定順序查找可用的加載方式,如果找到,則開始加載應用程序,加載完畢轉嚮應用程序執行。
實現並行存貯器加載的關鍵是建立一個加載表(boot
table),該表包括:一個或多個程序代碼段、部分需要初始化的寄存器值、程序入口等信息。CCS附帶有一個應用程序(C5000系列是hex500.exx)可以把.out程序轉成.hex格式的加載表,然後可以燒錄到非易失性存貯器中,如OTP/EPPOM/EEPROM/Flash中。
具體步驟:
1.修改項目的編譯選頂,使其生成可以轉化成加載表的.out文件格式
2.為hex500.exe建立一個配置文件
3.用hex500.exe把*.out轉化成加載表*.hex
4.在.hex未尾加上加載表起始地址
5.燒錄到非易失性存貯器中
6.安到目標板上進行加載實驗 各步驟詳述如下:
1.修改項目的編譯選頂,使其生成可以轉化成加載表的.out文件格式
在Project/Build Option…/Complier 裡面加一個選項:-v548,或在Basic/Process
verson一欄中填寫:548,然後點確定。
注意:如果不加這個選項,用hex500程序轉化出來的hex文件無法加載。
2.為hex500.exe建立一個配置文件
這個配置文件包含了hex500程序執行所需要的選頂,下面是一個樣板配置文件hex.cmd:
sample.out /* 待轉化的程序文件*/
-map hex.map /* 生成一個map文件便於查看轉化結果*/
-o hex.hex /* 輸出文件名*/
-i /* 輸出文件為Intel Hex 文件格式 */
-memwidth 8 /* 目標系統的存貯器為8位*/
-romwidth 8 /* 存貯器芯片的位寬為8位*/
-boot /* 生成加載表*/
-bootorg 0000h /* 加載表在存貯芯片中的起始位置*/
-e 80h /* 程序入口,即加載完畢後跳轉執行程序位置 */
初學者容易弄錯的是memwidth、romwidth和bootorg這三個選項。不同存貯器配置下設置不同,並且要注意的是上文註釋的(包括TI資料中的解釋)是程序脫機燒寫的設置,而在系統燒錄(EEPROM、Flash、NVSRAM可以支持)與脫機燒寫又會有所不同。
脫機燒寫指的是將存貯芯片放置在編程器裡燒寫,優點是直接可以利用hex500轉化出來的hex文件,缺點是普通編程器無法燒寫貼片封裝的芯片。
在系統燒寫相對靈活,但對不同系統,不同芯片需要編寫專用的燒錄程序,並且需要將hex文件進一步進行格式轉換成燒錄程序可以識別的程序。
不同情況設置方法如下表:
存貯器配置方案脫機燒寫在系統燒寫
8位存貯器-memwidth 8
-romwidth 8
16位存貯器-memwidth 16
-romwidth 16
兩片8位貯器並行組成16位存貯器-memwidth 16
-romwidth 8-memwidth 16
-romwidth 16
bootorg芯片中的起始地址系統中的起始地址
3.用hex500.exe把*.out轉化成加載表*.hex
在dos窗口下執行:hex500 hex.cmd
如果hex500.exe與hex.cmd以及待轉化的程序文件不在同一目錄下,需加上路徑或設置path環境變量。
4.在FFFFH加上加載表起始地址
當開始並行加載時,Bootloader程序會在外部程序空間的FFFFH(如果是8位系統,同時也會查FFFEH)尋找加載表的地址,如果熟悉hex文件格式可以直接在hex文件末尾加一條紀錄,也可以燒錄器軟件中加。
應用實例:
本實驗箱是用於程序加載的是8位EPROM或EEPROM,可以用脫機方式,如用EEPROM也可以用在系統方式。Hex500程序的配置文件如前hex.cmd文件,只需要把第一行的sample.out換成實際的程序名稱。EPROM/EEPROM在程序空間的地址為8000H,並且由於是8位系統,則要在FFFEH和FFFFH的值分別設為80H、00H。可以在生成的.hex文件倒數第二行加上:
:027FFE00800001
也可以在燒錄器軟件中修改FFFEH-FFFFH處的值,然後就可以燒寫了。
燒寫好後,將芯片安置在IC座中,確認MP/MC跳線置為低電平,INT3、INT2的跳線置於懸空後,打開電源,就可以看到程序運行的狀況。
========================================================================================================
[ 本帖最后由 c2535103 于 2009-2-9 05:53 编辑 ]