Разработка

Всё о таблицах (картах) Карно

Приветствую! Сейчас июнь, сессия в самом разгаре, но так вышло (парадокс), что в сессию у меня больше свободного времени, чем в течении учебного года, поэтому во время сессий и каникул в моём блоге можно наблюдать наибольшую активность. Что-же поделать? Нравиться мне учиться и всё.

Теперь перейдём к делу. Этот пост я посвящу таблице Карно, расскажу о том, что это такое, для чего оно надо и где это можно применить. Для тех кто не знает или ещё не понял — речь пойдёт о программировании и логике, а не о продаже квартир в Ядрине;-)

Таблица Карно представляет собой, по сути, таблицу истинности, но немного видоизменённую. Подробнее и понятнее расскажу позже на примере. Таблицы Карно, их ещё иногда называют картами Карно, используют для упрощения или составления логических функций, зная возможные значения переменных и зная значение функции при заданных значениях переменных. Это может быть полезно как при разработке логических схем, так и в программировании, например, мне пришлось прибегнуть к использованию таблицы Карно при составлении условия выполнения цикла while при написании программы. В итоге я хохотал, получив в результате простую базовую функцию логическое-И, но сейчас не об этом. Существует 7 правил построения указанной таблицы:

  1. Количество клеток таблицы равно количеству строк таблицы истинности

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

  3. В клетки заносятся соответствующие значения логической функции

  4. Клетки, где функция равна единице, объединяются в импликанты (прямоугольники) по 2n клеток. Импликанты могут перекрываться друг с другом. Желательно, составлять импликанты как можно большего размера.

  5. Для каждого импликанта записывается произведение тех аргументов, которые не изменяют своего значения в соседних клетках, для всего рассматриваемого импликанта.

  6. Переменные входят в произведение в прямом виде, если их значение равно единице и в инверсном, если нулю.

  7. Полученные произведения складываются по ИЛИ в искомую логическую функцию

Есть непонятности? Сейчас разберём на примере и точно поймёте.

Tablitsa-Karno

Рассмотрим пример функции исключающее-или. Эта функция равна единице только тогда, когда составляющие её переменные не равны между собой. Для простоты рассмотрим случай для двух переменных и составим соответствующую таблицу Карно, которая будет выглядеть следующим образом. Составив таблицу мы выполнили первые 3 шага. Теперь необходим объединить в импликанты ячейки с единицами или с нулями (в таком случае мы найдём инверсное значение логической функции). Я рассмотрю пример с единицами, а случай с нахождением инверсной логической функции оставлю вам потренироваться. Для правой верхней единицы мы можем записать следующее логическое произведение аргументов ~x2x1 (~ — это инверсия), т. к. в нашем случае ни одна из переменных не изменяет своего значения в пределах выбранного импликанта, но переменная x2 входит в произведение в инверсном виде, поскольку её значение, в данном случае, равно нулю. Для левой нижней единицы получим произведение x2~x1. Таким образом, искомая логическая функция выглядит следующим образом Y=~x2x1+x2~x1.

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

tablica-karno-formula-1

Если у вас остались вопросы или что-то не ясно, то пишите ваши вопросы, предложения и пожелания в комментариях к этому посту.

Микроконтроллеры и электроника

Arduino: работаем с SD картами

Здравствуйте, уважаемые! Снова я пишу пост про Arduino – это связано с тем, что на днях мне по почте пришёл, заказанный мною в начале июля, модуль для работы с SD-картами.  Поэтому, в этом посте я расскажу, как усовершенствовать, сделанный нами в прошлом, датчик для измерения температуры, а именно – мы избавим себя от надобности копирования данных из консоли вывода в Excel для дальнейшего анализа, заставив Arduino сохранять данные на SD-карту в виде csv-файла.  Данные будут записываться в файл в два столбца – в первом будут единицы времени (или просто номер строки), а во втором значение напряжения на термисторе или ином датчике, которое, как Вы помните, пропорционально температуре.

