LUT在宏C中

时间:2012-03-17 20:25:38

标签: c gcc macros pic lookup-tables

我目前正致力于在C中设置一个框架,以便在多个微控制器之间使用。 框架必须携带所有特定于设备的代码,因此应用程序仅包含外设的抽象用法(如SerialPort_Read,write,SetBaudRate等)。

我在C中寻找解决方案时遇到的一个问题是I / O引脚映射。我已经看过项目(比如非常流行的Arduino),其中pin地图被放置在运行时使用的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。

4 个答案:

答案 0 :(得分: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 - 特别是尝试以其在您的示例中的方式重载其含义可能很难解决。

答案 1 :(得分:1)

您定位的是哪个处理器或微控制器? 你可能低估了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结构值。该结构将包含初始化的端口和位偏移或位掩码值。编译器可能能够针对速度进行优化。这将避免使用LUT,但仍会使用相似数量的空间用于所使用的引脚。您可以对其进行排列,以便不需要将未使用的引脚包含在代码中,从而节省空间。

编辑:如果您愿意使用C ++,我建议使用C ++模板,它可以提供比宏更好的解决方案。它们可以是类型安全的,并且通常更容易调试(如果您有硬件调试,例如JTAG和gdb)

答案 2 :(得分: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);

这是你追求的那种东西吗?

答案 3 :(得分: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
...

您可以修改宏以采用直接整数而不是DIOn