битовые операции

project_vdc
Offline
Зарегистрирован: 26.08.2014

Подскажите пожалуйста, есть такая операция сброса бита, её часто встречаю, например

LED_PORT = LED_PORT & ~(1<<LED2);
Понятно что она делает, а вот такая операция
LED_PORT = LED_PORT & (<<LED2);

разве не делает тоже самое? И логичнее выглядит

project_vdc
Offline
Зарегистрирован: 26.08.2014

Что-то вставилось не так, вот ещё

LED_PORT = LED_PORT & (0<<LED2);
kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

По второй конструкции, если 0 сдвинуть на 5, на 7, на 1 бит влево, то он останется нулем. Потому в порт просто запишется 0.

Чем не нравится первая конструкция?

 

project_vdc
Offline
Зарегистрирован: 26.08.2014

Да, сенк, уже разобрался

Вторая операция просто обнулит весь регистр порта, а не только нужный бит

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

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

....при применении операций с DDRx , PORTx не хотелось бы портить значения других битов при изменении нужных СМЕЖНЫХ битов

установка битов в байте, сброс битов в байте - немного понял....

как безопасно прописать изменения в байт Bxxxxxxxx , например на Bxx1011xx ? в каких битах сброс, в каких установка - не известно же....

и есть ли разница ( с точки зрения МК ) - рулим одним битом или несколькими битами ( смежными ) ?

спасибо :)

project_vdc
Offline
Зарегистрирован: 26.08.2014
Пока тоже учусь, вот типа так:

Устанавливаем в лог. 1 порты используя маску и логическое ИЛИ

PORTB = PORTB | 0b00101100;

Там где в маске 1 с соотв. битом порта он даст 1 независимо был ли там 0 или 1 до этого, остальные биты не изменятся

Устанавливаем в лог. 0 порты используя маску и логическое И

PORTB = PORTB & 0b11101111;

Там где в маске 0 с соотв. битом порта он даст 0 независимо был ли там 0 или 1 до этого, остальные биты не изменятся

А с точки зрения мк он оперирует целым байтом в данном случае

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

это я понимаю.... 

как безопасно прописать изменения в байт Bxxxxxxxx , например на Bxx1011xx ? в каких битах сброс, в каких установка - не известно же....

...а тут - опасаюсь от незнания :( , как и установить , и сбросить ( не зная состояния битов ) ?

маска ? а по ИЛИ, И, ТИЛЬДА, НОТ ? как правильно ?

project_vdc
Offline
Зарегистрирован: 26.08.2014

А это не важно что там было до этого, можете сами проверить логическими операциями побитно

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

спасибо :)

Logik
Offline
Зарегистрирован: 05.08.2014

В глубинах системы есть макросы для установки и сброса битов: sbi и cbi. Помоему они не всегда доступны, что лечится кодом

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif 

 Просто маска бита - _BV(bit). Есть и для проверки состояния бита, типа bit_is_set(sfr, bit) есть и ожидания нужного состояния, ищите, всё уже давно написано до нас.

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

в том и трудность..... что искать.... после паскаля ( дельфи ) - голова кругом от определений cpp :(

спасибо :)

 

#9 - мне тройной интеграл прощееееее, чем эти описания :) - будем учиться 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

С конфигурационными регистрами особая ситуация, к примеру стоит в единице бит WGM13 в регистре TCCR1B.  А если в него записать ещё единицу, TCCR1B | = 1<<WGM13, то бит обнулится :) Я раньше этого не знал, один раз очень долго провозился, пока понял что они складываются.. И кажется в некоторых регистрах единица от сложения перекидывается на старший бит, где-то мне показалось, что такое явление видел..

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

dimax пишет:

С конфигурационными регистрами особая ситуация, к примеру стоит в единице бит WGM13 в регистре TCCR1B.  А если в него записать ещё единицу, TCCR1B | = 1<<WGM13, то бит обнулится :) Я раньше этого не знал, один раз очень долго провозился, пока понял что они складываются.. И кажется в некоторых регистрах единица от сложения перекидывается на старший бит, где-то мне показалось, что такое явление видел..

вопрос именно по этой ситуации ! тут низя ошибацца :(

Logik
Offline
Зарегистрирован: 05.08.2014

SU-27-16 пишет:

в том и трудность..... что искать....

 

Так их раз надо найти и запомнить. Или записать. Упрощают жизнь заметно. Ошибок меньше. Тройной интеграл сложней. Обясняю. Встретив строку sbi(X,3) компилятор найдет, где обявлялось "sbi" и это окажется#define sbi(sfr, bit) (sfr |= _BV(bit)). Он просто подставит параметры и вместо sbi(X,3) получит в программе текст  (X |=_BV(3)). Далее аналогично встретит _BV(3) найдет, что оно обявлялось как #define _BV(bit) (1 << (bit)) и теперь заменит _BV(3) на (1 << (3)). В результате в программе вместо sbi(X,3) как бы записано X |=(1 << (3))). Но вам о этом можно и не задумыватся, достаточно помнить что sbi устанавливает бит с заданым номером в заданой переменной.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

sbi/cbi легко запомнить по их полному названию SetBit / ClearBit,

правда толку от них не много ))

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Установка и сброс битов производится разными командами, т.е. нельзя одновременно сбросить и установить биты, разумеется мы говорим про то, что это только про часть битов, а не про весь порт сразу. Для простоты считаем, что говорим про один порт.
И так. Чтобы установить биты, используем конструкцию
Var |= (1 << номер бита1) | (1 << номер бита 2) и т.п. Например, если нужно установить в 1 биты 3 и 5, выполняем:
Var |= (1 << 3) | (1 << 5); Наверное это простая и более понятная операция.
Теперь о сбросе. Например, нужно сбросить биты 2 и 6, оставив остальные неизменными. Выполняем:
Var &= ~((1 << 2) | (1 << 6));
Это значит что биты 2 и 6 устанавливаются в 0, остальные не изменяются.
Боюсь, что проще не могу описать. Если непонятно, лучше спросить, что именно непонятно

Datak
Offline
Зарегистрирован: 09.10.2014

kisoft пишет:
Установка и сброс битов производится разными командами, т.е. нельзя одновременно сбросить и установить биты, разумеется мы говорим про то, что это только про часть битов, а не про весь порт сразу.

А вот не согласный я.

Выбираем маску, задающую биты, которы мы хотим изменить, и соответственно, маскирующую все остальные:







#define MASK b00011110

Допустим, нужно установить биты 1 и 3, и сбросить 2 и 4.
При этом биты 0, 5, 6, 7 должны, естественно, остаться неизменными. Выглядеть это будет так:







Val ^= ( Val & MASK ) ^ ( ( 1 << 1 ) | ( 1 << 3 ) );

или лучше даже так:

Val ^= MASK & ( Val ^ ( ( 1 << 1 ) | ( 1 << 3 ) ) );

Надеюсь, не ошибся. :)

gena
Offline
Зарегистрирован: 04.11.2012

  А зачем "загонять" установку и сброс битов в одну команду? Да, выглядит вроде компактней, но (лично мне) воспринимается тяжелее. Легче воспринимаю поотдельности: это сброс, а это установка. Да и неприятностей от компилятора будет меньше. К слову. Часто очень удобно в команде указывать не позицию, а логическое имя бита - сразу идёт смысловая подсказка. Для этого правда нужно неплохо ориентироваться в  регистрах микроконтроллера (медленное сползание к ассемблеру).

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

Посмотрим какой код генерит ArduinoIDE:

//  var |= (1 << 3) | (1 << 5);
lds	r24, 0x0100
ori	r24, 0x28	; 40
sts	0x0100, r24
// var &= ~((1 << 2) | (1 << 4));
lds	r24, 0x0100
andi	r24, 0xEB	; 235
sts	0x0100, r24

// var ^= (var & MASK) ^ ((1 << 3) | (1 << 5));
lds	r25, 0x0100
lds	r24, 0x0100
ldi	r18, 0x28	; 40
eor	r24, r18
andi	r25, 0x1E	; 30
eor	r24, r25
sts	0x0100, r24

Я не говорю, что код оптимален. Чисто по байтам, оба кода занимают по 20 байт. Разумеется, если убрать лишние промежуточные запись и чтение в первом варианте, то можно сильно съэкономить. Но это не главное, хотя иногда сильно может помочь, когда места мало.

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

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

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

 

gena
Offline
Зарегистрирован: 04.11.2012

   Ещё раз посмотрел на выражение   "Val ^= MASK & ( Val ^ ( ( 1 << 1 ) | ( 1 << 3 ) ) );" и вспомнил https://www.youtube.com/watch?v=rASm-qr19_o . Классика.

Datak
Offline
Зарегистрирован: 09.10.2014

gena пишет:
Ещё раз посмотрел на выражение  .....  и вспомнил

Ну да, об этом и писал. :)

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

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

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

Понимаю, при ардуинопрограммировании это скорее всего не пригодится. Но так, ради чисто научного интереса.

 

Datak
Offline
Зарегистрирован: 09.10.2014

Вот ещё, если кому-нибудь интересно битами шевелить - оптимизированная версия для одновременной записи:







in      r25, PORTB // r25 = PORTB;
andi    r25, 0x1E  // r25 &= b00011110
ldi     r24, 0x0A  // 
eor     r25, r24   // r25 ^= b00001010
out     PINB, r25  // PORTB ^= r25

и для не одновременной:







sbi     PORTB, 1
cbi     PORTB, 2
sbi     PORTB, 3
cbi     PORTB, 4
gena
Offline
Зарегистрирован: 04.11.2012

  Ну так, для полноты дискуссии, разве

3   ldi r24, 0x0A //
4   eor r25, r24 // r25 ^= b00001010

не упрощается до  

ori r25, 0x0A   // r25  |= b00001010    ?

(распугаем тут начинающих :-) ).

 

Datak
Offline
Зарегистрирован: 09.10.2014

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

Смотрите, там же байт записывается в PINB - не  в PORTB. Тоже трюк, конечно, пинайте меня,
но по атмеловскому даташиту запись в PINB инвертирует биты, в которые пишутся единицы. Другими словами, выполняет операцию XOR (исключающее "или") с текущим значением PORTB.

Начинающие, не пугайтесь - нам самим страшно. :)

-------

Эти начинающие, кстати, вебсерверы влёгкую поднимают, с использованием вифи всяких, и блюпупов. А я тут, до сих пор, так и не умею ничего, кроме двиганья битов и мигания светодиодами. :)

bwn
Offline
Зарегистрирован: 25.08.2014

Эти начинающие, кстати, вебсерверы влёгкую поднимают, с использованием вифи всяких, и блюпупов. А я тут, до сих пор, так и не умею ничего, кроме двиганья битов и мигания светодиодами. :)

Да лано, все у вас еще впереди. Мы ведь просто не осознаем во что ввязались, а оно глядишь и взлетело))))

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Datak, можно подумать тебе уже под 60 :) Всё и сразу бывает только в сказке ;)

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

Вот в ARM одной командой можно и сбросить и установить любое количество битов. Но это совсем другая история.

 

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

Вот в ARM одной командой можно и сбросить и установить любое количество битов. Но это совсем другая история.

вооооооооооооооооот именно этого и хочется..... хоть через макросы, хоть через написание отдельной функции :)

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Функция? Так если пофиг на размер и оптимальности, то в чем сложность передать в функцию номер порта, маску для установки и маску для сброса, а в функции это в две или в одну команду сделать. Только смысла особого нет. Впрочем, для практики, возьми и напиши, если не получится, по можем ;)

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

kisoft пишет:
Функция? Впрочем, для практики, возьми и напиши, если не получится, по можем ;)

дети мозги выносят, им захотелось ТАК сделать - а я, втихоря, вам :) , пока думают - если чё переспрошу....

спасибо :)

RANDREY
Offline
Зарегистрирован: 10.06.2012

А чем бы проверить состояние битов? опять фукцию потерял

типа 

boolen isBit (BYTE inn, BYTE nomer)