Необходимые компоненты

Собираем схему

Описывать схему сборки измерителя температуры я не буду, если Вы забыли или не знаете как его собирать – обратитесь к посту, ссылка на который указана в предыдущем разделе этого поста. (Картинка) Рассмотрим схему подключения SD модуля. Каждый модуль имеет следующие контакты: GND, 5V, 3V (3.3V), CS, MOSI, SCLK (SCK), MISO, GND. Подключим некоторые из этих контактов к следующим контактам Arduino:

  • GND – к земле с Arduino
  • 5V разъём к 5 вольтовому разъёму Arduino. (Аналогично 3.3V) Подключать оба разъёма одновременно – бессмысленно, я подключаю только 5V.
  • CS – к 4 разъёму (все разъёмы SD модуля подключаются ТОЛЬКО к цифровым выходам – никаких аналоговых здесь быть не должно!)
  • MOSI – к 11 разъёму
  • SCLK (SCK) – к 13 разъёму
  • MISO – к 12 разъёму
  • Ещё один GND – никуда не подключаем

На этом наше подключение окончено. Для подключения можете использовать макетную плату – просто втыкаете туда SD модуль и в правильном порядке протягиваете провода от соответствующих дорожек к нужным разъёмам.

Подготавливаем SD-карту

SD модуль работает только с SD картами, отформатированными под файловую систему FAT16 или FAT32. Карту необходимо заранее отформатировать на компьютере. БУДЬТЕ ВНИМАТЕЛЬНЫ! При форматировании все данные, находящиеся на носителе, будут уничтожены! Не забудьте переписать их в другое место перед форматированием!

Программируем Arduino

// Подключаем библиотеку для работы с шиной SPI (необходима для работы библиотеки SD)
#include <SPI.h>
// Подключаем библиотеку для работы с SD-картами
#include <SD.h>

File myFile;
// Задаём начальное время (точку отсчёта)
int time = 0;
// Указываем номер аналогового разъёма для считывания данны
int tempPin = 0;

void setup()
{
 // Открываем serial порт
  Serial.begin(9600);
  // Выдаём сообщение о том, что начинается инициализация SD-карты
  Serial.print("Initializing SD card...");
   pinMode(10, OUTPUT);
   // Проверяем готовность SD модуля. Если модуль не готов - выдаём сообщение
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  // Проверяем, существует ли на карте файл data.csv, если существует, то удаляем его.
  if(SD.exists("data.csv")) {
    SD.remove("data.csv");
  }
  // Выводим сообщение о том, что инициализация прошла успешно
  Serial.println("initialization done.");
}

void loop()
{
    // Считываем данные (напряжение) с нулевого аналогового разъёма.
    int temp = analogRead(tempPin);
    // Увеличиваем значение времени (номер строки) на единицу. Если вы будете записывать данные раз в минуту, но хотите чтобы время записывалось в секундах - прибавляйте не единицу, а 60.
    time = time + 1;
  // Открываем файл data.csv для записи
  myFile = SD.open("data.csv", FILE_WRITE);
  // Если удалось открыть файл для записи, то записываем данные
  if (myFile) {
    // Выводим данные на экран
    Serial.print(time);
    Serial.print("; ");
    Serial.println(temp);
    // Записываем время
    myFile.print(time);
    // Добавляем точку с запятой
    myFile.print(";");
    // Добавляем температуру и добавляем перенос строки
    myFile.println(temp);
    // закрываем файл
    myFile.close();
  } else {
    // Выводим сооб щение о том, что открыть файл не удалось
    Serial.println("error opening data.csv");
  }
  // Повторяем считывание данных с датчика и запись на флэш-карту через одну секунду
  delay(1000);
}

