Вопрос по readability, microprocessors, c, embedded – Сделать большие константы в C-источнике более читабельными?

7

Я работаю над кодом для микропроцессора.
У него есть несколько больших критических констант.

#define F_CPU 16000000UL

В данном случае это частота процессора. В герцах.

На самом деле, довольно трудно сказать, если это 1 600 000, 160 000 000 или 16 000 000 без ручного наведения курсора на цифры.

Если я поставлю запятые в число#define F_CPU 16,000,000UL, он усекает константу.

Я работал с несколькими эзотерическими языками, которые имеют определенный символ-разделитель цифр, чтобы сделать большие числа более читабельными (например,16_000_000в основном на языках, предназначенных для микроконтроллеров). Большие "магические числа" довольно часто встречаются во встроенных вещах, так как они необходимы для описания аспектов того, как MCU общается с реальным миром.

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

Термин «магическое число» обычно относится к числовым константам, внезапно появляющимся в середине кода. Т.е. что-то вроде:if (var == 16000000) ... (плохо), а неif (var == F_CPU) ... (хорошо). Lundin
Поможет ли это что-то вроде: #define F_CPU NUM_GROUPED_3ARGS (16 000 000UL)?stackoverflow.com/questions/10977260/… grenix

Ваш Ответ

8   ответов
10

##

Так что вы можете написать

#define F_CPU 16##000##000UL

у которого естьexactly то же значение, что и 16000000UL. (В отличие от других структур, таких как 16 * 1000 * 1000, где вы должны быть осторожны, чтобы не разместить их там, где умножение может вызвать проблемы.)

@jamesdlin Правда, как и в большинстве ответов здесь. Но не все.
@jamesdlin Ну, в выражениях с более высоким приоритетом операторов. Например, если у вас есть#define FIRST 16000000 а также#define SECOND 16*1000*1000 без скобок,~FIRST не будет таким же, как~SECOND.
В каких случаях умножение может вызвать проблемы?
## на самом делеconcatenation operator из cpp, который можно использовать и для других вещей.
Хорошо, тогда я определил бы вместо этого, что "нужно быть осторожным, чтобы заключить в скобки выражение", что обычно делал бы любой нормальный программист на Си.
0

Вы можете использовать научную запись:

#define F_CPU 1.6e+007

Или же:

#define K 1000

#define F_CPU (1.6*K*K)
Я должен остановиться и подумать, чтобы убедиться, что 1.6e7 == 16 000 000. 16.0e6 будет понятнее. Но все же лучше придерживаться простых целых чисел.
#define F_CPU ((int)1.6e7)
Числа с плавающей запятой, вероятно, не вариант, так как это для 8-битного MCU.
С научной нотациейF_CPU имеет типdouble, который вводит некоторые потенциальные подводные камни.
4

16*1000*1000 для вашего примера). Еще лучше, вы можете определить другой макрос,MHZ(x)и определите вашу константу какMHZ(16), что сделало бы код немного более самодокументированным - за счет создания вероятности столкновения пространства имен.

1
// constants.h
#define Hz   1u              // 16 bits
#define kHz  (1000u  *  Hz)  // 16 bits
#define MHz  (1000ul * kHz)  // 32 bits

// somecode.h
#define F_CPU (16ul * MHz)   // 32 bits

int is 16 bits on a 8 bit MCU. 16 bit literals will get optimized down to 8 bit ones (with 8 bit instructions), whenever possible. Signed integer literals are dangerous, particularly if mixed with bitwise operators, as common in embedded systems. Make everything unsigned by default. Consider using a variable notation or comments that indicate that a constant is 32 bits, since 32 bit variables are very very slow on most 8-bitters.
6

#define MHz(x) (1000000 * (x))
...
#define F_CPU MHz(16)

Кроме того, я не люблю#defines. Обычно лучше иметьenumс или константы:

