Архив рубрики ‘Микроконтроллеры’

На AliExpress продается маленький и недорогой модуль радиоуправления с четырьмя каналами. Называется он IC 2262/2272

80071-IC-2262-2272-315MHZ-4-Channel-Wireless-Remote-Control-Kits-4-key-For-Arduino-Free

Такой модуль идеально подойдет для машинки на радиоуправлении. Один канал пойдет на левые колеса, один на правые, один на фары и один на сигнал. Управлять моторчиками нужно будет, естественно, через транзисторы. Причем можно сигнал зашифровать, чтобы он случайно не управлял соседским модулем, если сосед тоже себе такой закажет.

Реклама

Хочется ли вам сделать свой дом немного умнее? Всем привет! Есть простая вещь, дающая почувствовать, что вы живете в мире высоких технологий, а именно — управление бытовыми приборами через смартфоны. Делается это с помощью микроконтроллеров и, в нашем случаем, реле. Создадим простой скетч в Протеусе, где будем моделировать управление обычной лампой, питающейся из розетки 220В, с помощью Ардуино.

dispenser

Реле старо как мир, принцип его работы основан на магните, но он все так же хорошо делает свое дело — отделяет силовую часть от логической. Скетч чрезвычайно прост, сейчас я его немного поясню. Здесь в качестве смартфона выступает виртуальный терминал. В реальной жизни можно написать программку для айфона или андроида, которая будет подключаться к bluetooth модулю Ардуино и посылать команды. Команды могут быть любыми, в данном случае у меня «1» для включения, «2» для выключения. Программа просто слушает последовательный порт и посылает сигнал на транзистор, который открывается и подает напряжение на реле, которое в свою очередь срабатывает и включает/выключает лампу.

#define RELAY_PIN 13
int m_state = -1;

void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW);

  Serial.begin(9600);
}

void loop() {
   while(m_state == -1){
    m_state = Serial.read();
  }
  switch(m_state){
    case '1' : 
      digitalWrite(RELAY_PIN, HIGH);
      break;
    case '2' : 
      digitalWrite(RELAY_PIN, LOW);
      break;
    default:
      break;
  }
 
  m_state = -1;
}

Напишем простенькое приложение, которое будет показывать нам температуру в комнате. В качестве аппаратной платформы возьмем Arduino и датчик влажности DHT11. Подключим библиотеку DHT и накидаем такой скетч

#include "DHT.h"

#define DHTPIN 2
#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  dht.begin();
}

void loop() {
  delay(2000);
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  Serial.println(t);
}

Он шлет температуру на последовательный порт. Теперь надо бы как-нибудь ее считывать и отображать. Но что-то не хочется ставить тяжеловесное решение вроде Visual Studio, писать программу на каком-нибудь WPF или еще круче — поднять веб сервер и отображать данные на сайте в реальном времени с помощью каких-нибудь веб сокетов… К счастью, существует замечательная платформа Processing. Более того, IDE для Arduino безбожно передирался с этого самого процессинга. В двух словах, это еще один С-образный язык, который позволяет легко делать несложные графики и анимации. Но самая интересная для нас фишка — это возможность взаимодействия с COM-портом. Можно сделать так, что цвет фона будет меняться в зависимости от температуры, но так как комнатная температура у меня не подвержена большим скачкам, что я решил просто выводить показание температуры на черном фоне. А вот и сама программа

import processing.serial.*; 

Serial port; 
float temperature =0; 

void setup() 
{ 
  size (200,200); 
  port = new Serial(this, "COM3", 9600); 
  port.bufferUntil('\n');
}

void draw () 
{ 
  background(0, 0, 0);  
  textSize(32);
  text(nf(temperature, 2, 1), 60, 100); 
} 

void serialEvent (Serial port) 
{ 
  temperature = float(port.readStringUntil('\n')); 
  println(temperature);
}

В конце увидим что-то похожее на это

Безымянный

Перейдем сразу к делу:

ArduinoIR

И машинка, и пульт дистанционного управления будут работать под управлением Arduino Uno. На схеме они обозначены Transmitter и Reciever. На передатчике у нас будет потенциометр для регулировки скорости движения, и кнопка для регулировки направления: вперед или назад. Пока что без поворотов. На приемнике у нас будет все тот же H-мост L293D и один моторчик, который будет крутить задние колеса. К сожаление, в Proteus нельзя добавить радио-приемник и передатчик, поэтому их будет симулировать провод, соединяющий 13-е порты Ардуин.
Перейдем к самой сложной части системы — шифрованию и дешифровке сигнала. Будем использовать термин «отсчет», который будет означать переполнение таймера. Начало посылки будет длиться 400 отсчетов, затем 255 отсчетов для регулировки скорости, 50 отсчетов на интервал между скоростью и направлением, и 255 отсчетов для регулировки направления. Скорость будет регулироваться ШИМ-сигналом. Насчет направления будем считать, что 255 означает вперед, 127 — назад, или наоборот.

сигнал