Загружаем наш скетч в Arduino, запускаем консоль (ctrl+shift+M) в среде Arduino и видим на экране каждую секунду две пары значений – номер строки (или же время) и значение напряжение, соответствующее данной температуре. Подождите, например, 10 секунд и отключите питание от Arduino. Извлеките карточку и вставьте её в компьютер. На карте у вас будет файл data.csv, открыв который в Excel вы увидите 2 столбика значений, что и в консоли Arduino. Теперь вы можете построить график или рассчитать среднее значение температуры в вашей комнате за какой-то промежуток времени.

На этом всё, задавайте свои вопросы мне в комментариях к этому посту, до скорых встреч!

Микроконтроллеры и электроника

Всё о делителях напряжения

Всё о делителях напряжения

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

Название говорит само за себя — делитель напряжения позволяет имея одно напряжение на входе цепи — разделить его на части. Как он работает? Очень просто! Чтобы собрать делитель напряжения нам понадобиться:

Необходимые компоненты

  • 2 Резистора на 10 [кОм]
  • Ручка
  • Листок бумаги 🙂

Схема и принцип работы делителя напряжения


Взгляните на схему. Допустим, между точками А и С приложено напряжение Up. Согласно второму закону Кирхгофа, вытекающего из закона сохранения энергии и «баланса мощностей» в данной цепи возникнет ток. Второй закон Кирхгофа говорит, что сумма падений напряжения на каждом элементе цепи равно сумме ЭДС в этой цепи.
Выразим отсюда ток, протекающий в нашей цепи.
Идём дальше! Зная ток, протекающий через резисторы, мы в два счёта находим  падения напряжения на этих элементах. Напишу формулу для расчёта падения напряжения на резисторе R1:

 

Аналогично напишите сами для резистора R2 (полезно для понимания). Подсказка: ответ будет тот же, но решение иным. Как мы видим, на первом резисторе падает половина напряжения и вторая половина на втором резисторе.  Отсюда следует, что в точке В мы имеем половину напряжения Up, что можно выразить также двумя способами. Первый способ — взять потенциал в точке C (Uc) и прибавить к нему падение напряжения UR2 на резисторе R2. Тут мы полагаем, что потенциал точки С нулевой, а точки А соответствует нашей ЭДС цепи.

Второй способ: взять потенциал в точке А и вычесть из него падение напряжения UR1 на резисторе R1


Поскольку резисторы у нас одинаковые, то и результат мы получаем одинаковый — половина ЭДС. Но что будет, если взять резистор R2 = 2R1 = R? Расписываем по аналогии  с самого начала и получаем

 

R в правой части сократятся и мы получим потенциал в точке В равный 2/3 от ЭДС.

Зачем это надо?

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

Также, можно просто понизить напряжение, например, если вам надо измерить напряжение в цепи +12 В с помощью АЦП микроконтроллера, который может измерять лишь в диапазоне от 0 до +5В. Собираете делитель, так чтобы при максимально возможном напряжении в цепи, в его средней точке напряжение не превышало максимально возможного для АЦП.

Однако, стоит помнить, о низкой эффективности такого преобразователя напряжений! Использовать делитель для понижения напряжения в целях питания какого-либо устройства не стОит! Делители применяются там, где нужны очень маленькие токи — например для цифровых входов, работающих на напряжении, а не на токе.

 

Ну вот и всё! В этом посте мы научились собирать и рассчитывать делитель напряжения, а также немного узнали об их применении. В следующем посте мы перейдём от теории к практике и соберём измеритель температуры. Если у вас есть какие-либо вопросы, что-то осталось непонятным или хотите узнать по подробнее, то пишите в комментариях к данному посту. Пока!

Разработка

Делаем светофор с Arduino

Светофор на Arduino Приветствую! В продолжение нашего знакомства с Arduino сегодня мы рассмотрим следующий простейший пример – светофор, какой мы видим каждый день на дорогах, который будет включаться, и выключаться по нажатию кнопки.

Необходимые компоненты:

  • 3 светодиода (зелёный, жёлтый и красный)
  • Push-кнопка
  • 3 резистора на 220 [Ом]
  • Резистор на 10 [кОм]

