Учебный курс. Как работать с битами. Макроопределения

19/09/2009 - 21:00
   При программировании микроконтроллеров постоянно приходится работать с битами. Устанавливать их, сбрасывать,  проверять их наличие в том или ином регистре. В AVR ассемблере для этих целей существует целый ряд команд. Во-первых, это группа команд операций с битами – они предназначены для установки или сброса битов в различных регистрах микроконтроллера, а во-вторых, группа команд передачи управления – они предназначены для организации ветвлений программ. В языке Си естественно нет подобных команд, поэтому у начинающих программистов часто возникает вопрос, а как в Си работать с битами. Эту тему мы сейчас и будем разбирать.

   В Си существуют 6 операторов для манипулирования битами. Их можно применять к любым целочисленным знаковым или беззнаковым типам переменных.

<<  - сдвиг влево
>>  - сдвиг вправо
~   -  поразрядная инверсия
|    - поразрядное ИЛИ
&  - поразрядное И
^   -  поразрядное исключающее ИЛИ

_______________ сдвиг влево << _______________

   Сдвигает число на n разрядов влево. Старшие n разрядов при этом исчезают, а младшие n  разрядов заполняются нулями.
        
        unsigned char tmp = 3;  //0b00000011    
        tmp = tmp << 1;   
        //теперь в переменной tmp число 6 или 0b00000110
        
        tmp = tmp << 3;
        //теперь в переменной tmp число 48  или 0b00110000

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

        tmp = 7;  //0b00000111    
        tmp <<= 2; //сокращенный вариант записи
        //теперь в переменной tmp число 28 или 0b00011100


Операция сдвига влево на n разрядов эквивалентна умножению переменной на 2n.

_______________ сдвиг вправо >> _______________

   Сдвигает число на n разрядов вправо. Младшие n разрядов при этом теряются. Заполнение старших n  разрядов зависит от типа переменной и ее значения. Старшие n разрядов заполняются нулями в двух случаях – если переменная беззнакового типа или если переменная знаковая и ее текущее значение положительное. Когда переменная знаковая и ее значение отрицательное – старшие разряды заполняются единицами.    

Пример для беззнаковой переменной

        unsigned char tmp = 255;  //0b11111111
        tmp = tmp >> 1;
        //теперь в переменной tmp число 127 или 0b01111111
        
        tmp >>=  3;  //сокращенный вариант записи
        //теперь в переменной tmp число 15 или 0b00001111

Пример для переменной знакового типа
        
        int tmp = 3400; //0b0000110101001000
        tmp >>= 2;
        //теперь в переменной число 850 или 0b0000001101010010

        tmp = -1200; //0b1111101101010000
        tmp >>= 2;
        //теперь в tmp число -300 или 0b1111111011010100
        //видите - два старших разряда заполнились единицами


   Операция сдвига вправо на n разрядов эквивалентна делению на 2n. При этом есть некоторые нюансы. Если потерянные младшие разряды содержали единицы, то результат подобного “деления” получается грубоватым.

Например    9/4 = 2,5         а 9>>2      (1001>>2)    равно 2
                  11/4 = 2,75     а 11>>2      (1011>>2)    равно 2    
                  28/4 = 7          а 28>>2    (11100>>2)    равно 7     
   
Во втором случае ошибка больше, потому что оба младших разряда единицы. В третьем случае ошибки нет, потому что потерянные разряды нулевые.

_______________поразрядная инверсия ~ _______________

   Поразрядно инвертирует число. Разряды, в которых были нули – заполняются единицами. Разряды, в которых были единицы – заполняются нулями. Оператор поразрядной инверсии являтся унарным оператором, то есть используется с одним операндом.

        unsigned char tmp =  94;     //0b01011110
        tmp = ~tmp;
        //теперь в переменной tmp число 161 или 0b10100001
        
        tmp = ~tmp;
        //теперь в tmp снова число 94 или 0b01011110

_______________ поразрядное ИЛИ | ______________

 
 
Оператор | осуществляет операцию логического ИЛИ между соответствующими битами  двух операндов. Результатом операции логического ИЛИ между двумя битами будет 0 только в случае, если оба бита равны 0. Во всех остальных случаях результат будет 1. Это проиллюстрировано в табице истинности.
 
 
Оператор | обычно используют для установки заданных битов переменной в единицу.
        
    tmp = 155
    tmp = tmp | 4; //устанавливаем в единицу второй бит переменной tmp

    155     0b10011011    
|
        4     0b00000100    
    159     0b10011111

   Использовать десятичные числа для установки битов довольно неудобно. Гораздо удобнее это делать с помощью операции сдвига влево <<.

    tmp = tmp | (1<<4); //устанавливаем в единицу четвертый бит переменной tmp

   Читаем справа налево – сдвинуть единицу на четыре разряда влево, выполнить операцию ИЛИ между полученным числом и значением переменной tmp, результат присвоить переменной tmp.

Установить несколько битов в единицу можно так

    tmp = tmp | (1<<7)|(1<<5)|(1<<0);
   //устанавливаем в единицу седьмой, пятый и нулевой биты переменной tmp
    
С помощью составного оператора присваивания  |= можно сделать запись компактней.

    tmp |= (1<<4);
    tmp |= (1<<7)|(1<<5)|(1<<0);

_______________ побитовое И & _______________


 
   Оператор & осуществляет операцию логического И между соответствующими битами двух операндов. Результатом операции логического И между двумя битами будет 1 только в том случае, если оба бита равны 1. Во всех других случаях результат будет 0. Это проиллюстрировано в таблице истинности.
 
 
Оператор & обычно применяют, чтобы обнулить один или несколько битов.
    
    tmp = 155;        
    tmp = tmp & 247; //обнуляем третий бит переменной tmp


    155        0b10011011    
&
    247        0b11110111    
    147        0b10010011

Видите, третий бит стал равен 0, а остальные биты не изменились.

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

    tmp = 155;
    tmp = tmp & (~(1<<3)); //обнуляем третий бит

1<<3                          0b00001000
~(1<<3)                     0b11110111
tmp & (~(1<<3))        0b10011011 & 0b11110111
результат                    0b10010011

   Читаем справа налево – сдвинуть единицу на три разряда влево, выполнить инверсию полученного числа, выполнить операцию & между значением переменной tmp и проинвертированным числом, результат присвоить переменной tmp.

Обнулить несколько битов можно так

    tmp = tmp & (~((1<<3)|(1<<5)|(1<<6))); //обнуляем третий, пятый и шестой биты

   Здесь сначала выполняются операции сдвига, потом операции поразрядного ИЛИ, затем инверсия, поразрядное И, присвоение результата переменной tmp.

Используя составной оператор присваивания &= ,можно записать выражение более компактно

    tmp &= (~((1<<3)|(1<<5)|(1<<6)));

   Как проверить установлен ли бит в переменной?
Нужно обнулить все биты, кроме проверочного, а потом сравнить полученное значение с нулем
    
   if ((tmp & (1<<2)) != 0 ){
      // блок будет выполняться, только если установлен
      // второй бит переменной tmp

   }

    if ((tmp & (1<<2)) == 0 ){
      // блок будет выполняться, только если не установлен
      // второй бит переменной tmp

    }

    _______________побитовое исключающее ИЛИ ^ _______________  


   Оператор ^ осуществляет операцию логического исключающего ИЛИ между соответствующими битами двух операндов. Результатом операции логического исключающего ИЛИ будет 0 в случае равенства битов. Во всех остальных случаях результат будет 1. Это проиллюстрировано в табице истинности.

  
   Оператор ^ применяется не так часто как остальные битовые операторы, но и для него находится работенка. Например, с помощью него можно инвертировать один или несколько битов переменной.

    tmp = 155;
    tmp = tmp ^ 8; // инвертируем четвертый бит переменой tmp
    
    155         0b10011011    
^
    8             0b00001000    
    147         0b10010011

Четвертый бит изменил свое значение на противоположное, а остальные биты остались без изменений.

    tmp = tmp ^ 8; // опять инвертируем четвертый бит переменой tmp

    147         0b10010011    
^
    8             0b00001000    
    155         0b10011011

Видите, четвертый бит снова изменил свое значение на противоположное.  

Так записывать выражение намного удобнее

    tmp = tmp ^ (1<<3); // инвертируем третий бит переменой tmp
    
А так и удобно и компактно

    tmp ^= (1<<4);  //инвертируем четверый бит

Можно инвертировать несколько битов одновременно

    tmp ^= ((1<<4)|(1<<2)|(1<<1)); //инвертируем 4,2 и 1 биты   

    У поразрядного исключающего ИЛИ есть еще одно интересное свойство. Его можно использовать, для того чтобы поменять значения двух переменных местами. Обычно для этого требуется третья переменная.

tmp = var1;
var1 = var2;
var2 = tmp;

Но используя оператор ^  переставить значения можно так:

var1 ^= var 2;
var 2 ^= var 1;
var 1 ^= var 2;

Чистая магия, хотя, честно говоря,  я ни разу не пользовался таким приемом.

________________Директива #define__________________


   Теперь мы знаем, как устанавливать, обнулять и инвертировать биты, знаем, как проверять установлен ли бит или нет. Рассмотренные выше выражения довольно громоздки, но с помощью директивы препроцессора #define, им можно придать более приятный вид.
   Директива #define используется для присваивания символических имен константам и для макроопределений. Использование символических имен делают программу более модифицируемой и переносимой.
    Например, вы используете в тексте программы константу, и вдруг вам понадобилось изменить ее значение. Если она встречается всего в трех местах, то исправить ее можно и в ручную, а что делать, если она встречается в пятидесяти строчках? Мало того, что исправление займет много времени, так еще и ошибиться в этом случае проще простого. Здесь то, как раз и выручает директива #define. В начале программы задается символическое имя константы, которое  используется по ходу программы. Если нам нужно изменить это значение, это делается всего лишь в одном месте. А перед компиляцией препроцессор сам подставит во все выражения вместо имени константы ее значение.
    Программирование микроконтроллера неразрывно связано  с его аппаратной частью и чаще всего с внешней обвязкой. Взять хотя бы кнопки - опрашивая их в своей программе, мы обращаемся к реальным выводам микроконтроллера. А если нам вдруг понадобилось использовать программу опроса кнопок в другой схеме, где кнопки подключены к другим выводам? Придется исправлять программу. Опять таки, задав с помощью директивы #define символическое имя для соответствующих выводов, модифицировать программу будет проще простого

Пример:

#include "iom8535.h"

//порт, к которому подключены кнопки
#define PORT_BUTTON     PORTA
#define PIN_BUTTON        PINA
#define DDRX_BUTTON     DDRA

//выводы, к которым подключены кнопки
#define DOWN        3
#define CANCEL      4
#define UP              5
#define ENTER         6

int main()
{
    //конфигурируем порт на вход,
    //и включаем подтягивающие резисторы

    DDRX_BUTTON = 0;    
    PORT_BUTTON = 0xff;
    
…..
}


При задании символического имени можно использовать и выражения

#define MASK_BUTTONS       ((1<<DOWN)|(1<<CANCEL)|(1<<UP)|(1<<ENTER))

пример использования:
tmp = PORTB & MASK_BUTTONS;

Используя #define не жалейте скобок чтобы четко задать последовательность вычисления выражений!

Некоторые выражения можно замаскировать под «функции».

#define ADC_OFF()             ADCSRA = 0

пример использования:
ADC_OFF();

Можно использовать многострочные определения, используя в конце каждой строки символ \

#define INIT_Timer()        TIMSK = (1<<OCIE0);\
                                       TCCR0 = (1<<WGM01)|(0<<WGM00)|(1<<CS02);\
                                       TCNT0 = 0;\
                                       OCR0 = 0x7d

пример использования:
INIT_Timer();

 
Ну и самое мощное применение директивы #define – это задание макроопределений (или просто макросов). Вот как с помощью #define можно задать макросы для рассмотренных ранее операций с битами

#define   SetBit(reg, bit)          reg |= (1<<bit)           
#define   ClearBit(reg, bit)       reg &= (~(1<<bit))
#define   InvBit(reg, bit)          reg ^= (1<<bit)
#define   BitIsSet(reg, bit)       ((reg & (1<<bit)) != 0)
#define   BitIsClear(reg, bit)    ((reg & (1<<bit)) == 0)


