2016-08-07 2 views
1

Я только начал делать C (из Java). Я пытаюсь понять, что подход языка к условному основанию зависит от имени определения.Условные утверждения, основанные на имени # define

например. У меня есть огромный заголовочный файл, который я не могу (не должен) редактировать с большим количеством определений.

#define GPIO_OTYPER_OT_0      ((uint32_t)0x00000001) 
#define GPIO_OTYPER_OT_1      ((uint32_t)0x00000002) 
#define GPIO_OTYPER_OT_2      ((uint32_t)0x00000004) 
#define GPIO_OTYPER_OT_3      ((uint32_t)0x00000008) 
#define GPIO_OTYPER_OT_4      ((uint32_t)0x00000010) 
#define GPIO_OTYPER_OT_5      ((uint32_t)0x00000020) 

И так далее;

Я хочу сделать функцию/декларацию (или что-то еще для решения), чтобы воздействовать на _ # часть определения.

(псевдокод)

void initialize(int X) { 
    GPIOA->MODER |= GPIO_MODER_MODER%X_5; 
    GPIOA->OTYPER &= ~GPIO_OTYPER_OT_%X; 
    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR%X; 
    GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5; 
    GPIOA->ODR |= GPIO_ODR_ODR_%X; 
} 

Где% Х INT X

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

Редактировать: https://github.com/espruino/Espruino/blob/master/targetlibs/stm32f4/lib/stm32f411xe.h - это заголовочный файл.

+2

Я бы сгенерировал второй заголовок, который имитирует исходный (неизменяемый) заголовок, но делает это здорово. Например: '#define GPIO_OTYPER_OT (x) ((uint32_t) 1 << (x))', а затем, возможно, '#define GPIO_OTYPER_OT_0 GPIO_OTYPER (0)' и т. Д. И сгенерируйте код для перекрестной проверки результатов. Затем используйте заголовок, а не безумный. –

+1

Я бы посмотрел на проблему, которую вы пытаетесь решить, чтобы понять, почему то, что вы делаете, является неправильным способом ее решения. – stark

+1

Исправьте меня, если я ошибаюсь, но 'initialize()' сильно пахнет, как и следующая вещь, которую вы делаете, - это вызвать его в цикле ", чтобы инициализировать каждый вывод GPIO"; что было бы определенным делом мышления на неправильном уровне. Если вы инициализируете, сделайте это на уровне _port_, т. Е. «GPIOA-> OTYPER = GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_2 | GPIO_OTYPER_OT_5; '. Заграждение операций чтения-изменения-записи в одном и том же регистре является полной потерей циклов. Я подозреваю, что вы не прыгаете _straight_ в двунаправленные протоколы бит-бит или что-то еще, что может потребовать перенастройки произвольных GPIO во время выполнения. – Notlikethat

ответ

1

слой абстракции Используйте С.Т. GPIO, который можно найти here. Примечательно, что см. GPIO_InitTypeDef, который дает вам структуру для того, что вы делаете выше, и GPIO_Init, который действительно будет делать то, что вы хотите. Структура инициализации принимает булавки в виде битовой маски, так как @artless noise, предложенный в комментарии, вы можете просто сделать 1<<X, чтобы создать свою маску. Все специфические для MCU поведение и сопоставление регистров скрыты от вашего кода.

Если вы пытаетесь реализовать свой собственный уровень драйвера в качестве упражнения или потому, что считаете, что библиотека ST не очень хороша, я бы по-прежнему взглянул на то, как они реализовали GPIO_Init в C file. Они используют сдвиг, но вы заметите, что при работе с регистрами это не всегда так просто, как 1<<X (хотя обратите внимание, что для их структуры конфигурации это всегда так просто). Некоторые регистры имеют несколько бит для каждого вывода (режим: 2 бита, pull config: 2 бит, альтернативная функция: 4 бита, разделенные на несколько регистров).

