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

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"

 

Comments   

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

SetBitVal(reg, bit, val)

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

#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 параметра вместо одного.
Reply | Reply with quote | Quote
# anisan 2011-11-12 11:52
тоже нужно так сделать, но вложенный define не цепляет определения...П одскажите как решили этот вопрос
Reply | Reply with quote | Quote
# N.I.K. 2011-10-07 06:46
Это сообщение можно удалить.

В интересующем меня вопросе я разобрался сам.
Reply | Reply with quote | Quote
# obstinatus 2012-01-14 19:50
А как быть, если с помощью SetBit() необходимо выставить более одного бита за раз? Пробовал так:
Code:
// ...

#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
Reply | Reply with quote | Quote
# wukrlvy 2012-02-07 22:04
И не будет работать. Ты вместо слова bit вставляешь выражение (BTN_A + BTN_B), компилятор вычисляет выражение (до сдвига), сумма равна 7. Вот он и сдвигает единицу на 7.
Во втором случае побитное ИЛИ 0011 | 0100 == 0111, тоже дает 7.
В третьем случае - надо пробовать на месте. До сих пор я считал, что компилятор перед логической операцией производит приведение типов, т.е. BTN_A и BTN_B превращает в TRUE. А как известно TRUE == 1. Раз ты пробовал, и результат получился 0x80, то это не так. Никакого приведения произведено не было.

