Я бы к этому всему прибавил бы еще и заметки о переносимости и кроссплатформенности кода.
Причем обеспечить при этом и оптимальность в плане производительности, размеров и масштабируемость.
Чаще всего в этом плане стоит избегать использование переменных, таких как
int в тех местах, где размер
int критичен.
Ведь на разных платформах, этот тип имеет разную длину.
По умолчанию для восьмибитных архитектур:
Код: Выделить всё
int = short int = 16bit
short int = int = 16bit
long int = long int = 32bit
По умолчанию для 16-тибитных архитектур:
Код: Выделить всё
int = short int = 16bit
short int = int = 16bit
long int = long int = 32bit
По умолчанию для 32-хбитных архитектур:
Код: Выделить всё
int = long int = 32bit
short int = short int = 16bit
long int = long int = 32bit
Для решения этих проблем были придуманы иные типы и файл
inttypes.hКод: Выделить всё
int8_t = char = 8bit
int16_t = short int =16bit
int32_t = long int = 32bit
int64_t = __int64 = 64bit
Рекомендуется пользоваться ими, так как они четко указывают размер
типа для любой архитектуры и так-же могут сыграть роль и в размерах кода и его производительности.
К примеру, нет смысла применять в
AVR uint32_t в цикле, где количество итераций этого цикла фиксированное
и меньше
255-ти. Там лучше применить
uint8_t.
Структуры:
Недавно где-то читал статью, что допустим такой код:
Код: Выделить всё
uint8_t data[4] = {1, 2, 3, 4};
......
uint32_t reg = ((data[3]<<24)|(data[2]<<16)|(data[1]<<8)| data[0]);
будет более затратным, чем применение
union-структур. Даже были приведены тесты.
В таком случае, операции с
union будут гарантированно сведены к простому копированию байт с одних ячеек
ОЗУ в другие (для распространенных архитектур). Сам не проверял, прошу знающих прокомментировать.
Сами структуры в AVR-ках стоит паковать директивой
#pragma pack(1) перед определением структуры
или запускать компиляцию с ключом
-fpack-struct Эти приемы превращают структуру в сплошной массив и тем самым экономится память.
К примеру структура:
Код: Выделить всё
typedef struct _PackUartRcv
{
uint16_t address;
uint8_t cmd;
uint32_t flags;
uint16_t crc16;
}PackUartRcv, *pPackUartRcv;
съест 16 байт, так как возникают пустоты в полях с типами меньшей длинны (когда-то я на эти грабли наступил)
А структура:
Код: Выделить всё
typedef struct _PackUartRcv
{
uint16_t address;
uint8_t cmd;
uint32_t flags;
uint16_t crc16;
}PackUartRcv, *pPackUartRcv;
съест свои положенные 9 байт.
Плюс к этому, так можно разбирать принятые пакеты сразу в переменные,
если структуру при приеме использовать, как простой массив их
uint8_t переменных.
Код: Выделить всё
uint8_t UART_Rcv(uint8_t *buff, uint8_t sz_buff, uint16_t tmout);
...................
PackUartRcv PacketStruct;
.................
if(!UART_Rcv((uint8_t*)&PacketStruct, sizeof(PackUartRcv), RCV_TIMEOUT))
{
if(PacketStruct.crc16 != CRC16_Calc((uint8_t*)&PacketStruct, sizeof(PackUartRcv), CRC_CONSTANT))
{
return ERROR_CRC;
}
........
........
}
И да. Статьи
avreal ценны. Советую написать статью в разделы статей РадиоКота.
Материал полезный.