пример использования:

SetBit(PORTB, 0);    //установить нулевой бит порта B    
InvBit(tmp,6);         //инвертировать шестой бит переменной tmp


if  (BitIsClear(PIND, 0)) {   //если очищен нулевой бит в регистре PIND                                    
…..                                     //выполнить блок
}

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

#define SQUARE(x)    x*x

выражение    
tmp =  SQUARE(my_var);
даст корректный результат.

А что будет если в качестве аргумента макроопределения использовать выражение my_var+1

tmp =  SQUARE(my_var +1);

Препроцессор заменит эту строчку на

tmp = my_var + 1 * my_var +1;

а это вовсе не тот результат, который мы ожидаем.

Чтобы избежать таких ошибок не скупитесь на скобки при объявлении макросов!

Если объявить макрос так

#define SQUARE(x)    ((x)*(x))

выражение
tmp =  SQUARE(my_var +1);
даст корректный результат, потому что препроцессор заменит эту строчку на
tmp = ((my_var + 1) * (my_var +1));
 
записываем их в папку проекта, а в начале файла main.c  прописываем #include "bits_macros.h"

 

Комментарии   

# Guest 31.01.2010 08:53
В примере " Проверка установки бита в переменной" операцию инвертирования скорее не надо проводить. Или я не прав?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 01.02.2010 05:21
Да, не нужно. Исправил. Спасибо
Ответить | Ответить с цитатой | Цитировать
# Guest 05.02.2010 12:06
Согласно стандарту языка С результат сдвига вправо для отрицательного числа зависит от компилятора. Стоит это отметить.
Ответить | Ответить с цитатой | Цитировать
# Guest 05.02.2010 12:19
Да чтож оно у вас сообщение режет по сдвигу-то??
tmp = tmp & (~(1 < < 3)); //обнуляем третий бит - внешние скобки лишние, это хорошо видно по следующему примеру - tmp &= (~((1
Ответить | Ответить с цитатой | Цитировать
# Guest 05.02.2010 12:23
рррррр.... добавьте, пожалуйста, кнопку для оформления кода. Заменяю сдвиги на shl: по примеру
tmp = tmp & (~(1 shl 3)); //обнуляем третий бит
---------
tmp = tmp ^ 8; // инвертируем четвертый бит переменой tmp
tmp = tmp ^ (1 shl 3); // инвертируем третий бит переменой tmp
в первом случае, наверное, тоже третий бит.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 05.02.2010 17:49
скобки только для наглядности
Ответить | Ответить с цитатой | Цитировать
# Pashgan 05.02.2010 17:50
а как добавить кнопку я еще не разобрался... сам с этими сдвигами мучаюсь..
Ответить | Ответить с цитатой | Цитировать
# Guest 13.05.2010 16:15
самый толковый сайт про Си,спасибо авторам ;-)
Ответить | Ответить с цитатой | Цитировать
# Pashgan 13.05.2010 21:00
Автор пока один.. это я.
Ответить | Ответить с цитатой | Цитировать
# Guest 06.06.2010 17:25
Теоретически побитовые операции знают все,
но за их применения автору огромный респект!!!!!
Ответить | Ответить с цитатой | Цитировать
# Guest 04.08.2010 09:56
До этого писал в аврстудии и алгоритмбилдере . Решил перейти на си. Как реализуется сдвиг влево или вправо с переносом бита С из регистра SREG.
А вообще очень рад, что наткнулся на такой великолепный сайт, где пишет такой энтузиаст своего дела. :-)
Ответить | Ответить с цитатой | Цитировать
# Pashgan 04.08.2010 19:47
А зачем тебе думать на эту тему. Это задача компилятора. Где нужно он сам это реализует. Например для выражения v = v>>1; где v - 16-ти разрядная переменная. Компилятор сгенерит такой код - lsr R17;ror R16. В r17 старший байт, в r16 младший.
Ответить | Ответить с цитатой | Цитировать
# Alexiy 17.10.2010 11:36
Очень полезный и непонятный макрос, который приведен по ссылке.

