битовые операции
- Войдите на сайт для отправки комментариев
Ср, 04/03/2015 - 10:42
Подскажите пожалуйста, есть такая операция сброса бита, её часто встречаю, например
LED_PORT = LED_PORT & ~(1<<LED2);
Понятно что она делает, а вот такая операция
LED_PORT = LED_PORT & (<<LED2);
разве не делает тоже самое? И логичнее выглядит
Что-то вставилось не так, вот ещё
По второй конструкции, если 0 сдвинуть на 5, на 7, на 1 бит влево, то он останется нулем. Потому в порт просто запишется 0.
Чем не нравится первая конструкция?
Да, сенк, уже разобрался
Вторая операция просто обнулит весь регистр порта, а не только нужный бит
уважаемый kisoft , не могли бы вы подсказать типовые решения ?
....при применении операций с DDRx , PORTx не хотелось бы портить значения других битов при изменении нужных СМЕЖНЫХ битов
установка битов в байте, сброс битов в байте - немного понял....
как безопасно прописать изменения в байт Bxxxxxxxx , например на Bxx1011xx ? в каких битах сброс, в каких установка - не известно же....
и есть ли разница ( с точки зрения МК ) - рулим одним битом или несколькими битами ( смежными ) ?
спасибо :)
Там где в маске 1 с соотв. битом порта он даст 1 независимо был ли там 0 или 1 до этого, остальные биты не изменятся
Там где в маске 0 с соотв. битом порта он даст 0 независимо был ли там 0 или 1 до этого, остальные биты не изменятся
А с точки зрения мк он оперирует целым байтом в данном случае
это я понимаю....
как безопасно прописать изменения в байт Bxxxxxxxx , например на Bxx1011xx ? в каких битах сброс, в каких установка - не известно же....
...а тут - опасаюсь от незнания :( , как и установить , и сбросить ( не зная состояния битов ) ?
маска ? а по ИЛИ, И, ТИЛЬДА, НОТ ? как правильно ?
А это не важно что там было до этого, можете сами проверить логическими операциями побитно
спасибо :)
В глубинах системы есть макросы для установки и сброса битов: sbi и cbi. Помоему они не всегда доступны, что лечится кодом
1
// defines for setting and clearing register bits
2
#ifndef cbi
3
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
4
#endif
5
#ifndef sbi
6
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
7
#endif
Просто маска бита - _BV(bit). Есть и для проверки состояния бита, типа bit_is_set(sfr, bit) есть и ожидания нужного состояния, ищите, всё уже давно написано до нас.
в том и трудность..... что искать.... после паскаля ( дельфи ) - голова кругом от определений cpp :(
спасибо :)
#9 - мне тройной интеграл прощееееее, чем эти описания :) - будем учиться
С конфигурационными регистрами особая ситуация, к примеру стоит в единице бит WGM13 в регистре TCCR1B. А если в него записать ещё единицу, TCCR1B | = 1<<WGM13, то бит обнулится :) Я раньше этого не знал, один раз очень долго провозился, пока понял что они складываются.. И кажется в некоторых регистрах единица от сложения перекидывается на старший бит, где-то мне показалось, что такое явление видел..
С конфигурационными регистрами особая ситуация, к примеру стоит в единице бит WGM13 в регистре TCCR1B. А если в него записать ещё единицу, TCCR1B | = 1<<WGM13, то бит обнулится :) Я раньше этого не знал, один раз очень долго провозился, пока понял что они складываются.. И кажется в некоторых регистрах единица от сложения перекидывается на старший бит, где-то мне показалось, что такое явление видел..
вопрос именно по этой ситуации ! тут низя ошибацца :(
в том и трудность..... что искать....
Так их раз надо найти и запомнить. Или записать. Упрощают жизнь заметно. Ошибок меньше. Тройной интеграл сложней. Обясняю. Встретив строку 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 устанавливает бит с заданым номером в заданой переменной.
sbi/cbi легко запомнить по их полному названию SetBit / ClearBit,
правда толку от них не много ))
Установка и сброс битов производится разными командами, т.е. нельзя одновременно сбросить и установить биты, разумеется мы говорим про то, что это только про часть битов, а не про весь порт сразу. Для простоты считаем, что говорим про один порт.
И так. Чтобы установить биты, используем конструкцию
Var |= (1 << номер бита1) | (1 << номер бита 2) и т.п. Например, если нужно установить в 1 биты 3 и 5, выполняем:
Var |= (1 << 3) | (1 << 5); Наверное это простая и более понятная операция.
Теперь о сбросе. Например, нужно сбросить биты 2 и 6, оставив остальные неизменными. Выполняем:
Var &= ~((1 << 2) | (1 << 6));
Это значит что биты 2 и 6 устанавливаются в 0, остальные не изменяются.
Боюсь, что проще не могу описать. Если непонятно, лучше спросить, что именно непонятно
А вот не согласный я.
Выбираем маску, задающую биты, которы мы хотим изменить, и соответственно, маскирующую все остальные:
1
#define MASK b00011110
Допустим, нужно установить биты 1 и 3, и сбросить 2 и 4.
При этом биты 0, 5, 6, 7 должны, естественно, остаться неизменными. Выглядеть это будет так:
1
Val ^= ( Val & MASK ) ^ ( ( 1 << 1 ) | ( 1 << 3 ) );
или лучше даже так:
1
Val ^= MASK & ( Val ^ ( ( 1 << 1 ) | ( 1 << 3 ) ) );
Надеюсь, не ошибся. :)
А зачем "загонять" установку и сброс битов в одну команду? Да, выглядит вроде компактней, но (лично мне) воспринимается тяжелее. Легче воспринимаю поотдельности: это сброс, а это установка. Да и неприятностей от компилятора будет меньше. К слову. Часто очень удобно в команде указывать не позицию, а логическое имя бита - сразу идёт смысловая подсказка. Для этого правда нужно неплохо ориентироваться в регистрах микроконтроллера (медленное сползание к ассемблеру).
Datak, не согласный, так не согласный. Трюки хороши, пока себе что то доказать нужно. Впрочем, это не важно, давайте посмотрим, что получается если использовать тот или иной код.
Посмотрим какой код генерит ArduinoIDE:
01
// var |= (1 << 3) | (1 << 5);
02
lds r24, 0x0100
03
ori r24, 0x28 ; 40
04
sts 0x0100, r24
05
// var &= ~((1 << 2) | (1 << 4));
06
lds r24, 0x0100
07
andi r24, 0xEB ; 235
08
sts 0x0100, r24
09
10
// var ^= (var & MASK) ^ ((1 << 3) | (1 << 5));
11
lds r25, 0x0100
12
lds r24, 0x0100
13
ldi r18, 0x28 ; 40
14
eor r24, r18
15
andi r25, 0x1E ; 30
16
eor r24, r25
17
sts 0x0100, r24
Я не говорю, что код оптимален. Чисто по байтам, оба кода занимают по 20 байт. Разумеется, если убрать лишние промежуточные запись и чтение в первом варианте, то можно сильно съэкономить. Но это не главное, хотя иногда сильно может помочь, когда места мало.
Второй довод против использования трюков - читабельность кода значительно хуже. Т.е. допустить ошибку в таком коде много проще. К тому же, вернувшись к этому коду (особенно если не написал комментарий), можешь и не вспомнить , что почем.
Никого не заставляю, просто лично я привык работать в группе и знаю, что чем проще код, тем его легче доработать и меньше шансов допустить ошибки. Конечно Ардуино не особо требует групповой работы, однако некоторые постулаты и в Африке постулаты.
Можно было бы еще устроить разбор по поводу слова "команда" в моем исходном сообщении, но смысла не вижу, на мой взгляд доводов вполне достаточно, чтобы не юзать трюки.
Ещё раз посмотрел на выражение "Val ^= MASK & ( Val ^ ( ( 1 << 1 ) | ( 1 << 3 ) ) );" и вспомнил https://www.youtube.com/watch?v=rASm-qr19_o . Классика.
Ну да, об этом и писал. :)
Не, я тоже никого к таким трюкам не призываю, а если даже они используются, их лучше прятать куда-нибудь в макросы или функции.
Но в некоторых случаях они действительно могут пригодиться. В данном случае ведь о портах была речь, вот я и предложил способ записать несколько битов именно одновременно.
Таким способом, например, несколько разных кусков кода могут делить между собой разные биты одного порта, при этом даже не запрещая прерывания на время чтения/записи.
Понимаю, при ардуинопрограммировании это скорее всего не пригодится. Но так, ради чисто научного интереса.
Вот ещё, если кому-нибудь интересно битами шевелить - оптимизированная версия для одновременной записи:
1
in
r25, PORTB
// r25 = PORTB;
2
andi r25, 0x1E
// r25 &= b00011110
3
ldi r24, 0x0A
//
4
eor r25, r24
// r25 ^= b00001010
5
out
PINB, r25
// PORTB ^= r25
и для не одновременной:
1
sbi PORTB, 1
2
cbi PORTB, 2
3
sbi PORTB, 3
4
cbi PORTB, 4
Ну так, для полноты дискуссии, разве
3 ldi r24, 0x0A //
4 eor r25, r24 // r25 ^= b00001010
не упрощается до
ori r25, 0x0A // r25 |= b00001010 ?
(распугаем тут начинающих :-) ).
Не, кажется нет. OR просто установит заданные биты, а нам нужно установить их в состояние противоположное тому что у них было. Тогда после последнего XOR'а, в пятой строке, они станут единицами.
Смотрите, там же байт записывается в PINB - не в PORTB. Тоже трюк, конечно, пинайте меня,
но по атмеловскому даташиту запись в PINB инвертирует биты, в которые пишутся единицы. Другими словами, выполняет операцию XOR (исключающее "или") с текущим значением PORTB.
Начинающие, не пугайтесь - нам самим страшно. :)
-------
Эти начинающие, кстати, вебсерверы влёгкую поднимают, с использованием вифи всяких, и блюпупов. А я тут, до сих пор, так и не умею ничего, кроме двиганья битов и мигания светодиодами. :)
Эти начинающие, кстати, вебсерверы влёгкую поднимают, с использованием вифи всяких, и блюпупов. А я тут, до сих пор, так и не умею ничего, кроме двиганья битов и мигания светодиодами. :)
Да лано, все у вас еще впереди. Мы ведь просто не осознаем во что ввязались, а оно глядишь и взлетело))))
Datak, можно подумать тебе уже под 60 :) Всё и сразу бывает только в сказке ;)
Про PINB не знал, видимо читал только между строк :) В узких местах такое вполне может пригодиться, особенно если еще и комментарий написать. Другое дело - это фича всех камней или только каких то особых, тогда - только узкое применение.
Вот в ARM одной командой можно и сбросить и установить любое количество битов. Но это совсем другая история.
Вот в ARM одной командой можно и сбросить и установить любое количество битов. Но это совсем другая история.
вооооооооооооооооот именно этого и хочется..... хоть через макросы, хоть через написание отдельной функции :)
Функция? Так если пофиг на размер и оптимальности, то в чем сложность передать в функцию номер порта, маску для установки и маску для сброса, а в функции это в две или в одну команду сделать. Только смысла особого нет. Впрочем, для практики, возьми и напиши, если не получится, по можем ;)
дети мозги выносят, им захотелось ТАК сделать - а я, втихоря, вам :) , пока думают - если чё переспрошу....
спасибо :)
А чем бы проверить состояние битов? опять фукцию потерял
типа
boolen isBit (BYTE inn, BYTE nomer)
Проверка установлен ли бит
inn число байт
nomer - номер бита 0....7
Result= TRUE установлен FALSE - нет
http://arduino.cc/en/Reference/BitRead Не подойдёт?
RANDREY, есть макросы: bit_is_set(REG,BIT) - проверка, установлен ли бит BIT в регистре REG
bit_is_clear(REG,BIT) - аналогично предыдущему, но проверка на сброшенность бита
ввсе таксложно, после #5 поста тупик
просьба, поясните действия процессора
1
PORTB &= ~(1<<1);
я понял так:
в первый бит записать НЕ (~) 1. не понял зачем &?
как продвинутый лошара :) поясняю....
1 - в первый бит регистра в ОЗУ записать 1
2 - тильда - поразрядно проинвертировать пункт 1
3 - поразрядно перемножить регистр B со значением из 2
итого - обнуление бита 1 регистра B без изменения состояния битов 0,2,3,4,5,6,7
...кататак :) - сам долго вникал :(
опытные может как покруче пояснят :)-
С++ - это не Паскаль :(
как продвинутый лошара :) поясняю....
1 - в первый бит регистра в ОЗУ записать 1
2 - тильда - поразрядно проинвертировать пункт 1
3 - поразрядно перемножить регистр B со значением из 2
итого - обнуление бита 1 регистра B без изменения состояния битов 0,2,3,4,5,6,7
...кататак :) - сам долго вникал :(
опытные может как покруче пояснят :)-
С++ - это не Паскаль :(
если в 2 пункте бит 1 обнулен, то зачем перемножать(пункт 3)?
зы прошу как непродвинутый лошара)
в первый бит записать НЕ (~) 1. не понял зачем &?
Это не просто "записать", это операция сдвига. Вот ЭТО видео посмотрите, более-менее понятно объясняется битовый сдвиг на Си.
UPD: Если не понятны основы управления портами через регистры, можно глянуть ещё ЭТО видео. Или даже так - сразу смотреть его, а потом уже про сдвиг. Так целостная картина получается.
Я хоть и не сторонник "видеообучалок", но там толково рассказывают и показывают.
http://arduino.cc/en/Reference/BitRead Не подойдёт?
спасибо, всегда заглядываю в этот справочник, если там http://arduino.ru/Reference нету значит думаю не реализовано в данном субязыке(
в первый бит записать НЕ (~) 1. не понял зачем &?
Это не просто "записать", это операция сдвига. Вот ЭТО видео посмотрите, более-менее понятно объясняется битовый сдвиг на Си.
UPD: Если не понятны основы управления портами через регистры, можно глянуть ещё ЭТО видео. Или даже так - сразу смотреть его, а потом уже про сдвиг. Так целостная картина получается.
Я хоть и не сторонник "видеообучалок", но там толково рассказывают и показывают.
Спасибо, дошло немного
в первый бит записать НЕ (~) 1. не понял зачем &?
Это не просто "записать", это операция сдвига. Вот ЭТО видео посмотрите, более-менее понятно объясняется битовый сдвиг на Си.
UPD: Если не понятны основы управления портами через регистры, можно глянуть ещё ЭТО видео. Или даже так - сразу смотреть его, а потом уже про сдвиг. Так целостная картина получается.
Я хоть и не сторонник "видеообучалок", но там толково рассказывают и показывают.
в той лексеме нету СДВИГА.... сдвинуть мысленно единицу во второй разряд - это не сдвиг физический....
ТСу - изучи битовые операции :) , ор , анд и тильда
1
PORTB &= ~(1<<1);
Оператор побитового сдвига был, а самого сдвига не было?