static const long MHz = 1000*1000;
static const long F_CPU = 16 * MHz;
Они разрешаются во время компиляции для установки целого пакета различных констант, которые затем записываются в различные аппаратные регистры, и, как таковые, устанавливают различные битовые времена для последовательного интерфейса и т. Д. Вы действительно не хотите использовать перечисления или другие вещи, так как они могут оказаться где-то в оперативной памяти. Этот MCU имеет в общей сложности 2 КБ ОЗУ (и 32 КБ программного пространства), что весьма ценно. Fake Name
Черт возьми, посмотри на это#define: #define ,Lin_set_btr_brr(bt,br) { U8 __jacq__; __jacq__ = LINCR; LINCR &= ~(1<<LENA); LINBTR = ((1<<LDISR) | (LBT_MASK & bt)); LINBRRH = (U8)((((((((U32)F_CPU*1000)<<1)/(((U32)br)*bt))+1)>>1)-1)>>8); LINBRRL = (U8)(( (((((U32)F_CPU*1000)<<1)/(((U32)br)*bt))+1)>>1)-1) ; LINCR = __jacq__; } Fake Name
ВашMHz макрос должен заключать в скобкиx так что то типаMHz(1 + 2) ведет себя правильно.
@FakeName Да, это немного безумно :) Программа низкого уровня имеет свои правила.
Примечание: я не написал это, это из фактической документации MCU. Я пытаюсь сделать его читабельным. Fake Name
0

#define F_CPU_HZ 16000000UL

Таким образом, вы знаете, какой тип данных в нем. В нашем ПО у нас есть несколько периферийных устройств, которые требуют установки разных прескалеров, поэтому у нас есть#defines как это:

#define SYS_CLK_MHZ    (48)
#define SYS_CLK_KHZ    (SYS_CLK_MHZ * 1000)
#define SYS_CLK_HZ     (SYS_CLK_KHZ * 1000)
0

акросе.

#define NUM_GROUPED_4ARGS(a,b,c,d) (##a##b##c##d)
#define NUM_GROUPED_3ARGS(a,b,c)   (##a##b##c)

#define F_CPU NUM_GROUPED_3ARGS(16,000,000UL)

int num = NUM_GROUPED_4ARGS(-2,123,456,789); //int num = (-2123456789);
int fcpu = F_CPU; //int fcpu = (16000000UL);

Это как-то WYSIWYG, но не защищено от неправильного использования. Например вы можете захотеть компилятор жаловаться на

int num = NUM_GROUPED_4ARGS(-2,/123,456,789);  //int num = (-2/123456789); 

но это не так.

5

#define F_CPU (16 * 1000 * 1000)

альтернативно

#define MHz (1000*1000)
#define F_CPU (16 * MHz)

Изменить: МГц (х) другие предложенные могут быть лучше

@ Фальшивое имя: Полагаю, я упоминал об этом только потому, что вопрос помечен буквой C, но оказывается, что вы не программируете на C, вы программируете какой-то другой язык, связанный с C. В отличие от более распространенных вариантов C что вы получаете из-за незначительных дефектов в компиляторах для ПК и тому подобного, большинство случайных прохожих не будут знать все о C-подобных языках для 8-битных процессоров и, конечно же, не будут знать, какой компилятор (ы) вы должны поддерживать , так как вы не сказали. Это может повлиять на ответы. Хотя не этот ответ, потому что даже в реальном C вам нужно по крайней мереlong гарантировать 32 бита.
Я завелась с помощью:#define MHz (1000UL*1000UL) #define F_CPU (16 * MHz) Fake Name
Мне действительно нравится эта запись, поскольку она ближе к тому, как на самом деле будут указаны часы. Вы бы не сказали, что это «МГц (16)». CPU, вы бы сказали, что это «16 МГц» ЦПУ. Fake Name
Я должен указать номера какULиначе они будут усечены до собственного размера MCU, который составляет 8 бит. Что плохо кончается. Fake Name
@Поддельное имя:int не может иметь размер 8 битов в соответствующей реализации, он должен составлять, по меньшей мере, 16 битов. Это все еще плохо кончается в таком размере, очевидно.

Похожие вопросы