Проверка установлен ли бит

inn число байт

nomer - номер бита 0....7

Result= TRUE установлен FALSE - нет

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011
dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

RANDREY,   есть макросы: bit_is_set(REG,BIT) - проверка, установлен ли бит BIT в регистре REG
bit_is_clear(REG,BIT) - аналогично предыдущему, но проверка на сброшенность бита

dmitriykisliy
Offline
Зарегистрирован: 03.03.2015

ввсе таксложно, после #5 поста тупик

просьба, поясните действия процессора

PORTB &= ~(1<<1);

я понял так:

 в первый бит записать НЕ (~) 1. не понял зачем &?

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

как продвинутый лошара :) поясняю....
1 - в первый бит регистра в ОЗУ записать 1
2 - тильда - поразрядно проинвертировать пункт 1
3 - поразрядно перемножить регистр B со значением из 2
итого - обнуление бита 1 регистра B без изменения состояния битов 0,2,3,4,5,6,7
...кататак :) - сам долго вникал :(
опытные может как покруче пояснят :)-

С++ - это не Паскаль :(

dmitriykisliy
Offline
Зарегистрирован: 03.03.2015

SU-27-16 пишет:

как продвинутый лошара :) поясняю....
1 - в первый бит регистра в ОЗУ записать 1
2 - тильда - поразрядно проинвертировать пункт 1
3 - поразрядно перемножить регистр B со значением из 2
итого - обнуление бита 1 регистра B без изменения состояния битов 0,2,3,4,5,6,7
...кататак :) - сам долго вникал :(
опытные может как покруче пояснят :)-

С++ - это не Паскаль :(

если в 2 пункте бит 1 обнулен, то зачем перемножать(пункт 3)?

зы прошу как непродвинутый лошара)

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

dmitriykisliy пишет:

 в первый бит записать НЕ (~) 1. не понял зачем &?

Это не просто "записать", это операция сдвига. Вот ЭТО видео посмотрите, более-менее понятно объясняется битовый сдвиг на Си.  

UPD: Если не понятны основы управления портами через регистры, можно глянуть ещё ЭТО видео. Или даже так - сразу смотреть его, а потом уже про сдвиг. Так целостная картина получается.

Я хоть и не сторонник "видеообучалок", но там толково рассказывают и показывают. 

RANDREY
Offline
Зарегистрирован: 10.06.2012

trembo пишет:

http://arduino.cc/en/Reference/BitRead  Не подойдёт?

спасибо, всегда заглядываю в этот справочник, если там http://arduino.ru/Reference нету значит думаю не реализовано в данном субязыке(

dmitriykisliy
Offline
Зарегистрирован: 03.03.2015

Jeka_M пишет:

dmitriykisliy пишет:

 в первый бит записать НЕ (~) 1. не понял зачем &?

Это не просто "записать", это операция сдвига. Вот ЭТО видео посмотрите, более-менее понятно объясняется битовый сдвиг на Си.  

UPD: Если не понятны основы управления портами через регистры, можно глянуть ещё ЭТО видео. Или даже так - сразу смотреть его, а потом уже про сдвиг. Так целостная картина получается.

Я хоть и не сторонник "видеообучалок", но там толково рассказывают и показывают. 

Спасибо, дошло немного

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

Jeka_M пишет:

dmitriykisliy пишет:

 в первый бит записать НЕ (~) 1. не понял зачем &?

Это не просто "записать", это операция сдвига. Вот ЭТО видео посмотрите, более-менее понятно объясняется битовый сдвиг на Си.  

UPD: Если не понятны основы управления портами через регистры, можно глянуть ещё ЭТО видео. Или даже так - сразу смотреть его, а потом уже про сдвиг. Так целостная картина получается.

Я хоть и не сторонник "видеообучалок", но там толково рассказывают и показывают. 

в той лексеме нету СДВИГА....    сдвинуть мысленно единицу во второй разряд - это не сдвиг физический....
ТСу - изучи битовые операции :) , ор , анд и тильда

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014
PORTB &= ~(1<<1);

Оператор побитового сдвига был, а самого сдвига не было?