Вытянуть из байта полубайт или сложить полубайты

MaksVV
Offline
Зарегистрирован: 06.08.2015

Задача такая. Имеем сообщение допустим из 4 байт :  0x02  0x10  0xB3  0x3A

Опытным путем выяснил, что в него забита проверка контрольной суммы. Контрольной суммой является правый полубайт (как правильно младший?) последнего байта, т.е. полубайт "A"

этот "A" должен быть равен последнему правому полубайту (младшему) результата выражения:   полубайт E минус сумма всех предыдущих полубайт, т.е. Е - (0+2+1+0+B+3+3)=E-14=FFFFFFFFFFFFFFFA, вот он младший полубайт "А" - совпадает. 

Попробовал через bitRead и Write сделать, вроде получилось, но насколько это коряво? Научите, если это не сложно,  попроще через битовые сдвиги и т.д. На примере быстрее пойму. 

Вот как я сделал. Создадим массив  - сообщение с ошибкой (ложной контрольной суммой). Младший полубайт последнего байта вместо "А" сделаем "0"           byte Message[4] = {0x02, 0x10, 0xB3, 0x30};

потом подсчитаем CRC и переправим в массиве как должно быть. 

byte Message[4] = {0x02, 0x10, 0xB3, 0x30};

byte A,B;
byte CRC = 0;


void setup() {
  

Serial.begin (9600);

// распечатаем сначала сообщение как было, с ошибкой 

for (int i=0; i<4; i++) {Serial.print (Message[i], HEX); Serial.print ("    ");}
Serial.println ("");


// сосчитаем контрольную сумму полубайт первых трёх байт массива,
//используя переменные А и B для промежуточных расчетов 
for (int n=0; n<3; n++){ 
for (int i=4; i<8; i++) bitWrite(A, i-4, bitRead(Message[n],i)) ;
for (int i=0; i<4; i++) bitWrite(B, i, bitRead(Message[n],i)) ;
CRC = CRC + A+B;
}

// сосчитаем и приплюсуем старший полубайт последнего четвертого байта массива
for (int i=4; i<8; i++) bitWrite(A, i-4, bitRead(Message[3],i)) ;
CRC = CRC + A;

// сосчитаем контрольную сумму по правилу с вычитом из 0x0E,
//используя переменную B для промежуточных расчетов 
B = 0x0E - CRC;


// забьем в младший полубайт последнего байта массива правильную контрольную сумму
 for (int i=0; i<4; i++) bitWrite (Message[3], i, bitRead(B,i));


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

for (int i=0; i<4; i++)  {Serial.print (Message[i], HEX); Serial.print ("    ");}
Serial.println ("");

}


void loop() {}

 

b707
Offline
Зарегистрирован: 26.05.2017

Извлечь из байта (a) старший полубайт - (a & 0xF0)>>4

младший соответсвенно a & 0x0F

sadman41
Offline
Зарегистрирован: 19.10.2016
byte x,y,u;

x = 0x3A;

u = x & 0xF0;

Serial.println(u, HEX);

y = x & 0x0F;

Serial.println(y, HEX);

 

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 - в первом случае сдвиг потеряли

Кстати. старший проще  так:   x>>4

А младший еще можно так:   (x<<4)>>4

MaksVV
Offline
Зарегистрирован: 06.08.2015

спасибо!

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

sadman41 - в первом случае сдвиг потеряли

Кстати. старший проще  так:   x>>4

А младший еще можно так:   (x<<4)>>4

Откровенно говоря - я и не пытался ничего сдвинуть, просто намекнул на то, как нибблы выделить.

А вообще - с этими сдвигами осторожней нужно быть, а то забудешься или описку допустишь, двинешь знаковый тип, потом сидишь и башку чешешь:

int8_t x,y;
x = 0xF0;
y = x >> 4;
printf("%x\n", y);

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

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

 for (int i=0; i<4; i++) bitWrite (Message[3], i, bitRead(B,i));

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Так же, как и выделили - с помощью битовых операций: https://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D0%B1%D0%B8%D1%82%D0%BE%D0%B2%D1%8B%D0%B5_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B8

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

b707
Offline
Зарегистрирован: 26.05.2017

оператором "|"

MaksVV
Offline
Зарегистрирован: 06.08.2015

вопрос, а сильно стремно делать через цикл фор? Есть ли негатив в том методе, который применил я? Т.е. в моём случае я понял, МК по одному биту всё выполняет. А при сдвигах  - за раз все 4 бита. Через цикл for сильно медленнее будет? Если нет, то мне так гораздо понятнее программу писать. 

b707
Offline
Зарегистрирован: 26.05.2017

MaksVV пишет:

Т.е. в моём случае я понял, МК по одному биту всё выполняет. А при сдвигах  - за раз все 4 бита. Через цикл for сильно медленнее будет? Если нет, то мне так гораздо понятнее программу писать. 