Для упрощения работы с таймером будем использовать библиотеку TimerOne. Далее приведен полный код приемника и передатчика с некоторыми комментариями:

// Передатчик
#include <TimerOne.h>

const int infra_red_pin = 13;
const int direction_pin = 3;
const int potentiometer_pin  = A0; 

int direction_of_motion;
int counter_cycles;
int m_speed = 0;

const int timer1_period = 1000; // Значение для переполнения мкс
const int package_start = 400;
const int package_gap = 50;

void setup() {
  pinMode(infra_red_pin, OUTPUT);
  pinMode(direction_pin, INPUT);
  pinMode(potentiometer_pin, INPUT);

  Timer1.initialize(timer1_period);
  Timer1.attachInterrupt(blinky);
}

void loop() {
  if (digitalRead(direction_pin)) {
    direction_of_motion = 127;
  }
  else {
    direction_of_motion = 255;
  }

  m_speed = map(analogRead(potentiometer_pin), 0, 1023, 0, 255);
}

// Обработка прерываний по таймеру
void blinky()
{
  if (counter_cycles == package_start) {
    digitalWrite(infra_red_pin, HIGH);
  }
  if (counter_cycles == (package_start + m_speed)) {
    digitalWrite(infra_red_pin, LOW);
  }

  if (counter_cycles == package_start + 255 + package_gap) {
    digitalWrite(infra_red_pin, HIGH);
  }
  if (counter_cycles == (package_start + 255 + package_gap + direction_of_motion)) {
    digitalWrite(infra_red_pin, LOW);
  }

  counter_cycles++;
  if (counter_cycles > package_start + (2 * 255) + package_gap) {
    counter_cycles = 0;
  }
}

// Приемник
#include <TimerOne.h>

const int infra_red_pin = 13;
const int motor_b1 = 10;
const int motor_b2 = 11;

int counter_cycles;
int m_speed = 200;

const int timer1_period = 1000; // Значение для переполнения мкс
const int package_start = 400;

void setup() {
  pinMode(infra_red_pin, INPUT);
  pinMode(motor_b1, OUTPUT);
  pinMode(motor_b2, OUTPUT);

  Timer1.initialize(timer1_period);
  Timer1.attachInterrupt(blinky);
}

void loop() {
  // ждем начала пакета
  while(digitalRead(infra_red_pin)!=0);
  counter_cycles = 0;
  while(digitalRead(infra_red_pin)!=1);

  if (counter_cycles < 400) return; //Если пауза не выдержана...

  // считываем скорость
  while(digitalRead(infra_red_pin)!=0);
  m_speed = counter_cycles;

  // считываем направление
  while(digitalRead(infra_red_pin)!=1);
  counter_cycles = 0;
  while(digitalRead(infra_red_pin)!=0);

  if(counter_cycles < 50){
    stop_car();
  }else if(counter_cycles < 150){
    forward();
  }else{
    backward();
  }

  counter_cycles = 0;
}

// Обработка прерываний по таймеру
void blinky()
{
  counter_cycles++;
}

// Остановиться
void stop_car(){
  analogWrite(motor_b1, LOW);
  analogWrite(motor_b2, LOW);
}

// Едем вперед
void backward(){
  analogWrite(motor_b1, m_speed);
  analogWrite(motor_b2, LOW);
}

// Едем назад
void forward(){
  analogWrite(motor_b1, LOW);
  analogWrite(motor_b2, m_speed);
}

В прошлой части было рассказано как сделать радиоуправляемую машинку с использованием ATtiny2313 на языке ассемблер. Теперь давайте посмотрим как ее можно сделать с использованием Arduino. Возьмем самую популярную модель Arduino Uno. Она хороша тем, что в ней есть АЦП. Его мы будем использовать для регулировки скорости движения. Сама регулировка будет осуществляться с помощью обычного потенциометра. Еще хотелось бы управлять такой машинкой дистанционно, например через последовательный порт компьютера, но тогда нам нужен будет провод, или через Bluetooth мобильного телефона. В Proteus есть замечательная вещь, которая называется «Виртуальный терминал», он нам заменит оба этих случая, достаточно только подключить RX с TX и наоборот. Теперь можно считать, что либо мы протянули длинный провод от компьютера, либо поставили на Arduino какой-нибудь Bluetooth shield или HC-05 модуль. Во втором случае можно даже написать простенькую программку для смартфона под управлением ОС Android или, зная команды, которые мы сейчас придумаем, просто скачать любой bluetooth терминал.
Схема в Proteus будет выглядеть следующим образом:
ArduinoBluetooth
В качестве H-моста используется все тот же L293D. По-хорошему, скорость тоже надо бы передавать через блютуз, но, чтобы продемонстрировать работу АЦП, сделаем такую химеру, где направление движения передается от пульта, а скорость регулируется на самой машинке. Жуть, но с такими примерами программисты сталкиваются практически каждый день 😉 Еще хорошо было бы добавить второй мотор для возможности поворачивать, но тут очень сильно зависит от конструкции самой машинки: либо как трактор будет управляться включением только одной гусеницы, либо серводвигатель будет поворачивать специальную конструкцию. Так что пока ограничимся одним моторчиком.
Система команд будет следующая:
«f» — вперед
«b» — назад
«s» — стоп
Простенький скетч для ардуино

