Ардуино функция или

Логические операторы

Логические операторы чаще всего используются в проверке условия оператора if. Базовые сведения о логических операциях, смотрите в Википедии.

&& (логическое И)

Истина, если оба операнда истина (true).

Истина если оба порта вход/выхода HIGH

|| (логическое ИЛИ)

Истина, если хотя бы один операнд истина, например:

будет верно (истина) если x или y больше 0.

! (логическое отрицание)

True, если операнд false, и наоборот, например:

условие верно, если x — false (x равно 0).

Важно различать логический оператор «И» — && и битовый оператор «И» — &. Тоже самое относится к логическому оператору «ИЛИ» — || и битовому оператору «ИЛИ» — |.

Пример

Смотрите также

Железо

Это расширенный стартовый набор. В комплект входит Arduino Mega R3, макетные платы, множество датчиков, управляемые механизмы и необходимые радиоэлектронные компоненты. Полный список.

Arduino Uno — плата на базе микроконтроллера ATmega328P с частотой 16 МГц. На плате есть все необходимое для удобной и быстрой работы.

Макетная плата на 830 точек и ничего лишнего.

Источник

Логические:

Данные:

Однобайтовые типы данных:

принимают любые, возвращают 0 или 1

если принят не 0, то вернётся 1

Целочисленные значения или символы

от -128 до 127

Беззнаковые целочисленные значения

от 0 до 255

Двухбайтовые типы данных:

от -32’768 до 32’767

В Arduino Due, тип int идентичен типу long

Беззнаковые целочисленные значения

от 0 до 65’535

В Arduino Due, тип unsigned int идентичен типу unsigned long

Четырёхбайтовые типы данных:

от -2’147’483’648 до 2’147’483’647

Беззнаковые целочисленные значения

от 0 до 4’294’967’295

Числа с плавающей точкой

от -2’147’483’648,0 до 2’147’483’647,0

Восьмибайтовые типы данных:

Числа с плавающей точкой удвоенной точности

от -9’223’372’036’854’775’808,0 до 9’223’372’036’854’775’807,0

Тип double действует как тип float, кроме Arduino Due

от -9’223’372’036’854’775’808 до 9’223’372’036’854’775’807

Беззнаковые целочисленные значения

от 0 до 18’446’744’073’709’551’615

Типы данных с определяемым размером

Нет принимаемых или возвращаемых значений

Массив A указанного размера, с элементами указанного типа

Массив А указанного типа, без прямого указания размера

Двумерный массив А указанного типа и размера

Двумерный массив А указанного типа, без прямого указания размера

Массив или строка, состоящая из указанного кол-ва символов

Массив или строка, без прямого указания количества символов

Преобразование числовых типов:

Приводит значение A к указанному типу.

Приводит результат A+B к указанному типу.

Сначала приводит A и B , к указанному типу, а потом вычисляет результат A+B .

Приводит указатель A к указанному типу указателя.

Спецификаторы памяти:
(указываются перед типом)

Объявление переменной в виде константы, её можно читать, но нельзя менять, т.к. она хранится в области flash памяти.

Объявление переменной, значение которой может быть изменено без явного использования оператора присвоения =. Используются для работы с прерываниями.

Объявление локальной переменной, значение которой не теряется, между вызовами функции. Если переменная объявлена глобально (вне функций), то её нельзя подключить в другом файле.

Объявление глобальной переменной, которая определена во внешнем файле.

Объявление локальной переменной, значение которой требуется хранить в регистрах процессора, а не в ОЗУ, для обеспечения ускоренного доступа.

Значения некоторых констант:

Ложь, используются вместо 0
Истина, используется вместо 1

Низкий уровень
Высокий уровень

Конфигурация вывода как вход
Конфигурация вывода как выход
Конфигурация вывода как вход с подтяжкой

Передача младшим битом вперёд
Передача старшим битом вперёд

Тактовая частота Arduino в Гц

Число Пи
Половина числа Пи
Два числа Пи
Число Эйлера

Префиксы:

Запись числа в 2ой системе ( 0b 10101)

Запись числа в 2ой системе ( B 10101)

Запись числа в 8ой системе ( 0 12345)

Запись числа в 16ой системе ( 0x 1234A)

Модификаторы:

Число типа long (12345 L )

Число типа long lond (12345 LL )

Число беззнакового типа (12345 U )

Комбинация модификаторов (12345 UL )

Показатель экспоненты (3 E -5 = 3•10-5)

Переменные, массивы, объекты, указатели, ссылки, . :

Это указание имени и типа переменной.
int A; // объявление переменной А

Это выделение памяти под переменную.
A =1; // определение ранее объявленной A

Действуют постоянно, в любом месте кода.

Создаются внутри функций, циклов и т.д.
удаляются из памяти при выходе из них.

Указывается в одинарных кавычках.
char A=’ Z ‘; // присвоение символа «Z»

Указывается в двойных кавычках.
String A=» Z «; // присвоение строки «XY»

Это переменная с указанием класса, вместо типа, через объект можно обращаться к методам класса

Ссылка, это альтернативное имя переменной, она возвращает значение переменной, на которую ссылается.

int A=5; // создана переменная A = 5
int & C=A; // создана ссылка C на переменную A
A++; C++; // в результате A=7 и C=7
// Ссылку нельзя переопределить: &C=Z;

