繁体   English   中英

在宏C中查找

[英]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)))

您必须确保以系统可以理解的方式定义LATAPORTA ,尤其是尝试重试其含义(如您的示例中所示)可能很难解决。

您定位到哪个处理器或微控制器? 您可能低估了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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM