Битовые операции, как правильно?

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014
Есть устройство TEA5767
Читаем из него 5 байт информации, согласно даташиту, и сохраняем в переменную unsigned char teaBuf[5].
Опять же по даташиту в 4 байте есть 3 параметра:
1. 7-4 бит уровень сигнала
2. 3-1 бит идентифткатор чипа
3. 0 бит не используется.
 
Мне нужно получить уровень сигнала, делаю так:
byte level = (teaBuf[3] & 0xF0) >> 4;

То есть операцией & 0xF0 устанавливаем биты с 3 по 0 в 0, потом сдвигаем биты 7-4 на позицию 3-0 и получем результат в byte level от 0 до 15.

А можно ли не обнулять биты 3-0, а просто произвести сдвиг? То есть сделать так:
byte level = teaBuf[3] >> 4;

Я так понимаю в этом случе просто произойдет сдвиг вправо, и биты 3-0 заместатся битами 7-4? Не будет ли каких либо проблем?

Пробовал делать и как в первом варианте и как во втором, результат одинаковый, но хотелось бы знать какой метод более правильный.
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Можно.

Более правильно - не использовать лишних команд.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Можно наложить маску в виде структуры и тогда не нужны "магические цифры" (3, 4, ...). Так более правильно, так как будут использоваться именованные поля, что проще в сопровождении.

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

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

uni, Вы всерьез считаете, что при сдвиге на полбайта будет понятнее, если вместо "4" применить именованную константу?

 

Хотя, кто знает...

#define fourthElement 3
#define halfbyte 4

byte level = teaBuf[fourthElement] >> halfbyte;

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Я считать лучше использовать комментарий

byte level = teaBuf[3] >> 4;// получить уровень сигнала с TEA5767

И все. И не важно какого цвета кошка . Главное что бы она ловила мышей. А для тех кто сомнивается , то написать комментарий:"Это кошка. Она ловит тут мышей."

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Спасибо за ответы, главное всетаки было убедится что в моем случае сдвига достаточно.

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

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

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

Точно так же делают, когда работают с заголовочниками bmp или wav файлов.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Дайте ссылку на используемый datasheet и я покажу как это обычно делается.

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

uni пишет:

Дайте ссылку на используемый datasheet и я покажу как это обычно делается.

Даташит тут

За пример использования буду признателен.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Как-то вот так (пояснение):

typedef struct _TEA5767HN_READDATA
{
    // Format of 1st data byte.
    unsigned PLLHigh        : 6;
    unsigned BandLimitFlag  : 1;
    unsigned ReadyFlag      : 1;
    
    unsigned                : 0;
    
    // Format of 2nd data byte.
    unsigned PLLLow         : 8;
    
    unsigned                : 0;
   
    // Format of 3rd data byte.
    unsigned IFCounter      : 7;
    unsigned Stereo         : 1;

    unsigned                : 0;

    // Format of 4th data byte.
    unsigned Reserved0      : 1;
    unsigned ChipId         : 3;
    unsigned Level          : 4;
    	
} * PTEA5767HN_READDATA, TEA5767HN_READDATA;

TEA5767HN_READDATA data;
PTEA5767HN_READDATA pData;

void setup() 
{   
    unsigned char buf[5] = { 0x80, 0x00, 0x80, 0xAE, 0x00 };

    pData = ( PTEA5767HN_READDATA ) buf;   
    
    data.ReadyFlag = pData->ReadyFlag;
    data.Stereo = pData->Stereo;
    
    data.Level = pData->Level;
    data.ChipId = pData->ChipId;    
}

 

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Спасибо отгромное, выглядит классно. 

 unsigned                : 0;

Я правильно понимаю что конструкция выше говорит о том что следующие за ней данные пишутся в новый байт?

И могу ли я использовать структуру так? То есть почему PTEA5767HN_READDATA определяется как сылка а TEA5767HN_READDATA нет?

int ReadyFlag = pData->ReadyFlag;
int Stereo = pData->Stereo;
int Level = pData->Level;
int ChipId = pData->ChipId;   

Либо в условии

if (pData->Stereo) { .... }

А структуру обязательно объявлять как глобальную?
У меня беда в том что делаю под управлением NANO на ATmega168 радиоприемник, памяти всего килобайт, выбрасывать ее жалко.