Собираем плату

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

Соединяем 13 разъём (далее – порт) с Arduino с длинной ногой зелёного светодиода (не забывайте, что короткая нога светодиода ВСЕГДА соединяется с Землёй), а короткую ногу через резистор в 220 [Ом] соединяем с Землёй. Аналогично соединяем жёлтый светодиод через 12 порт  и красный через 11 порт.

Схема, необходимая для работы светофора собрана, теперь необходимо подключить кнопку, для управления им.

При установке кнопки есть некоторая хитрость — кнопку надо устанавливать на разделительную полосу между двумя половинками брэдборда.

Теперь подключаем кнопку. Тут всё очень просто – кнопка проводит в обоих направлениях, в отличие от различных диодов, поэтому нет никакой разницы, какой из контактов будет соединён с землёй. Соедините один контакт кнопки с портом №7 Arduino  и этот же контакт соедините через резистор в 10 [кОм] с Землёй, а другой с питанием в 5 [В] — рельса со значком +.

ВАЖНО!! Во всех схемах Земля должна быть всегда одна – для всех подключаемых элементов, по крайней мере, до тех пор, пока вы не используете внешний источник питания в цепи Arduino.

Сборка на этом закончена! Приступаем к программированию Arduino!

Программируем Arduino

// Задаём номера портов для светодиодов
int GPin = 13; // Зелёный
int YPin = 12; // Жёлтый
int RPin = 11; // Красный
int switchPin = 7; // Порт кнопки
boolean lastButton = LOW; // Устанавливаем последнее значение кнопки - выключено
boolean currentButton = LOW; // Устанавливаем текущее значение кнопки - включено
boolean ledOn = false; // Состояние светофора: false - выключен (мигает жёлтый), true - включен
boolean yellowOn = LOW; // Мигающий жёлтый.
unsigned long loopTime; // Вспомогающая переменная для цикла
unsigned long currentTime; // Переменная хранящая текущее значение времени

void setup() {
  // Устанавливаем 3 порта как выход для светодиодов и один как вход для отслеживания кнопки
  pinMode(GPin, OUTPUT);
  pinMode(YPin, OUTPUT);
  pinMode(RPin, OUTPUT);
  pinMode(switchPin, INPUT);
  currentTime = millis();
  loopTime = currentTime;
}

// Функция для лучшего отрабатывания нажатия на кнопку - ждёт установившегося значения, а затем передаёт его в программу.
boolean debounce(boolean last)
{
  boolean current = digitalRead(switchPin);
  if(last != current)
  {
    delay(5);
    current = digitalRead(switchPin);
  }
  return current;
}

