Ошибки при работе с arduino и другими микроконтроллерами

Урок 55. Работа с инкрементальным энкодером в Ардуино. Библиотека Encod_er.h

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

Предыдущий урок     Список уроков     Следующий урок

Мне заказали разработку программы для устройства, в котором в качестве управляющего элемента используется инкрементальный энкодер. Поэтому я решил написать внеплановый урок о работе с энкодером в системе Ардуино.

Совсем коротко, о чем идет речь, т.е. о классификации энкодеров.

Энкодеры это цифровые датчики угла поворота. Другими словами преобразователи угол-код.

Я подчеркиваю, что это цифровые датчики, потому что существует большое число датчиков с выходными аналоговыми сигналами. Простой переменный резистор можно считать датчиком угла. Энкодеры формируют на выходе дискретные сигналы.

Энкодеры бывают абсолютные и накапливающие (инкрементальные).

Абсолютные энкодеры формируют на выходе код, соответствующий текущему углу положения вала. У них нет памяти. Можно выключить устройство, повернуть вал энкодера, включить и на выходе будет новый код, показывающий новое положение вала. Такие энкодеры сложные, дорогие, часто используют для подключения стандартные цифровые интерфейсы RS-485 и им подобные.

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

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

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

Мы крутим ручку энкодера и смотрим, как изменяется значение параметра на экране дисплея.

В подобных случаях инкрементальные энкодеры становятся идеальными устройствами управления, установки параметров, выбора меню. Они намного удобнее, чем кнопки ”+” и ”-”.

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

По моей партнерской ссылке механический инкрементальный энкодер с кнопкой EC11 стоит всего 50 руб. При покупке 5 штук 45 руб., при 10 — 40 руб.

Принцип действия механического инкрементального энкодера.

Импульсы на выходе инкрементального энкодера должны сообщать не только о повороте вала, но и о направлении поворота. Поэтому необходимо использовать 2 сигнала, которые обычно обозначаются A и B. Эти сигналы подключены к механическим контактам энкодера, другие выводы которых соединены на выводе C.

Если подключить к сигналам A и B подтягивающие резисторы, то диаграмма выходных сигналов при вращении энкодера будет выглядеть так.

Если вы покрутите вал энкодера, то заметите, что у него есть фиксированные положения. У моего энкодера 20 таких положений на оборот. Т.е. точность определения угла 360° / 20 = 18° или 20 импульсов на оборот.

Эти, механически зафиксированные, положения отмечены на диаграмме стрелочками снизу вверх. В этот момент оба контакта разомкнуты, сигналы A и B находятся в высоком уровне.

На первых 2 отрезках диаграммы энкодер повернули по часовой стрелке. Сначала в низкий уровень упал сигнал A, затем B. На 3 и 4 интервалах вал вращают против часовой стрелки. После фиксированного положения сначала становится равным нулю сигнал B. По последовательности изменения сигналов можно определить, в какую сторону повернули энкодер.

Формализованный алгоритм анализа сигналов энкодера выглядит так.

  • Если в момент перехода сигнала A в низкое состояние сигнал B находится в высоком уровне, то произошел поворот по часовой стрелке.
  • Если в момент перехода сигнала A в низкое состояние сигнал B находится в низком уровне, то был поворот против часовой стрелки.

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

Параметры инкрементальных энкодеров.

Подробно технические характеристики и параметры энкодера EC11 в формате PDF можно посмотреть по этой ссылке EC11.pdf. Я приведу и поясню основные.

  • Главный параметр – число импульсов на оборот. Определяет точность измерения угла. У моего датчика 20 импульсов на оборот, точность 18°.
  • Предельно допустимые электрические параметры определяют предельные значения тока и напряжения для контактов. В моем случае это 10 мА и 5 В. Эти параметры влияют на выбор подтягивающих резисторов. Сопротивление резисторов не должно быть ниже 500 Ом.
  • Прочность изоляции. Я бы не рискнул использовать подобный энкодер в цепях, гальванически связанных с высоким напряжением.
  • Износоустойчивость. Разработчики гарантируют от 15 000 до 1 000 000 циклов, в зависимости от модификации.
  • Рабочий диапазон температур -30 … +85 °C.
  • Механические параметры: габаритные и установочные размеры, моменты вращения и т.п.

Подключение энкодера к плате Ардуино.

С электрической точки зрения энкодер это 3 кнопки: сигналы A, B и кнопка. Я использовал внутренние подтягивающие резисторы, но в рабочих схемах лучше добавить внешние резисторы сопротивлением 2 – 10 кОм.

Программная обработка сигналов энкодера.

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

