文章程式碼顯示

2018年2月13日 星期二

《筆記》C語言 - 07_7:讀取及修改指定位址的資料、操作 MCU 暫存器、指標定義最終整理

讀取及修改指定位址的資料

#include "stdio.h"

void main(){

    unsigned int *ptr; //定義一個名為 ptr 的指標變數,指向 unsigned int 型別
    int i;

    unsigned int buf[4] = { 0x123f, 0x3fa4, 0xfab6, 0xffff }; //定義一個陣列

    ptr = buf; //使 ptr 指向 buf

    // 取得 buf 第一個元素的地址
    printf("The address of the first element of 'buf' = %p , (0x%x in ptr) \n", buf, ptr);

    // 以指標(位址) 列印出 buf 陣列的各個元素
    for ( i = 0; i < sizeof(buf)/sizeof(buf[0]); i++){
        printf("0x%x\n", *(ptr + i) );
    }

    // 由上述結果我們得知,陣列 buf 的起始位址為 0x0028FF28
    // 因 unsigned int 所佔的長度為 4 bytes, 故陣列中第二個元素的地址為
    // 0x0028FF28 + 0x04 = 0x0028FF2C

    // 嘗試將 ptr 指向 0x0028FF2C
    ptr = 0x0028FF2C;
    printf("ptr = 0x%x \n", ptr);

    // 列印看看此時 ptr 所對應到的內容
    printf("0x%x \n", *ptr);

    // 嘗試以此地址修改該位址的數據
    *ptr = 0x789A;
    printf("Now, buf = \n");

    // 以指標(位址) 列印出 buf 陣列的各個元素
    for ( i = 0; i < sizeof(buf)/sizeof(buf[0]); i++){
        printf("0x%x\n", *(buf+i) );
    }

}


在 MCU 的應用領域,我們常對一個 "暫存器位址" 進行値的讀取或是修改

其作法可為以下(對一個 32 bits ( int* )的暫存器進行値的修改,其位址為 0x0028ff2c

#include "stdio.h"

void main(){

 // 宣告一個指標變數 ptr ,指向 unsigned int 型別
 // 因 unsigned int 為 32 bits,故此定義適用於 32 bits 的暫存器
 // 若要操作的是 8 位元 MCU ,則此處應改成 unsigned char
 unsigned int *ptr;

 ptr = (unsigned int*)0x0028ff2c; //指向地址 0x0028ff2c

 *ptr = 265462; // 修改位於 0x0028ff2c 的暫存器的值

 int value = *ptr; //讀取位於 0x0028ff2c 的暫存器的值
 printf("The value in address 0x%x = %d", ptr, value);

}

對 8bits 的暫存器進行修改

以下為很經典的寫法,搭配了 Base address 對各個暫存器讀寫



#include "stdio.h"

#define getValue(base,offset) (*(volatile unsigned char *)(base + offset))
#define DDRB getValue(BasePtr, 6)

int main(){

    unsigned char BASE[] = { 0x00, 0x01, 0x02, 0x03, 0x1A, 0x56, 0x77, 0xff };
    unsigned char* const BasePtr = BASE;

    printf("BASE address = %p\n", BasePtr);
    printf("DDRB = BASE address offset 6 = 0x%x\n\n", DDRB );
    // DDRB = (*(volatile unsigned char *)(BasePtr + 6));

    DDRB = 0x99; // 修改 DDRB 的值
    printf("DDRB = BASE address offset 6 = 0x%x\n\n", DDRB );

    DDRB = DDRB & 0x01; // 修改 DDRB 的值
    printf("DDRB = BASE address offset 6 = 0x%x", DDRB );

    return 0;
}




針對關鍵語句說明

#define getValue(base,offset) (*(volatile unsigned char *)(base + offset))
#define DDRB getValue(BasePtr, 6)


第一個語句意謂著我們定義一個函數 getValue 其具有兩個輸入參數,分別為 Base address 與要 offset 的大小

後續的部分需要拆為兩個部份來看,首先

(volatile unsigned char *)(base + offset)

意謂著將 (base + offset) 數據強制轉型為 (unsigned char *) 指標,表明 (base + offset) 是一個位址而不是普通的數值,volatile 會在此出現的原因請參考此連結 與 此連結

接著我們就可以用 "*(取值運算子)" 來對該位址進行取值了

第二個語句我們定義 DDRB 為 getValue(BasePtr, 6) ,簡而言之就是呼叫了第一個語句

當然,我們可以不要搞得這麼複雜

假設 DDRB 的位址為 0x0028FF3A,我們可以直接這樣進行定義

#define DDRB (*(volatile unsigned char *)(0x0028FF3A))

但擴充性較為不足,或許更常出現的是如以下的定義

#define BASE 0x0028FF34
#define PORTA (*(volatile unsigned char *)(BASE + 0x01))
#define PORTB (*(volatile unsigned char *)(BASE + 0x02))
#define PORTC (*(volatile unsigned char *)(BASE + 0x03))
#define PORTD (*(volatile unsigned char *)(BASE + 0x04))
#define DDRA (*(volatile unsigned char *)(BASE + 0x05))
#define DDRB (*(volatile unsigned char *)(BASE + 0x06))
#define DDRC (*(volatile unsigned char *)(BASE + 0x07))

指標定義最終整理


↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引