成功燒寫TMS320F2812經驗
阿新 • • 發佈:2018-11-12
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
flash內得程式程式碼以最快速度執行,需要設定flash配置暫存器的等待週期數,而“設定flash配置暫存器的等待週期數”的這段程式碼是不能在flash內部執行的,這樣就會出現兩種實際的配置方式:一種是2812系統無外擴程式儲存器,此時程式只能寫在2812內部得flash區,程式執行開始後,需要將“設定flash配置暫存器的等待週期數”的這段程式碼用資料複製的方式複製到內部sram區,比如
L0 區,然後使用跳轉指令跳轉到L0
區“設定flash配置暫存器的等待週期數”的程式碼進行flash配置暫存器的設定,設定完成後再使用跳轉指令跳轉到flash程式區執行正常的功能程式。這樣以後的flash內的程式碼就會以最快速度執行。
還有一種是2812系統外擴了部分“引導”程式儲存器,“設定flash配置暫存器的等待週期數”的這段程式碼放置在外擴的“引導”程式儲存器內,而其他的所有程式程式碼都放在2812內部flash區,系統復位後,首先執行外擴的“引導”程式儲存器內的程式,一旦完成了對內部flash配置暫存器的設定後,即跳轉到flash程式區執行正常的功能程式。
正如上述,對2812外部介面XINTF配置暫存器的設定程式碼,也是不能在“外部”程式程式碼區進行的,為了實現對其的正常設定,可仿照上述辦法進行相關操作。
2007.8.13 19:06 作者:wind 收藏 | 評論:0
成功燒寫TMS320F2812經驗
分類:預設欄目用的是CCS2.20版本提供的SDFLASH,設定好SDFLASH後,主要是要將需要下載的.out檔案寫進去.
關鍵在於線上模擬時的cmd檔案要加以更換成新的.使用的是:
-o .DebugPULSE_AD.out
-m PULSE_AD.map
MEMORY
{
PAGE 0 :
OTP : origin = 0x3D7800, length = 0x000800
FLASHJ : origin = 0x3D8000, length = 0x002000
FLASHI : origin = 0x3DA000, length = 0x002000
FLASHH : origin = 0x3DC000, length = 0x004000
FLASHG : origin = 0x3E0000, length = 0x004000
FLASHF : origin = 0x3E4000, length = 0x004000
FLASHE : origin = 0x3E8000, length = 0x004000
FLASHD : origin = 0x3EC000, length = 0x004000
FLASHC : origin = 0x3F0000, length = 0x004000
FLASHB : origin = 0x3F4000, length = 0x002000
FLASHA : origin = 0x3F6000, length = 0x001FF6
BEGIN : origin = 0x3F7FF6, length = 0x000002
PASSWDS : origin = 0x3F7FF8, length = 0x000008
ROM : origin = 0x3FF000, length = 0x000FC0
VECTORS : origin = 0x3FFFC2, length = 0x00003E
PAGE 1 :
/* SARAM */
RAMM0M1 : origin = 0x000000, length = 0x000800
/* Peripheral Frame 0: */
DEV_EMU : origin = 0x000880, length = 0x000180
FLASH_REGS : origin = 0x000A80, length = 0x000060
CSM : origin = 0x000AE0, length = 0x000010
XINTF : origin = 0x000B20, length = 0x000020
CPU_TIMER0 : origin = 0x000C00, length = 0x000008
CPU_TIMER1 : origin = 0x000C08, length = 0x000008
CPU_TIMER2 : origin = 0x000C10, length = 0x000008
PIE_CTRL : origin = 0x000CE0, length = 0x000020
PIE_VECT : origin = 0x000D00, length = 0x000100
/* Peripheral Frame 1: */
ECAN_A : origin = 0x006000, length = 0x000100
ECAN_AMBOX : origin = 0x006100, length = 0x000100
/* Peripheral Frame 2: */
SYSTEM : origin = 0x007010, length = 0x000020
SPI_A : origin = 0x007040, length = 0x000010
SCI_A : origin = 0x007050, length = 0x000010
XINTRUPT : origin = 0x007070, length = 0x000010
GPIOMUX : origin = 0x0070C0, length = 0x000020
GPIODAT : origin = 0x0070E0, length = 0x000020
ADC : origin = 0x007100, length = 0x000020
EV_A : origin = 0x007400, length = 0x000040
EV_B : origin = 0x007500, length = 0x000040
SPI_B : origin = 0x007740, length = 0x000010
SCI_B : origin = 0x007750, length = 0x000010
MCBSP_A : origin = 0x007800, length = 0x000040
/* CSM Password Locations */
CSM_PWL : origin = 0x3F7FF8, length = 0x000008
/* SARAM */
RAML0L1 : origin = 0x008000, length = 0x002000
RAMH0 : origin = 0x3F8000, length = 0x002000
}
SECTIONS
{
/* Allocate program areas: */
.reset : > BEGIN PAGE = 0
vectors : > VECTORS PAGE = 0
.cinit : > FLASHJ PAGE = 0
.text : > FLASHA PAGE = 0
/* Allocate data areas: */
.stack : > RAMM0M1 PAGE = 1
.bss : > RAML0L1 PAGE = 1
.ebss : > RAML0L1 PAGE = 1
.const: load = FLASHB PAGE 0, run = RAML0L1
PAGE 1
{
/* Get Run Address */
__const_run = .;
/* Mark Load Address*/
*(.c_mark)
/* Allocate .const */
*(.const)
/* Compute Length */
__const_length = .-__const_run;
}
.econst: load = FLASHB PAGE 0, run = RAML0L1
PAGE 1
{
/* Get Run Address */
__econst_run = .;
/* Mark Load Address*/
*(.ec_mark)
/* Allocate .const */
*(.econst)
/* Compute Length */
__econst_length = .-__econst_run;
}
.sysmem : > RAMH0 PAGE = 1
/* Allocate IQ math areas: */
IQmath : > FLASHI PAGE = 0 /* Math Code */
IQmathFastTables : > FLASHI PAGE = 0 /* Math Tables in fast memory
*/
IQmathTables : > ROM PAGE = 0 /* Math Tables In ROM */
/* Allocate Peripheral Frame 0 Register Structures: */
DevEmuRegsFile : > DEV_EMU PAGE = 1
FlashRegsFile : > FLASH_REGS PAGE = 1
CsmRegsFile : > CSM PAGE = 1
XintfRegsFile : > XINTF PAGE = 1
CpuTimer0RegsFile : > CPU_TIMER0 PAGE = 1
CpuTimer1RegsFile : > CPU_TIMER1 PAGE = 1
CpuTimer2RegsFile : > CPU_TIMER2 PAGE = 1
PieCtrlRegsFile : > PIE_CTRL PAGE = 1
PieVectTable : > PIE_VECT PAGE = 1
/* Allocate Peripheral Frame 2 Register Structures: */
ECanaRegsFile : > ECAN_A PAGE = 1
ECanaMboxesFile : > ECAN_AMBOX PAGE = 1
/* Allocate Peripheral Frame 1 Register Structures: */
SysCtrlRegsFile : > SYSTEM PAGE = 1
SpiaRegsFile : > SPI_A PAGE = 1
SciaRegsFile : > SCI_A PAGE = 1
XIntruptRegsFile : > XINTRUPT PAGE = 1
GpioMuxRegsFile : > GPIOMUX PAGE = 1
GpioDataRegsFile : > GPIODAT PAGE = 1
AdcRegsFile : > ADC PAGE = 1
EvaRegsFile : > EV_A PAGE = 1
EvbRegsFile : > EV_B PAGE = 1
ScibRegsFile : > SCI_B PAGE = 1
McbspaRegsFile : > MCBSP_A PAGE = 1
/* CSM Password Locations */
CsmPwlFile : > CSM_PWL PAGE = 1
}
然後把lib檔案替換成:rts2800_fl040830.lib
就可以啦.
希望你也成功!!!
本人qq: 15007807 歡迎以後交流.
點評:
FLASH不用分這麼細的,編大一點的程式會有麻煩
以下是本人做過的某專案CMD檔案
MEMORY
{
PAGE 0 :
/* For this example, H0 is split between PAGE 0 and PAGE 1 */
/* BEGIN is used for the "boot to HO" bootloader mode */
/* RESET is loaded with the reset vector only if */
/* the boot is from XINTF Zone 7. Otherwise reset vector */
/* is fetched from boot ROM. See .reset section below */
RAMM0 : origin = 0x000000, length = 0x000400
BEGIN : origin = 0x3f7ff6, length = 0x000002
PRAMH0 : origin = 0x3d8000, length = 0x004000 /* */
RESET : origin = 0x3FFFC0, length = 0x000002
PAGE 1 :
/* For this example, H0 is split between PAGE 0 and PAGE 1 */
LSARAM : origin = 0x3de000, length = 0x008000 /* */
RAMM1 : origin = 0x000400, length = 0x000400
DRAMH0 : origin = 0x3f8000, length = 0x001000 /* */
BUFA : origin = 0x8000, length=0x1000
BUFB : origin = 0x9000, length=0x1000
BUFC : origin = 0x3f9000, length=0x1000
RECORDDATA : origin = 0x80020, length=0x700
PARAMETER : origin = 0x80725, length=0x20
PASSWORD : origin = 0x80005, length=0x08
}
SECTIONS
{
/* Setup for "boot to H0" mode:
The codestart section (found in DSP28_CodeStartBranch.asm)
re-directs execution to the start of user code.
Place this section at the start of H0 */
codestart : > BEGIN, PAGE = 0
ramfuncs : > PRAMH0 PAGE = 0
.text : > PRAMH0, PAGE = 0
.cinit : > PRAMH0, PAGE = 0
.pinit : > PRAMH0, PAGE = 0
.switch : > RAMM0, PAGE = 0
.reset : > RESET, PAGE = 0, TYPE = DSECT /* not used, */
.stack : > RAMM1, PAGE = 1
.ebss : > DRAMH0, PAGE = 1
.econst : > LSARAM, PAGE = 1
.esysmem : > DRAMH0, PAGE = 1
.bufferA : {}>BUFA PAGE 1
.bufferB : {}>BUFB PAGE 1
.bufferC : {}>BUFC PAGE 1
.RecordData : {}>RECORDDATA PAGE 1
.parameter : {}>PARAMETER PAGE 1
.password : {}>PASSWORD PAGE 1
}
2007.8.13 19:04 作者:wind 收藏 | 評論:0
於DDS的簡單介紹
分類:預設欄目DDS同DSP(數字訊號處理)一樣,是一項關鍵的數字化技術。DDS是直接數字式頻率合成器(Direct Digital
Synthesizer)的英文縮寫。與傳統的頻率合成器相比,DDS具有低成本、低功耗、高解析度和快速轉換時間等優點,廣泛使用在電信與電子儀器領域,是實現裝置全數字化的一個關鍵技術。
一塊DDS晶片中主要包括頻率控制暫存器、高速相位累加器和正弦計算器三個部分(如Q2220)。頻率控制暫存器可以序列或並行的方式裝載並寄存使用者輸入的頻率控制碼;而相位累加器根據頻率控制碼在每個時鐘週期內進行相位累加,得到一個相位值;正弦計算器則對該相位值計算數字化正弦波幅度(晶片一般通過查表得到)。DDS晶片輸出的一般是數字化的正弦波,因此還需經過高速D/A轉換器和低通濾波器才能得到一個可用的模擬頻率訊號。
另外,有些DDS晶片還具有調幅、調頻和調相等調製功能及片內D/A變換器(如AD7008)。
DDS有如下優點:⑴ 頻率解析度高,輸出頻點多;⑵頻率切換速度快,可達us量級;⑶ 頻率切換時相位連續;⑷ 可以輸出寬頻正交訊號;⑸
輸出相位噪聲低,對參考頻率源的相位噪聲有改善作用;⑹可以產生任意波形;⑺
全數字化實現,便於整合,體積小,重量輕,因此八十年代以來各國都在研製和發展各自的DDS產品,如美國QUALCOMM公司的Q2334,Q2220;STANFORD公司的STEL-1175,STEL-1180;AD公司的AD7008,AD9850,AD9854等。這些DDS晶片的時鐘頻率從幾十兆赫茲到幾百兆赫茲不等,晶片從一般功能到整合有D/A轉換器和正交調製器。
2007.8.13 18:44 作者:wind 收藏 | 評論:0
使用C/C 語言編寫基於DSP程式的注意事項
分類:預設欄目 1、
不影響執行速度的情況下,可以使用c或c/c++語言提供的函式庫,也可以自己設計函式,這樣更易於使用“裁縫師”優化處理,例如:進行絕對值運算,可以呼叫fabs()或abs()函式,也可以使用if...else...判斷語句來替代。
2、 要非常謹慎地使用區域性變數,根據自己專案開發的需要,應儘可能多地使用全域性變數和靜態變數。
3、
一定要非常重視中斷向量表的問題,很多朋友對中斷向量表的呼叫方式不清楚。其實中斷向量表中的中斷名是任意取定的,dsp是不認名字的,它只認地址!!中斷向量表要重新定位。這一點很重要。
4、
要明確dsp軟體開發的第一步是對可用儲存空間的分析,儲存空間分配好壞關係到一個dsp程式設計師的水平。對於dsp,我們有兩種名稱的儲存空間,一種是物理空間,另一種是對映空間。物理空間是dsp上可以存放資料和程式的實際空間(包括外部儲存器),我們的資料和程式最終放到物理空間上,但我們並不能直接訪問它們。我們要訪問物理空間,必須藉助於對映空間才行!!但是對映空間本身是個“虛”空間,是個不存在的空間。所以,往往是對映空間遠遠大於實際的物理空間,有些對映空間,如io對映空間,它本身還代表了一種介面。只有那些物理空間對映到的對映空間才是我們真正可訪問(讀或寫)的儲存空間。
5、 儘可能地減少除法運算,而儘可能多地使用乘法和加法運算代替。
6、
如果ti公司或第三方軟體合作商提供了dsplib或其他的合法子程式庫供呼叫,應儘可能地呼叫使用。這些子程式均使用用匯編寫成,更為重要之處是通過了tms320演算法標準測試。而且,常用的數字訊號處理演算法均有包括!!
7、 儘可能地採用行內函數!!而不用一般的函式!!可以提高程式碼的整合度。
8、 程式設計風格力求簡煉!!儘可能用c語言而不用c++語言。我個人感到雖然c++終程式碼長了一些,好象對執行速度沒有影響。
9、
因為在c5000中double型和float型均佔有2個字,所以都可以使用,而且,可以直接將int型賦給float型或double型,但,儘可能地多使用int資料型別代替!這一點需要注意!!
10、 程式最後至少要加上一個空行,編譯器當這個空行為結尾提示符。
11、 大膽使用位運算子,非常好用!!
2007.8.13 18:09 作者:wind 收藏 | 評論:0
CMD檔案的作用
分類:預設欄目在DSP裡,程式執行過程中也有好多地方需要跳轉,所以需要跳轉的目標地址。如果你在程式設計序時知道你所要跳轉的地址,那就更好的,但實際上,這是很不好控制的。所以就產生了.CMD。它有一個最大的好處,可以把每個段分配地址,所以比如你想從一個段跳到另一個段的時候,就很方便的知道這個段的起始地址。
CMD
它是用來分配rom和ram空間用的,告訴連結程式怎樣計算地址和分配空間.所以不同的晶片就有不同大小的rom和ram.放使用者程式的地方也不盡相同.所以要根據你的晶片進行修改.cmd檔案分兩部分.MEMORY和SECTIONS.
MEMORY
{
PAGE 0 ..........
PAGE 1.........
}
SECTIONS
{SECTIONS
{
.vectors .................
.reset .................
................
}
MEMORY是用來指定晶片的rom和ram的大小和劃分出幾個區間.
PAGE 0 對應rom;PAGE 1對應ram
PAGE 裡包含的區間名字與其後面的引數反映了該區間的起始地址和長度.
如: PAGE 0 : VECS(區間名字): origin(起始地址) = 0h , length (長度)=040h /*VECTORS*/
SECTIONS:(在程式裡新增下面的段名如.vectors.用來指定該段名以下,另一個段名以上的程式(屬於PAGE0)或資料(屬於PAGE1)放到“>”符號後的空間名字所在的地方。
如引用欄位名“.vectors ”的程式或資料將被放到VECS ,VECS是PAGE0即是ROM空間 00H至40H的地方
SECTIONS
{
.vectors : { } > VECS PAGE 0 /* Interrupt vector table */
.reset : { } > VECS PAGE 0 /* Reset code */
............
}
.vectors,.reset都是段名。 加不加“.”隨你便,即.vectors表示名為 “.vectors”的段。
{}表示段的全部,{}> VECS PAGE 0表示將段的全部放入名為 VECS PAGE 0的記憶體區。
example:
/****************************************************************************/
/******** Specify the memory configuration **********************************/
/****************************************************************************/
MEMORY
{
PAGE 0: VECS: origin = 00000h, length = 00040h
LOW: origin = 00040h, length = 03FC0h
SARAM: origin = 04000h, length = 00800h
B0: origin = 0FF00h, length = 00100h
PAGE 1: B0: origin = 00200h, length = 00100h
B1: origin = 00300h, length = 00100h
B2: origin = 00060h, length = 00020h
SARAM: origin = 08000h, length = 00800h
}
/*--------------------------------------------------------------------------*/
/* SECTIONS ALLOCATION */
/*--------------------------------------------------------------------------*/
SECTIONS
{
.text : { } > LOW PAGE 0
.cinit : { } > LOW PAGE 0
.switch : { } > LOW PAGE 0
.const : { } > SARAM PAGE 1
.data : { } > SARAM PAGE 1
.bss : { } > SARAM PAGE 1
.stack : { } > SARAM PAGE 1
.sysmem : { } > SARAM PAGE 1
}
1,系統定義:
.cinit 存放C程式中的變數初值和常量;
.const 存放C程式中的字元常量、浮點常量和用const宣告的常量;
.switch 存放C程式中switch語句的跳針表;
.text 存放C程式的程式碼;
.bss 為C程式中的全域性和靜態變數保留儲存空間;
.far 為C程式中用far宣告的全域性和靜態變數保留空間;
.stack 為C程式系統堆疊保留儲存空間,用於儲存返回地址、函式間的引數傳遞、儲存區域性變數和儲存中間結果;
.sysmem 用於C程式中malloc、calloc和realloc函式動態分配儲存空間
2,使用者定義:
#pragma CODE_SECTION (symbol, "section name";
#pragma DATA_SECTION (symbol, "section name"
DSP的C語言的特殊性
大家在使用51系列C語言時已經注意到,控制器的C語言和PC機上使用的C有一個顯著的特點:經常要對硬體操作,程式中有大量針對控制器內部資源進行操作的語句。所以,開發者要明白怎樣用C語言來操縱控制器的內部資源,既怎樣用C語句操作暫存器和內部儲存器等。
舉個例子,在51彙編中我們寫 MOV A,#20H,彙編程式能夠識別A是指累加器,而在51 C程式中我們寫
ACC=32;,編譯器能夠識別ACC是指累加器而不是一般的變數。即每一個暫存器都有一個專有名字供開發者使用,它們定義在一個頭檔案reg51.h
中,程式設計師只需在程式的開始部分用#include“reg51.h”語句將該檔案包含進來即可。注意:這些暫存器的名字不能用做變數名。
同樣,在TMS320F240的C語言中也有一個頭檔案C240.H定義各個暫存器的名稱,這裡摘錄幾條語句進行介紹。
比如:#define IMR ((PORT)0x0004)
#define XINT1_CR ((PORT)0x07070)
IMR
、XINT1_CR就對應兩個暫存器,實際是暫存器的地址,用高階語言的說法是指標。我們也在程式的開始部分用#include“c240.h”語句將該檔案包含進來。這樣,在DSP的C語言中使用它們只需在前面加一個星號(*),例如,
*IMR=0X1010;/* 將16進位制數1010H賦給IMR暫存器 */
*XINT1_CR=0X0A0B0;/*將16進位制數A0B0H賦給XINT1_CR暫存器 */
最好將c240.h這個檔案打印出來,弄清楚各個暫存器的定義名稱。
TMS320F240晶片的C語言開發過程
簡單地說,整個過程包括以下五個步驟:
1、編輯C語言源程式
2、編譯源程式(注意編譯引數)
3、連結目標檔案(注意用CMD檔案)
4、線上模擬
5、固化程式
2007.8.13 17:06 作者:wind 收藏 | 評論:0
電容的作用
分類:預設欄目1.電容器主要用於交流電路及脈衝電路中,在直流電路中電容器一般起隔斷直流的作用。
2.電容既不產生也不消耗能量,是儲能元件。
3.電容器在電力系統中是提高功率因數的重要器件;在電子電路中是獲得振盪、濾波、相移、旁路、耦合等作用的主要元件。
4.因為在工業上使用的負載主要是電動機感性負載,所以要並電容這容性負載才能使電網平衡.
5.在接地線上,為什麼有的也要通過電容後再接地咧?
答:在直流電路中是抗干擾,把干擾脈衝通過電容接地(在這次要作用是隔直——電路中的電位關係);交流電路中也有這樣通過電容接地的,一般容量較小,也是抗干擾和電位隔離作用.
6.電容補嘗功率因數是怎麼回事?
答:因為在電容上建立電壓首先需要有個充電過程,隨著充電過程,電容上的電壓逐步提高,這樣就會先有電流,後建立電壓的過程,通常我們叫電流超前電壓90度(電容電流回路中無電阻和電感元件時,叫純電容電路)。電動機、變壓器等有線圈的電感電路,因通過電感的電流不能突變的原因,它與電容正好相反,需要先線上圈兩端建立電壓,後才有電流(電感電流回路中無電阻和電容時,叫純電感電路),純電感電路的電流滯後電壓90度。由於功率是電壓乘以電流,當電壓與電流不同時產生時(如:當電容器上的電壓最大時,電已充滿,電流為0;電感上先有電壓時,電感電流也為0),這樣,得到的乘積(功率)也為0!這就是無功。那麼,電容的電壓與電流之間的關係正好與電感的電壓與電流的關係相反,就用電容來補償電感產生的無功,這就是無功補償的原理。
2007.8.13 15:16 作者:wind 收藏 | 評論:0
volatile的用法
分類:預設欄目避免編譯器優化的用法 轉自<海濤的筆記> _lindwen
volatile的本意是“易變的”
由於訪問暫存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優化。比如:
static int i=0;
int main(void)
{
...
while (1)
{
if (i) dosomething();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
程式的本意是希望ISR_2中斷產生時,在main當中呼叫dosomething函式,但是,由於編譯器判斷在main函式裡面沒有修改過i,因此
可能只執行一次對從i到某暫存器的讀***作,然後每次if判斷都只使用這個暫存器裡面的“i副本”,導致dosomething永遠也不會被
呼叫。如果將將變數加上volatile修飾,則編譯器保證對此變數的讀寫***作都不會被優化(肯定執行)。此例中i也應該如此說明。
一般說來,volatile用在如下的幾個地方:
1、中斷服務程式中修改的供其它程式檢測的變數需要加volatile;
2、多工環境下各任務間共享的標誌應該加volatile;
3、儲存器對映的硬體暫存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;
另外,以上這幾種情況經常還要同時考慮資料的完整性(相互關聯的幾個標誌讀了一半被打斷了重寫),在1中可以通過關中斷來實
現,2中可以禁止任務排程,3中則只能依靠硬體的良好設計了。
volatile 的含義
volatile總是與優化有關,編譯器有一種技術叫做資料流分析,分析程式中的變數在哪裡賦值、在哪裡使用、在哪裡失效,分析結果可以用於常量合併,常量傳播等優化,進一步可以死程式碼消除。但有時這些優化不是程式所需要的,這時可以用volatile關鍵字禁止做這些優化,volatile的字面含義是易變的,它有下面的作用:
1 不會在兩個***作之間把volatile變數快取在暫存器中。在多工、中斷、甚至setjmp環境下,變數可能被其他的程式改變,編譯器
自己無法知道,volatile就是告訴編譯器這種情況。
2 不做常量合併、常量傳播等優化,所以像下面的程式碼:
volatile int i = 1;
if (i > 0) ...
if的條件不會當作無條件真。
3 對volatile變數的讀寫不會被優化掉。如果你對一個變數賦值但後面沒用到,編譯器常常可以省略那個賦值***作,然而對Memory Mapped
IO的處理是不能這樣優化的。
前面有人說volatile可以保證對記憶體***作的原子性,這種說法不大準確,其一,x86需要LOCK字首才能在SMP下保證原子性,其二,RISC根本不能對記憶體直接運算,要保證原子性得用別的方法,如atomic_inc。
對於jiffies,它已經宣告為volatile變數,我認為直接用jiffies++就可以了,沒必要用那種複雜的形式,因為那樣也不能保證原子性。
你可能不知道在Pentium及後續CPU中,下面兩組指令
inc jiffies
;;
mov jiffies, %eax
inc %eax
mov %eax, jiffies
作用相同,但一條指令反而不如三條指令快。
現舉例說明(以Keil-c與a51為例
例子來自Keil FQA),看完例子後你應該明白volatile的意思了,如果還不明白,那隻好
再看一遍了。
例1.
void main (void)
{
volatile int i;
int j;
i = 1; //1 不被優化 i=1
i = 2; //2 不被優化 i=1
i = 3; //3 不被優化 i=1
j = 1; //4 被優化
j = 2; //5 被優化
j = 3; //6 j = 3
}
---------------------------------------------------------------------
例2.
函式:
void func (void)
{
unsigned char xdata xdata_junk;
unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;
t1 = *p;
t2 = *p;
}
編譯的彙編為:
0000 7E00 R MOV R6,#HIGH xdata_junk
0002 7F00 R MOV R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----
0004 8F82 MOV DPL,R7
0006 8E83 MOV DPH,R6
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意
0008 E0 MOVX A,@DPTR
0009 F500 R MOV t1,A
000B F500 R MOV t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
000D 22 RET
將函式變為:
void func (void)
{
volatile unsigned char xdata xdata_junk;
volatile unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;
t1 = *p;
t2 = *p;
}
編譯的彙編為:
0000 7E00 R MOV R6,#HIGH xdata_junk
0002 7F00 R MOV R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----
0004 8F82 MOV DPL,R7
0006 8E83 MOV DPH,R6
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
0008 E0 MOVX A,@DPTR
0009 F500 R MOV t1,A ;a處
000B E0 MOVX A,@DPTR
000C F500 R MOV t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
000E 22 RET
比較結果可以看出來,未用volatile關鍵字時,只從*p所指的地址讀一次
如在a處*p的內容有變化,則t2得到的則不是真正*p的內容。
---------------------------------------------------------------------
例3
volatile unsigned char bdata var; // use volatile keyword here
sbit var_0 = var^0;
sbit var_1 = var^1;
unsigned char xdata values[10];
void main (void) {
unsigned char i;
for (i = 0; i < sizeof (values); i++) {
var = values[i];
if (var_0) {
var_1 = 1; //a處
values[i] = var; // without the volatile keyword, the compiler
// assumes that 'var' is unmodified and does not
// reload the variable content.
}
}
}
在此例中,如在a處到下一句執行前,var如有變化則不會,如var=0xff; 則在
values[i] = var;得到的還是values[i] = 1;
---------------------------------------------------------------------
應用舉例:
例1.
#define DBYTE ((unsigned char volatile data *) 0)
說明:此處不用volatile關鍵字,可能得不到真正的內容。
---------------------------------------------------------------------
例2.
#define TEST_VOLATILE_C
//***************************************************************
// verwendete Include Dateien
//***************************************************************
#if __C51__ < 600
#error: !! Keil 版本不正確
#endif
//***************************************************************
// 函式 void v_IntOccured(void)
//***************************************************************
extern void v_IntOccured(void);
//***************************************************************
// 變數定義
//***************************************************************
char xdata cValue1; //全域性xdata
char volatile xdata cValue2; //全域性xdata
//***************************************************************
// 函式: v_ExtInt0()
// 版本:
// 引數:
// 用途:cValue1++,cValue2++
//***************************************************************
void v_ExtInt0(void) interrupt 0 {
cValue1++;
cValue2++;
}
//***************************************************************
// 函式: main()
// 版本:
// 引數:
// 用途:測試volatile
//***************************************************************
void main() {
char cErg;
//1. 使cErg=cValue1;
cErg = cValue1;
//2. 在此處模擬時手動產生中斷INT0,使cValue1++; cValue2++
if (cValue1 != cErg)
v_IntOccured();
//3. 使cErg=cValue2;
cErg = cValue2;
//4. 在此處模擬時手動產生中斷INT0,使cValue1++; cValue2++
if (cValue2 != cErg)
v_IntOccured();
//5. 完成
while (1);
}
//***************************************************************
// 函式: v_IntOccured()
// 版本:
// 引數:
// 用途: 死迴圈
//***************************************************************
void v_IntOccured() {
while(1);
}
模擬可以看出,在沒有用volatile時,即2處,程式不能進入v_IntOccured();
但在4處可以進入v_IntOccured();
2007.8.13 09:41 作者:wind 收藏 | 評論:0
結構體(1)
分類:預設欄目什麼是結構體?
簡單的來說,結構體就是一個可以包含不同資料型別的一個結構,它是一種可以自己定義的資料型別,它的特點和陣列主要有兩點不同,首先結構體可以在一個結構中宣告不同的資料型別,第二相同結構的結構體變數是可以相互賦值的,而陣列是做不到的,因為陣列是單一資料型別的資料集合,它本身不是資料型別(而結構體是),陣列名稱是常量指標,所以不可以做為左值進行運算,所以陣列之間就不能通過陣列名稱相互複製了,即使資料型別和陣列大小完全相同。
定義結構體使用struct修飾符,例如:
struct test
{
float a;
int b;
};
上面的程式碼就定義了一個名為test的結構體,它的資料型別就是test,它包含兩個成員a和b,成員a的資料型別為浮點型,成員b的資料型別為整型。
由於結構體本身就是自定義的資料型別,定義結構體變數的方法和定義普通變數的方法一樣。
test pn1;
這樣就定義了一test結構體資料型別的結構體變數pn1,結構體成員的訪問通過點操作符進行,pn1.a=10 就對結構體變數pn1的成員a進行了賦值操作。
注意:結構體生命的時候本身不佔用任何記憶體空間,只有當你用你定義的結構體型別定義結構體變數的時候計算機才會分配記憶體。
結構體,同樣是可以定義指標的,那麼結構體指標就叫做結構指標。
結構指標通過->符號來訪問成員,下面我們就以上所說的看一個完整的例子:
#include <iostream>
#include <string>
using namespace std;
struct test//定義一個名為test的結構體
{
int a;//定義結構體成員a
int b;//定義結構體成員b
};
void main()
{
test pn1;//定義結構體變數pn1
test pn2;//定義結構體變數pn2
pn2.a=10;//通過成員操作符.給結構體變數pn2中的成員a賦值
pn2.b=3;//通過成員操作符.給結構體變數pn2中的成員b賦值
pn1=pn2;//把pn2中所有的成員值複製給具有相同結構的結構體變數pn1
cout<<pn1.a<<"|"<<pn1.b<<endl;
cout<<pn2.a<<"|"<<pn2.b<<endl;
test *point;//定義結構指標
point=&pn2;//指標指向結構體變數pn2的記憶體地址
cout<<pn2.a<<"|"<<pn2.b<<endl;
point->a=99;//通過結構指標修改結構體變數pn2成員a的值
cout<<pn2.a<<"|"<<pn2.b<<endl;
cout<<point->a<<"|"<<point->b<<endl;
cin.get();
}
總之,結構體可以描述陣列不能夠清晰描述的結構,它具有陣列所不具備的一些功能特性。
2007.8.12 20:31 作者:wind 收藏 | 評論:0
位段
分類:預設欄目位段,將一個位元組分為幾段來存放幾個資訊。所謂位段是以位為單位定義長度的結構體型別中的成員。如:
struct packed-data{
unsigned a:2;
unsigned b:6;
unsigned c:4;
unsigned d:4;
int I;
}data;
其中a,b,c,d分別佔2位,6位,4位,4位。I為整型,佔4 個位元組。
其中a、b、c、d分別佔2位、6位、4位、4位。i為整型。共佔4個位元組。也可以使各個位段不恰好佔滿一個位元組。如:
struct packed-data
{unsigned a∶2;
unsigned b∶3;
unsigned c∶4;
int i;
};
struct packed-data data;
其中a、b、c共佔9位,佔1個位元組多,不到2個位元組。它的後面為int型,佔2個位元組。在a、b、c之後7位空間閒置不用,i從另一位元組開頭起存放。
對於位段成員的引用如下:
data.a =
2;等,但要注意賦值時,不要超出位段定義的範圍。如位段成員a定義為2位,最大值為3,即(11)2,所以data.a=5;就會取5的兩個低位進行賦值,就得不到想要的值了。
關於位段的定義和引用,有幾點重要說明:
①若某一個段要從另一個字開始存放,可以定義:
unsigned a:1;
unsigned b:2;
unsigned :0;
unsigned c:3; (另一單元)
使用長度為0的位段,作用就是使下一個位段從下一個儲存單元開始存放。
②一個位段必須存放在用一個儲存單元中,不能跨兩個單元。
如:
struct T {
unsigned char a : 4;
unsigned char b : 6;
};
結構T的成員a在一個儲存單元中,b則在另一個儲存單元中。
③可以定義無名位段。如:
unsigned a:1;
unsigned :2; (這兩位空間不用)
unsigned b:3;
④位段的長度不能大於儲存單元的長度,也不能定義位段陣列。
2007.8.12 20:26 作者:wind 收藏 | 評論:0
不同編譯器處理位段的差異
分類:預設欄目CCS裡面認位段是從高位開始的,而Keil和凌陽微控制器的編譯器UNSP IDE以及NIOS II
IDE,C++Builder裡認位段是從低位開始的
同樣做一個結構體,在Keil、UNSP IDE、NIOS II IDE、C++Builder裡要這樣:
#define Uint unsigned int
typedef struct
{
Uint bit0 : 1;
Uint bit1 : 1;
Uint bit2 : 1;
Uint bit3 : 1;
Uint bit4 : 1;
Uint bit5 : 1;
Uint bit6 : 1;
Uint bit7 : 1;
Uint bit8 : 1;
Uint bit9 : 1;
Uint bit10 : 1;
Uint bit11 : 1;
Uint bit12 : 1;
Uint bit13 : 1;
Uint bit14 : 1;
Uint bit15 : 1;
}Bit;
而在CCS裡就應該這樣:
#define Uint unsigned int
typedef struct
{
Uint bit15 : 1;
Uint bit14 : 1;
Uint bit13 : 1;
Uint bit12 : 1;
Uint bit11 : 1;
Uint bit10 : 1;
Uint bit9 : 1;
Uint bit8 : 1;
Uint bit7 : 1;
Uint bit6 : 1;
Uint bit5 : 1;
Uint bit4 : 1;
Uint bit3 : 1;
Uint bit2 : 1;
Uint bit1 : 1;
Uint bit0 : 1;
}Bit;
2007.8.12 17:21 作者:wind 收藏 | 評論:1
在ccs下建立一個工程的步驟
分類:預設欄目建立一個工程的步驟:1.在C:ti或任意目錄下建立工程檔名如###.pjt
2.將dsp標頭檔案拷到此資料夾下。將這些標頭檔案新增到工程中,注意:有一個檔案DSP281x_usDelay.asm特殊.由於它是asm檔案,當在預設的檔案型別下全選的時候可能不會選到它,要在檔案型別列表裡點All
files然後全選。3。在工程下建立主函式C檔案,並新增到工程中。4。新增F2812_EzDSP_RAM_lnk.cmd和DSP281x_Headers_nonBIOS.cmd檔案 5。新增庫檔案在C:tic2000cgtoolslibrts2800_ml.6.編譯執行。
如果出現下面的錯誤
undefined first referenced
symbol in file
--------- ----------------
_main C:tic2000cgtoolslibrts2800_ml.lib
>> error: symbol referencing errors - './Debug/sci7.out' not built
Build Complete,
1 Errors, 0 Warnings, 0 Remarks.
這是因為缺少main 這個檔案,只要將它新增到工程檔案裡!
2007.8.7 21:32 作者:wind 收藏 | 評論:0
DSP外部中斷
分類:預設欄目要使用外部中斷要從兩方面進行準備:首先,在硬體上必須將中斷訊號引到DSP的外部中斷引腳上。比如:FPGA的一個output發出中斷請求訊號那麼就將這個管腳與DSP的XINT1相連,XINT1可以通過一個4.7k的電阻上拉到3.3v,
也可以不拉。其次是在軟體上的配置:1.初始外部中斷暫存器
void Xint1_init(void)
{
DINT;
XIntruptRegs.XINT1CR.bit.ENABLE=1;//使能外部中斷
XIntruptRegs.XINT1CR.bit.POLARITY=1;//中斷產生在上升沿,若為0中斷產生在下降沿
EINT;
}
由於這是一個函式,所以也要有函式宣告,函式呼叫,函式定義。
2 使能cpu INT1 中斷
PieCtrlRegs.PIEIER1.bit.INTx4 = 1; //Enable all XINT1 interrupt
IER |= 0x100; // Enable CPU INT
IER |= 0x0001; // enable PIEIER1, and INT1
3.編寫中斷服務程式,
interrupt void Xint1_ISR(void)
{
.......
PieCtrlRegs.PIEACK.all|=0x0001;
}
4將中斷服務程式入口地址賦給中斷向量表
PieVectTable.XINT1=&Xint1_ISR;
總結起來使用外部中斷的步驟如下:
1宣告外部中斷服務程式和外部中斷初始化函式
void Xint1_init(void);
interrupt void Xint1_ISR(void);
2將中斷服務程式入口地址賦給中斷向量表
PieVectTable.XINT1=&Xint1_ISR;
3使能cpu INT1 中斷
IER |= 0x0001; // enable PIEIER1, and INT1
PieCtrlRegs.PIEIER1.bit.INTx4 = 1; //Enable all XINT1 interrupt
4. 在主函式體外定義專斷初始化函式和中斷服務程式
2007.8.7 21:30 作者:wind 收藏 | 評論:0
ti2812中用C語言來實現中斷的說明
分類:預設欄目F2812中用C語言來實現中斷的說明:
1.首先在.cmd中定位系統中斷表:
MEMORY
{
PAGE 0 :
......................................
PAGE 1 :
......................................
PIE_VECT : origin = 0x000D00, length = 0x000100
......................................
}
SECTIONS
{
...................................
PieVectTable : > PIE_VECT, PAGE = 1
.....................................
}
2.在C中制定該中斷的結構體:
#pragma DATA_SECTION(PieVectTable,"PieVectTable");
struct PIE_VECT_TABLE PieVectTable;(在DSP28_GlobalVariableDefs.C中初始化)
3.用一組常數(按照中斷向量的順序)初始化該名字為PIE_VECT_TABLE的表:
typedef interrupt void(*PINT)(void);這裡有些一問,一下應該為函式名??
// Define Vector Table:
struct PIE_VECT_TABLE {
// Reset is never fetched from this table.
// It will always be fetched from 0x3FFFC0 in either
// boot ROM or XINTF Zone 7 depending on the state of
// the XMP/MC input signal. On the F2810 it is always
// fetched from boot ROM.
PINT PIE1_RESERVED;
PINT PIE2_RESERVED;
PINT PIE3_RESERVED;
PINT PIE4_RESERVED;
PINT PIE5_RESERVED;
PINT PIE6_RESERVED;
PINT PIE7_RESERVED;
PINT PIE8_RESERVED;
PINT PIE9_RESERVED;
PINT PIE10_RESERVED;
PINT PIE11_RESERVED;
PINT PIE12_RESERVED;
PINT PIE13_RESERVED;
// Non-Peripheral Interrupts:
PINT XINT13; // XINT13
PINT TINT2; // CPU-Timer2
PINT DATALOG; // Datalogging interrupt
PINT RTOSINT; // RTOS interrupt
PINT EMUINT; // Emulation interrupt
PINT XNMI; // Non-maskable interrupt
PINT ILLEGAL; // Illegal operation TRAP
PINT USER0; // User Defined trap 0
PINT USER1; // User Defined trap 1
PINT USER2; // User Defined trap 2
PINT USER3; // User Defined trap 3
PINT USER4; // User Defined trap 4
PINT USER5; // User Defined trap 5
PINT USER6; // User Defined trap 6
PINT USER7; // User Defined trap 7
PINT USER8; // User Defined trap 8
PINT USER9; // User Defined trap 9
PINT USER10; // User Defined trap 10
PINT USER11; // User Defined trap 11
// Group 1 PIE Peripheral Vectors:
PINT PDPINTA; // EV-A
PINT PDPINTB; // EV-B
PINT rsvd1_3;
PINT XINT1;
PINT XINT2;
PINT ADCINT; // ADC
PINT TINT0; // Timer 0
PINT WAKEINT; // WD
// Group 2 PIE Peripheral Vectors:
PINT CMP1INT; // EV-A
PINT CMP2INT; // EV-A
PINT CMP3INT; // EV-A
PINT T1PINT; // EV-A
PINT T1CINT; // EV-A
PINT T1UFINT; // EV-A
PINT T1OFINT; // EV-A
PINT rsvd2_8;
// Group 3 PIE Peripheral Vectors:
PINT T2PINT; // EV-A
PINT T2CINT; // EV-A
PINT T2UFINT; // EV-A
PINT T2OFINT; // EV-A
PINT CAPINT1; // EV-A
PINT CAPINT2; // EV-A
PINT CAPINT3; // EV-A
PINT rsvd3_8;
// Group 4 PIE Peripheral Vectors:
PINT CMP4INT; // EV-B
PINT CMP5INT; // EV-B
PINT CMP6INT; // EV-B
PINT T3PINT; // EV-B
PINT T3CINT; // EV-B
PINT T3UFINT; // EV-B
PINT T3OFINT; // EV-B
PINT rsvd4_8;
// Group 5 PIE Peripheral Vectors:
PINT T4PINT; // EV-B
PINT T4CINT; // EV-B
PINT T4UFINT; // EV-B
PINT T4OFINT; // EV-B
PINT CAPINT4; // EV-B
PINT CAPINT5; // EV-B
PINT CAPINT6; // EV-B
PINT rsvd5_8;
// Group 6 PIE Peripheral Vectors:
PINT SPIRXINTA; // SPI-A
PINT SPITXINTA; // SPI-A
PINT rsvd6_3;
PINT rsvd6_4;
PINT MRINTA; // McBSP-A
PINT MXINTA; // McBSP-A
PINT rsvd6_7;
PINT rsvd6_8;
// Group 7 PIE Peripheral Vectors:
PINT rsvd7_1;
PINT rsvd7_2;
PINT rsvd7_3;
PINT rsvd7_4;
PINT rsvd7_5;
PINT rsvd7_6;
PINT rsvd7_7;
PINT rsvd7_8;
// Group 8 PIE Peripheral Vectors:
PINT rsvd8_1;
PINT rsvd8_2;
PINT rsvd8_3;
PINT rsvd8_4;
PINT rsvd8_5;
PINT rsvd8_6;
PINT rsvd8_7;
PINT rsvd8_8;
// Group 9 PIE Peripheral Vectors:
PINT RXAINT; // SCI-A
PINT TXAINT; // SCI-A
PINT RXBINT; // SCI-B
PINT TXBINT; // SCI-B
PINT ECAN0INTA; // eCAN
PINT ECAN1INTA; // eCAN
PINT rsvd9_7;
PINT rsvd9_8;
// Group 10 PIE Peripheral Vectors:
PINT rsvd10_1;
PINT rsvd10_2;
PINT rsvd10_3;
PINT rsvd10_4;
PINT rsvd10_5;
PINT rsvd10_6;
PINT rsvd10_7;
PINT rsvd10_8;
// Group 11 PIE Peripheral Vectors:
PINT rsvd11_1;
PINT rsvd11_2;
PINT rsvd11_3;
PINT rsvd11_4;
PINT rsvd11_5;
PINT rsvd11_6;
PINT rsvd11_7;
&nbs