外部資料結構
在寫 CPU 或介面晶片的驅動程式時, 經常會使用到某些固定位址, 如: ARM Cortex-M 把系統設定及狀態對應到記憶體的特定位址, 某些周邊 IC, 例如 PCI, USB... 也是使用一小段記憶體空間. (註: 常見的周邊 IC 暫存器定址有二種: 一種是只有二個位址空間, 一個是 '指令/地址暫存器', 一個是 '資料暫存器'; 另一種則是直接使用 CPU 的記憶體空間來對應)
軟體功力較佳的 SoC 晶片製造廠所提供的驅動程式, 通常會將這一小段記憶體空間以結構 (struct) 的方式定義好, 再使用 define 將特定的位址值 (常數), 定義成一虛擬變數. 當然如果晶片功能比較簡單就不見得是用這種方法.
// Define Hardware register structures
// Key word 'volatile' is necessary for not to cache value in CPU registers
typedef struct {
volatile unsigned long DATA; // BASE+0x00
volatile unsigned long DSR; // 0x04
unsigned long RESERVED[4]; // 0x08~0x14
volatile unsigned long FLAG; // 0x18
...
} CHIP_t;
// Define virtual pointers point to every hardware unit
#define pCHIP_unit0 ((CHIP_t *) 0x40003000)
#define pCHIP_unit1 ((CHIP_t *) 0x40004000)
#define pCHIP_unit2 ((CHIP_t *) 0x40005000)
...
// Codes that reference the hardware
pCHIP_unit0->DSR = 0xA0; // 0x40003004
pCHIP_unit1->DATA = value; // 0x40004000
上例是使用 '結構指標' 型式的表示方法 (->) 來存取結構元素, 當然我們也可以直接使用 '結構變數' 型式的表示方法 (.) 來存取結構元素.
// Define virtual variable for every hardware unit
#define CHIP_unit0 (*(CHIP_t *) 0x40003000)
#define CHIP_unit1 (*(CHIP_t *) 0x40004000)
#define CHIP_unit2 (*(CHIP_t *) 0x40005000)
...
// Codes that reference the hardware
CHIP_unit0.DSR = 0xA0; // 0x40003004
CHIP_unit1.DATA = value; // 0x40004000
要注意的是這二種寫法編輯出來的 obj. code 應該要一模一樣的 (因為 compiler 知道: 變數的地址是一個常數). 應用上完全取決於實際的需求, 或者你習慣看哪一種型式.
外部函數
位於外部的特定位址的函數宣告法如下:
/* Define virtual function (external function)
* at specified address
*/
#define FLASH_WRITE(a,b,c) \
((uint8_t (*)(void *, void *, uint16_t))0x003F00)(a, b, c)
置換說明:
FLASH_WRITE 被換成了常數(特定位址) 0x003F00, 同時這個常數被 (uint8_t (*)(void*, void*, uint16_t)) 作了型別轉換 (type casting), 其中:
- (*)(...) 是把常數(特定位址)轉型為函數的位址
- uint8_t 是此函數的回傳值型別
- (void *, void *, uint16_t) 是後面的函數參數 (a, b, c) 各自的型別 (依序一個對應一個), 用來作為型別檢查
- 最後面的 (a, b, c) 和前面 FLASH_WRITE 的參數位置保持一致, 所以呼叫函數時的參數傳遞也保持一致

指定位址 0x003000 的型別轉換
例二: (上例轉成 Keil C51)
/* Define virtual function (external function)
* at specified address
*/
#define FLASH_WRITE(a,b,c) \
((U8_T (CODE *)(void XDATA *, void CODE *, U16_T))0x003F00)(a, b, c)
參考資料
function pointer
#include <stdio.h>
void my_int_func(int x)
{
printf( "%d\n", x );
}
int main()
{
void (*foo)(int);
foo = &my_int_func; // the ampersand '&' is actually optional
return 0;
}
相關文章
- C 語言:型別轉換 (Type Casting) 技巧之一
- C 語言:型別轉換 (Type Casting) 技巧之二 (不同型別資料之轉換)
- C 語言:型別轉換 (Type Casting) 技巧之三 指定位址 (資料結構, 函數)
- C 語言:型別轉換 (Type Casting) 技巧之四 (結構運算子 "->" 和 "." 互換)
文章標籤
全站熱搜