void loop() {
  currentButton = debounce(lastButton);
  if(lastButton == LOW && currentButton == HIGH) // При нажатии на кнопку включаем\выключаем светофор
  {
    ledOn = !ledOn;
  }
  lastButton = currentButton;
  currentTime = millis();

  // Цикл действий при выключенном светофоре
  if(currentTime >= (loopTime + 500) && ledOn == false)
  {
    yellowOn = !yellowOn; // Инвертируем значение переменной.
    digitalWrite(GPin, LOW);
    digitalWrite(YPin, yellowOn);
    digitalWrite(RPin, LOW);
    loopTime = currentTime;
  }

  // Цикл действий при включенном светофоре
  if(ledOn == true) {
    // Горит зелёный
    if(currentTime >= loopTime && currentTime < (loopTime + 10000)) {       digitalWrite(GPin, HIGH);       digitalWrite(YPin, LOW);       digitalWrite(RPin, LOW);     }     
// Зелёный начинает мигать     

if(currentTime >= (loopTime + 10000) && currentTime < (loopTime + 10500)) {
       digitalWrite(GPin, LOW);     
}      
if(currentTime >= (loopTime + 10500) && currentTime < (loopTime + 11000)) {
       digitalWrite(GPin, HIGH);     
} 
if(currentTime >= (loopTime + 11000) && currentTime < (loopTime + 11500)) {
      digitalWrite(GPin, LOW);     
}
if(currentTime >= (loopTime + 11500) && currentTime < (loopTime + 12000)) { 
      digitalWrite(GPin, HIGH);     
}     
if(currentTime >= (loopTime + 12000) && currentTime < (loopTime + 12500)) {
       digitalWrite(GPin, LOW);     
}     
if(currentTime >= (loopTime + 12500) && currentTime < (loopTime + 13000)) {
       digitalWrite(GPin, HIGH);     
}     
if(currentTime >= (loopTime + 13000) && currentTime < (loopTime + 13500)) {
       digitalWrite(GPin, LOW);     
}     
if(currentTime >= (loopTime + 13500) && currentTime < (loopTime + 1400)) {
       digitalWrite(GPin, HIGH);     
}     
if(currentTime >= (loopTime + 14000) && currentTime < (loopTime + 14300)) {
       digitalWrite(GPin, LOW);     
}
// Загорается жёлтый 
if(currentTime >= (loopTime + 14300) && currentTime < (loopTime + 17500)) {
       digitalWrite(YPin, HIGH);     
}
// Загорается красный 
if(currentTime >= (loopTime + 17500) && currentTime < (loopTime + 27500)) {
       digitalWrite(YPin, LOW);       digitalWrite(RPin, HIGH);     
}
// Загорается красный с жёлтым     
if(currentTime >= (loopTime + 27500) && currentTime < (loopTime + 30500)) {
       digitalWrite(YPin, HIGH);     
}
// Загорается зелёный 
if(currentTime >= (loopTime + 30500)) {
      digitalWrite(GPin, HIGH);
      digitalWrite(YPin, LOW);
      digitalWrite(RPin, LOW);
      loopTime = currentTime;
    }
  }

}

Код достаточно прост. Если светофор выключен, то просто мигает жёлтым, иначе включаем и выключаем определённые светодиоды через определённые промежутки времени.

Конечно, вместо столько сложного метода мы могли бы использовать простую задержку методом delay(), однако в использовании функции delay() есть один очень большой минус, который не должна обладать наша программа. Во время паузы — процессор не реагирует на другие операции, таким образом, во время задержки, например, на горение красного светодиода, сколько кнопку не нажимай — светофор не выключиться. Чтобы выключить светофор — вам придётся попадать по кнопке в тот момент, когда изменяется состояние светодиода, что, согласитесь, очень неудобно. Поэтому мы используем метод millis(), которые возвращает количество миллисекунд, прошедшее с момента запуска приложения, и отсчитываем нужные нам интервалы времени.

Ну что, сохраняем нашу программу (скетч) и загружаем её в Arduino. Смотрим что у нас получилось.

В следующем посте мы рассмотрим создание простейшего делителя напряжения и сделаем датчик температуры используя термистор.

Задавайте любые, интересующие Вас, вопросы, а я отвечу на них в комментариях к этому посту. До скорых встреч!

Разработка

Знакомство с Arduino

Знакомство с Arduino Приветствую! С недавних пор, а именно с моего прошедшего дня рождения у меня появилась такая популярная вещь, как Arduino. Кто не знает, Arduino — это инструмент для проектирования электронных устройств (электронный конструктор — почти как лего властелин колец) более плотно взаимодействующих с окружающей физической средой, чем стандартные персональные компьютеры, которые фактически не выходят за рамки виртуальности. Проще говоря, это микроконтроллер, которые легко можно программировать с компьютера через USB провод + набор резисторов и прочих датчиков.

Сегодня я покажу как реализовать простейший пример — мигающий светодиод с периодом в 1 секунду, и расскажу о том, как пользоваться макетной платой (брэдборд) и подключать её к ардуино.

Существует несколько версий платформы Arduino — я стал владельцем платы Arduino Uno (смотреть фото).