Указатель, это переменная, значением которой является адрес.

int * Y1=&A; // указателю Y1, передан адрес переменной A
int ( * Y2)(int)=F; // указателю Y2, передан адрес функции F
B=Y1; // получаем адрес переменной A из указателя Y1
B= * Y1; // получаем значение A разыменовывая указатель
// Указатель можно переопределять: Y1=&Z;

Создание альтернативного имени для типа

typedef bool dbl; // создаём свой тип «dbl», как тип bool
dbl A=1; // создаём переменную A типа bool

Это переменная состоящая из нескольких однотипных элементов, доступ к значениям которых осуществляется по их индексу.

int A[5]; // объявлен массив A из 5 элементов типа int
int A[2]=<7,9>; // объявлен и определён массив A из 2 эл-тов
char A[ ]=»Hi»; // создана строка A, как массив символов

Это объединение нескольких переменных под одним именем.

struct < int A=5; float B=3; >C; // создана структура «С»
int D = C.A; // получаем значение переменной A структуры С
int Z = C.A; // присваиваем Z значение A структуры С

Значения некоторых констант:

Ложь, используются вместо 0
Истина, используется вместо 1

Источник

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

Данный урок посвящён битовым операциям (операциям с битами, битовой математике, bitmath). Из него вы узнаете, как оперировать с битами – элементарными ячейками памяти микроконтроллера.

Данная тема является одной из самых сложных для понимания в рамках данного курса уроков, так что давайте разберёмся, зачем вообще нужно уметь работать с битами:

  • Гибкая и быстрая работа напрямую с регистрами микроконтроллера.
  • Работа напрямую с внешними микросхемами (датчики и прочее), управление которыми состоит из записи и чтения регистров, данные в которых могут быть запакованы в байты самым причудливым образом.
  • Более эффективное хранение данных: упаковка нескольких значений в одну переменную и распаковка обратно.
  • Создание символов и другой информации для матричных дисплеев.
  • Максимально быстрые вычисления.
  • Разбор чужого кода.

Данный урок основан на оригинальном уроке по битовым операциям от Arduino, можете почитать его здесь – там всё описано чуть более подробно.

Двоичная система

В цифровом мире, к которому относится также микроконтроллер, информация хранится, преобразуется и передается в цифровом виде, то есть в виде нулей и единиц. Соответственно элементарная ячейка памяти, которая может запомнить 0 или 1 , называется бит (bit).

Минимальная ячейка памяти, которую мы можем изменить1 бит, а ячейка памяти, которая которая имеет адрес в памяти и мы можем к ней обратитьсябайт, который состоит из 8-ми бит, каждый занимает своё место (примечание: в других архитектурах в байте может быть больше или меньше бит, в данном уроке речь идёт об AVR и 8-ми битном байте). Вспомним двоичную систему счисления из школьного курса информатики:

Двоичная Десятичная
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
10000 16

Здесь также нужно увидеть важность степени двойки – на ней в битовых операциях завязано абсолютно всё. Давайте посмотрим на первые 8 степеней двойки в разных системах счисления:

2 в степени DEC BIN
0 1 0b00000001
1 2 0b00000010
2 4 0b00000100
3 8 0b00001000
4 16 0b00010000
5 32 0b00100000
6 64 0b01000000
7 128 0b10000000

Таким образом, степень двойки явно “указывает” на номер бита в байте, считая справа налево (примечание: в других архитектурах может быть иначе). Напомню, что абсолютно неважно, в какой системе исчисления вы работаете – микроконтроллеру всё равно и он во всём видит единицы и нули. Если мы “включим” все биты в байте, то получится число 0b11111111 в двоичной системе или 255 в десятичной.

Если “сложить” полный байт в десятичном представлении каждого бита: 128+64+32+16+8+4+2+1 – получится 255. Нетрудно догадаться, что число 0b11000000 равно 128+64, то есть 192. Именно таким образом и получается весь диапазон от 0 до 255, который умещается в один байт. Если взять два байта – будет всё то же самое, просто ячеек будет 16, то же самое для 4 байт – 32 ячейки с единицами и нулями, каждая имеет свой номер согласно степени двойки.

Другие системы счисления

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

Теперь по сути: Arduino поддерживает четыре классических системы счисления: двоичную, восьмеричную, десятичную и шестнадцатеричную.

  • Двоичная (Binary) имеет префикс 0b (ноль бэ) или B, то есть двоичное число 101 запишется как 0b101 или B101 .
  • С десятичной (DEC) всё просто, пишем числа так, как они выглядят. 10 это десять, 25 это двадцать пять и так далее.
  • Восьмеричная (Octal) может содержать числа от 0 до 7 и имеет префикс 0 (ноль), например 012 .
  • 16-ричная (hexademical) система имеет 16 значений на один разряд, первые 10 как у десятичной, остальные – первые буквы латинского алфавита: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. При записи имеет префикс 0x (ноль икс), число FF19 запишется как 0xFF19 .
Базис Префикс Пример Особенности
2 (двоичная) B или 0b (ноль бэ) B1101001 цифры 0 и 1
8 (восьмеричная) 0 (ноль) 0175 цифры 0 – 7
10 (десятичная) нет 100500 цифры 0 – 9
16 (шестнадцатеричная) 0x (ноль икс) 0xFF21A цифры 0-9, буквы A-F

