C переменные переменные

в PHP у меня есть что-то вроде

function doStuff($in, $value)  
{  
   $var = "V_" . $in;  
   $$var = $value;
}

Есть ли способ сделать что-то подобное на C?

По сути, я пытаюсь понять, как сделать своего рода библиотеку, чтобы упростить работу с выводами ввода-вывода на AVR. Так, например, будет функция для установки определенного вывода на ВЫХОД. Этот контакт в AVR является частью PORTB. Установка его на вывод и присвоение ему значения требует, чтобы я ссылался на константы DDRB и PORTB и устанавливал их значения. Вместо того, чтобы проходить через все это, я хотел бы иметь возможность вызывать такую ​​функцию, как SetMode(Pin #, Mode);. Я просто не могу понять, как это сделать.


person Bocochoco    schedule 23.07.2010    source источник
comment
То, что вы предлагаете, плохо вписывается в парадигму AVR. Вы действительно должны просто выполнять битовые манипуляции с предоставленными переменными. Это хорошая идея, чтобы обернуть его для конкретных задач (например, для управления ЖК-дисплеем), но такой общий интерфейс добавляет дополнительные накладные расходы в ограниченной среде, фактически не добавляя никакой ценности. Если вы действительно намерены это сделать и используете avr-libc, nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass может вам помочь.   -  person Steve S    schedule 24.07.2010


Ответы (8)


Ваш вопрос все еще немного неясен (на что указывает ассортимент интерпретаций в ответах). Я предполагаю, что вы хотите ссылаться на контакты по физическому номеру контакта. Если это не так, уточните свой вопрос, чтобы мы могли дать более точные ответы.

Вот примерно, как бы я это сделал, если бы кто-то приставил пистолет к моей голове:

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я не проверял это и не особо тщательно проверял документацию. Код написан для avr-gcc/avr-libc в Linux, хотя он может работать и в других местах.

// Map from physical pin number to associated direction register.
volatile uint8_t *ddr_map[] = {
    NULL, // Vcc, GND, or some other non-IO pin.
    &DDRB,
    &DDRB,
    &DDRC,
    // etc...  Values will vary for different target chips.
};

// Map from physical pin number to port mask.
uint8_t mask_map[] = {
    0x00,
    _BV(0),
    _BV(1),
    _BV(0),
    // etc...  Values will vary for different target chips.
}

typedef enum {
    IN,
    OUT
} PinDir;

void setMode(int pin, PinDir dir) {
    if(dir == OUT) {
        *ddr_map[pin] |= mask_map[pin];
    } else {
        *ddr_map[pin] &= ~mask_map[pin];
    }
}

См. http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass

И вот почему это не очень хорошая идея:

  1. Он не абстрагирует какое-либо значимое поведение (на самом деле он удаляет абстракцию — номер физического вывода находится на более низком уровне, чем логический порт/вывод). Более того, физический контактный номер не обязательно одинаков для разных форматов пакетов. Выводы PORTB не могут быть назначены тем же физическим номерам выводов на корпусе QFP, что и на пакете PDIP. Так что этот код на самом деле более запутан.

  2. Это добавляет накладные расходы. У вас есть дополнительный вызов функции (который требует циклов и стека) и два (или более) массива, используемых для поиска (которые требуют флэш-памяти и ОЗУ на AVR, если вы не принимаете специальные меры, и в этом случае они требуют дополнительных циклов и флэш-памяти или EEPROM) не говоря уже обо всех косвенных действиях (поиск массива, разыменование указателя) и дополнительных сравнениях и ветвлениях. В настольной и веб-разработке вы были бы правы, если бы посмеялись над моей озабоченностью по поводу таких небольших затрат, но на AVR эти потери оказывают гораздо большее влияние. (ПРИМЕЧАНИЕ: возможно, вам удастся убедить компилятор оптимизировать некоторые из них, но если вы используете -Os, это будет сложно. И теперь вы беспокоитесь о деталях еще более низкого уровня, чем раньше...)

  3. Предоставленные средства манипулирования булавками не настолько сложны, чтобы их стоило прятать таким образом. Вы должны освоиться с преобразованием между шестнадцатеричными и двоичными числами в уме (это несложно). Даже если вы не хотите возиться с шестнадцатеричным кодом, макрос _BV() делает манипуляции с контактами довольно простыми (или просто используйте (1 << x), который более переносим и будет распознан большим количеством программистов).

Кстати, PORTB, DDRB и т.д. не константы. Это переменные, привязанные к определенным адресам или регистрам. Попытка изменить константу чем-то вроде CONST_THINGY |= 0x03 вызовет ошибку компилятора.

Переменные переменные

C не имеет функции, которую вы описали. Это язык низкого уровня (иногда его называют «высокоуровневой сборкой»), который не предоставляет много причудливых функций (по сегодняшним стандартам). Вот почему это предпочтительный язык для AVR — вы хочете быть ближе к аппаратному обеспечению, и вам не нужны дополнительные накладные расходы.

В C есть указатели. Основываясь на вашем вопросе и комментариях, я предполагаю, что вы не очень хорошо с ними знакомы, поэтому вот краткое объяснение:

  • Оператор & возвращает указатель на переменную и используется следующим образом: pointer = &variable;
  • * actually has a couple of uses.
    • The first is declaring a pointer variable (i.e. a variable that holds a pointer instead of an int, char, or float): int *pointer; Notice that you have to specify what type of variable it will point at.
    • Второе использование — это то, что называется разыменованием указателя. По сути, это означает доступ к переменной через указатель. Если pointer указывает на variable, *pointer = 42; установит variable равным 42, а other_var = *pointer установит other_var значение variable.
  • Существует также арифметика указателя, но это выходит за рамки этого ответа.

Суть всего этого в том, что вы можете эффективно обращаться с самими переменными как со значениями, сохраняя их и передавая. Вы не можете изменить их каким-либо значимым образом, кроме как манипулировать их значением, но вам это и не нужно.

person Steve S    schedule 28.07.2010
comment
Выдающийся! tl;dr: C не PHP, ни в коем случае, даже если они оба имеют фигурные скобки. Чтобы эффективно использовать C, изучите C. - person Domingo Ignacio; 29.07.2010

Короче говоря, нет, в C нет переменных переменных. что вы могли бы сделать, это создать некоторый тип хэш-карты переменных с именами в качестве ключа и использовать это.

person GSto    schedule 23.07.2010
comment
Хэш-карта не будет работать, поскольку я пытаюсь получить доступ к определенным константам, а не получать конкретное значение. - person Bocochoco; 23.07.2010
comment
Создайте массив констант и инициализируйте его, чтобы он содержал эти константы. Ведь их не так много. - person Ben Voigt; 28.07.2010

Определения препроцессора или макросы являются типичными способами достижения желаемой цели в C.

person Domingo Ignacio    schedule 23.07.2010

C имеет функцию макросов, и ее можно использовать следующим образом.

#define oof(a, b) a##b

int x1 = 5;
oof(x, 1) = 10;
printf("%d", x1); //prints 10
int oof(x, 2) = 2;
printf("%d", x2); //printf 2

Это может быть функция, она может использовать другие функции, она может вызывать другие макросы и т. д. А здесь '##' — это оператор препроцессора, который объединяет объекты рядом с ним.

person Reltpid    schedule 16.04.2019
comment
Хотя этот код может решить вопрос, включение объяснения действительно помогает улучшить качество вашего сообщения. - person Tiago Martins Peres 李大仁; 16.04.2019

Когда вы говорите номер контакта, вы имеете в виду фактический номер контакта на физическом чипе, верно?

если это. Вы могли бы сделать это.

1- создайте функцию карты, которая принимает номер контакта и возвращает соответствующий ПОРТ и PIN-код.

ex.

Вы хотите получить доступ к контакту № 1 на чипе

SetMode( int pinNumber, char mode ) {

    typedef struct  {
        int pin;
        int port;
    }pinValues;


    pinValues pinStruct;
    mapPin( &pinStruct, pinNumber );  // this resolves the pin # on the chip to a port            
    // and pin.
    GPIO_init( pinStruct, mode ); // this initializes the pin;
}

функция mapPin должна быть довольно простой, просто создайте один массив, содержащий номера контактов

ex.

говорят, что у чипа всего 4 контакта

const char GPIO_pin[5] = {1,2,3,4};

и создайте структуру для порта и вывода, соответствующего каждому выводу #

ex

typedef struct {
  int pin;
  int port;
}pinPort;


pinPort pinPortStruct[5] = { (PORTA,0), (PORTA,1), (PORTB,1), (PORTB,1) };

поэтому контакт № 1 соответствует PORTA 0

поэтому вы просто выполняете поиск по GPIO_pin, а затем возвращаете структуру, соответствующую этому индексу.

for( int i = 0;i <4; i++)
{
 if( pin == GPIO_pin[i] )
     return pinPortStruct[i];
} 

Надеюсь, это то, что вам нужно.

person jramirez    schedule 23.07.2010

Все регистры AVR имеют адреса. Вы можете использовать адреса для реализации универсальных функций.

person Peter G.    schedule 23.07.2010

В зависимости от того, сколько контактов/портов вы имеете в виду, может быть проще всего использовать оператор case:

void SetMode(int pin, int mode) {
    switch (pin) {
        case PIN_A:
            DDRA = mode;
            PORTA = mode;
            break;

        case PIN_B:
            DDRB = mode;
            PORTB = mode;
            break;
        ...
    }
}

Константы PIN_A, PIN_B и т. д. могут быть определены с помощью макросов #define или enum. Одним из преимуществ этого подхода является то, что вы можете ссылаться на все свои порты/выводы, используя одинаковые обозначения, даже если вам приходится обрабатывать некоторые из них иначе, чем другие (каждый case может быть другим). Если у вас есть большое количество контактов/портов, то это может быть не оптимальным подходом.

person bta    schedule 23.07.2010

В общем случае указатели максимально близки. C не обязательно имеет какую-либо концепцию имен во время выполнения, особенно на микроконтроллере (некоторые имена обычно существуют в ОС с динамической компоновкой, но даже там это не требуется).

Для сценария с номером контакта могут работать таблицы поиска для определения порта, бита в порту и т. Д. Для любого заданного номера. Это метод, используемый Arduino, который пытается абстрагироваться от программирования C++ на AVR. Им нравится переименовывать вещи, например, называть сигналы PWM «аналоговой записью», C++ «проводкой» и программами «эскизами», а все контакты ввода-вывода нумеруются в соответствии с их положением на макетной плате. Недостатками являются массовая путаница, как только вы программируете что-то другое, кроме этой первой платы, и необходимость выяснять побочные эффекты, спрятанные в их библиотеке, когда вы хотите сделать что-то низкоуровневое.

person Yann Vernier    schedule 25.08.2010