void setup() {
  Serial.

begin(9600); // инициализируем порт, скорость 9600
  pinMode(2, INPUT_PULLUP); // определяем вывод как вход
  pinMode(8, INPUT_PULLUP); // определяем вывод как вход
}
void loop() {
  if( digitalRead(2) == HIGH ) Serial.print(«H «);
  else Serial.print(«L «);
  if( digitalRead(8) == HIGH ) Serial.println(«H»);
  else Serial.println(«L»);
  delay(200);
}

У меня энкодер подключен к выводам 2 и 8. Программа в цикле выводит через последовательный порт состояния сигналов A и B.

Если я плавно вращаю вал по часовой стрелке у меня монитор последовательного порта выводит:
HH -> LH -> LL -> HL -> HH

При вращении  против часовой стрелки:
HH -> HL -> LL -> LH -> HH

Все как на диаграмме выше.

Алгоритм обработки сигналов энкодера простой:

  • выделяем отрицательный фронт сигнала A;
  • считываем состояние сигнала B;
  • если у сигнала B:
    • высокий уровень – был поворот по часовой стрелке;
    • низкий  уровень – был поворот против часовой стрелки.

Но у механического энкодера механические контакты. Им свойственно явление дребезга. К тому же энкодеры часто устанавливаю на передней панели устройства, и подключают к контроллеру достаточно длинными проводами. Т.е. нужна цифровая фильтрация сигналов и устранение дребезга. Все это реализовано в моей библиотеке Button.h. Ее вполне можно использовать для работы с энкодером.

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

#include
#include<\p>

Button encoderA (2, 4); // сигнал A
Button encoderB (8, 4); //  сигнал B
Button encoderButton(10, 40); // кнопка

long pos=0; // пооложение энкодера

void setup() {
  Serial.

begin(9600); // инициализируем порт, скорость 9600
  Timer1.initialize(250); // инициализация таймера 1, период 250 мкс
  Timer1.attachInterrupt(timerInterrupt, 250); // задаем обработчик прерываний
}

void loop() {
// сброс положения 
  if( encoderButton.flagClick == true ) {
    encoderButton.flagClick= false;
    pos= 0;
  }
  Serial.println(pos); // вывод положения
}

// обработчик прерывания 250 мкс
void timerInterrupt() {
  encoderA.filterAvarage(); // вызов метода фильтрации
  encoderB.filterAvarage(); // вызов метода фильтрации 
  encoderButton.filterAvarage(); // вызов метода фильтрации

// обработка сигналов энкодера
  if( encoderA.flagClick == true ) {
    encoderA.flagClick= false;
      if( encoderB.flagPress == true) {
        // против часовой стрелки
        pos—;
      }
      else {
        // по часовой стрелке
        pos++;
      }
  }
}

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

Вопрос, с какой частотой необходимо опрашивать энкодер. У меня в программе цикл прерывания 250 мкс и 4 выборки при фильтрации. В итоге период реакции на изменения состояния сигналов 1 мс.

Все зависит от того с какой частотой сигналы энкодера могут изменять свое состояние. А это определяют максимальная скорость вращения энкодера и параметр — число импульсов на оборот.

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

  • Запомнить механическое положение вала.
  • Сбросить вычисленное положение кнопкой (привязать к механическому положению).
  • Теперь сколько бы вы ни вращали вал, при попадании в механическое положение, к которому он был привязан, на экране должно быть значение кратное числу импульсов на оборот. В моем случае это 0,20, 40…

Библиотека Encod_er.h.

Представленный выше способ обработки энкодера вполне имеет право на жизнь. Но в моей задаче надо было определять, с какой скоростью вращают энкодер.

Энкодером перемещался шпиндель сверлильного станка. С одной стороны нужна была высокая точность позиционирования.  С другой – приходилось долго крутить ручку, чтобы переместить шпиндель в нижнее положение.

Решили при медленном вращении энкодера перемещать шпиндель по одному шагу, а при увеличении скорости вращения – увеличивать число шагов перемещения шпинделя на каждый шаг энкодера. Т.е. если медленно крутить энкодер, то шпиндель перемещается точно и медленно. При быстром вращении – шпиндель перемещается быстро.

Для реализации этого алгоритма необходимо вычислять скорость вращения энкодера. Я разработал библиотеку обработки энкодера с этой функцией.

Загрузить библиотеку Encod_er.h можно по этой ссылке:

Читайте также:  Уроки arduino: создаем вольтметр на 5 вольт своими руками

 Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта! 

Описание класса Encod_er.

Я привел только public свойства и методы.

class Encod_er {
  public:
    Encod_er(byte pinA, byte pinB, byte timeFilter);
    byte timeRight; // время/признак вращения вправо (* 8 периодов)
    byte timeLeft; // время/признак вращения влево (* 8 периодов)
    long position; // текущее положение 
    void scanState(); // метод проверки состояния
    long read(); // метод чтения положения 
};

Encod_er(byte pinA, byte pinB, byte timeFilter)  — конструктор.

  • pinA – вывод сигнала A;
  • pinB – вывод сигнала B;
  • timeFilter – число выборок фильтрации сигналов.

Encod_er myEncoder(2, 3, 5);   // энкодер к выводам 2 и 3, 5 выборок фильтрации

void  scanState() – метод сканирования состояния энкодера. Должен вызываться регулярно в параллельном процессе.

// обработчик прерывания 250 мкс
void timerInterrupt() {
  myEncoder.scanState();
}

byte timeRight и timeLeft – признаки поворота энкодера соответственно вправо и влево. Если равны 0, то поворота не было. Если не равны 0, то содержат время поворота на один шаг. Время вычисляется, как

Tповорота = Tвызова scanState()  * 8 * timeRight.

Т.е. единица значения timeRight или timeLeft это период вызова scanState(),  умноженный на 8. В моей программе это 250 мкс * 8 = 2 мс. На 8 я умножил, чтобы использовать один байт. Значение переменных  timeRight или timeLeft ограничиваются на уровне 255.

Если признаки используются, то должны сбрасываться  в основном цикле.

if(encoder.timeRight != 0) {
  encoder.timeRight= 0;
………..
}

long  position – текущее положение энкодера. Может быть прочитано или записано.

long  read() – метод чтения положения энкодера. Возвращает переменную position.

Все просто. Предыдущую программу можно переписать с использованием библиотеки Encod_er.h.

#include
#include<\p>

Encod_er encoder( 2, 8, 4);

void setup() {
  Serial.

begin(9600); // инициализируем порт, скорость 9600
  Timer1.initialize(250); // инициализация таймера 1, период 250 мкс
  Timer1.attachInterrupt(timerInterrupt, 250); // задаем обработчик прерываний
}

void loop() {
  if(encoder.timeRight != 0) {
    Serial.print(«R=»);
    Serial.print(encoder.timeRight);
    Serial.print(» Pos=»);
    Serial.println(encoder.read()); // вывод текущего положения 
    encoder.

timeRight= 0;
  }
  if(encoder.timeLeft != 0) {
    Serial.print(«L=»);
    Serial.print(encoder.timeLeft);
    Serial.print(» Pos=»);
    Serial.println(encoder.read()); // вывод текущего положения
    encoder.

timeLeft= 0;
  }
}

// обработчик прерывания 250 мкс
void timerInterrupt() {
  encoder.scanState();
}

На каждый поворот вала в монитор последовательного порта выводится направление вращение и значение timeRight или timeLeft.

Обработку кнопки энкодера я в библиотеку не включил. Кнопку лучше обрабатывать библиотекой Button.h.

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

Анализировал признаки timeRight и timeLeft. При ненулевом значении признаков шаговый двигатель перемещения шпинделя делал количество шагов в зависимости от значения активного признака. Зависимость задал в массиве.

Предыдущий урок     Список уроков     Следующий урок

Источник: http://mypractic.ru/urok-55-rabota-s-inkrementalnym-enkoderom-v-arduino-biblioteka-encod_er-h.html

Arduino. Внешние прерывания. Подключение энкодера

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

Подготовка к работе

На нашей плате TutorShield установлены две кнопки, которые подключены к цифровым выводам D2 и D3. Подробно подключение кнопок описано в предыдущей статье цикла. Помимо них, для дальнейших экспериментов подключим еще две секции трехцветного светодиода (к выводам D9 и D10). Установите перемычки так, как показано на рисунке:

Установка перемычек

Первый пример

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

Специально для этих целей можно использовать прерывания. Прерывание — это функция микроконтроллера, которая при наступлении какого-либо события позволяет остановить выполнение основной программы и обработать произошедшие события.

Например, прерывание можно использовать для обработки приема сигналов COM-порта: микроконтроллер выполняет основную программу, а когда модуль UART сообщает о завершении передачи, то МК прерывает работу и обрабатывает принятый байт.

Прерывании в микроконтроллере Atmega8 ровно девятнадцать штук и два из них — это внешние прерывания, вызывающие при изменении напряжении на выводе D2 иди D3 Arduino-совместимой платы.

Рассмотрим работу с прерываниями на конкретном примере:

#define LED 11 void setup() { attachInterrupt(0, button1, RISING); attachInterrupt(1, button2, RISING); pinMode(LED, OUTPUT); } void loop() { } void button1() { digitalWrite(LED,HIGH); } void button2() { digitalWrite(LED,LOW); }

Для работы с внешним прерыванием в первую очередь его надо инициализировать. Этим занимается функция attachInterrupt(pin,ISR,mode). У этой функции три параметра:

  • pin — номер вывода, на который настроено прерывание. Для платы EduBoard с МК Atmega8 прерывание может быть настроено только на 2ом и 3ем выводе
  • ISR — имя функции-обработчика прерывания
  • mode — режим работы прерывания, может принимать четыре значения. LOW — вызывает обработчик прерывания, когда на выводе низкое напряжение, CHANGE — при изменении, RISING — при переходе от низкого к высокому уровню, FALLING — от высокому к низкому

В приведенном примере настроено два прерывания. Прерывание «0» вызывает обработчик button1() при изменении напряжения с низкого на высокий уровень на выводе D2.

Прерывание «1» вызывает обработчик button2() при изменении напряжения с низкого на высокий уровень на выводе D3.
В обработчике прерывания button1() зажигается светодиод, а при обработке прерывания button2() он гасится.

То есть при нажатии на кнопку, подключенную к выводу D2 светодиод включится, а при нажатии D3 — выключится.

Второй пример

В работе кнопок есть одна проблема, которая связана с дребезгом контактов.

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

При этом обработчик прерывания может быть вызван несколько раз.
Для демонстрации этого эффекта добавим в предыдущий пример изменение переменной и вывод ее значения в COM-порт.

#define LED 11 int val = 0; void setup() { attachInterrupt(0, button1, RISING); attachInterrupt(1, button2, RISING); pinMode(LED, OUTPUT); Serial.begin(9600); } void loop() { Serial.println(val); delay(500); } void button1() { digitalWrite(LED,HIGH); val++; } void button2() { digitalWrite(LED,LOW); val—; }

После загрузки кода на плату, откройте монитор COM-порта (Ctrl+Shift+M) и посмотрите на то, как изменяются данные при нажатии на кнопки. Чаще всего все будет работать правильно, но иногда переменная будет меняться не на единицу, а сразу на два за одно нажатие.

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

Есть много способов борьбы с дребезгом контактов, как программных, так и аппаратных. Пока мы не будем останавливаться на них.

Обработка энкодера

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

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

Если он в высоком состоянии, то значит энкодер вращали по-часовой стрелке. Если на входе низкое напряжение — то против. Рассмотрим на примере:

#define LED 11 int val = 0; void setup() { attachInterrupt(0, button1, RISING); pinMode(LED, OUTPUT); pinMode(3, INPUT); } void loop() { if(val255) val = 250; analogWrite(LED,val); } void button1() { if( digitalRead(3) == HIGH) val=val+10; else val=val-10; }

В результате работы этой программы при вращении энкодера по-часовой яркость светодиода будет увеличиваться, а при вращении против часовой — уменьшаться. Изменение яркости происходит с шагом в 10 единиц. Чтобы переменная не вышла за пределы нормальных значений в основном цикле она нормируется.

Индивидуальные задания

  1. Измените код первого примера так, чтобы при нажатии на кнопку светодиод менял свое состояние. То есть при каждом последующем нажатии он должен включаться или выключаться.
  2. Используя знания о работе с подключите его и выведите на дисплей значение переменной, изменяемой энкодером

Остальные статьи цикла можно найти здесь.

Мы будем очень рады, если вы поддержите наш ресурс и посетите магазин наших товаров shop.customelectronics.ru.

Источник: http://www.customelectronics.ru/arduino-vneshnie-preryivaniya-podklyuchenie-enkodera/

Оптимизируем digitalWrite на Arduino | РОБОТОША

Сегодня я протестирую фактическую скорость работы функции digitalWrite на своей Arduino Mega2560 и расскажу как ускорить работу программы в 50 раз! В основе отладочной платы Arduino Mega2560 лежит микроконтроллер AT2560, работающий с тактовой частотой 16 Мгц. Если перевести эти 16 миллионов колебаний во временной интервал, то получим достаточно небольшой период, равный 62.5 нс. Это быстро, но действительно ли Arduino выполняет операции с такой же скоростью? Давайте посмотрим.

Команды, которые мы пишем на языке Wiring, в процессе компиляции преобразуются в более простые команды, так называемый, машинный код, которые микроконтроллер уже непосредственно выполняет. Некоторые команды микроконтроллера выполняются за один такт работы микроконтроллера, на некоторые требуются больше тактов, соответственно и времени выполнения.

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

Вопрос только на сколько.

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

Читайте также:  Отправляем данные из esp на веб-страницу с минимальными усилиями
void setup(){pinMode(13, OUTPUT);}void loop(){digitalWrite(13, HIGH);digitalWrite(13, LOW);}