Основная фишка 16-ричной системы в том, что она позволяет записывать длинные десятичные числа короче, например один байт (255) запишется как 0xFF , два байта (65 535) как 0xFFFF , а жуткие три байта (16 777 215) как 0xFFFFFF .

Двоичная система обычно используется для наглядного представления данных и низкоуровневых конфигураций различного железа. Например конфиг кодируется одним байтом, каждый бит в нём отвечает за отдельную настройку (вкл/выкл), и передав один байт вида 0b10110100 можно сразу кучу всего настроить, к этому мы вернёмся в уроке работа с регистрами из раздела продвинутых уроков. В документации по этому поводу пишут в стиле “первый бит отвечает за это, второй за то” и так далее. Перейдём к изменению состояний битов.

Макросы для манипуляций с битами

В библиотеке Arduino.h есть несколько удобных макросов, которые позволяют включать и выключать биты в байте:

Макрос Действие
bitRead(value, bit) Читает бит под номером bit в числе value
bitSet(value, bit) Включает (ставит 1) бит под номером bit в числе value
bitClear(value, bit) Выключает (ставит 0) бит под номером bit в числе value
bitWrite(value, bit, bitvalue) Ставит бит под номером bit в состояние bitvalue (0 или 1) в числе value
bit(bit) Возвращает 2 в степени bit
Другие встроенные макросы
_BV(bit) Возвращает 2 в степени bit
bit_is_set(value, bit) Проверка на включенность (1) бита bit в числе value
bit_is_clear(value, bit) Проверка на выключенность (0) бита bit в числе value

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

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

Битовое И

И (AND), оно же “логическое умножение”, выполняется оператором & или and и возвращает следующее:

Основное применение операции И – битовая маска. Позволяет “взять” из байта только указанные биты:

То есть при помощи & мы взяли из байта 0b11001100 только биты 10000111, а именно – 0b11001100, и получили 0b10000100 Также можно использовать составной оператор &=

Битовое ИЛИ

ИЛИ (OR), оно же “логическое сложение”, выполняется оператором | или or и возвращает следующее:

Основное применение операции ИЛИ – установка бита в байте:

Также можно использовать составной оператор |=

Вы уже поняли, что указывать на нужные биты можно любым удобным способом: в бинарном виде (0b00000001 – нулевой бит), в десятичном виде (16 – четвёртый бит) или при помощи макросов bit() или _BV() ( bit(7) даёт 128 или 0b10000000, _BV(7) делает то же самое)

Битовое НЕ

Битовая операция НЕ (NOT) выполняется оператором

и просто инвертирует бит:

Также она может инвертировать байт:

Битовое исключающее ИЛИ

Битовая операция исключающее ИЛИ (XOR) выполняется оператором ^ или xor и делает следующее:

Данная операция обычно используется для инвертирования состояния отдельного бита:

То есть мы взяли бит №7 в байте 0b11001100 и перевернули его в 0, получилось 0b01001100, остальные биты не трогали.

Битовый сдвиг

Битовый сдвиг – очень мощный оператор, позволяет буквально “двигать” биты в байте вправо и влево при помощи операторов >> и , и соответственно составных >>= и . Если биты выходят за границы блока (8 бит, 16 бит или 32 бита) – они теряются.

Битовый сдвиг делает не что иное, как умножает или делит байт на 2 в степени. Да, это операция деления, выполняющаяся за один такт процессора! К этому мы ещё вернёмся ниже. Посмотрите на работу оператора сдвига и сравните её с макросами bit() и _BV() :

Возведение двойки в степень! Важный момент: при сдвиге дальше, чем на 15, нужно преобразовывать тип данных, например в ul: 1 даст результат 0 , потому что сдвиг выполняется в ячейке int (как при умножении, помните?). Но если мы напишем 1ul – результат будет верный.

Включаем-выключаем

Вспомним пример из пункта про битовое ИЛИ, про установку нужного бита. Вот эти варианты кода делают одно и то же:

Как насчёт установки нескольких бит сразу?

Или прицельного выключения бит? Тут чуть по-другому, используя &= и

Выключить несколько бит сразу? Пожалуйста!

Именно такие конструкции встречаются в коде высокого уровня и библиотеках, именно так производится работа с регистрами микроконтроллера. Вернёмся к устройству Ардуиновских макросов:

Я думаю, комментарии излишни: макросы состоят из тех же элементарных битовых операций и сдвигов!

Быстрые вычисления

Как я уже говорил, битовые операции – самые быстрые. Если требуется максимальная скорость вычислений – их можно оптимизировать и подогнать под “степени двойки”, но иногда компилятор делает это сам, подробнее смотри в уроке про оптимизацию кода. Рассмотрим базовые операции:

  • Деление на 2^n – сдвиг вправо на n . Например, val / 8 можно записать как val >> 3 . Компилятор не оптимизирует деление самостоятельно, что позволяет ускорить данную операцию приблизительно в 15 раз при ручной оптимизации.
  • Умножение на 2^n – сдвиг влево на n . Например, val * 8 можно записать как val . Компилятор оптимизирует умножение самостоятельно, поэтому в ручной оптимизации нет смысла. Но можно встретить в чужих исходниках.
  • Остаток от деления на 2^n – битовая маска на n младших битов. Например, val % 8 можно записать как val & 0b111 . Компилятор оптимизирует такие операции самостоятельно, поэтому в ручной оптимизации нет смысла. Но можно встретить в чужих исходниках.

