Является ли плохой практикой переопределять маски регистров для PIC24, чтобы улучшить читаемость? - PullRequest
6 голосов
/ 15 января 2020

Я относительно новичок в работе с чипами PI C, так что это может быть вопрос новичка, но я пытаюсь написать файл заголовка, содержащий, среди прочего, маски TRIS / ODC / INIT для всех из портов ввода / вывода.

На печатной плате, в которую встроен этот чип, любой данный компонент может использовать выводы из нескольких портов, и, вероятно, существует около дюжины отдельных компонентов, которые требуют подробного комментирования. Например, для взаимодействия с конкретным модулем SPI AD C используются контакты из портов A, D и F.

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

Например, отображаются только данные маски TRIS Вот фрагмент кода для конкретного модуля AD C, который я использую для демонстрации того, о чем я говорю:

#define PORTD_TRIS_MASK 0x00
#define PORTF_TRIS_MASK 0x00

// ...
// lots of hardware configuration stuff goes here
// ...

// ANALOG INPUT - THERMOCOUPLE 1
// Thermocouple ADC chip MAX31856 DNP communicates over SPI
// Accepts any type of thermocouple
// TC1_CS pulled high
// TC1_DRDY pulled high

#define TC1_MOSI    LATAbits.LATA14
#define TC1_MISO    PORTDbits.RD10
#define TC1_SCK     LATDbits.LATD11
#define TC1_CS      LATFbits.LATF6
#define TC1_DRDY    PORTFbits.RF7

#define TC1_MISO_SHIFT  1<<10
#define TC1_DRDY_SHIFT  1<<7

#define PORTD_TRIS_MASK ( PORTD_TRIS_MASK | TC1_MISO_SHIFT )
#define PORTF_TRIS_MASK ( PORTF_TRIS_MASK | TC1_DRDY_SHIFT )

Приведенный выше код не выдает никаких ошибок, но выдает предупреждение :

HardwareProfile.h:1173:0: warning: "PORTD_TRIS_MASK" redefined
HardwareProfile.h:1029:0: note: this is the location of the previous definition
HardwareProfile.h:1174:0: warning: "PORTF_TRIS_MASK" redefined
HardwareProfile.h:1095:0: note: this is the location of the previous definition

Тот факт, что компилятор жалуется на это, наводит меня на мысль, что такое поведение может и не поощряться, но ничего из этого мне не кажется проблематичным c. Я что-то упустил, или это разумный способ организовать код так, чтобы детали конфигурации выводов оставались рядом с их определениями?

В качестве альтернативы, есть более традиционный способ выполнения sh того, чего я хочу достичь sh Что касается поддержания читабельности, которая более широко используется или приемлема?


Обновление:
Возможно, я не был достаточно ясен в своем первоначальном посте. Это структурировано таким образом, потому что в заголовочном файле есть около десятка таких блоков кода. Предположим, что существует ровно 13 таких блоков кода, любая конкретная маска будет первоначально определена как 0x00 и переопределена 13 раз, идея состоит в том, что каждое переопределение добавляет информацию о конфигурации, относящуюся к конкретному блоку.


Обновление:
В ответ на вопрос о том, как используются эти макросы, они просто используются для одновременной настройки всех выводов в порту. На этом PIC24 каждый порт имеет 16 контактов, каждый из которых имеет регистр TRIS (управление направлением данных), регистр OD C (управление открытым стоком) и регистр LAT (защелка), который, если он настроен как выход, потребуется начальное значение. Обычно, запись в один бит за шестнадцать раз не рекомендуется в пользу записи всего порта один раз. Например, рассмотрим упрощенный случай, когда вместо шестнадцати используются четыре регистра. Вместо того, чтобы писать это:

// In source file
TRISABITS.TRISA0 = 1;
TRISABITS.TRISA1 = 1;
TRISABITS.TRISA2 = 0;
TRISABITS.TRISA3 = 0;

Обычно (насколько я понимаю) писать это:

// In header file
#define BIT_0_SHIFT ( 1<<0 )
#define BIT_1_SHIFT ( 1<<1 )
#define BIT_2_SHIFT ( 0<<2 )
#define BIT_3_SHIFT ( 0<<3 )
#define TRISA_MASK ( BIT_0_SHIFT | BIT_1_SHIFT | BIT_2_SHIFT | BIT_3_SHIFT )