Еще один постулат, перед выполнением вычислительных операций все исходные типы, которые могут быть представлены в int, в том числе и булева переменная.
Если считать, что TRUE - это все, что угодно, кроме нуля, то тогда сходится.
Reply | Reply with quote | Quote
# wukrlvy 2012-02-07 22:17
Используй макрос
[SetBitVal(reg, bit, val)]
вместо bit поставь 0, а вместо val - свое выражение (BTN_A | BTN_B). В макросе стоит выражение ((val)
Reply | Reply with quote | Quote
# wukrlvy 2012-02-08 14:31
Quoting wukrlvy:
Используй макрос
[SetBitVal(reg, bit, val)]
вместо bit поставь 0, а вместо val - свое выражение (BTN_A | BTN_B).


Не (BTN_A | BTN_B) а Code:((1 << BTN_A) | (1 << BTN_B))
Так, что проще написать свою функцию.
Reply | Reply with quote | Quote
# wukrlvy 2012-02-07 22:19
Code:SetBitVal(reg, bit, val)
равно
Code:(reg) = (reg) | ((val) << (bit))
Бит устанавливается или ничего не делается в зависимости от величины val.

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

Code:
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);
}
Reply | Reply with quote | Quote
# Иван 2012-11-29 00:29
Спасибо :D
Reply | Reply with quote | Quote
# maklakov 2013-01-12 09:55
Спасибо большое. А как лучше проверить бит на изменение? Просто проверить изменился бит или нет. Если изменился, выполнить какое то условие. Если при повторной проверки не изменился, то ничего не делать. Хочу что б события выполнялись по тикающему счётчику. Сейчас делаю через флаги, что не очень удобно. По делению без остатка тоже можно, но как то код большой выходит.
Reply | Reply with quote | Quote
# Pashgan 2013-01-12 16:12
Почитай материалы про событийную систему. Может такой вариант подойдет.
Reply | Reply with quote | Quote
# Андрей 2013-01-20 17:19
Ну Паша, ну орел!!!
После этой статьи меня как осенило)
Из-за этих макросов я ночи не спал, книжки читал...
Что ж ты сразу мне сылку на этот курс не дал?
Я одну электронную книгу по Си уже закинул себе в бошку)))
Спасибо Паша! Ты как всегда, молодца!
У меня просто слов нет.
Reply | Reply with quote | Quote
# Pashgan 2013-01-20 18:08
Quote:
Что ж ты сразу мне сылку на этот курс не дал?
Вся инфа вроде на виду. А по скольку отдельного материала по макросам и препроцессору у меня нет, я посоветовал тебе книгу.
Reply | Reply with quote | Quote
# Валерия 2013-02-18 20:32
А можно ли установить несколько битов в единицу не таким способом
tmp|= (1
Reply | Reply with quote | Quote
# Валерия 2013-02-18 20:34
а с помощью цикла for?
Reply | Reply with quote | Quote
# Pashgan 2013-02-19 18:42
Ну можно придумать какие-то варианты. А зачем?
Reply | Reply with quote | Quote
# alec220 2013-03-02 08:33
Первый и второй урок усвоил. Третий (если я правильно понял, с нумерацией на сайте как-то не сильно понятно) уже "чайника" запутал - прикладного примера не хватает. Крестики нолики красивая игра, вот только зачем. Вроде и помню как работает в стандартной логике & и ИЛИ... Но там было понятно куда и зачем сигнал побежал. А здесь чувствую себя человеком, гуляющим по мнимому лабиринту (без стен) поворотов много, тупиков тоже, только вот конечная цель не ясна... Сорри за критику, попробую дальше двинуться. Вот если бы еще и материал как-то пронумеровать. А то закрыл страничку и потом еле нашел, где остановился.
Reply | Reply with quote | Quote
# Pashgan 2013-03-02 09:08
Уроки не по порядку написаны и не по всем темам, поэтому нумерация пока невозможна.
Reply | Reply with quote | Quote
# Илья21 2013-08-01 13:14
Спасибо большое! Очень хорошие знания!
Reply | Reply with quote | Quote
# Кирилл 2013-10-01 17:35
В "Сдвиге вправо знакового типа (отрицательное) "
tmp=-1200;
tmp>>=2;

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

смысл в том, что если в лог умножении появляется хотя бы одна единица j растёт.
но условие не срабатывает.
Reply | Reply with quote | Quote
# Stein256 2014-11-09 15:42
В макросе SetBitVal можно вместо if else использовать тернарный оператор. Тогда можно будет не волноваться об ошибках.
Code:((val & 1) != 0) ? reg |= (1<<bit) : reg &= (~(1<<bit))
Можно также воспользоваться исключающим или. Тогда можно будет отслеживать был ли бит изменен. Лучше использовать просто if.
Code:(val<<bit != (reg & (1<<bit))) ? reg ^= (1<<bit) : 0
Reply | Reply with quote | Quote
# mash 2015-03-04 10:33
Можно ли в IAR Си проверять флаг переноса CARRY как здесь (http://www.pic24.ru/doku.php/osa/articles/pk2_osa_piano ):
Code:
#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; \
}
Reply | Reply with quote | Quote
# Pashgan 2015-03-06 17:29
Можно опрашивать флаг в регистре SREG.
Reply | Reply with quote | Quote
# mash 2015-03-07 18:11
Quoting Pashgan:
Можно опрашивать флаг в регистре SREG.

У меня STM8L101. Как из ИАР Си прочитать состояние флага CARRY? Можно с ассемблерной вставкой.
Reply | Reply with quote | Quote
# jendos 2015-03-07 20:08
А что мешает прочитать CC регистр и проверить флаг переноса?
Reply | Reply with quote | Quote
# mash 2015-03-08 16:52
Quoting jendos:
А что мешает прочитать CC регистр и проверить флаг переноса?
Можно мне простой пример на ИАР Си для STM8L101 - чем заменить: if (CARRY) ?
Reply | Reply with quote | Quote
# Леонардо 2015-11-30 16:28
У меня вопрос по работе с битами и регистрами. Допустим есть строка
REGISTR |= ABC | DEF;[code/] - здесь мы используем побитовое ИЛИ, т.е. 1 и 1 даёт 1, 1 и 0 даёт 1, 0 и 0 даёт 0. Чтобы выставить данные биты в единицу. Теперь вопрос - откуда в этих битах берётся 1, если они изначально равны 0 в начале программы?
Reply | Reply with quote | Quote
# LexxИК 2016-05-17 07:35
Здравствуйте!
CVAVR 2.05. Смотрю на подгружаемый файл mega8535_bits.h:
Code:/* 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 где-то ещё определяется или это фишка самой программы??
Reply | Reply with quote | Quote
# Arthur 2016-08-02 22:07
Разве так?:
" Как проверить установлен ли бит в переменной? Нужно обнулить все биты, кроме проверочного, а потом сравнить полученное значение с нулем

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

if ((tmp & (1<<2)) == 0 ){
// блок будет выполняться, только если не установлен
// второй бит переменной tmp
}
"
не получится ж таким образом проверить переменную на наличие установленного бита:
Code:1<<2 получится 0000 0100
операция и с tmp(допустим он равен 1111 0010)
получится 0000 0000 и это выражение равно 0, а значит выражение под if-ом не будет выполнятся, хотя бит №2 установлен. Подскажите, может я не прав)
Reply | Reply with quote | Quote
# Виталий 2017-07-30 07:51
Уважаемый Pashgan, прошу прощения, что вмешиваюсь, не стал вычитывать все комментарии, но раз это еще не исправлено, то думаю, что и ошибка эта еще не замечена. У Вас везде по разному написано целевое число разряда битов сдвига - в одних случаях правильно, а в других нет.
Например (...tmp
Reply | Reply with quote | Quote
# Виталий 2017-07-30 07:58
Так, Ваш блог не корректно различает спецсимволы, так что придется писать текстом:
Например (...1 "сдвиг влево" 3) сдвигает первый бит на 3 бита влево, а значит устанавливает !!! 4-ый бит, а не 3-ий. А у Вас везде ошибки, то правильно напишете, то неправильно. Прошу пересмотреть.
Reply | Reply with quote | Quote

Add comment