Экономия памяти

При помощи битовых операций можно экономить немного памяти, пакуя данные в блоки. Например, переменная типа boolean занимает в памяти 8 бит, хотя принимает только 0 и 1. В один байт можно запаковать 8 логических переменных, например вот так:

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

Пример сжатия 1

Таким же способом можно паковать любые другие данные других размеров для удобного хранения или сжатия. Как пример – моя библиотека microLED, в которой используется следующий алгоритм: изначально необходимо хранить в памяти три цвета для каждого светодиода, каждый цвет имеет глубину 8 бит, т.е. в общей сложности тратится 3 байта на один светодиод RRRRRRRR GGGGGGGG BBBBBBBB. Для экономии места и удобства хранения можно сжать эти три байта в два (тип данных int ), потеряв несколько оттенков результирующего цвета. Например вот так: RRRRRGGG GGGBBBBB. Сожмём и упакуем: есть три переменные каждого цвета, r , g , b :

Таким образом мы отбросили у красного и синего младшие (правые) биты, в этом и заключается сжатие. Чем больше битов отброшено – тем менее точно получится “разжать” число. Например сжимали число 0b10101010 (170 в десятичной) на три бита, при сжатии получили 0b10101000, т.е. потеряли три младших бита, и в десятичной уже получится 168. Для упаковки используется битовый сдвиг и маска, таким образом мы берём первые пять битов красного, шесть зелёного и пять синего, и задвигаем на нужные места в результирующей 16-битной переменной. Всё, цвет сжат и его можно хранить. Для распаковки используется обратная операция: выбираем при помощи маски нужные биты и сдвигаем их обратно в байт:

Таким образом можно сжимать, разжимать и просто хранить маленькие данные в стандартных типах данных.

Пример сжатия 2

Давайте ещё пример: нужно максимально компактно хранить несколько чисел в диапазоне от 0 до 3, то есть в бинарном представлении это 0b00 , 0b01 , 0b10 и 0b11 . Видим, что в один байт можно запихнуть 4 таких числа (максимальное занимает два бита). Запихиваем:

Как и в примере со светодиодами, мы просто брали нужные биты ( в этом случае младшие два, 0b11 ) и сдвигали их на нужное расстояние. Для распаковки делаем в обратном порядке:

И получим обратно наши байты. Также маску можно заменить на более удобную для работы запись, задвинув 0b11 на нужное расстояние:

Теперь, проследив закономерность, можно сделать для себя функцию или макрос чтения пакета:

Где x это пакет, а y – порядковый номер запакованного значения. Выведем посмотрим:

“Трюки” с битами

На битовых операциях можно сделать очень много всего интересного, и работать оно будет очень быстро и занимать мало места. Огромный список битовых трюков и хаков можно посмотреть в этой статье, их там очень много и все с примерами. Есть ещё один небольшой сборник самых простых и полезных хаков вот здесь (английский). Его я перевёл, смотрите ниже под спойлером. Другой вариант перевода (могут быть не все трюки) можно посмотреть здесь.

Перемотка бита

Перематывает один бит слева направо, то есть формирует последовательность 0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010, 0b00000001, 0b10000000 , или 128, 64, 32, 16, 8, 4, 2, 1, 128

Установка n го бита

Выключение n го бита

Инверсия n го бита

Округление до ближайшей степени двойки

Получение максимального целого

Получение минимального целого

Умножение на 2

Деление на 2

Умножение на m ую степень двойки

Деление на m ую степень двойки

Остаток от деления

Проверка равенства

Проверка на чётность (кратность 2)

Обмен значениями

Проверка на одинаковый знак

Смена знака

Вернёт 2 n

Является ли число степенью 2

Остаток от деления на 2 n на m

Среднее арифметическое

Получить m ый бит из n (от младшего к старшему)

Получить m ый бит из n (от старшего к младшему)

Проверить включен ли n ый бит

Выделение самого правого включенного бита

Выделение самого правого выключенного бита

Выделение правого включенного бита

Выделение правого выключенного бита

Получение отрицательного значения

if (x == a) x = b; if (x == b) x = a;

Поменять смежные биты

Быстрый обратный квадратный корень

Быстрый квадратный корень (2 байта)

Быстрый квадратный корень (4 байта)

Разбить float в массив бит (unsigned uint32_t)

Вернуть массив бит обратно в float

Быстрый обратный квадратный корень

Быстрый n ый корень из целого числа

Быстрая степень

Быстрый натуральный логарифм

Быстрая экспонента

Конвертировать в нижний регистр

Конвертировать в верхний регистр

Инвертировать регистр

Позиция буквы в алфавите (англ)

Позиция большой буквы в алфавите (англ)

Позиция строчной буквы в алфавите (англ)

Быстрая конвертация цвета R5G5B5 в R8G8B8

Приоритет операций

Чтобы не плодить скобки, нужно знать приоритет операций. В C++ он такой:

Источник