Этот код мигает светодиодом, расположенном на плате и подключенным к 13 выводу. Мы просто поочередно меняем состояние этого выхода.

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

Светодиод включается и выключается, просто наш глаз не может воспринимать колебания частотой свыше 25 Гц и такие колебания мы видим как постоянно горящий светодиод с яркостью, определяемой скважностью подаваемого сигнала.

Для того, чтобы посмотреть, что же на самом деле происходит на 13 выводе, я воспользуюсь осциллографом. Осциллограмма сигнала на 13 пине выглядит так:

Похоже, что команда digitalWrite (13, HIGH) выполняется за 6.6 мкс, а digitalWrite (13, LOW) за 7.2 мкс. Итого 13.8 мкс. Это намного дольше, чем 62.5 нс, в действительности, в 220 раз дольше. Также, можно заметить, что нахождение с состоянии LOW (7.2 мкс) занимает больше времени, чем нахождение в состоянии HIGH (6.6 мкс).

Проведем следующий эксперимент.

void setup(){pinMode(13, OUTPUT);}void loop(){digitalWrite(13, HIGH);digitalWrite(13, HIGH);digitalWrite(13, LOW);}

Теперь мы переводим дважды 13 пин в состояние HIGH, а затем один раз в LOW и цикл повторятся заново. Я ожидал увидеть значение времени для состояния HIGH равное 6.6 × 2 = 13.2 мкс и для LOW равное по прежнему 7.2 мкс. Посмотрим на фактическую осциллограмму сигнала, полученного в результате выполнения второго скетча.

По факту, две инструкции, переводящие вывод в состояние HIGH дважды занимают 19.4 мкс, или, в среднем, по 9.7 мкс на одну команду, на нахождение в состоянии LOW, по прежнему, уходит 7.2 мкс.

Попробуем реализовать теперь еще одну последовательность состояний: HIGH→LOW→LOW.

void setup(){pinMode(13, OUTPUT);}void loop(){digitalWrite(13, HIGH);digitalWrite(13, LOW);digitalWrite(13, LOW);}

В результате выполнения этого кода, осциллограмма сигнала на 13 пине выглядит так:

Единственная инструкция HIGH занимает 6.8 мкс — примерно так же как и ожидалось (6.6 мкс). Две подряд команды, переводящие вывод в состояние LOW занимают 13.8 мкс — это чуть меньше, чем ожидаемые 14.4 мкс (7.2 × 2 ).

Что получатеся? В цикле loop (), используя функцию digitalWrite, мы можем менять состояние пина с частотой максимум 72 кГц, а в отдельных случаях, эта частота может быть и ниже, например, как во втором случае — около 37 кГц.  Такая частота значительно меньше тактовых 16 Мгц, но если использовать прерывания по таймеру, то мы можем значительно увеличить этот показатель.

Реализация функции digitalWrite в языке Wiring является, мягко говоря, не оптимальной. Если посмотреть на ее реализацию на Ассемблере, то можно обнаружить несколько проверок, в частности, проверятся не нужно ли выключить таймер ШИМ после предыдущей функции analogWrite ().

Наиболее быстрая реализация, но, в то же время и наиболее затратная по времени ее написания, могла бы быть на языке Ассемблер. Но написание кода на Ассемлере — то еще насилие над собой. Я уже не говорю про отладку ассемблерного кода.

Одним из компромиссных решений может быть использование оптимизированных библиотек, реализующих различные функции ввода/вывода. В дальнейших своих публикациях я рассмотрю некоторые из таких библиотек.

Еще одной причиной задержки является сама функция loop (). Когда все команды внутри loop () выполнены, то неявно для нас выполняется еще код, который производит возврат к повторному выполнению команд внутри этого цикла.

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

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

Для изменения значений пинов с 8 по 13 используется регистр PORTB в который необходимо записать слово данных (два младших бита этого слова данных, то есть 1 и 2 биты не используются, а оставшиеся шесть — как раз и устанавливают состояния цифровых портов с 8 по 13). Для изменения состояний цифровых пинов с 0 по 7 используется PORTD.

Приведу пример:

void setup(){DDRB = B10000000; // устанавливаем 13 пин как OUTPUT}void loop(){// создаем свой бесконечный цикл while (1){  PORTB = B10000000; // устанавливаем состояние pin13 как HIGH  PORTB = B00000000; // устанавливаем состояние pin13 как LOW}}

И вновь смотрим на осциллограму получившигося сигнала на 13 выводе:

Мы получили заветные 62 нс! Наша программа выполняется за 4 рабочих такта микроконтроллера. Фантастика! Режим работы 13 пина в качестве цифрового выхода мы устанавливаем всего один раз и сколько уходит на это времени — нас мало интересует.

Один рабочий такт микроконтроллера уходит на перевод состояния 13 вывода в HIGH, еще один — для установки состояния этого пина в LOW и за два такта, с помощью бесконечного цикла while (1){…} мы циклически возвращаемся вновь к установке состояния в HIGH.

Зачем же здесь while (1), когда loop () делает фактически то же самое — циклически выполняет код, можете спросить вы? Изменим скетч, просто убрав из loop вложенный бесконечный цикл:

void setup(){DDRB = B10000000; // устанавливаем 13 пин как OUTPUT}void loop(){PORTB = B10000000; // устанавливаем состояние pin13 как HIGHPORTB = B00000000; // устанавливаем состояние pin13 как LOW}

И вновь обратимся к помощи осциллографа:

Как можно видеть, частота снизилась с 4 МГц до 1 Мгц. Время нахождения в состоянии HIGH не изменилось и по-прежнему равно 62.5 нс (на осцилограмме просто показано, Wid (1)

Источник: http://robotosha.ru/arduino/digitalwrite-optimizing-arduino.html

Автоматическая перезагрузка Arduino при подключении терминала

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

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

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

Перепроверив все несколько раз, осталось сделать вывод, что микроконтроллер перезагружается каждый раз, когда я подключаюсь к нему терминалом через последовательный порт. Тогда уже осталось прибегнуть к методу RTFM и убедиться, что это — стандартное поведение Arduino с USB-serial контроллером.

Именно USB-serial контроллер Arduino перезагружает МК каждый раз, когда терминальная программа (в т.ч. Serial monitor, встроенный в ПО Arduino IDE) устанавливает соединение.

Реализовано это следующим образом: у USB-serial контроллера вывод DTR (Data Terminal Ready) связан с выводом RESET.

Если программа, работающая с виртуальным последовательным портом, использует DTR, то при установке соединения МК перезагружается.

Само по себе это обеспечивает беспроблемную загрузку скетча из Arduino IDE — МК перезагружается перед загрузкой кода. В старых моделях было необходимо нажимать RESET вручную перед каждой загрузкой кода. В этом смысле автоматическая перезагрузка облегчает жизнь.

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

недавно порт был занят), так что увидеть вывод скетча в первые секунды его работы было бы нельзя. Перезагрузка в момент подключения терминала позволяет получить весь вывод, начиная с момента загрузки МК. Т.е. функцию автоматической перезагрузки оправдывает удобство работы в Arduino IDE.

Но для моих прикладных целей это нежелательно.

Простой способ избежать автоматической перезагрузки — не использовать USB-serial интерфейс вовсе, а подключаться напрямую к последовательному порту МК, используя выводы D0 (RX), D1 (TX).

Но для этого необходим ТТЛ-совместимый последовательный интерфейс со стороны терминала, которого может не оказаться.

Последовательный порт стандарта RS232 (COM порт) напрямую к выводам МК подключать нельзя!  Хотя можно сделать ТТЛ-совместимый адаптер из USB-RS232 адаптера.

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

Аппаратные решения сводятся к тому, чтобы не дать сигналу DTR запустить перезагрузку, а именно:

  • У Arduino Uno R3 есть специальное место на плате, обозначенное как RESET EN, где можно перерезать дорожку, соединяющую DTR и RESET, а при необходимости спаять обратно без особых проблем (см.фото)
  • Соединить вывод RESET и 5V резистором 120 Ом (для моделей с FTDI USB-serial чипом, напр. Diecimila) [источник]
  • Соединить вывод RESET и GND конденсатором 10 мкФ (для моделей с USB-serial контроллером на ATmega8U/ATmega16U, напр. Uno) [источник]
  • Удалить конденсатор, соединяющий вывод DTR USB-serial контроллера и RESET МК (самый жестокий способ) [источник]

Программные решения избегают подачи сигнала DTR:

  • Модификация кода терминальной программы и/или драйвера последовательного порта с целью исключить использование DTR [источник]
  • Перепрошивка USB-serial контроллера (для моделей с USB-serial контроллером ATmega8U/ATmega16U) с целью игнорировать сигнал DTR от терминала [источник]

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

Пока я остановился на использовании последовательного порта напрямую, минуя USB-serial контроллер. Для этого понадобился USB-TTL serial адаптер. Но для конечного, «промышленного» варианта все же планирую отключить эту функцию аппаратно.

Источник: http://atroshin.ru/ru/content/avtomaticheskaya-perezagruzka-arduino-pri-podklyuchenii-terminala

Манипуляции с портами

Главная → Библиотека Arduino → Описание языка Arduino на русском языке → Манипуляции с портами Библиотека Arduino → Описание языка Arduino на русском языке Регистры портовРегистры портов разрешают низкоуровневые высокоскоростные манипуляции с выводами микроконтроллера. Используемые в Arduino чипы имеют три порты::

Читайте также:  Rgb-светодиоды: адресуемая светодиодная лента - arduino+

B (цифровые выводы от 8 до 13)
C (аналоговые входы)
D (цифровые выводы от 0 до 7)

Каждый порт контроллируется тремя регистрами, каждый из которых отвечает за определенное состояние. Регистр DDR определяет, какой вывод входной, а какой выходной (INPUT OUTPUT). Регистр PORT устанавливает вывод в соответствующее состояние HIGH или LOW, и регистр PIN читает состояние входного выводаr.

Регистры DDR и PORT могут быть как прочитаны, так и записаны. Регистр PIN отвечает за состояние входынх портов, поэтому может быть лишь прочитан.


PORTD отвечает за выводы 0 — 7

DDRD — Регистр направления порта D -чтение/запись PORTD — Регистр данных порта D — чтение/записьPIND — Регистр входных данных порта D — чтение/запись

PORTB отвечает за выводы 8 — 13 Два старших бита (6 и 7), отвечающие за выводы кварца незадействованы.

DDRB — Регистр направления порта B -чтение/запись PORTB — Регистр данных порта B — чтение/записьPINB — Регистр входных данных порта B — чтение/запись

PORTC отвечает за аналоговые выводы 0 — 5. Выводы 6 & 7 доступны только на Arduino Mini

DDRC — Регистр направления порта C -чтение/запись PORTC — Регистр данных порта C — чтение/записьPINC — Регистр входных данных порта C — чтение/запись

Каждый бит в этих регистрах отвечает за соответствующий вывод, т.е. младший бит у DDRB, PORTB, и PINB ссылается на вывод PB0 (цифровой вывод 8). Для полного соответствия регистровых битов выводам МК посмотрите назначение выводов вашего МК. 

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

DDRD это регистр направления выводов для порта D (Arduino цифровые выводы 0-7). Биты этого регистра контролируют, как вывод регистра PORTD будет сконфигурирован — input или output, например: 

DDRD = B11111110; // устанавливает выводы от 1 до 7 как выходные, вывод 0 — входной//Ознакомьтесь с битовыми операциямиPORTB это регистр для установки выходных портов.;PORTD = B10101000; // устанавливает цифровые выводы 7,5,3 HIGH

Вы сможете видеть только 5В на выводе всегда, не вжно, использовали ли вы регистр DDRD или функцию pinMode ().

PINB это регистр состояния входных выводов. Он может прочесть одновременно прочесть состояние всех выводов порта.Конечно, с одной стороны данный мометод работы с портами имеет ряд мутных недостатков:Программный код становится более сложным для поиска ошибок и менее понятным для остальных людей. Эти операции выполняются за считанные такты микропроцессора, однако вам может потребоваться несколько часов, чтобы найти, что рабоотает неправильно и исправить это!.

Код становится менее переносимым — сказывается привязка к конкретному микроконтроллеру. Используя функции digitalRead () и digitalWrite (), очень просто перенести код на другой микроконтроллер. Одним из способов решения могут стать макроопределения #ifdef, с помощью которых возможно написание универсального кода, который, в зависимости от МК, будет сам решать — как ему компилироваться.

При прямом доступе к портам можно совершить ошибки. Например,строка DDRD = B11111110; оставит вывод 0 как входной. Мы помним, что на нем у нас висит приемная линия последовательного порта. Вы представляете что может случиться если мы возьмем и сделаем его как выходной? Мы нарушим работу нашего порта.

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

Вы можете изменить состояние сразу всех портов одновременно, не прибегая ко всяким изощренным циклам и проч. Если вы посмотрите на исходники Arduino в lib/targets/arduino/wiring.

c, вы увидите, что digitalRead () и digitalWrite () представляют собой кучу строк кода, которые буду скомпилированы в несколько команд процессора. Каждая команда процессора требует один такт тактового генератора 16Мгц.

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

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

Манипуляции с портами, Описание языка Arduino

06.04.2011, 16705 просмотров.

Источник: http://arduino.net.ua/Arduino_articles/Opisanie%20jazyka%20Arduino%20na%20russkom%20jazyke/Manipuljacii%20s%20portami/

Arduino IDE : Работа с Serial Monitor

Самой популярной средой разработки для Arduino является официальная программа от итальянских разработчиков — Arduino IDE.

Можно конечно использовать и другие программы, например, имеющая обширный функционал, Atmel Studio и другие.

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

Скачать Arduino IDE можно на официальном сайте Arduino, в разделе программное обеспечение. Вы можете скачать любую версию, как самую новую, так и самую старую. Однако, советую также использовать версию 1.6.5 или 1.6.9, поскольку они является одними из самых стабильных и реже выдают ошибки при компиляции, казалось бы, правильно написанного кода.

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

На верхней панели расположены несколько разделов: файл, инструменты и так далее.

С помощью раздела “Файл” мы можем управлять нашими файлами и скетчами: создавать, открывать, сохранять и распечатать, то есть самые стандартные функции в подобного рода программах. Особое внимание стоит уделить здесь нескольким подразделам.

Функция “Открыть недавние” достаточна полезная, так как поможет вам открыть программные кода, с которыми вы ранее работали, и не придется тратить лишнее время на их поиски.

В подразделе “Примеры” содержится огромное множество уже готовые программных кодов (скетчей), взятых на разные направления: механика, мигание светодиодом, циклы, работа с дисплеями и аналоговыми выходами и так далее.

При установке новых библиотек в Arduino (для различных модулей и датчиков) число примеров увеличится. Также, эти коды содержат подробные пояснения, и поэтому данный раздел будет очень полезен начинающим, программировать на платформе Ардуино. Еще есть подраздел “Настройки”.

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

Раздел “Правка” служит для редактирования текста, кода и пояснений для него.

Раздел “Скетч” позволит выполнить различные действия с вашим кодом, например, проверить его (отправить на компиляцию) или загрузить в память вашего устройства.

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

И также имеется раздел “Помощь”, который, вроде как, должен решать возникающие проблемы при работе с Arduino IDE.

Остановимся подробнее на разделе “Инструменты”. Когда вы подключаете плату к компьютеру, то в этом разделе нужно настроить все необходимые параметры: выбрать в списке предложенных вашу плату, указан номер порта, к которому она подключается (посмотреть номер можно в диспетчере устройств в панели управления компьютера).

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

В данной статье хотелось бы затронуть возможность открытия в программе специального дополнительного окна, на котором будут выводиться значения сигналов и различные данные с датчиков и модулей, подключенных к Ардуино. Называется эта функция Serial Monitor (в русскоязычной версии – монитор порта). Ее, также, можно открыть с помощью сочетания клавиш Ctrl+Shift+M

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

Есть также поле для получения данных, а внизу справа можно менять скорость последовательного соединения (измеряется в битах и или в бод на секунду). Чаще всего используется значение в 9600.

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

Чаще всего для работы с этим окном, используют две функции — Serial.print () и Serial.read(). Первая позволяет отправлять данные через порт последовательного соединения, а вторая – принимать их.

Приведем два кода для работы с монитором порта в Arduino IDE

Управление светодиодом с помощью монитора порта. Суть кода такова, что если мы введем цифру 1 в поле ввода и нажнем “отправить”, то светодиод загорится, если введем 0, то погаснет.

void setup() { Serial.begin(9600); // устанавливаем скорость передачи данных последовательного соединения pinMode(13, OUTPUT); // указываем номер контакта на плате, к которому подключен светодиод (13 — это встроенный светодиод на плате) } void loop() { // Получаем команды на включение-выключение светодиода на 13 порту. String str = Serial.readString(); // Если команда «1» то включаем светодиод. if (str == «1») digitalWrite(13, 1); // Если команда «0» то выключаем светодиод. if (str == «0») digitalWrite(13, 0); // Делаем временную задержку в 1 секунду (1000 миллисекунд) delay(1000); }

Следующий программный код позволит нам передавать данные из Arduino в Монитор порта, например слово «Amperkot»

void setup() { // устанавливаем скорость передачи данных последовательного соединения. Serial.begin(9600); } void loop() { // Отправляем «Amperkot». Serial.print(«Amperkot»); // Отправляем «Amperkot» включая спецсимволы, означающие переход на новую строку. Serial.println(«Amperkot»); // Делаем временную задержку в 1 секунду (1000 миллисекунд). delay(1000); }

Получаем такую картину:

Таким образом, прочитав данную статью, вы поняли основные функции и интерфейс программы Arduino IDE — среды разработки для Arduino и подобных ей плат, а также узнали о мониторе порта, о предназначении функций Serial.print () и Serial.read(), о том как и для чего их используют. Надеемся, что материал был для вас интересен, а главное полезен, и желаем всем удачной компиляции!

Купить плату Arduino Uno

Читать другие статьи по Arduino

Данная статья является собственностью Amperkot.ru. При перепечатке данного материала активная ссылка на первоисточник, не закрытая для индексации поисковыми системами, обязательна.

Источник: https://amperkot.ru/blog/arduino-ide-serial-monitor/

Ссылка на основную публикацию