Редактировать: Я не предлагаю добавить больше библиотек, которых у вас еще нет. База библиотеки/кода, на которую вы ссылались на файл заголовка, уже включает периферийную библиотеку ST, поэтому имеет смысл (мне) использовать ее.

+0

Спасибо, я хотел реализовать его через библиотеку CMSIS, чтобы лучше понять, как это работает (без «абстракции»). Я обязательно узнаю, как был реализован HAL. И, вероятно, начните использовать, когда мне будет удобно понимать мои вещи. – Enders

+0

Это хорошее упражнение, чтобы написать свои собственные драйверы, хотя это не самое интересное. Я обычно предлагаю понять, как низкоуровневые драйверы обычно структурируются, прежде чем пытаться самостоятельно, но все учатся по-другому. Сделайте то, что, по вашему мнению, поможет вам больше всего. – rjp

+0

Я бы также предложил проверить более новую версию заголовка из ST. Я полагаю, что NXP/Freescale приступили к созданию макросов, которые берут номер контакта в качестве параметра, поэтому может быть более новая версия от ST, которая имеет нечто похожее. – rjp

1

Невозможно вставить произвольное целое число в имя макроса. Тем не менее, ваши целые числа будут небольшими (это будет меньше 32, потому что все ваши константы имеют 32-разрядный тип). Таким образом, вы можете преобразовать переключатель-заявление в выражение одной строки, например, так:

x == 0 ? GPIO_OTYPER_OT_0 : \ 
x == 1 ? GPIO_OTYPER_OT_1 : \ 
x == 2 ? GPIO_OTYPER_OT_2 : \ 
... 
x == 31 ? GPIO_OTYPER_OT_31 : 0 

Здесь вы можете даже сделать выражение «по умолчанию», который будет генерировать ошибку времени выполнения - что-то вроде (abort(), (uint32_t)0).

Чтобы сделать это более общий характер, отделить GPIO_OTYPER_OT_ часть в макро аргумента, и использовать «вставить оператор» ##: пример

#define MY_MACRO(name, x) \ 
x == 0 ? name ## 0 : \ 
x == 1 ? name ## 1 : \ 
x == 2 ? name ## 2 : \ 
... 
x == 31 ? name ## _31 : \ 
(abort(), name ## 0) 

Использование:

GPIOA->ODR |= MY_MACRO(GPIO_ODR_ODR_, x); 

Вы должны сделать отдельный макрос для тех имен, которые имеют x посередине:

#define MY_MACRO2(prefix, x, suffix) (\ 
(x) == 0 ? prefix ## 0 ## suffix : \ 
(x) == 1 ? prefix ## 1 ## suffix : \ 
... 
(x) == 31 ? prefix ## 31 ## suffix : \ 
(abort(), prefix ## 0 ## suffix)) 

Здесь я также добавил необходимые скобки (около x и вокруг всего макроса), как это принято в макросах C.


P.S. Если ваш большой заголовочный файл не определяет макросы с номерами до 31, но имеет меньший предел, вы не можете использовать макрос, который упоминает все эти имена, потому что вы получите ошибку компиляции. В этом случае вставьте максимум в имя макроса. После этого вы можете определить их в «рекурсивного» способом:

#define MY_MACRO_MAX1(prefix, x) \ 
x == 0 ? prefix ## 0 ## suffix : prefix ## 1 ## suffix 

#define MY_MACRO_MAX2(prefix, x) \ 
x == 2 ? prefix ## 2 ## suffix : MY_MACRO_MAX1(prefix, x) 

#define MY_MACRO_MAX3(prefix, x) \ 
x == 3 ? prefix ## 3 ## suffix : MY_MACRO_MAX2(prefix, x) 

#define MY_MACRO_MAX4(prefix, x) \ 
x == 4 ? prefix ## 4 ## suffix : MY_MACRO_MAX3(prefix, x) 

#define MY_MACRO_MAX5(prefix, x) \ 
x == 5 ? prefix ## 5 ## suffix : MY_MACRO_MAX4(prefix, x) 

...