Arduino функция или

Логические:

Данные:

Однобайтовые типы данных:

принимают любые, возвращают 0 или 1

если принят не 0, то вернётся 1

Целочисленные значения или символы

от -128 до 127

Беззнаковые целочисленные значения

от 0 до 255

Двухбайтовые типы данных:

от -32’768 до 32’767

В Arduino Due, тип int идентичен типу long

Беззнаковые целочисленные значения

от 0 до 65’535

В Arduino Due, тип unsigned int идентичен типу unsigned long

Четырёхбайтовые типы данных:

от -2’147’483’648 до 2’147’483’647

Беззнаковые целочисленные значения

от 0 до 4’294’967’295

Числа с плавающей точкой

от -2’147’483’648,0 до 2’147’483’647,0

Восьмибайтовые типы данных:

Числа с плавающей точкой удвоенной точности

от -9’223’372’036’854’775’808,0 до 9’223’372’036’854’775’807,0

Тип double действует как тип float, кроме Arduino Due

от -9’223’372’036’854’775’808 до 9’223’372’036’854’775’807

Беззнаковые целочисленные значения

от 0 до 18’446’744’073’709’551’615

Типы данных с определяемым размером

Нет принимаемых или возвращаемых значений

Массив A указанного размера, с элементами указанного типа

Массив А указанного типа, без прямого указания размера

Двумерный массив А указанного типа и размера

Двумерный массив А указанного типа, без прямого указания размера

Массив или строка, состоящая из указанного кол-ва символов

Массив или строка, без прямого указания количества символов

Преобразование числовых типов:

Приводит значение A к указанному типу.

Приводит результат A+B к указанному типу.

Сначала приводит A и B , к указанному типу, а потом вычисляет результат A+B .

Приводит указатель A к указанному типу указателя.

Спецификаторы памяти:
(указываются перед типом)

Объявление переменной в виде константы, её можно читать, но нельзя менять, т.к. она хранится в области flash памяти.

Объявление переменной, значение которой может быть изменено без явного использования оператора присвоения =. Используются для работы с прерываниями.

Объявление локальной переменной, значение которой не теряется, между вызовами функции. Если переменная объявлена глобально (вне функций), то её нельзя подключить в другом файле.

Объявление глобальной переменной, которая определена во внешнем файле.

Объявление локальной переменной, значение которой требуется хранить в регистрах процессора, а не в ОЗУ, для обеспечения ускоренного доступа.

Значения некоторых констант:

Ложь, используются вместо 0
Истина, используется вместо 1

Низкий уровень
Высокий уровень

Конфигурация вывода как вход
Конфигурация вывода как выход
Конфигурация вывода как вход с подтяжкой

Передача младшим битом вперёд
Передача старшим битом вперёд

Тактовая частота Arduino в Гц

Число Пи
Половина числа Пи
Два числа Пи
Число Эйлера

Префиксы:

Запись числа в 2ой системе ( 0b 10101)

Запись числа в 2ой системе ( B 10101)

Запись числа в 8ой системе ( 0 12345)

Запись числа в 16ой системе ( 0x 1234A)

Модификаторы:

Число типа long (12345 L )

Число типа long lond (12345 LL )

Число беззнакового типа (12345 U )

Комбинация модификаторов (12345 UL )

Показатель экспоненты (3 E -5 = 3•10-5)

Переменные, массивы, объекты, указатели, ссылки, . :

Это указание имени и типа переменной.
int A; // объявление переменной А

Это выделение памяти под переменную.
A =1; // определение ранее объявленной A

Действуют постоянно, в любом месте кода.

Создаются внутри функций, циклов и т.д.
удаляются из памяти при выходе из них.

Указывается в одинарных кавычках.
char A=’ Z ‘; // присвоение символа «Z»

Указывается в двойных кавычках.
String A=» Z «; // присвоение строки «XY»

Это переменная с указанием класса, вместо типа, через объект можно обращаться к методам класса

Ссылка, это альтернативное имя переменной, она возвращает значение переменной, на которую ссылается.

int A=5; // создана переменная A = 5
int & C=A; // создана ссылка C на переменную A
A++; C++; // в результате A=7 и C=7
// Ссылку нельзя переопределить: &C=Z;

Указатель, это переменная, значением которой является адрес.

int * Y1=&A; // указателю Y1, передан адрес переменной A
int ( * Y2)(int)=F; // указателю Y2, передан адрес функции F
B=Y1; // получаем адрес переменной A из указателя Y1
B= * Y1; // получаем значение A разыменовывая указатель
// Указатель можно переопределять: Y1=&Z;

Создание альтернативного имени для типа

typedef bool dbl; // создаём свой тип «dbl», как тип bool
dbl A=1; // создаём переменную A типа bool

Это переменная состоящая из нескольких однотипных элементов, доступ к значениям которых осуществляется по их индексу.

int A[5]; // объявлен массив A из 5 элементов типа int
int A[2]=<7,9>; // объявлен и определён массив A из 2 эл-тов
char A[ ]=»Hi»; // создана строка A, как массив символов

Это объединение нескольких переменных под одним именем.

struct < int A=5; float B=3; >C; // создана структура «С»
int D = C.A; // получаем значение переменной A структуры С
int Z = C.A; // присваиваем Z значение A структуры С

