Файл с полезностями.
Во все свои контроллерные проекты я включаю файлик, в котором содержится куча разных дефайнов, полезных макросов и прочей лабуды. Предлагаю его и вам.
#define TRUE FALSE
Сразу перейду к главному. Скачать этот файлик можно тут:
Что там есть и зачем это нужно?
Там есть шапка с моим ником, которую никто обычно в своих проектах не сохраняет. Пусть это останется на их совести )).
Дальше идет секция с гордым названием настройки. В этой секции только одно определение с архитектурой. Я оставил AVR.
“Разные инклуды”. Обычно каждый контроллер требует включать заголовочные файлы компилятора, название которых очень лениво вспоминать. В этой секции они всегда будут на виду. Опять-же там есть только пара AVR и IAR. У каждого разработчика есть куча заголовочных фалов с разными полезностями, эта секция – идеальное место включить их одним махом.
В секции “Разные полезные типы данных” описаны типы данных с фиксированным размером. Стандартные типы (“int”, к примеру) не имеют фиксированного стандартами размера, и с этим связанно большое количество глюков. К примеру, у связки IAR+AVR sizeof(int) = 2, а у IAR+ARM7 – sizeof(int) = 4. По этому поводу даже есть рекомендация MISRA-C:
The basic types of char, int, short, long, float, and double should not be used, but specific-length equivalents should be typedef’d for the specific compiler, and these type names used in the code.
Я назвал свои переменные “uint8” (8 — длинна в битах), “int16”, итп. Некоторые программисты предпочитают более короткие имена – u8, s16. Как и все остальное в этом файле, конкретное название – дело вкуса.
Конечно-же, производители компиляторов не могли обойти стороной проблему фиксированной длинны и сделали файлик stdint.h, который входит в стандартную библиотеку. Имена которые определенны там (uint8_t, uint_least8_t) я считаю слишком длинными и некрасивыми.
С фиксированной длинной связанна еще одна маленькая проблемка – многие 32-битные контроллеры с 8-битными переменными работают медленнее, чем с 32-битными. В stdint.h для решения этой проблемы определили переменные типа uint_fast8_t. Гарантируется, что такие переменные будут как минимум 8 бит длинной и как можно быстрее работать.
Я обычно пишу библиотеку под конкретную архитектуру и не стараюсь ее сильно оптимизировать. Поэтому простых переменных фиксированной длинны мне хватает.
Дальше немного констант. Про значения MAXMAX и так все понятно, А вот коды возврата – это очень полезная штука. Часто в качестве кода возврата используют bool. Этот тип занимает целые 8 бит, а передает всего два значения. Я решил его немного исправить несправедливость и добавил несколько других значений, естественно их можно дополнять как хочется. Имена кодов возврата я взял из технологии COM, используемой в Windows. Все коды обозначающие ошибки начинаются с префикса E_ ( ERROR ), а коды обозначающие успешное выполнение – с S_ ( SUCCESS ). К примеру
S_OK – Все хорошо, уряяя!
E_ERR – Все плохо (
E_USER – Все плохо потому, что пользователь сделал какую-то глупость, к примеру прервал операцию.
итп.
Полезные функции. Тут несколько правильно написанных макросов которые часто используются. Разные общеизвестные MIN, MAX, ABS всем, надеюсь, ясны.
Макросы начиная с LOBYTE и до BYTE3 используются особо часто и возвращают соответствующий кусочек большего типа данных. К примеру, uint16 i = 0x1234; HIBYTE(i) = 0x12; LOBYTE(i) = 0x34. Надеюсь, вы поняли идею.
Ну, и самая большая полезность из этого всего — ARRAY_SIZE(a). К примеру у вас есть структура и массив экземпляров этой структуры. К примеру, очень часто нужно пробежаться по всем экземплярам структуры в поисках чего-то, это очень удобно сделать так:
struct People_t { uint8 *Name; uint32 Birthday; } ; People_t people[100]; for ( uint8 i; i<ARRAY_SIZE(people); i++ ) { ... }
Биты – тоже очень полезны. К примеру, ножки контроллеров можно обозначать как
#define DF_MISO_PB BIT4
Теперь можно писать
DDRB &= ~DF_MISO_PB;
Вместо
DDRB &= ~(1<<DF_MISO_PB);
Лично я часто забывал про “1<<” и меня начало это раздражать.
Дальше идут ассерты. Я предлагаю при переходе на новую архитектуру/процессор на раскомментировать их. Они проверят – являются ли длинны наших переменных с фиксированной длинной такими, какими они должны быть. Дальше этот блок можно закомментировать обратно.
Критика, комментарии и дополнения приветствуются.
#define BYTE0(a) (*((uint8*)&(a) + 0))
#define BYTE1(a) (*((uint8*)&(a) + 1))
#define BYTE2(a) (*((uint8*)&(a) + 2))
#define BYTE3(a) (*((uint8*)&(a) + 3))
Вот это прикольная штучка, также как и биты.
А то я не особо думая надеюсь на умный компилятор, распознающий сдвиги с масками, как доступ к байтам и словам (полусловам). )))
Хотя лучше иметь всем под боком, в регистрах.