// In source file
TRISA = TRISA_MASK;

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

Ответы [ 2 ]

7 голосов
/ 15 января 2020

Препроцессор работает не так, как работает код. Например, рассмотрим следующий код:

int main(void)
{
    int A = (B+C);
    int B = (C+2);
    int C =  3;
    int x = A;
    return x;
}

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

cc -Wall demo.c -o demo
demo.c:3:14: error: use of undeclared identifier 'B'
    int A = (B+C);
             ^
demo.c:3:16: error: use of undeclared identifier 'C'
    int A = (B+C);
               ^
demo.c:4:14: error: use of undeclared identifier 'C'
    int B = (C+2);
             ^

Теперь попробуйте то же самое, используя #define s для A, B и C:

#define A (B+C)
#define B (C+2)
#define C  3

int main(void)
{
    int x = A;
    return x;
}

На этот раз нет предупреждения или ошибки, даже если #define не в порядке. Когда препроцессор видит #define, он просто добавляет запись в свой словарь. Таким образом, после прочтения трех #define s словарь содержит

Search   Replacement
 Text       Text
--------------------
   A       (B+C)
   B       (C+2)
   C       3

. Обратите внимание, что препроцессор не оценил замещающий текст. Он просто хранит текст. Когда препроцессор находит поисковый запрос в коде, он использует замещающий текст. Таким образом, строка

int x = A;

становится

int x = (B+C);

После выполнения подстановки препроцессор повторно сканирует текст, чтобы увидеть, возможны ли другие подстановки. После второго сканирования у нас есть:

int x = ((C+2)+3);

и конечный результат:

int x = ((3 +2)+3);

У большинства компиляторов есть возможность вывести код после завершения предварительной обработки. С g cc или clang используйте опцию -E, чтобы увидеть выходные данные препроцессора.


ОК, теперь у нас должно быть достаточно информации, чтобы ответить на ваш вопрос. Рассмотрим следующие определения:

#define PORTD_TRIS_MASK 0x00
#define PORTD_TRIS_MASK ( PORTD_TRIS_MASK | TC1_MISO_SHIFT )
#define PORTD_TRIS_MASK ( PORTD_TRIS_MASK | SB1_DATA_SHIFT )

Здесь у нас есть 3 основных проблемы:

  1. Текст подстановки не оценивается, поэтому биты не объединяются или объединяются.
  2. Только одно из этих определений (последнее) будет сохранено в словаре препроцессора. Вот причина предупреждения. Препроцессор сообщает вам, что у него уже есть запись для этого поискового запроса, и он отбрасывает предыдущий текст замены.
  3. Когда в коде найдено PORTD_TRIS_MASK, препроцессор заменяет его на ( PORTD_TRIS_MASK | SB1_DATA_SHIFT ) , Затем он сканирует заново и снова находит PORTD_TRIS_MASK. Результатом является бесконечная рекурсия. К счастью, препроцессор имеет защиту от таких вещей и остановится. Компилятор сгенерирует ошибку позже.

Решение состоит в том, чтобы создать определения с уникальным именем для каждого компонента:

#define TRIS_MASK_D1 TC1_MISO_SHIFT
#define TRIS_MASK_F1 TC1_DRDY_SHIFT

#define TRIS_MASK_D2 SB1_DATA_SHIFT
#define TRIS_MASK_F2 0

И затем ИЛИ все вместе:

#define PORTD_TRIS_MASK (TRIS_MASK_D1 | TRIS_MASK_D2 | ... | TRIS_MASK_D13)
#define PORTF_TRIS_MASK (TRIS_MASK_F1 | TRIS_MASK_F2 | ... | TRIS_MASK_F13)
0 голосов
/ 15 января 2020

Макросы, подобные PORTD_TRIS_MASK, определяются PI C на основе используемого микроконтроллера.

Я бы не рекомендовал вам изменять или переопределять эти макросы.

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

#define TC1_MISO_SHIFT  1<<10
#define TC1_DRDY_SHIFT  1<<7

#define TC1_MISO_MASK ( PORTD_TRIS_MASK | TC1_MISO_SHIFT )
#define TCI_DRDY_MASK ( PORTF_TRIS_MASK | TC1_DRDY_SHIFT )
...