В комплекте с Arduino идут:

  1. Сама плата Arduino с микроконтроллером
  2. Макетная плата для сбора своих схем (на фото — белая планка внизу)
  3. Набор контактных проводов разной длинны.
  4. Набор пассивных элементов (резисторы, тиристоры, транзисторы, различные датчики и прочее)
  5. Ну и конечно же USB провод

Программируется Arduino очень просто, т.к. использует свой упрощённый язык. Чтобы написать простейшую программу (мигание встроенного в плату светодиода) вам понадобится лишь несколько строк кода.

На плате к 13 разъёму подключен встроенный светодиод. Наша задача — заставить этот светодиод мигать с периодичностью в одну секунду.

Подключаем Arduino к компьютеру и приступаем к написанию программы! Для этого вам понадобиться скачать среду разработки для Arduino — вот Вам ссылочка на страницу закачки. Среда разработки не требует установки — просто распакуйте архив и запустите исполняемый файл!

// Создаём переменную ledPin, где задаём номер выхода, к которому подключен светодиод
int ledPin = 13;

// Создаём первый обязательный метод с настройками платы

void setup() {
  // Указываем Arduino, что необходимый нам разъём будет выходом
  // Arduino может использовать одни и те же разъёмы как входы или как выходы.
  pinMode(ledPin, OUTPUT);
}

/*
* Теперь создаём "цикл" - это вторая обязательная часть программы будет непосредственно выполняться на Arduino и повторяться многократно.
*/
void loop() {
  // Задаём для соответствующего порта значение HIGH - напряжение есть (включаем светодиод)
  digitalWrite(ledPin, HIGH);
  // Одну секунду ждём
  delay(1000);
  // Убираем напряжение с 13 порта (выключаем светодиод)
  digitalWrite(ledPin, LOW);
  // и снова задержка на одну секунду
  delay(1000);
}

Вот и всё! Нажмите кнопку «загрузка» (со стрелочкой вправо) и дождитесь компиляции и загрузки программы в Ваш Arduino. Теперь вы наблюдаете как встроенный светодиод начнёт мигать. Всё просто!

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

Устройство и работа с макетной платой

Теперь поговорим о макетной плате. Кликните по этому фото и смотрите. По краям, с двух сторон, идут 2 продольные рельсы (назовём их контактными рельсами) — это рельсы для подачи напряжения (отмечена символом +) и земли (отмечена символом ).  Как вы видите, в середине платы идёт небольшое углубление — оно как бы разделяет две половины между собой — это важно знать, потому что рельсы по разные стороны от этого разделения никак не связаны между собой — это 2 разные цепи.

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

На примере нашей предыдущей задачи, рассмотрим схему подключения отдельного светодиода (примерно как на фото) к порту 13 для выполнения той же задачи — мигание светодиода с периодичностью в 1 секунду.

ВНИМАНИЕ! Ни когда не производите каких либо действий с вашими цепями при включенном питании на Arduino и в цепи. Вы можете просто повредить Arduino.

  1. Сначала соедините выход с Arduino на 5V с положительным контактным рельсом, а землю с Arduino (выход с подписью GND), с отрицательным рельсом. Теперь весь продольный положительный рельс — это источник в 5V, а отрицательный — земля. (ВНИМАНИЕ! Соединять эти рельсы напрямую опасно!!)
  2. Теперь соедините светодиод с пятивольтным рельсом длинной ногой (длинная нога всегда к источнику, а короткая к земле), а короткую воткните в любой другой свободный поперечный рельс.
  3. Соедините резистором на 220 [Ом] короткую ногу светодиода с любым другим поперечным рельсом
  4. И наконец, замыкаем цепь — соедините свободную ногу резистора с 13 выходом Arduino.

Наша цепь готова! Подключаем к питанию и радуемся!

В следующий раз я расскажу как собрать простейший светофор из 3 светодиодов, который можно будет включать и выключать (жёлтый мигающий сигнал) по нажатию кнопки. До скорых встреч!