简体   繁体   English

在宏C中查找

[英]LUT in a macro C

I am currently working on setting up a framework in C for usage between several microcontrollers. 我目前正在使用C建立框架,以便在多个微控制器之间使用。 The framework has to carry all device specific code, so the application only contains the abstract usage of peripherals (like SerialPort_Read, write, SetBaudRate, etc etc.) 该框架必须包含所有设备特定的代码,因此该应用程序仅包含外围设备的抽象用法(例如SerialPort_Read,write,SetBaudRate等)。

One of the things I am struggling with to find a solution for in C are the I/O pin map. 我努力寻找C语言解决方案的其中一件事情是I / O引脚图。 I've seen projects (like the very very popular Arduino) where the pin map is putten in a LUT (look up table) which is used during runtime. 我见过一些项目(例如非常受欢迎的Arduino),其中将引脚图放置在运行时使用的LUT(查找表)中。 However, this LUT will never be modified during runtime, so there is no use to have this in the memory. 但是,此LUT永远不会在运行时被修改,因此在内存中没有使用它。 For example, this function resolves some bit indexes and registers from some 'const uint' tables, and either sets or clears a bit: 例如,此函数从某些“ 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;
        }
}

Because this is actual C code running on the controller it's draining effiency and speed. 由于这是在控制器上运行的实际C代码,因此会浪费效率和速度。 I'd rather define some sort of macro that does the same thing, but is already resolved during compilation to a 'one-liner' that can be compiled much more efficiently: 我宁愿定义某种功能相同的宏,但在编译过程中已经将其解析为可以更高效地编译的“单一代码”:

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

Does anyone know if it's possible (and how) to define and use a look-up table with a macro in C? 有谁知道是否有可能(以及如何)在C语言中使用宏定义和使用查找表?

At this moment I am using the MicroChip C30 compiler, which I believe is based in GCC. 目前,我正在使用MicroChip C30编译器,我相信它是基于GCC的。 It's supposed to be portable between different compilers, including MicroChip C18, C32 and in further also ARM and AVR. 它应该可以在不同的编译器之间移植,包括MicroChip C18,C32以及ARM和AVR。

For your specific example, something along these lines will work: 对于您的特定示例,遵循以下原则的方法将起作用:

#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)))

You'll have to make sure to define LATA and PORTA in a way the system will understand - in particular trying to overload its meaning the way it seems to be in your example might be hard to resolve. 您必须确保以系统可以理解的方式定义LATAPORTA ,尤其是尝试重试其含义(如您的示例中所示)可能很难解决。

Which processor or microcontroller are you targeting? 您定位到哪个处理器或微控制器? You might be underestimating the usefulness of the LUT. 您可能低估了LUT的用途。

For many processors, the LUT does more than map a 'logical' pin number to a single value, the 'physical' pin number. 对于许多处理器,LUT所做的不只是将“逻辑”引脚号映射到单个值(“物理”引脚号)。 The LUT maps the 'logical' pin number to several pieces of information. LUT将“逻辑”引脚号映射到几条信息。

In general, the 'logical' pin is mapped to the port address of the appropriate read/input or write/output register, and the bit offset within the read or write register. 通常,“逻辑”引脚映射到适当的读/输入或写/输出寄存器的端口地址,以及读或写寄存器内的位偏移。 So the pin value, on many MCU's, is really mapped to a struct. 因此,在许多MCU上,引脚值实际上已映射到结构。 It might also include a mapping to the data direction register and fields within it, as well as registers which set the state of pull-up or pull-down resistors. 它还可能包括到数据方向寄存器和其中的字段的映射,以及设置上拉或下拉电阻器状态的寄存器。

For example, I have code to multiplex a 8x8 display. 例如,我有用于多路8x8显示的代码。 At run-time, I need to use pinMode to turn a pin from an output to a high impedance input, and so that information needs to be encoded somehow. 在运行时,我需要使用pinMode将引脚从输出变为高阻抗输入,以便信息需要以某种方式进行编码。

It is possible to do this sort of thing, with some ingenuity, on some MCU's. 可以在某些MCU上做一些巧妙的事情。 ARM MCU's (and I believe 8051, though I've never used one) using 'bit band addressing' http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/CHDJHIDF.html ARM MCU(我相信8051,尽管我从未使用过)使用“位带寻址” http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/CHDJHIDF html的

This assigns a unique memory address for each port pin, and fixed offsets can derive the address of the pin for the other data register, and other functions. 这会为每个端口引脚分配一个唯一的存储器地址,固定偏移量可以得出其他数据寄存器和其他功能的引脚地址。 This is not magic, the code encodes the information that is often stored in the LUT. 这不是魔术,代码对经常存储在LUT中的信息进行编码。

For other MCU's, they really do need both port and bit position, so it is two values for each pin number. 对于其他MCU,它们确实确实需要端口和位的位置,因此每个引脚号都是两个值。

If you are willing to ditch the idea of using integers for pins, and instead use names, like P0, P1, then you could initialise a lot of const struct 's, one per pin name, and your functions would take the const struct values. 如果您愿意放弃使用整数作为引脚的想法,而是使用诸如P0,P1之类的名称,那么您可以初始化很多const struct ,每个引脚名一个,并且您的函数将使用const struct值。 The struct would contain the initialised port and bit offset or bit mask values. 该结构将包含初始化的端口和位偏移或位掩码值。 The compiler may be able to optimise for speed. 编译器可能能够优化速度。 This would avoid having a LUT, but would still use similar amounts of space for pins that are used. 这样可以避免使用LUT,但仍会为所使用的引脚使用相似的空间。 You might be able to arrange it so that unused pins would not need to be included in the code, and hence saving space. 您可能可以对其进行排列,从而无需在代码中包括未使用的引脚,从而节省了空间。

Edit: If you are willing to use C++, I'd suggest C++ templates which can give a much better solution than macros. 编辑:如果您愿意使用C ++,我建议使用C ++模板,它比宏可以提供更好的解决方案。 They can be type safe, and are often easier to debug (if you have hardware debugging, eg JTAG and gdb) 它们可以是类型安全的,并且通常更易于调试(如果您具有硬件调试功能,例如JTAG和gdb)

Consider the following macro: 考虑以下宏:

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

Usage: 用法:

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

Is that the kind of thing you were after? 那是你所追求的吗?

I've seen it done ( https://github.com/triffid/Teacup_Firmware/blob/Gen7/arduino.h ) with a couple macros: 我已经看到它通过几个宏完成了( 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)

with: 有:

#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
...

You could modify the macros to take straight integers rather than DIOn 您可以修改宏以使用直接整数而不是DIOn

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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