C語言是一門通用計算機編程語言,應用廣泛。C語言的設計目標是提供一種能以簡易的方式編譯、處理低級存儲器、產生少量的機器碼以及不需要任何運行環境支持便能運行的編程語言。盡管C語言提供了許多低級處理的功能,但仍然保持著良好跨平臺的特性。
以一個標準規格寫出的C語言程序可在許多電腦平臺上進行編譯,甚至包含一些嵌入式處理器(單片機或稱MCU)以及超級電腦等作業平臺。二十世紀八十年代,為了避免各開發廠商用的C語言語法產生差異,由美國國家標準局為C語言訂定了一套完整的國際標準語法,稱為ANSI C,作為C語言最初的標準。
控制器(英文名稱:controller)是指按照預定順序改變主電路或控制電路的接線和改變電路中電阻值來控制電動機的啟動、調速、制動和反向的主令裝置。由程序計數器、指令寄存器、指令譯碼器、時序產生器和操作控制器組成,它是發布命令的“決策機構”,即完成協調和指揮整個計算機系統的操作。
那么C語言如何控制寄存器呢?接下來我們一起來了解一下。
C語言控制寄存器
在嵌入式軟件的開發過程中,我們常用的語言主要是:匯編語言和C語言。相比較于匯編語言,C語言對我們來說,更貼近我們的一些語言習慣。在DSP的開發過程中,我們主要還是用C語言,其中最最常用的操作就是對于DSP各個寄存器的控制了。
那么如何用C語言對DSP的寄存器進行操作呢?
我們先來說書單片機里面是如何操作的:一般寄存器在單片機頭文件中的宏定義都有如下的形式:
#define TIFR *((volatile unsigned char *)0x58) /*ATmega16的TIFR寄存器*/
在ATmega16中TIFR寄存器的地址是0x0058,我們要實現:
TIFR = 0x01
這條,就是要把0x58這個地址的內容修改成0x01。 而在C語言中,指針就是地址。現在要告訴編譯器0x58是地址,就要把0x58強制轉換成指針(unsigned char *)0x58。 這樣(unsigned char *)0x58就表示TIFR在ATmega16 中的地址了,而*((unsigned char *)0x58)表示這個地址的內容。
然后如果想對寄存器TIFR單個的位進行下面的操作,
(1)將寄存器TIFR的第1位置“1”
TIFR |= (1 《《 1);
(2) 將寄存器REG的第3位清零
TIFR &= ~(1 《《 3);
(3) 將寄存器REG的第3、5位置“1”
TIFR |= (1 《《 5) | (1 《《 3);
(4) 將寄存器REG的第3、5位清零
TIFR &= ~( (1 《《 5) | (1 《《 3) );
在單片機里面是使用宏定義的方式來對寄存器進行操作。而在DSP里面,是使用位定義+共同體的方式來定義和操作寄存器。如下:
// ECCTL1控制寄存器的位定義如下:
struct ECCTL1_BITS { // bits description
Uint16 CAP1POL:1; // 0 Capture Event 1 Polarity select
Uint16 CTRRST1:1; // 1 Counter Reset on Capture Event 1
Uint16 CAP2POL:1; // 2 Capture Event 2 Polarity select
Uint16 CTRRST2:1; // 3 Counter Reset on Capture Event 2
Uint16 CAP3POL:1; // 4 Capture Event 3 Polarity select
Uint16 CTRRST3:1; // 5 Counter Reset on Capture Event 3
Uint16 CAP4POL:1; // 6 Capture Event 4 Polarity select
Uint16 CTRRST4:1; // 7 Counter Reset on Capture Event 4
Uint16 CAPLDEN:1; // 8 Enable Loading CAP1-4 regs on a Cap Event
Uint16 PRESCALE:5; // 13:9 Event Filter prescale select
Uint16 FREE_SOFT:2; // 15:14 Emulation mode
};
struct ECCTL1_BITS bit;
在上面的代碼中我們用位域的方式表示了 TCR寄存器的數據結構。同時聲明了一個
struct TCR_BITS類型的變量bit,那么我們就可以通過bit對寄存器每個位進行控制,比如
bit.CTRRST2=0;
此時有一個問題,就是如果我們想對整個寄存器進行整體的控制該如何呢?我們通過定義共同體來實現既可以對寄存器的每個位進行控制,又能對寄存器整體方便的控制。如下:
union ECCTL1_REG {
Uint16 all;
struct ECCTL1_BITS bit;
};
TCR_REG.all=0xxxx; //對寄存器整體操作
TCR_REG.CTRRST2=0; //對寄存器單個位操作
而在DSP里面,某一個功能是靠一個模塊來實現的,而每一個功能模塊包含了許多不同的寄存器,比如28335里面CAP脈沖捕獲模塊的寄存器就有以下這么多:
為了方便統一管理和編程開發方便,TI公司將ECap模塊的所有寄存器定義成一個結構體ECAP_REGS,如下:
struct ECAP_REGS {
Uint32 TSCTR; // Time stamp counter
Uint32 CTRPHS; // Counter phase
Uint32 CAP1; // Capture 1
Uint32 CAP2; // Capture 2
Uint32 CAP3; // Capture 3
Uint32 CAP4; // Capture 4
Uint16 rsvd1[8]; // reserved
union ECCTL1_REG ECCTL1; // Capture Control Reg 1
union ECCTL2_REG ECCTL2; // Capture Control Reg 2
union ECEINT_REG ECEINT; // ECAP interrupt enable
union ECFLG_REG ECFLG; // ECAP interrupt flags
union ECFLG_REG ECCLR; // ECAP interrupt clear
union ECEINT_REG ECFRC; // ECAP interrupt force
Uint16 rsvd2[6]; // reserved
};
我們可以看到,在結構體ECAP_REGS中有的成員是 Uint32形式,有的是union形式,其中定義為union形式的成員既可以對寄存器整體操作,又可以對寄存器進行位操作,而定義為Uint16 的成員只能進行位操作。
在定義了ECAP_REGS結構體之后,需要聲明ECAP_REGS類型的變量,而28335有6路的ECap,所以聲明如下:
extern volatile struct ECAP_REGS ECap1Regs;
extern volatile struct ECAP_REGS ECap2Regs;
extern volatile struct ECAP_REGS ECap3Regs;
extern volatile struct ECAP_REGS ECap4Regs;
extern volatile struct ECAP_REGS ECap5Regs;
extern volatile struct ECAP_REGS ECap6Regs;
其中extern表示聲明的是一個全局變量,關鍵字volatile代表寄存器的值可以被外部代碼任意改變,比如被中斷改變。
volatile 在 DSP 中的理解:該單詞的意思是可變的,易變的。在 DSP 中一些寄存器的值的變化有兩種情況:
(2)軟件上的變化,例如對某個變量賦值等。
當加入了關鍵字 volatile,對軟件來說,硬件上變化的值是不可預知的,提示編譯器每次讀取該變量時,都要直接讀取該變量地址中的寄存器,保證了數據的正確性。
以上我們回顧一下:經過位定義——共同體——結構體的過程就是TI公司提供給我們的DSP2833x_ECap.h里面的內容。
而28335的寄存器結構是固定不變的,所以這個頭文件我們在項目的開發中就可以直接拿來用了,添加到include中即可。
此外,大家也不必去死記各種各樣的寄存器,因為CCS軟件有“感應”功能。在我們加載了頭文件之后,輸入寄存器名字之后,輸入 “。”就會彈出可選的下拉列表來選擇你需要的寄存器。
ECap1Regs.ECCTL1_REG.bit.CAP2POL=0xxxxx;
上面一行代碼從右至左依次代表:
功能模塊結構體——某一個寄存器——寄存器的某一位;
這樣我們就可以在自己的.c源文件中對各種各樣的寄存器進行配置和操作,來實現自己的開發目的。
評論
查看更多