Значения некоторых констант:

Ложь, используются вместо 0
Истина, используется вместо 1

Источник

Логические операторы

Логические операторы чаще всего используются в проверке условия оператора if. Базовые сведения о логических операциях, смотрите в Википедии.

&& (логическое И)

Истина, если оба операнда истина (true).

Истина если оба порта вход/выхода HIGH

|| (логическое ИЛИ)

Истина, если хотя бы один операнд истина, например:

будет верно (истина) если x или y больше 0.

! (логическое отрицание)

True, если операнд false, и наоборот, например:

условие верно, если x — false (x равно 0).

Важно различать логический оператор «И» — && и битовый оператор «И» — &. Тоже самое относится к логическому оператору «ИЛИ» — || и битовому оператору «ИЛИ» — |.

Пример

Смотрите также

Железо

Это расширенный стартовый набор. В комплект входит Arduino Mega R3, макетные платы, множество датчиков, управляемые механизмы и необходимые радиоэлектронные компоненты. Полный список.

Arduino Uno — плата на базе микроконтроллера ATmega328P с частотой 16 МГц. На плате есть все необходимое для удобной и быстрой работы.

Макетная плата на 830 точек и ничего лишнего.

Источник

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

Данный урок посвящён битовым операциям (операциям с битами, битовой математике, bitmath). Из него вы узнаете, как оперировать с битами – элементарными ячейками памяти микроконтроллера.

Данная тема является одной из самых сложных для понимания в рамках данного курса уроков, так что давайте разберёмся, зачем вообще нужно уметь работать с битами:

  • Гибкая и быстрая работа напрямую с регистрами микроконтроллера.
  • Работа напрямую с внешними микросхемами (датчики и прочее), управление которыми состоит из записи и чтения регистров, данные в которых могут быть запакованы в байты самым причудливым образом.
  • Более эффективное хранение данных: упаковка нескольких значений в одну переменную и распаковка обратно.
  • Создание символов и другой информации для матричных дисплеев.
  • Максимально быстрые вычисления.
  • Разбор чужого кода.

Данный урок основан на оригинальном уроке по битовым операциям от Arduino, можете почитать его здесь – там всё описано чуть более подробно.

Двоичная система

В цифровом мире, к которому относится также микроконтроллер, информация хранится, преобразуется и передается в цифровом виде, то есть в виде нулей и единиц. Соответственно элементарная ячейка памяти, которая может запомнить 0 или 1 , называется бит (bit).

Минимальная ячейка памяти, которую мы можем изменить1 бит, а ячейка памяти, которая которая имеет адрес в памяти и мы можем к ней обратитьсябайт, который состоит из 8-ми бит, каждый занимает своё место (примечание: в других архитектурах в байте может быть больше или меньше бит, в данном уроке речь идёт об AVR и 8-ми битном байте). Вспомним двоичную систему счисления из школьного курса информатики:

Двоичная Десятичная
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
10000 16

Здесь также нужно увидеть важность степени двойки – на ней в битовых операциях завязано абсолютно всё. Давайте посмотрим на первые 8 степеней двойки в разных системах счисления:

2 в степени DEC BIN
0 1 0b00000001
1 2 0b00000010
2 4 0b00000100
3 8 0b00001000
4 16 0b00010000
5 32 0b00100000
6 64 0b01000000
7 128 0b10000000

Таким образом, степень двойки явно “указывает” на номер бита в байте, считая справа налево (примечание: в других архитектурах может быть иначе). Напомню, что абсолютно неважно, в какой системе исчисления вы работаете – микроконтроллеру всё равно и он во всём видит единицы и нули. Если мы “включим” все биты в байте, то получится число 0b11111111 в двоичной системе или 255 в десятичной.

Если “сложить” полный байт в десятичном представлении каждого бита: 128+64+32+16+8+4+2+1 – получится 255. Нетрудно догадаться, что число 0b11000000 равно 128+64, то есть 192. Именно таким образом и получается весь диапазон от 0 до 255, который умещается в один байт. Если взять два байта – будет всё то же самое, просто ячеек будет 16, то же самое для 4 байт – 32 ячейки с единицами и нулями, каждая имеет свой номер согласно степени двойки.

Другие системы счисления

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

Теперь по сути: Arduino поддерживает четыре классических системы счисления: двоичную, восьмеричную, десятичную и шестнадцатеричную.

  • Двоичная (Binary) имеет префикс 0b (ноль бэ) или B, то есть двоичное число 101 запишется как 0b101 или B101 .
  • С десятичной (DEC) всё просто, пишем числа так, как они выглядят. 10 это десять, 25 это двадцать пять и так далее.
  • Восьмеричная (Octal) может содержать числа от 0 до 7 и имеет префикс 0 (ноль), например 012 .
  • 16-ричная (hexademical) система имеет 16 значений на один разряд, первые 10 как у десятичной, остальные – первые буквы латинского алфавита: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. При записи имеет префикс 0x (ноль икс), число FF19 запишется как 0xFF19 .
Базис Префикс Пример Особенности
2 (двоичная) B или 0b (ноль бэ) B1101001 цифры 0 и 1
8 (восьмеричная) 0 (ноль) 0175 цифры 0 – 7
10 (десятичная) нет 100500 цифры 0 – 9
16 (шестнадцатеричная) 0x (ноль икс) 0xFF21A цифры 0-9, буквы A-F

