Переменные C переменные - PullRequest
2 голосов
/ 23 июля 2010

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

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

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

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

Ответы [ 8 ]

4 голосов
/ 28 июля 2010

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

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я не проверял это и не особенно внимательно относился к проверке документации.Код написан для 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;
  • * на самом деле имеет несколько применений.
    • Первый - объявление переменной-указателя (т. Е. Переменной, которая содержит указатель вместо int, char или float): int *pointer; Обратите внимание, что вам нужно указать, на какой тип переменной он будет указывать.
    • Второе использование - это разыменование указателя.По сути, это означает доступ к переменной через указатель.Если pointer указывает на variable, *pointer = 42; установит variable равным 42, а other_var = *pointer установит other_var на значение variable.
  • Существует также арифметика указателей, но это выходит за рамки этого ответа.

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

3 голосов
/ 23 июля 2010

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

1 голос
/ 16 апреля 2019

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

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

1 голос
/ 24 июля 2010

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

0 голосов
/ 25 августа 2010

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

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

0 голосов
/ 24 июля 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 может отличаться).Если вам нужно иметь дело с большим количеством выводов / портов, это может быть не лучшим подходом.

0 голосов
/ 24 июля 2010

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

0 голосов
/ 24 июля 2010

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

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

1 - создать функцию карты, которая принимает номер пин-кода и возвращает соответствующие порты и PIN-код

ех.

Вы хотите получить доступ к выводу № 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 должна быть довольно простой, просто создайте один массив, содержащий номера выводов

ех.

говорят, что чип имеет только 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];
} 

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

...