const int pot_pin  = A0;
const int motor_b1 = 10;
const int motor_b2 = 11;

int m_potentiometer = 0;
int m_speed = 0;
int m_state = 0;

void setup() {
  pinMode(pot_pin, INPUT);
  pinMode(motor_b1, OUTPUT);
  pinMode(motor_b2, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  while(m_state == -1){
    m_state = Serial.read();
  }

  m_potentiometer = analogRead(pot_pin);
  m_speed = map(m_potentiometer, 0, 1023, 0, 255);

  switch(m_state){
    case 'f' :
      forward();
      break;
    case 'b' :
      backward();
      break;
    case 's' :
      stop_car();
      break;
    default:
      break;
  }

  m_state = -1;
}

void stop_car(){
  analogWrite(motor_b1, LOW);
  analogWrite(motor_b2, LOW);
}

void backward(){
  analogWrite(motor_b1, m_speed);
  analogWrite(motor_b2, LOW);
}

void forward(){
  analogWrite(motor_b1, LOW);
  analogWrite(motor_b2, m_speed);
}

Запустим симуляцию в протеусе и будем в виртуальном терминале посылать наши команды.
Безымянный

Давайте сделаем машинку на радиоуправлении. Начнем с простого — пусть будет один моторчик на передние или задние колеса, и ездить она пока сможет только вперед или назад. Поворачивать пока не будем, для этого понадобится поставить на каждое колесо по моторчику, и поворачивать она будет, если моторчики крутятся только с одной стороны. Пульт управления тоже сделаем попозже. Сердцем нашей машинки будет микроконтроллер ATtiny2313, в качестве Н-моста возьмем готовое решение под названием L293D. Скорость будем регулировать при помощи ШИМ, которая создается таймером 0. Вот простейшая схема в Proteus.
Безымянный
Слева находятся две кноки, собственно говоря они и управляют направление движения: верхняя — вперед, нижняя — назад. А дальше я приведу код программы на ассемблере.

.include "tn2313def.inc"

.def temp =R16
.def count =R17

.equ BT_FORWARD =PD2 ;кнопка вперед
.equ BT_BACKWARD =PD3 ;кнопка назад
.equ PWM =128

.cseg
.org 0x00
     rjmp init ;(0х00) переход на инициализацию (начальную настройку)
     rjmp go_forward ;(0х01) внешнее прерывание 0
     rjmp go_bakward ;(0х02) внешнее прерывание 1
     reti ;(0х03)
     reti ;(0х04)
     reti ;(0х05)
     reti ;(0х06)
     reti ;(0х07)
     reti ;(0х08)
     reti ;(0х09)
     reti ;(0х0A)
     reti ;(0х0B)
     reti ;(0х0C)
     reti ;(0х0D)
     reti ;(0х0E)
     reti ;(0х0F)
     reti ;(0х10)
     reti ;(0х11)
     reti ;(0х12)

.org INT_VECTORS_SIZE
init:
     ldi temp,RAMEND ;инициализация стека
     out SPL,temp

     ser temp ;порт В на выход
     out DDRB,temp
     ser temp
     out PORTB,temp

     ldi temp,(1<<PD5) ;все выводы порта D, за исключением PD5, на вход.
     out DDRD,temp ;PD5 на выход
     ser temp ;На всех выводах порта D включены подтягивающие резисторы.
     out PORTD,temp

     ;шим
     ldi temp,(1<<COM0B1|0<<COM0B0|1<<COM0A1|0<<COM0A0|1<<WGM00)
     out TCCR0A,temp
     ldi temp,(0<<CS02|0<<CS01|1<<CS00) ;деление 1
     out TCCR0B,temp ;запуск таймера

     ldi count,0
     out OCR0A,count
     out OCR0B,count

     ;1 настройка прерываний
     ldi temp,(0<<ISC01|1<<ISC00|0<<ISC11|1<<ISC10)
     out MCUCR,temp

     ;2 разрешение прерываний
     ldi temp,(1<<INT1|1<<INT0|0<<PCIE)
     out GIMSK,temp
     sei

main:
     rjmp main

;едем вперед
go_forward:
     sbic PIND,BT_FORWARD ;кнопка нажата
     rjmp stop
     ldi count,0
     out OCR0B,count
     ldi count,PWM
     out OCR0A,count
     reti

;едем назад
go_bakward:
     sbic PIND,BT_BACKWARD ;кнопка нажата
     rjmp stop
     ldi count,0
     out OCR0A,count
     ldi count,PWM
     out OCR0B,count
     reti

;ничего не нажато
stop:
     ldi count,0
     out OCR0A,count
     out OCR0B,count
     reti