2012-03-17 6 views
0

В настоящее время я работаю над настройкой структуры в C для использования между несколькими микроконтроллерами. Рамка должна нести код всего устройства, поэтому приложение содержит только абстрактное использование периферийных устройств (например, SerialPort_Read, write, SetBaudRate и т. Д.)LUT в макросе C

Одна из вещей, с которыми я изо всех сил пытаюсь найти решение для в C - карта вывода ввода/вывода. Я видел проекты (например, очень популярный Arduino), где карта контактов помещена в LUT (таблица поиска), которая используется во время выполнения. Однако этот LUT никогда не будет изменен во время выполнения, поэтому нет необходимости использовать это в памяти. Например, эта функция устраняет некоторые битовые индексы и регистров из некоторых «константных 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.

+0

Пожалуйста, не ссылайтесь на код на внешних сайтах. –

+0

Хорошо, спасибо, я удалил ссылки и объяснил это здесь. – Hans

+0

В вашем примере кода, что вы хотите, чтобы оптимизатор сделал? Если порт может иметь разные значения во время выполнения, в зависимости от того, как вызывается функция. – blueshift

ответ

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

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

+0

Это кажется правильным. Не думайте, что вам нужен дополнительный макрос TRIS_, хотя должен делать порт PORT ##? Возможно, пример использования поможет его получить. – blueshift

+0

Хороший звонок - это будет чистить вещи. Что касается примера, я заставил их работать точно так же, как в своем вопросе. –

+0

Да, но вы не * показывали * их работу. Если вы не знакомы с ##, то попросивший может не вешать, что они просто звонят, например. (A, 1, 1). – blueshift

1

Какой процессор или микроконтроллер вы используете? Возможно, вы недооцениваете полезность LUT.

Для многих процессоров LUT делает больше, чем отображение «логического» номера штыря на одно значение, «физический» номер штыря. LUT сопоставляет «логический» контактный номер нескольким частям информации.

В общем случае логический вывод отображается на адрес порта соответствующего регистра чтения/ввода или записи/вывода и смещение бита в регистре чтения или записи. Таким образом, значение pin на многих MCU действительно отображается в структуру. Он может также включать отображение в регистр направления данных и поля внутри него, а также регистры, которые устанавливают состояние подтягивающих или выпадающих резисторов.

Например, у меня есть код для мультиплексирования дисплея 8x8. Во время выполнения мне нужно использовать pinMode, чтобы вывести вывод с выхода на вход с высоким импедансом, и чтобы информация каким-то образом была закодирована.

Это можно сделать с некоторой изобретательностью, на некоторых микроконтроллерах. ARM MCU (и я считаю, 8051, хотя я никогда не использовал один), используя «битовой группы адресации» http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/CHDJHIDF.html

Это присваивает уникальный адрес памяти для каждого вывода порта, и фиксированные смещения можно вывести адрес штифта для другой регистр данных и другие функции. Это не волшебство, код кодирует информацию, которая часто хранится в LUT.

Для других микроконтроллеров им действительно нужны как положение порта, так и бит, поэтому для каждого номера штыря это два значения.

Если вы готовы отказаться от идеи использования целых чисел для контактов, а вместо этого используйте имена, такие как P0, P1, тогда вы можете инициализировать много const struct, по одному на каждое имя, а ваши функции будут const struct values. Структура будет содержать инициализированные значения смещения порта и бита или бит-маски. Компилятор может оптимизировать скорость. Это позволит избежать использования LUT, но все равно будет использовать одинаковое количество пространства для используемых штырей. Возможно, вы сможете организовать его так, чтобы неиспользованные контакты не нужно было включать в код и, следовательно, экономить место.

Редактировать: Если вы хотите использовать C++, я бы предложил шаблоны C++, которые могут дать гораздо лучшее решение, чем макросы. Они могут быть безопасными типа, и зачастую проще отлаживать (если у вас есть аппаратные отладки, например, JTAG и GDB)

+0

Я в настоящее время нацелен на 16-битные и 32-разрядные микросхемы PIC, я не думаю, что у них есть бит-диапазон. Мне нужно будет посмотреть на поддержку C++, я не знаю, какие точные возможности моей среды на C++, я буду искать ее. Индекс порта - самая большая проблема, и его разрешение на «одномерное» кажется намного проще, но, к сожалению, многие структуры const очень похожи на то, что делает Ардуино. – Hans

+0

Боюсь, я очень мало знаю о 16-битном ПОС. PIC32 не имеет бит-обвязки, хотя у него есть проницательные + быстрые, четкие и переключаемые биты. Я не знаю, имеет ли PIC32 16 или 32 вывода ввода/вывода на каждый адрес порта. ARM имеет только 16 контактов ввода/вывода/порт, поэтому есть возможность для упаковки двух частей данных в 32-битное слово. Тогда это можно было бы манипулировать макросами, чтобы получить порт и бит, надеясь, что компилятор сможет вычислять значения во время компиляции. – gbulmer

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

ли что ты делал?

+0

Да, очень похоже на то, что опубликовал Карл. Я также использовал C-стиль, если сам, потому что компилятор микрочипа C не получал его, что опубликовал Карл. Все еще не самая гибкая и читаемая структура таблицы, которую я искал, но, я думаю, единственным решением будет структура. – Hans

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

Вы можете изменить макросы взять прямые целые, а не Дион