Основная фишка 16-ричной системы в том, что она позволяет записывать длинные десятичные числа короче, например один байт (255) запишется как 0xFF , два байта (65 535) как 0xFFFF , а жуткие три байта (16 777 215) как 0xFFFFFF .

Двоичная система обычно используется для наглядного представления данных и низкоуровневых конфигураций различного железа. Например конфиг кодируется одним байтом, каждый бит в нём отвечает за отдельную настройку (вкл/выкл), и передав один байт вида 0b10110100 можно сразу кучу всего настроить, к этому мы вернёмся в уроке работа с регистрами из раздела продвинутых уроков. В документации по этому поводу пишут в стиле “первый бит отвечает за это, второй за то” и так далее. Перейдём к изменению состояний битов.

Макросы для манипуляций с битами

В библиотеке Arduino.h есть несколько удобных макросов, которые позволяют включать и выключать биты в байте:

Макрос Действие
bitRead(value, bit) Читает бит под номером bit в числе value
bitSet(value, bit) Включает (ставит 1) бит под номером bit в числе value
bitClear(value, bit) Выключает (ставит 0) бит под номером bit в числе value
bitWrite(value, bit, bitvalue) Ставит бит под номером bit в состояние bitvalue (0 или 1) в числе value
bit(bit) Возвращает 2 в степени bit
Другие встроенные макросы
_BV(bit) Возвращает 2 в степени bit
bit_is_set(value, bit) Проверка на включенность (1) бита bit в числе value
bit_is_clear(value, bit) Проверка на выключенность (0) бита bit в числе value

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

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

Битовое И

И (AND), оно же “логическое умножение”, выполняется оператором & или and и возвращает следующее:

Основное применение операции И – битовая маска. Позволяет “взять” из байта только указанные биты:

То есть при помощи & мы взяли из байта 0b11001100 только биты 10000111, а именно – 0b11001100, и получили 0b10000100 Также можно использовать составной оператор &=

Битовое ИЛИ

ИЛИ (OR), оно же “логическое сложение”, выполняется оператором | или or и возвращает следующее:

Основное применение операции ИЛИ – установка бита в байте:

Также можно использовать составной оператор |=

Вы уже поняли, что указывать на нужные биты можно любым удобным способом: в бинарном виде (0b00000001 – нулевой бит), в десятичном виде (16 – четвёртый бит) или при помощи макросов bit() или _BV() ( bit(7) даёт 128 или 0b10000000, _BV(7) делает то же самое)

Битовое НЕ

Битовая операция НЕ (NOT) выполняется оператором

и просто инвертирует бит:

Также она может инвертировать байт:

Битовое исключающее ИЛИ

Битовая операция исключающее ИЛИ (XOR) выполняется оператором ^ или xor и делает следующее:

Данная операция обычно используется для инвертирования состояния отдельного бита:

То есть мы взяли бит №7 в байте 0b11001100 и перевернули его в 0, получилось 0b01001100, остальные биты не трогали.

Битовый сдвиг

Битовый сдвиг – очень мощный оператор, позволяет буквально “двигать” биты в байте вправо и влево при помощи операторов >> и , и соответственно составных >>= и . Если биты выходят за границы блока (8 бит, 16 бит или 32 бита) – они теряются.

Битовый сдвиг делает не что иное, как умножает или делит байт на 2 в степени. Да, это операция деления, выполняющаяся за один такт процессора! К этому мы ещё вернёмся ниже. Посмотрите на работу оператора сдвига и сравните её с макросами bit() и _BV() :

Возведение двойки в степень! Важный момент: при сдвиге дальше, чем на 15, нужно преобразовывать тип данных, например в ul: 1 даст результат 0 , потому что сдвиг выполняется в ячейке int (как при умножении, помните?). Но если мы напишем 1ul – результат будет верный.

Включаем-выключаем

Вспомним пример из пункта про битовое ИЛИ, про установку нужного бита. Вот эти варианты кода делают одно и то же:

Как насчёт установки нескольких бит сразу?

Или прицельного выключения бит? Тут чуть по-другому, используя &= и

Выключить несколько бит сразу? Пожалуйста!

Именно такие конструкции встречаются в коде высокого уровня и библиотеках, именно так производится работа с регистрами микроконтроллера. Вернёмся к устройству Ардуиновских макросов:

Я думаю, комментарии излишни: макросы состоят из тех же элементарных битовых операций и сдвигов!

Быстрые вычисления

Как я уже говорил, битовые операции – самые быстрые. Если требуется максимальная скорость вычислений – их можно оптимизировать и подогнать под “степени двойки”, но иногда компилятор делает это сам, подробнее смотри в уроке про оптимизацию кода. Рассмотрим базовые операции:

  • Деление на 2^n – сдвиг вправо на n . Например, val / 8 можно записать как val >> 3 . Компилятор не оптимизирует деление самостоятельно, что позволяет ускорить данную операцию приблизительно в 15 раз при ручной оптимизации.
  • Умножение на 2^n – сдвиг влево на n . Например, val * 8 можно записать как val . Компилятор оптимизирует умножение самостоятельно, поэтому в ручной оптимизации нет смысла. Но можно встретить в чужих исходниках.
  • Остаток от деления на 2^n – битовая маска на n младших битов. Например, val % 8 можно записать как val & 0b111 . Компилятор оптимизирует такие операции самостоятельно, поэтому в ручной оптимизации нет смысла. Но можно встретить в чужих исходниках.