SetBitVal(reg, bit, val)

Результат понятен. А как работает?
Что означает обратный слеш?
Как понять Wile (0) в конце?
Как возможно, что точка с запятой в середине?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 20.10.2010 19:47
Обратный слеш используется для многострочных макросов. Макрос обрамлен в do while(0) для безопасности. Вот здесь почитай http://chipenable.ru/index.php/programming-c/21-trick-define.html
Ответить | Ответить с цитатой | Цитировать
# Gena572 24.11.2010 12:00
Прошу прощения за глупый вопрос а бит "дефайном" можно определить? Если да то как это сделать?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 30.11.2010 17:31
Ммм.. ну смотря что подразумевается под выражением определить бит. Бит можно определить так
Код:
define BIT(x) (1<<(x))
Ответить | Ответить с цитатой | Цитировать
# Gena572 07.12.2010 15:48
Спасибо за ответ.
Я имел ввиду можно ли присвоить символическое имя определенному биту в определенном регистре.
Например бит MUX0 в регистре ADMUX хочу обозвать CHANNEL такое возможно. И могу ли я его проверять при помощи конструкции If...else?
Ответить | Ответить с цитатой | Цитировать
# JoJo 07.12.2010 19:40
Возможно, нужно написать свой заголовочный файл микроконтроллера.
Биты можно проверять так
Код:
if (BitIsSet(ADMUX, MUX0){
...
}
Ответить | Ответить с цитатой | Цитировать
# Новичок 30.12.2010 07:58
Почему обрезает сообщение?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 01.01.2011 16:47
потому что сишный код нужно заключать между тегами code
Ответить | Ответить с цитатой | Цитировать
# Raspro 20.02.2011 08:47
В IAR с битами можно работать и стандартными средствами.
пример:
Код:
PORTB_Bit0 = 1; // Установка бита 0 в PORTB
PORTB_Bit0 = 0; // Сброс бита 0 в PORTB
Ответить | Ответить с цитатой | Цитировать
# Pashgan 20.02.2011 21:20
Ага, только неудобно пользоваться, потому что запись длинная.
Ответить | Ответить с цитатой | Цитировать
# Роман 14.07.2011 12:44
Код: MCUCR &= ~(1<<ISC11)
Простите за такой мусор в коментах.
Вопрос вы поняли надеюсь.
про теги прочел только сейчас )))
Ответить | Ответить с цитатой | Цитировать
# Jackkum 15.08.2011 04:12
Код:MCUCR &= ~(1<<ISC11)
Устанавливает в ноль бит с номером ISC11
Ответить | Ответить с цитатой | Цитировать
# Jackkum 15.08.2011 04:10
Помогите пожалуйста! :)
Код:

#define LED_GREEN PORTB,5

#define SETBIT(PORT,PIN ) do{PORT|=(1<<(PIN));}while(0)
#define UNSETBIT(PORT,PIN ) do{PORT&=(~(1<<(PIN)));}while(0)

// --------------
for(;;){
SETBIT(LED_GREEN);
_deelay_ms(1000);
UNSETBIT(LED_GREEN);
_deelay_ms(1000);
}


По идее в макрос SETBIT(PORT,PIN ) вместо PORT,PIN должно подставиться макрос LED_GREEN ко компилятор рузается, говорит нужно 2 параметра вместо одного.
Ответить | Ответить с цитатой | Цитировать
# anisan 12.11.2011 11:52
тоже нужно так сделать, но вложенный define не цепляет определения...П одскажите как решили этот вопрос
Ответить | Ответить с цитатой | Цитировать
# N.I.K. 07.10.2011 06:46
Это сообщение можно удалить.

В интересующем меня вопросе я разобрался сам.
Ответить | Ответить с цитатой | Цитировать
# obstinatus 14.01.2012 19:50
А как быть, если с помощью SetBit() необходимо выставить более одного бита за раз? Пробовал так:
Код:
// ...

#define BTN_A 3
#define BTN_B 4

#define SetBit(reg, bit) reg |= (1<<bit)

// ...

