[英]LUT in a macro C
我目前正在使用C建立框架,以便在多個微控制器之間使用。 該框架必須包含所有設備特定的代碼,因此該應用程序僅包含外圍設備的抽象用法(例如SerialPort_Read,write,SetBaudRate等)。
我努力尋找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語言中使用宏定義和使用查找表?
目前,我正在使用MicroChip C30編譯器,我相信它是基於GCC的。 它應該可以在不同的編譯器之間移植,包括MicroChip C18,C32以及ARM和AVR。
對於您的特定示例,遵循以下原則的方法將起作用:
#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)))
您必須確保以系統可以理解的方式定義LATA
和PORTA
,尤其是嘗試重試其含義(如您的示例中所示)可能很難解決。
您定位到哪個處理器或微控制器? 您可能低估了LUT的用途。
對於許多處理器,LUT所做的不只是將“邏輯”引腳號映射到單個值(“物理”引腳號)。 LUT將“邏輯”引腳號映射到幾條信息。
通常,“邏輯”引腳映射到適當的讀/輸入或寫/輸出寄存器的端口地址,以及讀或寫寄存器內的位偏移。 因此,在許多MCU上,引腳值實際上已映射到結構。 它還可能包括到數據方向寄存器和其中的字段的映射,以及設置上拉或下拉電阻器狀態的寄存器。
例如,我有用於多路8x8顯示的代碼。 在運行時,我需要使用pinMode將引腳從輸出變為高阻抗輸入,以便信息需要以某種方式進行編碼。
可以在某些MCU上做一些巧妙的事情。 ARM MCU(我相信8051,盡管我從未使用過)使用“位帶尋址” http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/CHDJHIDF html的
這會為每個端口引腳分配一個唯一的存儲器地址,固定偏移量可以得出其他數據寄存器和其他功能的引腳地址。 這不是魔術,代碼對經常存儲在LUT中的信息進行編碼。
對於其他MCU,它們確實確實需要端口和位的位置,因此每個引腳號都是兩個值。
如果您願意放棄使用整數作為引腳的想法,而是使用諸如P0,P1之類的名稱,那么您可以初始化很多const struct
,每個引腳名一個,並且您的函數將使用const struct值。 該結構將包含初始化的端口和位偏移或位掩碼值。 編譯器可能能夠優化速度。 這樣可以避免使用LUT,但仍會為所使用的引腳使用相似的空間。 您可能可以對其進行排列,從而無需在代碼中包括未使用的引腳,從而節省了空間。
編輯:如果您願意使用C ++,我建議使用C ++模板,它比宏可以提供更好的解決方案。 它們可以是類型安全的,並且通常更易於調試(如果您具有硬件調試功能,例如JTAG和gdb)
考慮以下宏:
#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);
那是你所追求的嗎?
我已經看到它通過幾個宏完成了( 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
...
您可以修改宏以使用直接整數而不是DIOn
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.