2014-02-27 83 views
0

對於ARM(以及針對該問題的編程)而言,這並不陌生,並且發現I/O端口的位尋址會令人困惑。您可以在特定的端口引腳上定義一個常量,但仍然必須寫入其位值來設置它。例如:將指針的地址轉換爲int

#define MyOutput (*((volatile unsigned long *)0x40025040)) //PF4 
// But to set this bit you must write 
MyOutput = 0x10; 

這讓我覺得很奇怪。如果我解決某個引腳問題,我應該可以將它設置爲1.所以,爲了避免忘記我必須寫入它的位值,我想爲此做一個函數。我已經想出了以下內容,但是我在指針語法或指針轉換爲int時遇到了問題。

int SetOutput(volatile unsigned long* PIN), int ONOFF){ //ON defined as 1, OFF defined as 0 
    volatile unsigned long PortBit = (PIN & 0xFF); 
    if (ONOFF){ 
     return ((PortBit & 0xFF)>>2); 
    } else { 
     return 0; 
    } 
} 

//Called by 
MyOutput = SetOutput(&MyOutput, ON); 

任何人有任何想法或建議?謝謝!

+0

你能告訴我們更多關於'PF4'或地址* 0x40025040 *?這是什麼SOC/CPU?並非所有的GPIO內存控制器都是相同的。 –

+0

這是一個ARM Cortex M4。地址0x40025040是端口F引腳4的基礎。它允許單獨的位尋址,但您仍然必須使用它的位值進行設置,比如MyOutput = 0x10而不是PF4的1。我不想爲每個IO的SET值創建很多常量,而是希望創建一個基於引腳地址創建引腳位掩碼的函數。傳入您想要的地址和值,並返回該引腳的正確位掩碼。我認爲這個邏輯是正確的,但似乎我無法將地址轉換爲int。 Getinvalid操作數爲二進制表達式('long *'和'int')。 – chrismec

+0

您可以將指針轉換爲int並對其進行掩碼。例如,'bit =((uint32)PIN&0x1f0UL)>> 4;'然後使用'mask = 1 << bit;'。我猜測端口是0x40025000,0x40025010,0x40025020,0x40025030,0x40025040,...所以選擇第二個最低半字節並將其用作位移。通常這些引腳是固定的間距,我猜是16個字節。 –

回答

1

您無法解決個別位; C中的最小可尋址單元(通常以硬件)是一個char,即通常是8位的一個字節。

典型的方法是編寫包裝宏或函數。至於你的SetOutput,它似乎很破碎,例如,它試圖從void函數返回一個值,並且0xFF掩碼隔離8位,而不是1(推測可能是該引腳),並且它從不寫入輸出寄存器。如果該位0x10控制你想要的腳,典型的方法是:

MyOutput |= 0x10; // set bit 
MyOutput &= ~0x10; // unset bit 
MyOutput ^= 0x10; // toggle bit 

您可以創建圍繞這些必要的宏。要檢查所有的輸入寄存器中的相應位的狀態,你可以使用:

if (MyInput & 0x10) { 
    // bit is set 
} 
+0

另外,它實際上並沒有設置位。 –

+1

某些ARM CPU(例如Cortex-M)[bit biting](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/Behcjiic.html);在硬件方面,正常假設可能不正確。也就是說,我不知道這個問題的正確性。但我認爲這個答案中可能有一些假設,在大多數情況下這可能是正確的。 –

+1

@artlessnoise這個假設基本上是標準C,它將'sizeof(char)'定義爲'1',所以從這個意義上說,純C無法直接使用指針算法來更精確地處理任何更精確的事物。我想比特帶是一種通過將特定的字或字節映射到單個位來解決這個問題的方法。 – Arkku

2