Думаю, что разница будет в сотни раз. В вашем случае мало того что биты по одному - так еще и через высокоуровневые функции библиотеки.

Другой вопрос - важна ли для вашего случая эта разница. В кухонном таймере разница между 10мкс и 1 мс незаметна, а при управлении боеголовкой - критична :)

MaksVV
Offline
Зарегистрирован: 06.08.2015

 в байте "B"  подсчитана контрольная сумма. Вытягиваем из неё младший ниббл. 

A = B & 0x0F;

и вот правильно ли я записываю ниббл "А" в младший ниббл Message[3] ?

Message[3] = Message[3]|A;

по результатам вроде работает. Всем спасибо. Вот итоговый скетч 

byte Message[4] = {0x02, 0x10, 0xB3, 0x30};

byte A,B;
byte CRC = 0;


void setup() {
  

Serial.begin (9600);

// распечатаем сначала сообщение как было, с ошибкой 

for (int i=0; i<4; i++) {Serial.print (Message[i], HEX); Serial.print ("    ");}
Serial.println ("");


// сосчитаем контрольную сумму полубайт первых трёх байт массива,
//используя переменные А и B для промежуточных расчетов 



for (int n=0; n<3; n++){ 
A = ((Message[n]) & 0xF0)>>4;
B = (Message[n]) & 0x0F;

//раньше это делал так:
//for (int i=4; i<8; i++) bitWrite(A, i-4, bitRead(Message[n],i)) ;
//for (int i=0; i<4; i++) bitWrite(B, i, bitRead(Message[n],i)) ;

CRC = CRC + A+B; // суммыруем нибблы 
}

// сосчитаем и приплюсуем старший полубайт последнего четвертого байта массива
A = (Message[3] & 0xF0)>>4;
//A  = for (int i=4; i<8; i++) bitWrite(A, i-4, bitRead(Message[3],i)) ;
CRC = CRC + A;

// сосчитаем контрольную сумму по правилу с вычитом из 0x0E,
//используя переменнst A и B для промежуточных расчетов 

B = 0x0E - CRC;  // контрольная сумма

A = B & 0x0F; // вытянутый младший полубайт из контрольной суммы

// забьем в младший полубайт последнего байта массива правильную контрольную сумму
 
Message[3] = Message[3]|A;

//раньше это делал так:
//for (int i=0; i<4; i++) bitWrite (Message[3], i, bitRead(B,i)); 


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

for (int i=0; i<4; i++)  {Serial.print (Message[i], HEX); Serial.print ("    ");}
Serial.println ("");

}


void loop() {}

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

b707 пишет:
Другой вопрос - важна ли для вашего случая эта разница. В кухонном таймере разница между 10мкс и 1 мс незаметна, а при управлении боеголовкой - критична :)

думаю важна . Тут работа с CAN шиной и если медлить, то сообщения будут пропускаться и вообще там много чем ещё МК будет занят . Благодарю за оптимизацию. 

MaksVV
Offline
Зарегистрирован: 06.08.2015

проверил на устройстве эта строка видимо не работает

Message[3] = Message[3]|A;

b707
Offline
Зарегистрирован: 26.05.2017

MaksVV пишет:

 в байте "B"  подсчитана контрольная сумма. Вытягиваем из неё младший ниббл. 

A = B & 0x0F;

и вот правильно ли я записываю ниббл "А" в младший ниббл Message[3] ?

Message[3] = Message[3]|A;

Я бы перед записью для уверенности почистил бы младший полубайт Message[3]:

Message[3] = (Message[3] & 0xF0) |A;

sadman41
Offline
Зарегистрирован: 19.10.2016

Это даже не для уверенности, а насущная необходимость...

MaksVV, Давайте рассмотрим две ситуации с одним нибблом. В каком случае результат операции OR не будет равен ожидаемому (перенос ниббла) ?

1)
0000 - Old nibble
0101 - New nibble
???? - result 
 
2)
1010 - Old nibble
0101 - New nibble
???? - result 
 
MaksVV
Offline
Зарегистрирован: 06.08.2015

b707,  красавчик ! с чисткой ниббла заработало! Круть.

MaksVV
Offline
Зарегистрирован: 06.08.2015

sadman41 пишет:

Это даже не для уверенности, а насущная необходимость...

MaksVV, Давайте рассмотрим две ситуации с одним нибблом. В каком случае результат операции OR не будет равен ожидаемому (перенос ниббла) ?

1)
0000 - Old nibble
0101 - New nibble
???? - result 
 
2)
1010 - Old nibble
0101 - New nibble
???? - result 
 

да, именно так

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:

Это даже не для уверенности, а насущная необходимость...

Я предполагал, что младшие разряды могут быть почищены заранее...

MaksVV
Offline
Зарегистрирован: 06.08.2015

Респект друзья за ликбез