Экономия памяти

При помощи битовых операций можно экономить немного памяти, пакуя данные в блоки. Например, переменная типа boolean занимает в памяти 8 бит, хотя принимает только 0 и 1. В один байт можно запаковать 8 логических переменных, например вот так:

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

Пример сжатия 1

Таким же способом можно паковать любые другие данные других размеров для удобного хранения или сжатия. Как пример – моя библиотека microLED, в которой используется следующий алгоритм: изначально необходимо хранить в памяти три цвета для каждого светодиода, каждый цвет имеет глубину 8 бит, т.е. в общей сложности тратится 3 байта на один светодиод RRRRRRRR GGGGGGGG BBBBBBBB. Для экономии места и удобства хранения можно сжать эти три байта в два (тип данных int ), потеряв несколько оттенков результирующего цвета. Например вот так: RRRRRGGG GGGBBBBB. Сожмём и упакуем: есть три переменные каждого цвета, r , g , b :

Таким образом мы отбросили у красного и синего младшие (правые) биты, в этом и заключается сжатие. Чем больше битов отброшено – тем менее точно получится “разжать” число. Например сжимали число 0b10101010 (170 в десятичной) на три бита, при сжатии получили 0b10101000, т.е. потеряли три младших бита, и в десятичной уже получится 168. Для упаковки используется битовый сдвиг и маска, таким образом мы берём первые пять битов красного, шесть зелёного и пять синего, и задвигаем на нужные места в результирующей 16-битной переменной. Всё, цвет сжат и его можно хранить. Для распаковки используется обратная операция: выбираем при помощи маски нужные биты и сдвигаем их обратно в байт:

Таким образом можно сжимать, разжимать и просто хранить маленькие данные в стандартных типах данных.

Пример сжатия 2

Давайте ещё пример: нужно максимально компактно хранить несколько чисел в диапазоне от 0 до 3, то есть в бинарном представлении это 0b00 , 0b01 , 0b10 и 0b11 . Видим, что в один байт можно запихнуть 4 таких числа (максимальное занимает два бита). Запихиваем:

Как и в примере со светодиодами, мы просто брали нужные биты ( в этом случае младшие два, 0b11 ) и сдвигали их на нужное расстояние. Для распаковки делаем в обратном порядке:

И получим обратно наши байты. Также маску можно заменить на более удобную для работы запись, задвинув 0b11 на нужное расстояние:

Теперь, проследив закономерность, можно сделать для себя функцию или макрос чтения пакета:

Где x это пакет, а y – порядковый номер запакованного значения. Выведем посмотрим:

“Трюки” с битами

На битовых операциях можно сделать очень много всего интересного, и работать оно будет очень быстро и занимать мало места. Огромный список битовых трюков и хаков можно посмотреть в этой статье, их там очень много и все с примерами. Есть ещё один небольшой сборник самых простых и полезных хаков вот здесь (английский). Его я перевёл, смотрите ниже под спойлером. Другой вариант перевода (могут быть не все трюки) можно посмотреть здесь.

Перемотка бита

Перематывает один бит слева направо, то есть формирует последовательность 0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010, 0b00000001, 0b10000000 , или 128, 64, 32, 16, 8, 4, 2, 1, 128

Установка n го бита

Выключение n го бита

Инверсия n го бита

Округление до ближайшей степени двойки

Получение максимального целого

Получение минимального целого

Умножение на 2

Деление на 2

Умножение на m ую степень двойки

Деление на m ую степень двойки

Остаток от деления

Проверка равенства

Проверка на чётность (кратность 2)

Обмен значениями

Проверка на одинаковый знак

Смена знака

Вернёт 2 n

Является ли число степенью 2

Остаток от деления на 2 n на m

Среднее арифметическое

Получить m ый бит из n (от младшего к старшему)

Получить m ый бит из n (от старшего к младшему)

Проверить включен ли n ый бит

Выделение самого правого включенного бита

Выделение самого правого выключенного бита

Выделение правого включенного бита

Выделение правого выключенного бита

Получение отрицательного значения

if (x == a) x = b; if (x == b) x = a;

Поменять смежные биты

Быстрый обратный квадратный корень

Быстрый квадратный корень (2 байта)

Быстрый квадратный корень (4 байта)

Разбить float в массив бит (unsigned uint32_t)

Вернуть массив бит обратно в float

Быстрый обратный квадратный корень

Быстрый n ый корень из целого числа

Быстрая степень

Быстрый натуральный логарифм

Быстрая экспонента

Конвертировать в нижний регистр

Конвертировать в верхний регистр

Инвертировать регистр

Позиция буквы в алфавите (англ)

Позиция большой буквы в алфавите (англ)

Позиция строчной буквы в алфавите (англ)

Быстрая конвертация цвета R5G5B5 в R8G8B8

Приоритет операций

Чтобы не плодить скобки, нужно знать приоритет операций. В C++ он такой:

Источник

Adblock
detector