2012-03-17 53 views
0

我目前正在設置一個C語言框架,用於幾個微控制器之間的使用。 該框架必須攜帶所有設備特定的代碼,因此該應用程序只包含外設的抽象用法(如SerialPort_Read,Write,SetBaudRate等)在宏中的查詢表C

我一直在努力尋找解決方案在C中是I/O引腳圖。我已經看到了一些項目(比如非常流行的Arduino),其中的引腳映射被放置在運行時使用的LUT(查找表)中。但是,這個LUT在運行時永遠不會被修改,所以沒有必要在內存中使用它。 例如,該功能解決了一些比特索引和從一些「const的UINT」表寄存器,以及設置或清除位:

void pinMode(uint8_t pin, uint8_t mode) 
{ 
     uint8_t bit = digitalPinToBitMask(pin); 
     uint8_t port = digitalPinToPort(pin); 
     volatile uint8_t *reg; 

     if (port == NOT_A_PIN) return; 

     // JWS: can I let the optimizer do this? 
     reg = portModeRegister(port); 

     if (mode == INPUT) { 
       uint8_t oldSREG = SREG; 
       cli(); 
       *reg &= ~bit; 
       SREG = oldSREG; 
     } else { 
       uint8_t oldSREG = SREG; 
       cli(); 
       *reg |= bit; 
       SREG = oldSREG; 
     } 
} 

因爲這是在控制器上運行它的排水的效率及速度實際的C代碼。我寧願定義某種宏,做同樣的事情,但編制的「一個班輪」,可以更有效地編譯時已經解析:

GPIO_Write(PORTA, 5, 1); // Write '1' to pin 5 on PORTA 
> LATA |= 1<<5; // Sets bit 5 high 
GPIO_Tris(PORTA, 4, OUTPUT); // Set pin 4 on PORTA to output 
> PORTA &= ~(1<<4); // sets pin 4 as output I/O type 

有誰知道這是否可能(以及如何)定義和使用查找表與C中的宏?

此時我正在使用我認爲基於GCC的MicroChip C30編譯器。它應該可以在不同的編譯器之間移植,包括MicroChip C18,C32以及ARM和AVR。

+0

請請勿鏈接到外部網站上的代碼。 – 2012-03-17 20:27:35

+0

好的,謝謝,我刪除了鏈接並在此處解釋它。 – Hans 2012-03-17 20:32:47

+0

在您的示例代碼中,您希望優化器執行什麼操作?如果端口在運行時具有不同的值,具體取決於函數的調用方式。 – blueshift 2012-03-17 20:44:18

回答

2

爲了您的具體的例子,這些方針將工作:

#define WRITE_PORTA LATA 
#define GPIO_Write(port, pin, value)   \ 
    (value ? WRITE_##port |= (1U << (pin)) \   
      : WRITE_##port &= ~(1U << (pin))) 

#define INPUT 0 
#define OUTPUT 1 
#define GPIO_Tris(port, pin, direction)      \ 
    ((direction) == INPUT ? port |= (1U << (pin)) \ 
          : port &= ~(1U << (pin))) 

你必須確保的方式,系統會明白定義LATAPORTA - 特別是試圖超載它的意義在你的例子中看起來似乎很難解決。

+0

這似乎是正確的。不要以爲你需要額外的TRIS_宏,只是PORT ##端口應該做的?也許一個使用示例會幫助提問者獲得它。 – blueshift 2012-03-17 21:28:11

+0

良好的通話 - 這將清理事情。就一個例子而言,我讓他們的工作與OP在他的問題中完全一樣。 – 2012-03-17 21:33:04

+0

是的,但你沒有*顯示*他們的工作。如果不熟悉##,提問者可能不會用他們剛剛打過電話的小樹枝。 (A,1,1)。 – blueshift 2012-03-17 21:37:20

1

您定位了哪個處理器或微控制器? 您可能會低估LUT的實用性。

對於許多處理器來說,查詢表不僅僅是將'邏輯'引腳號映射到單個值,即'物理'引腳號。 LUT將'邏輯'引腳號映射到若干條信息。通常,「邏輯」引腳被映射到相應讀/輸入或寫/輸出寄存器的端口地址,以及讀或寫寄存器內的位偏移量。所以許多MCU的引腳值都被映射到一個結構。它可能還包括映射到數據方向寄存器和其中的字段,以及設置上拉或下拉電阻狀態的寄存器。

例如,我有代碼複用8x8顯示。在運行時,我需要使用pinMode將引腳從輸出轉換爲高阻抗輸入,並且需要以某種方式對信息進行編碼。

在某些MCU上可以做到這一點,有些聰明才智。 ARM MCU的使用「位帶尋址」 http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/CHDJHIDF.html

這對於每個端口引腳分配一個唯一的存儲地址和固定偏移可以推導出銷的地址(我相信8051,雖然我從來沒有使用)其他數據寄存器和其他功能。這並不神奇,代碼對通常存儲在LUT中的信息進行編碼。

對於其他MCU,它們確實需要端口和位的位置,所以每個引腳編號都有兩個值。

如果你願意拋棄使用整數作爲引腳的想法,而是使用名稱,比如P0,P1,那麼你可以初始化很多const struct的,每個引腳名稱一個,並且你的函數將採用const結構值。該結構將包含初始化的端口和位偏移量或位掩碼值。編譯器可以針對速度進行優化。這樣可以避免使用LUT,但仍然會爲使用的引腳使用相似數量的空間。您可以安排它,以便不需要將未使用的引腳包含在代碼中,從而節省空間。

編輯:如果你願意使用C++,我會建議C++模板,它可以提供比宏更好的解決方案。它們可以是類型安全的,而且往往更容易調試(如果你有硬件調試,如JTAG和gdb)

+0

我目前針對的是16位和32位Microchip PIC,我認爲它沒有位帶。我將不得不考慮C++的支持,我不知道我的環境在C++上的確切可能性,我會查找它。端口索引是最大的麻煩,將其解決爲「單維」似乎簡單得多,但不幸的是,許多常量結構與Arduino正在做的非常相似。 – Hans 2012-03-17 21:17:23

+0

恐怕我對16位PIC知之甚少。 PIC32不具有位帶,儘管它具有偷偷摸摸+快速設置,清除和切換位。我不知道PIC32是否有每個端口地址16或32個I/O引腳。 ARM只有16個I/O引腳/端口地址,因此可以將兩個數據打包成一個32位字。然後,可以用宏來操縱它以獲取端口和位,並希望編譯器可以在編譯時計算值。 – gbulmer 2012-03-17 23:42:15

1

考慮以下宏:

#define write(port, pin, value) do { \ 
    if (value) \ 
    LAT##port |= 1 << pin; \ 
    else \ 
    LAT##port &= ~(1 << pin); \ 
} while (0) 

用法:

write(A, 3, 1); // compiles to LATA |= 1 << 3; 
write(B, 2, 0); // compiles to LATB &= ~(1 << 2); 

是那是你以後的事情?

+0

與Carl發佈的內容非常相似。如果我自己也使用C風格,因爲微芯片C編譯器沒有得到卡爾發佈的內容。 仍然不是我尋找的最靈活和可讀的表結構,但我想唯一的解決方案是結構。 – Hans 2012-03-17 22:21:48

0

我見過它做(https://github.com/triffid/Teacup_Firmware/blob/Gen7/arduino.h)與一對夫婦宏:

/// Read a pin 
#define  _READ(IO)     (IO ## _RPORT & MASK(IO ## _PIN)) 
/// write to a pin 
#define  _WRITE(IO, v)   do { if (v) { IO ## _WPORT |= MASK(IO ## _PIN); } else { IO ## _WPORT &= ~MASK(IO ## _PIN); }; } while (0) 

/// set pin as input 
#define  _SET_INPUT(IO)  do { IO ## _DDR &= ~MASK(IO ## _PIN); } while (0) 
/// set pin as output 
#define  _SET_OUTPUT(IO)  do { IO ## _DDR |= MASK(IO ## _PIN); } while (0) 



// why double up on these macros? see http://gcc.gnu.org/onlinedocs/cpp/Stringification.html 

/// Read a pin wrapper 
#define  READ(IO)     _READ(IO) 
/// Write to a pin wrapper 
#define  WRITE(IO, v)   _WRITE(IO, v) 
/// set pin as input wrapper 
#define  SET_INPUT(IO)   _SET_INPUT(IO) 
/// set pin as output wrapper 
#define  SET_OUTPUT(IO)  _SET_OUTPUT(IO) 

有:

#define DIO0_PIN  PIND0 
#define DIO0_RPORT  PIND 
#define DIO0_WPORT  PORTD 
#define DIO0_PWM  &OCR0B 
#define DIO0_DDR  DDRD 

#define DIO1_PIN  PIND1 
#define DIO1_RPORT  PIND 
#define DIO1_WPORT  PORTD 
#define DIO1_PWM  &OCR2B 
#define DIO1_DDR  DDRD 
... 

您可以修改宏取直的整數,而不是翁