Пока прикрутил только экран на ssd1306 и начал прикручивать радио на tea5767, еще на очереди esp8266.
Поэтому на счету каждый байт :-)
Попробую сегодня сделать со структурой, посмотрю что с памятью будет.

uni
uni аватар
Offline
Зарегистрирован: 24.09.2015

Можно использовать только указатель PTEA5767HN_READDATA pData, переменную TEA5767HN_READDATA  я указал для примера. Описание типа PTEA5767HN_READDATA обычно указывают глобально, а сами переменные этого типа могут объявлятся где угодно.

int ReadyFlag = pData->ReadyFlag;
int Stereo = pData->Stereo;
int Level = pData->Level;
int ChipId = pData->ChipId;   

Да, можно, так и было задумано. Вы как-бы накладываете описание структуры на буфер с данными и через это описание обращаетесь к нужным вам полям в этом буфере. При этом не нужно думать о самих битовых полях в буфере, компилятор разберётся, используя описание структуры.

> То есть почему PTEA5767HN_READDATA определяется как сылка а TEA5767HN_READDATA нет?

Просто пример использования и переменной, и указателя. Специально объявлять переменную не нужно. Иногда бывает удобным хранить данные в виде переменной с таким типом. Можно ведь сделать и обратную операцию. Создать такую переменную и наложить на неё маску в виде указателя на массив байт. Тогда можно обращаться к каждому отдельному байту структуры по этому указателю.

В более сложных случаях struct совмещают с union, чтобы можно было обращаться к одной и той же области памяти как к целому объекту или как к набору полей.

unsigned                : 0;

В данном случае - да, это делитель на байты, но в общем случае компилятор считает сколько битов использовалось до делителя и делает выравнивание на целое число байт (я не проверял, но должно быть что-то вроде этого).

 

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

uni пишет:

typedef struct _TEA5767HN_READDATA
{
    // Format of 1st data byte.
    unsigned PLLHigh        : 6;
    unsigned BandLimitFlag  : 1;
    unsigned ReadyFlag      : 1;
    
    unsigned                : 0;
    
    // Format of 2nd data byte.
    unsigned PLLLow         : 8;
    
    unsigned                : 0;
   
    // Format of 3rd data byte.
    unsigned IFCounter      : 7;
    unsigned Stereo         : 1;

    unsigned                : 0;

    // Format of 4th data byte.
    unsigned Reserved0      : 1;
    unsigned ChipId         : 3;
    unsigned Level          : 4;
    	
} * PTEA5767HN_READDATA, TEA5767HN_READDATA;

TEA5767HN_READDATA data;
PTEA5767HN_READDATA pData;

void setup() 
{   
    unsigned char buf[5] = { 0x80, 0x00, 0x80, 0xAE, 0x00 };

    pData = ( PTEA5767HN_READDATA ) buf;   
    
    data.Stereo = 0;
}

 

Медленно, но разбираюсь, а подскажите, как из структуры передать данные в массив байт?

Например поменял я часть значений в стркутурке, теперь хочу записать измеренные данные назад в приемник. Как пример изменил стерео на моно. теперь надо передать 1 байт в который входит бит Stereo. Для этого из структуры надо получить третий байт. А вот как преобразовать структуру в массив байт или один байт структуры в байт для записи в чип я не пойму. Подскажите пожалуйста как быть.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

IMHO самое простое - берете адрес структуры, меняете его тип на указететь на байт или на символ и выбираете байты по нужному смещению.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Некузяво. Логичнее объявить union с массивом байтов и работать средствами языка, а не указателей фиг пойми на что.

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

Arhat109-2 пишет:

Некузяво. Логичнее объявить union с массивом байтов и работать средствами языка, а не указателей фиг пойми на что.

Спасибо за подсказку, с union действительно красиво получается. Не все еще проверил, но часть что изменил работает. 

А вот с указателями и вычислением адреса не понял как делать, то есть получить адрес структуры, и читать значения именно с адресов, а зная размер полей структуры можно адрес каждого байта рассчитать. Так?

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

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

В одном из моих сообщений есть и реально проверенный код с указателями.

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

andriano пишет:

Andrey12, загляните в соседнюю тему про фиксированную точку....

Заглянул, почитал. Но вчера с union разобрался. Переделал, теперь работает как и хотел. 

Всем спасибо за помощь.