克里斯, 我不是Cortex-M的專家,所以你的MMV。但是根據你上面的描述,你試圖用一種非常複雜的方式來修改外設寄存器中的一個位。有兩種方式可以正常處理它:

  1. 使用一組宏(我最熟悉的)。由於宏在編譯期間直接轉換爲所需的精確值/操作,因此這會減少空間和CPU時間與每次讀/寫引腳的調用函數相比的開銷。

  2. 改爲使用PF4寄存器的位帶地址(從未使用過Cortex-M)。這看起來像是用你的架構去做的首選方式。

根據ARM Cortex-M4技術參考手冊的第3.4節,您可以使用位帶別名地址修改PF4位。關於如何運作的細節可以在TRM的第3.7節中找到。

基於您的代碼以上,PF4的是4位在地址0x40025040,對位帶式給出(從TRM下合理使用截取):

bit_band_base是別名區的起始地址。 (0x42000000) •byte_offset是包含目標位的位帶區域中的字節數。 (0x00025040) •bit_number是目標位的位位置0到7。 (爲0x4)

bit_word_offset = (byte_offset x 32) + (bit_number × 4) 

bwo = 0x25040* 0x20 + 0x4 * 0x4 = 0x004A0810 

bit_word_addr = bit_band_base + bit_word_offset 

bwa = 0x42000000 + 0x4A0810 = 0x424A0810 

bit_word_offset處於位帶存儲區中的目標位的位置。 •bit_word_addr是映射到 目標位的別名存儲器區域中的字地址。

所以

*(volatile unsigned long *)0x424A0810 = 0x1; 

等同於寫

*MyOutput |= 0x10; 

如果你真的想要去使用功能和直接寫入的航線,試試這個,而不是(限制僅PF31,如果PF需要高於31,實現留給讀者);此代碼包含基於PC的測試#define,因此您可以在命令行上使用gcc進行編譯。

#include <inttypes.h> 
#include <stdio.h> 

#define PC_TESTING 1 

#if PC_TESTING 
    unsigned long FAKE_PFBASE; 
    #define PFBASE &FAKE_PFBASE 
#else 
    #define PFBASE (volatile unsigned long *) 0x40025040 
#endif 

#define SUCCESS 0 
#define ERROR_INVALID_PIN -1 
#define ERROR_INVALID_STATE -2 

typedef enum {OFF = 0, ON} ONOFF_t; 

typedef enum { PF0 = 0, PF1, PF2, PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF10, PF11, PF12, PF13, PF14, PF15, PF16, PF17, PF18, PF19, PF20, PF21, PF22, PF23, PF24, PF25, PF26, PF27, PF28, PF29, PF30, PF31 } PIN_t; 

int SetOutput(PIN_t PIN, ONOFF_t ONOFF) 
{ 
    uint32_t mask, value; 

    // Implementing more than 32 pins is exercise for the reader 
    if (PIN > 31) 
     return ERROR_INVALID_PIN; 

    // In case someone did something wrong 
    if (ONOFF != OFF && ONOFF != ON) 
     return ERROR_INVALID_STATE; 

    //Broken into separate steps for ease of reading 
    //Select the bit of interest 
    mask = 1 << PIN; 

    //Clear the bit of interest 
    value = *PFBASE & ~mask; //~ is a bit-wise invert. 0x0000 0010 becomes 0xFFFF FFEF 

    //Set the bit of interest if that is requested 
    if(ON == ONOFF) 
     value |= mask; 

    *PFBASE = value; 

    return SUCCESS; 
} 

int main() 
{ 
int success = 0; 

FAKE_PFBASE = 0l; 

success = SetOutput(PF4, ON); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

success = SetOutput(PF0, ON); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

success = SetOutput(PF0, OFF); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

//Error handling left to reader 
success = SetOutput(33, OFF); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

success = SetOutput(PF2, 2); 
printf (" Success = %d, *PFBASE = 0x%08x\n", success, *PFBASE); 

return 0; 
} 

輸出示例:

$ ./a.out 
Success = 0, *PFBASE = 0x00000010 
Success = 0, *PFBASE = 0x00000011 
Success = 0, *PFBASE = 0x00000010 
Success = -1, *PFBASE = 0x00000010 
Success = -2, *PFBASE = 0x00000010