int main(void)
{
SetBit(DDRD,(BTN_A+BTN_B));
SetBit(DDRD,(BTN_A|BTN_B));
SetBit(DDRD,(BTN_A||BTN_B));
}

// ...

Но результат всегда неверный (0x80).
Проверял в AvrStudio и VMLab
Ответить | Ответить с цитатой | Цитировать
# wukrlvy 07.02.2012 22:04
И не будет работать. Ты вместо слова bit вставляешь выражение (BTN_A + BTN_B), компилятор вычисляет выражение (до сдвига), сумма равна 7. Вот он и сдвигает единицу на 7.
Во втором случае побитное ИЛИ 0011 | 0100 == 0111, тоже дает 7.
В третьем случае - надо пробовать на месте. До сих пор я считал, что компилятор перед логической операцией производит приведение типов, т.е. BTN_A и BTN_B превращает в TRUE. А как известно TRUE == 1. Раз ты пробовал, и результат получился 0x80, то это не так. Никакого приведения произведено не было.

Еще один постулат, перед выполнением вычислительных операций все исходные типы, которые могут быть представлены в int, в том числе и булева переменная.
Если считать, что TRUE - это все, что угодно, кроме нуля, то тогда сходится.
Ответить | Ответить с цитатой | Цитировать
# wukrlvy 07.02.2012 22:17
Используй макрос
[SetBitVal(reg, bit, val)]
вместо bit поставь 0, а вместо val - свое выражение (BTN_A | BTN_B). В макросе стоит выражение ((val)
Ответить | Ответить с цитатой | Цитировать
# wukrlvy 08.02.2012 14:31
Цитирую wukrlvy:
Используй макрос
[SetBitVal(reg, bit, val)]
вместо bit поставь 0, а вместо val - свое выражение (BTN_A | BTN_B).


Не (BTN_A | BTN_B) а Код:((1 << BTN_A) | (1 << BTN_B))
Так, что проще написать свою функцию.
Ответить | Ответить с цитатой | Цитировать
# wukrlvy 07.02.2012 22:19
Код:SetBitVal(reg, bit, val)
равно
Код:(reg) = (reg) | ((val) << (bit))
Бит устанавливается или ничего не делается в зависимости от величины val.

Если нужно в байт вставить поле, в котором одни биты сбрасываются, а другие устанавливаются , нужно писать спещиальную функцию. Но этого никто не делает, поскольку, если идет работа с SFR, то надо контролировать каждое чтение и запись в регистр. Иначе могут быть "эффекты". Поэтому все предпочитают обходиться самыми простыми макросами.
Ответить | Ответить с цитатой | Цитировать
# Stein256 09.11.2014 15:05
Как вы и написали "Бит устанавливается или ничего не делается", т.е. таким способом нельзя обнулить бит. А нужно чтоб в переменной reg бит c номером bit принял значение val.
Ответить | Ответить с цитатой | Цитировать
# wukrlvy 07.02.2012 22:40
Свой макрос для работы с двумя битами.
Код:#define SetBit2(reg, bit1, bit2) ((1 << (bit1)) | (1 << (bit2)))
Ответить | Ответить с цитатой | Цитировать
# Robert 29.03.2012 13:11
Привет!!!
Вопрос: во время выполнения Line_ON (); одновременно выполняется и Pulse_OFF();
(выставляется линия в «1», строб в «0» ) Line и Pulse биты одного порта. Если Line и Pulse биты разных портов, к примеру, PORTE и PORTF все работает правильно. Выставляется линия затем пауза далее строб и по циклу, пока не «выплюнет» слово 0Х80 в порт. Каким образом «обмануть» компилятор KEIL. Камушек 1986ВЕ92У?

Код:
while (1)
{
uint8_t i=0X80; //0b10010000
Opros_OFF (1); //включение опросной линии

while (i != 0x00)
{
if ((MDR_TIMER3->CNT & i) == i ) {Line_ON ();} else {Line_OFF ();}
i = i >> 1 ;
__ nop ();
Pulse_OFF(); //строб импульс
Pulse_ON(); //строб импульс
}
i=0X80;
Opros_ON (1);
}
Ответить | Ответить с цитатой | Цитировать
# Иван 29.11.2012 00:29
Спасибо :D
Ответить | Ответить с цитатой | Цитировать
# maklakov 12.01.2013 09:55
Спасибо большое. А как лучше проверить бит на изменение? Просто проверить изменился бит или нет. Если изменился, выполнить какое то условие. Если при повторной проверки не изменился, то ничего не делать. Хочу что б события выполнялись по тикающему счётчику. Сейчас делаю через флаги, что не очень удобно. По делению без остатка тоже можно, но как то код большой выходит.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 12.01.2013 16:12
Почитай материалы про событийную систему. Может такой вариант подойдет.
Ответить | Ответить с цитатой | Цитировать
# Андрей 20.01.2013 17:19
Ну Паша, ну орел!!!
После этой статьи меня как осенило)
Из-за этих макросов я ночи не спал, книжки читал...
Что ж ты сразу мне сылку на этот курс не дал?
Я одну электронную книгу по Си уже закинул себе в бошку)))
Спасибо Паша! Ты как всегда, молодца!
У меня просто слов нет.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 20.01.2013 18:08
Цитата:
Что ж ты сразу мне сылку на этот курс не дал?
Вся инфа вроде на виду. А по скольку отдельного материала по макросам и препроцессору у меня нет, я посоветовал тебе книгу.
Ответить | Ответить с цитатой | Цитировать
# Валерия 18.02.2013 20:32
А можно ли установить несколько битов в единицу не таким способом
tmp|= (1
Ответить | Ответить с цитатой | Цитировать
# Валерия 18.02.2013 20:34
а с помощью цикла for?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 19.02.2013 18:42
Ну можно придумать какие-то варианты. А зачем?
Ответить | Ответить с цитатой | Цитировать
# alec220 02.03.2013 08:33
Первый и второй урок усвоил. Третий (если я правильно понял, с нумерацией на сайте как-то не сильно понятно) уже "чайника" запутал - прикладного примера не хватает. Крестики нолики красивая игра, вот только зачем. Вроде и помню как работает в стандартной логике & и ИЛИ... Но там было понятно куда и зачем сигнал побежал. А здесь чувствую себя человеком, гуляющим по мнимому лабиринту (без стен) поворотов много, тупиков тоже, только вот конечная цель не ясна... Сорри за критику, попробую дальше двинуться. Вот если бы еще и материал как-то пронумеровать. А то закрыл страничку и потом еле нашел, где остановился.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 02.03.2013 09:08
Уроки не по порядку написаны и не по всем темам, поэтому нумерация пока невозможна.
Ответить | Ответить с цитатой | Цитировать
# Илья21 01.08.2013 13:14
Спасибо большое! Очень хорошие знания!
Ответить | Ответить с цитатой | Цитировать
# Кирилл 01.10.2013 17:35
В "Сдвиге вправо знакового типа (отрицательное) "
tmp=-1200;
tmp>>=2;

Должно стоять значение -300, вместо -600.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 02.10.2013 10:37
Спасибо. Исправил.
Ответить | Ответить с цитатой | Цитировать
# Vimork 22.01.2014 22:56
Сдвиг вправо, значение переменной равно -1200, мне кажется бинарная запись числа не соответствует десятичной, там должно быть 0b1000010010110 000. После сдвига переменная должна принять значение 0b1000000100101 100. Поправьте меня, если я ошибаюсь.
Ответить | Ответить с цитатой | Цитировать
# 0867532 10.07.2014 12:09
Либо я дурак либо лыжи не едут...
вот проверяю наложение одного массива на другой(solid[15 ] CurrentFig[3])
Код: for(i=0; i<4; i++){
if(solid[i+1+CurrentFigY] & CurrentFig > 0)
j++;
}

смысл в том, что если в лог умножении появляется хотя бы одна единица j растёт.
но условие не срабатывает.
Ответить | Ответить с цитатой | Цитировать
# Stein256 09.11.2014 15:42
В макросе SetBitVal можно вместо if else использовать тернарный оператор. Тогда можно будет не волноваться об ошибках.
Код:((val & 1) != 0) ? reg |= (1<<bit) : reg &= (~(1<<bit))
Можно также воспользоваться исключающим или. Тогда можно будет отслеживать был ли бит изменен. Лучше использовать просто if.
Код:(val<<bit != (reg & (1<<bit))) ? reg ^= (1<<bit) : 0
Ответить | Ответить с цитатой | Цитировать
# mash 04.03.2015 10:33
Можно ли в IAR Си проверять флаг переноса CARRY как здесь (http://www.pic24.ru/doku.php/osa/articles/pk2_osa_piano ):
Код:
#define SOUND(x) \
if (S[x].key) { \
temp_dac += Sample[*((char*)&S[x].f+1) & 0x3F]; \
*((char*)&S[x].f+1) += *((char*)&S[x].F+1); \
*((char*)&S[x].f+0) += *((char*)&S[x].F+0); \
if (CARRY) *((char*)&S[x].f+1) += 1; \
}
Ответить | Ответить с цитатой | Цитировать
# Pashgan 06.03.2015 17:29
Можно опрашивать флаг в регистре SREG.
Ответить | Ответить с цитатой | Цитировать
# mash 07.03.2015 18:11
Цитирую Pashgan:
Можно опрашивать флаг в регистре SREG.

У меня STM8L101. Как из ИАР Си прочитать состояние флага CARRY? Можно с ассемблерной вставкой.
Ответить | Ответить с цитатой | Цитировать
# jendos 07.03.2015 20:08
А что мешает прочитать CC регистр и проверить флаг переноса?
Ответить | Ответить с цитатой | Цитировать
# mash 08.03.2015 16:52
Цитирую jendos:
А что мешает прочитать CC регистр и проверить флаг переноса?
Можно мне простой пример на ИАР Си для STM8L101 - чем заменить: if (CARRY) ?
Ответить | Ответить с цитатой | Цитировать
# Леонардо 30.11.2015 16:28
У меня вопрос по работе с битами и регистрами. Допустим есть строка
REGISTR |= ABC | DEF;[code/] - здесь мы используем побитовое ИЛИ, т.е. 1 и 1 даёт 1, 1 и 0 даёт 1, 0 и 0 даёт 0. Чтобы выставить данные биты в единицу. Теперь вопрос - откуда в этих битах берётся 1, если они изначально равны 0 в начале программы?
Ответить | Ответить с цитатой | Цитировать
# LexxИК 17.05.2016 07:35
Здравствуйте!
CVAVR 2.05. Смотрю на подгружаемый файл mega8535_bits.h:
Код:/* PORTA - Port A Data Register */ #define PORTA0 0 // Port A Data Register bit 0 #define PORTA1 1 // Port A Data Register bit 1 #define PORTA2 2 // Port A Data Register bit 2 #define PORTA3 3 // Port A Data Register bit 3 #define PORTA4 4 // Port A Data Register bit 4 #define PORTA5 5 // Port A Data Register bit 5 #define PORTA6 6 // Port A Data Register bit 6 #define PORTA7 7 // Port A Data Register bit 7

Когда мы будем работать просто запишем PORTA0, но ведь в коде выше не пишется что в PORTA0 это сокращённая запись именно для PORTA или в CVAVR где-то ещё определяется или это фишка самой программы??
Ответить | Ответить с цитатой | Цитировать
# Arthur 02.08.2016 22:07
Разве так?:
" Как проверить установлен ли бит в переменной? Нужно обнулить все биты, кроме проверочного, а потом сравнить полученное значение с нулем

Код: if ((tmp & (1<<2)) != 0 ){
// блок будет выполняться, только если установлен
// второй бит переменной tmp
}

if ((tmp & (1<<2)) == 0 ){
// блок будет выполняться, только если не установлен
// второй бит переменной tmp
}
"
не получится ж таким образом проверить переменную на наличие установленного бита:
Код:1<<2 получится 0000 0100
операция и с tmp(допустим он равен 1111 0010)
получится 0000 0000 и это выражение равно 0, а значит выражение под if-ом не будет выполнятся, хотя бит №2 установлен. Подскажите, может я не прав)
Ответить | Ответить с цитатой | Цитировать

Добавить комментарий

Защитный код
Обновить