Ардуинка + инкрементальный энкодер

Онотоле
Offline
Зарегистрирован: 31.03.2013

Добрый вечер уважаемые форумчане!

Пытаюсь подключить икремент оптический энкодер к ардуине уно. Помогите пожалуйста найти ошибку. Я написал простенькую програмку на внешних прерываниях по каналу А и по метке оборотов Z. Когда кручу энкодер медленно, считает импульсы правильно. Начинаю крутить (относительно) быстро начинается расколбас счетчиков.

Вот скетч:



#define encoder0PinA 2                  //вход А  пин 2
#define encoder0PinB 7                  //вход B  пин 7
#define encoder0PinZ 3                  //вход Z  пин 3
volatile long encoder0Pos = 0.0;        //  переменная в обработчике прерываний по подсчету импульсов
volatile long encoder0Rotations = 0.0;  // переменная по подсчету оборотов.
void setup() {
  pinMode(encoder0PinA, INPUT);          
  pinMode(encoder0PinB, INPUT); 
  pinMode(encoder0PinZ, INPUT); 
  
// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, RISING);          //функция обработки внешнего прерывания INT0
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderZ, RISING);        //функция обработки внешнего прерывания INT1
  Serial.begin (9600); 
  Serial.println("START READING");
}

void doEncoderA(){                                    // функция действие вызываемое прерыванием1
  // look for a low-to-high on channel A
  if (digitalRead(encoder0PinA) == HIGH) { 
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == LOW) {  
      encoder0Pos = encoder0Pos + 1;         // по Ч.С.
    } 
    else {
      encoder0Pos = encoder0Pos - 1;         // против Ч.С.
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (digitalRead(encoder0PinB) == HIGH) {   
      encoder0Pos = encoder0Pos + 1;          // по Ч.С.
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // против Ч.С
    }
  }
 
}   

void doEncoderZ()
  {
    if(digitalRead(encoder0PinZ) == HIGH)
      {
      encoder0Pos = 0;
      encoder0Rotations = encoder0Rotations +1;
   //   delay(50);
      }
   }
void loop ()
{
Serial.print("  ");
Serial.print(encoder0Pos);
Serial.print("  ");
Serial.print(encoder0Rotations);
Serial.print(" \n ");
delay(10);
}

 

Онотоле
Offline
Зарегистрирован: 31.03.2013

Работают счетчики правильно если крутить в одну сторону, но я так и кручу.

При плавном кручении в одну сторону счетчик оборотов encoder0Pos идет по возрастающей и при получении метки Z сбрасывается и прибавляет отмеренный оборот. При ускорении счетчик encoder0Pos резко начинает прибавлять -1 вместо +1. и может насчитать даже больше импульсов чем фактическое число импульсов на оборот (3000имп).

энкодер http://www.megachip.ru/pdf/AUTONICS/E30S4.pdf  подключаю его напрямую к ардуине. подсоединяю питание- земля-A-B-Z 

Ps. извиняюсь, вставил предыдущий файл 

эти строчки лишние. отредактировать не найду в каком меню.(для параметра Change в вызове Аттач интерраптс())

30 else // must be a high-to-low edge on channel A
31 {
32 // check channel B to see which way encoder is turning
33 if (digitalRead(encoder0PinB) == HIGH) {
34 encoder0Pos = encoder0Pos + 1; // по Ч.С.
35 }
36 else {
37 encoder0Pos = encoder0Pos - 1; // против Ч.С
38 }
39 }

 

Axell
Offline
Зарегистрирован: 23.03.2013

Привет коллега..:)
Я тоже таким проектом занимаюсь.. Подскажу вот что..По внешним прерываниям (INT) ты(можно на ты?) не сможешь посчитать кол-во импульсов..Тебе надо их заводить на тактирующие входа таймеров. Внешнее прерывание можно использовать на 0-метку, для начала отсчета, а в прерывании уже настроить таймер/счетчик на подсчет входящих импульсов..Я так и сделал..Считает идеально, но один минус -- можно одновременно задействовать только один канал, т.к. на плате Ардуины( у меня Мега R3) разведен только 1 16-разр. счетчик, ост. у меня не задействованы..Но для меня это не проблема -- я потом ост.разведу..
Вот здесь arduino.ru/forum/programmirovanie/preryvaniya-po-taimeru есть приблизительный код моей программы..Если надо, то могу выложить нормальный рабочий скеч, подправишь под свою платку Ардуины и бут все ОК..

Онотоле
Offline
Зарегистрирован: 31.03.2013

Axell пишет:
Привет коллега..:) Я тоже таким проектом занимаюсь.. Подскажу вот что..По внешним прерываниям (INT) ты(можно на ты?) не сможешь посчитать кол-во импульсов..Тебе надо их заводить на тактирующие входа таймеров. Внешнее прерывание можно использовать на 0-метку, для начала отсчета, а в прерывании уже настроить таймер/счетчик на подсчет входящих импульсов..Я так и сделал..Считает идеально, но один минус -- можно одновременно задействовать только один канал, т.к. на плате Ардуины( у меня Мега R3) разведен только 1 16-разр. счетчик, ост. у меня не задействованы..Но для меня это не проблема -- я потом ост.разведу.. Вот здесь arduino.ru/forum/programmirovanie/preryvaniya-po-taimeru есть приблизительный код моей программы..Если надо, то могу выложить нормальный рабочий скеч, подправишь под свою платку Ардуины и бут все ОК..

   Привет - привет! На ты можно.

а почему нельзя по внешним прерываниям? люди же пишут что работает - http://easyelectronics.ru/avr-uchebnyj-kurs-inkrementalnyj-enkoder.html

 от туда скопипастил - "Пунктиром показано предполагаемое положение в произвольный момент. Красные стрелки это фронты по которым сработают прерывания при движении либо в одну, либо в другую сторону.
А в обработчике прерывания INT0 щупаем вторым выводом канал В. И дальше все элементарно! Если там высокий уровень — делаем +1, если низкий -1 нашему счетному регистру. Кода на три строчки, мне даже писать его лень."

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

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

И если можешь, поделись скетчем)

Клапауций
Offline
Зарегистрирован: 10.02.2013

Здесь библиотека для энкодеров лежит, достаточно быстрая(до 127 кгц).

http://www.pjrc.com/teensy/td_libs_Encoder.html

Онотоле
Offline
Зарегистрирован: 31.03.2013

Запустил, но почему-то считает импульсов за оборот в 4 раза больше чем на диске энкодера

Клапауций
Offline
Зарегистрирован: 10.02.2013

Онотоле пишет:

Запустил, но почему-то считает импульсов за оборот в 4 раза больше чем на диске энкодера

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

зы

Вам шашечки или ехать? :D

leshak
Offline
Зарегистрирован: 29.09.2011

Онотоле пишет:

. Красные стрелки это фронты по которым сработают прерывания при движении либо в одну, либо в другую сторону.

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

Онотоле пишет:

И если можешь, поделись скетчем)

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

Используем Энкодер | Аппаратная платформа Arduino

и еще ветки есть.

Axell
Offline
Зарегистрирован: 23.03.2013




#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//Объявляем переменные для счета
volatile int nul;// Кол-во импульсов нульметки
volatile int z_imp;// Кол-во импульсов датчика
volatile int nul_s;// Начало отсчета импульсов датчика

void setup(){
// здесь мы подкл.0-метку на внешнее прерывание INT0
    attachInterrupt(0, inerrupt_0, RISING);    // Разрешить прерывание(pin2) по фронту на подъём
 
    TCCR5A=0x00;//  выставляем
    TCCR5B=0x00;//  регистры
    TCCR5B=0x00;//  маски
    TIMSK5=0x00;//  и  
    TIFR5=0xFF;//  флаги
    TCNT5H=0x00;// для
    TCNT5L=0x00;// TMR5    
    SREG=0x80; // разрешаем глобальные
    nul=0;
    nul_s=0;
    z_imp=0;   
    pinMode(47,INPUT);
    digitalWrite(47, HIGH);   
}


 
void loop(){
  
    if (z_imp >=1 && nul_s==2){  // если поступило начало отсчета для датчика
      lcd.setCursor(0, 1);       // и есть импульсы, то выводим их на экран
      lcd.print("Zi=");          
      lcd.setCursor(3, 1);
      lcd.print(z_imp);}
 
void inerrupt_0(){
     nul++;                      // считаем 0-метки
     nul_s++;                    //
  if (nul==99){nul=1;}   
  if (nul_s==1){TCCR5B=0x07;}   // запускаем внешнее тактирование
  if (nul_s==2){TCCR5B=0x00;}   // после оборота дачика вводим их на экран и запрещаем тактирование  
  if (nul_s==3){ 
     TCNT5H=0x00; // для
     TCNT5L=0x00; // следующего
     TIFR5=0xFF; //  цикла
     nul_s=0;   //  отсчета
}}

Как то так...

У меня МЕГА R3 и все сделано под нее..Пины входов могут не совпадать с другими..

Начало отсчета(0-метку) повесили на внешнее прерывание(INT0)..По приходу события запускаем обработчик, в котором включаем таймер на внешнее тактирование(Т5) и по проходу одного оборота датчика отключаем внешнее тактирование..

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

чот как то сложновата у вас программа.

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

подключил его канал  А на int0(в прогр. PIND2_bit), В на int1(в прогр. PIND3_bit) соответственно. Внешние прерывания настроил на прерывание по фронту.

void INT0_ISR() org IVT_ADDR_INT0{       // на сложение
    if(PIND3_bit)crd--; else crd++;
 }
 void INT1_ISR() org IVT_ADDR_INT1{       // на вычитание
      if(PIND2_bit)crd++; else crd--;
 }
 

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

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

 

Osseum
Offline
Зарегистрирован: 25.04.2013

Может быть, digitalRead() слишком долго работает. Попробуйте заменить на PORTD & (1 << 2), PORTD & (1 << 3), PORTD & (1 << 7)

Онотоле
Offline
Зарегистрирован: 31.03.2013

Michal пишет:

чот как то сложновата у вас программа.

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

подключил его канал  А на int0(в прогр. PIND2_bit), В на int1(в прогр. PIND3_bit) соответственно. Внешние прерывания настроил на прерывание по фронту.

void INT0_ISR() org IVT_ADDR_INT0{       // на сложение
    if(PIND3_bit)crd--; else crd++;
 }
 void INT1_ISR() org IVT_ADDR_INT1{       // на вычитание
      if(PIND2_bit)crd++; else crd--;
 }
 

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

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

 

 

Ну у меня тоже примерно так же с энкодером, но у вас 90.000 на оборот, а у меня 3000, мб и этого тоже много для ардуинки(у меня уно).

А как определили что нехватало именно производительности ардуины??

И расскажите потом как там с Due дела будут)

ЗЫ. А кстати если датчик на таймер вешать, то мне кажется вероятность пропуска импульса будет гораздо выше чем при ловле их по прерываниям. Если привод (в среднем, не очень быстро) крутить со скоростью 100 об/мин= 1.6об/сек = 144000 имп/сек, то случаем процессорное время не урежется ли для других операций?  

Онотоле
Offline
Зарегистрирован: 31.03.2013

Osseum пишет:

Может быть, digitalRead() слишком долго работает. Попробуйте заменить на PORTD & (1 << 2), PORTD & (1 << 3), PORTD & (1 << 7)

Поясните пожалуйста по подробнее, я в битовых операциях при обращении к определенным регистрам нуб)

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

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

вот например что выдает ассемблер от microC(ниже листинг):

;isr_mstr.c,121 ::         void INT0_ISR() org IVT_ADDR_INT0{       // на сложение
;isr_mstr.c,122 ::         if(PIND3_bit)crd--; else crd++;
0x07BA    0xB3B0        IN         R27, PIND3_bit+0
0x07BC    0xFFB3        SBRS       R27, 3
0x07BE    0xC005        RJMP       L_INT0_ISR44
0x07C0    0xE0B1        LDI        R27, 1
0x07C2    0x1A6B        SUB        crd+0, R27
0x07C4    0xE0B0        LDI        R27, 0
0x07C6    0x0A7B        SBC        crd+1, R27
0x07C8    0xC004        RJMP       L_INT0_ISR45
L_INT0_ISR44:
0x07CA    0xEFBF        LDI        R27, 255
0x07CC    0x1A6B        SUB        crd+0, R27
0x07CE    0xEFBF        LDI        R27, 255
0x07D0    0x0A7B        SBC        crd+1, R27
L_INT0_ISR45:
;isr_mstr.c,123 ::         }
L_end_INT0_ISR:
0x07D2    0x91BF        POP        R27
0x07D4    0xBFBF        OUT        SREG, R27
0x07D6    0x91BF        POP        R27
0x07D8    0x91FF        POP        R31
0x07DA    0x91EF        POP        R30
0x07DC    0x9518        RETI
; end of _INT0_ISR

под координату у меня уходило 4 байта, т.е. надо было в обработчике складывать два 4-байтных числа, если усреднить то ето гдето 20 команд=20 циклов(приблизительно) ~ 1.25 мкс( при кварце на 16МГц) на прерывание...т.к. смотрим 2 входа и у меня 90000 имульсов и скорость поворта взять 1об/с, то раз в 1/180000 = 5.6 мкс будут приходить запросы на прерывание, т.е. видите сильно не разгонишся... все время процессора будут съедать ети 2 прерывания.

Вот и пришлось счетчик для датчика делать на тригерах, цеплять к ним мелкую мегу и по SPI скармливать на основной контроллер координату :)

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

для Онотоле:

вот глянь неплохой сайт, много примеров на С разбирают, да и много чего полезного есть

http://easyelectronics.ru/avr-uchebnyj-kurs-programmirovanie-na-si-chast-4.html (здесь про битовые операции расписано)

Axell
Offline
Зарегистрирован: 23.03.2013

 

Я вот не понимаю, зачем Вам эти прерывания сдались..И digitalRead..??
Они пропускают импульсы, особенно на больших оборотах..Тот код, что я привел(внешнее тактирование) работает на датчике 2500 имп/об на скорости ~ 1500-5000 об/мин(больше не проверял) -- считает все исправно..Да мне и не надо такие скоростя, я обхожусь 100-200 об/мин для точной настройки датчика, т.к. которые приходят с завода(т.н. новые) имеют скважность 40-60%. Рекламациями ничего не добились, вот и пришлось сварганить стендик, так сча скважность настраиваем 50%+-1(даже осцилл. сча не надо -- все делается без него)..Да и работать они(и станки) стали заметно стабильнее..

Онотоле
Offline
Зарегистрирован: 31.03.2013

да вот в образовательных целях поставлено задание такое- сделать на прерываниях и чтоб стабильно работало))

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

Axell пишет:

 

Я вот не понимаю, зачем Вам эти прерывания сдались..И digitalRead..??
Они пропускают импульсы, особенно на больших оборотах..Тот код, что я привел(внешнее тактирование) работает на датчике 2500 имп/об на скорости ~ 1500-5000 об/мин(больше не проверял) -- считает все исправно..Да мне и не надо такие скоростя, я обхожусь 100-200 об/мин для точной настройки датчика, т.к. которые приходят с завода(т.н. новые) имеют скважность 40-60%. Рекламациями ничего не добились, вот и пришлось сварганить стендик, так сча скважность настраиваем 50%+-1(даже осцилл. сча не надо -- все делается без него)..Да и работать они(и станки) стали заметно стабильнее..

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

Axell
Offline
Зарегистрирован: 23.03.2013

Michal пишет:

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

Почерпнул инфу вот отсюда..http://arduino.ru/forum/apparatnye-voprosy/ispolzuem-enkoder

Немножко переделал код под свои нужды и все показывает исправно..

Повторюсь, это стенд для проверки и настройки, и мне не важна мах скорость..Мне важна правильная настройка датчика..

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

если вы проверяетет датчик, то вам хватает для етого 1/4 его точности?

кстати кому интересно вот ссылочка про енкодеры

 

Axell
Offline
Зарегистрирован: 23.03.2013

Michal пишет:

если вы проверяетет датчик, то вам хватает для етого 1/4 его точности?

А при чем здесь 1/4...? Я не вел речь об одном канале..Да и датчики у меня без Грея -- для таких у нас другой стенд..:)

Osseum
Offline
Зарегистрирован: 25.04.2013

Регистр PORTD содержит состояния пинов с нулевого по седьмой (PORTD & (1 << BIT) ) равно true, если на пине под номером BIT высокий уровень и faulse если наоборот.

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



#define encoder0PinA 2                  //вход А  пин 2
#define encoder0PinB 7                  //вход B  пин 7
#define encoder0PinZ 3                  //вход Z  пин 3
volatile long encoder0Pos = 0.0;        //  переменная в обработчике прерываний по подсчету импульсов
volatile long encoder0Rotations = 0.0;  // переменная по подсчету оборотов.
void setup() {
  pinMode(encoder0PinA, INPUT);          
  pinMode(encoder0PinB, INPUT); 
  pinMode(encoder0PinZ, INPUT); 
  
// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, RISING);          //функция обработки внешнего прерывания INT0
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderZ, RISING);        //функция обработки внешнего прерывания INT1
  Serial.begin (9600); 
  Serial.println("START READING");
}

void doEncoderA(){                                    // функция действие вызываемое прерыванием1
  // look for a low-to-high on channel A
  if ( PORTD & (1 << 2) ) { 
    // check channel B to see which way encoder is turning
    if (   !(PORTD & (1 << 7 ) ) ) {  
      encoder0Pos = encoder0Pos + 1;         // по Ч.С.
    } 
    else {
      encoder0Pos = encoder0Pos - 1;         // против Ч.С.
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (PORTD & (1 << 7 ) ) {   
      encoder0Pos = encoder0Pos + 1;          // по Ч.С.
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // против Ч.С
    }
  }
 
}   

void doEncoderZ()
  {
    if (PORTD & (1 << 3 ) )
      {
      encoder0Pos = 0;
      encoder0Rotations = encoder0Rotations +1;
   //   delay(50);
      }
   }
void loop ()
{
Serial.print("  ");
Serial.print(encoder0Pos);
Serial.print("  ");
Serial.print(encoder0Rotations);
Serial.print(" \n ");
delay(10);
}

 

maksim
Offline
Зарегистрирован: 12.02.2012

Osseum пишет:

Регистр PORTD содержит состояния пинов с нулевого по седьмой (PORTD & (1 << BIT) ) равно true, если на пине под номером BIT высокий уровень и faulse если наоборот....

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

http://easyelectronics.ru/avr-uchebnyj-kurs-ustrojstvo-i-rabota-portov-vvoda-vyvoda.html

Osseum
Offline
Зарегистрирован: 25.04.2013

Вы правы.



#define encoder0PinA 2                  //вход А  пин 2
#define encoder0PinB 7                  //вход B  пин 7
#define encoder0PinZ 3                  //вход Z  пин 3
volatile long encoder0Pos = 0.0;        //  переменная в обработчике прерываний по подсчету импульсов
volatile long encoder0Rotations = 0.0;  // переменная по подсчету оборотов.
void setup() {
  pinMode(encoder0PinA, INPUT);          
  pinMode(encoder0PinB, INPUT); 
  pinMode(encoder0PinZ, INPUT); 
  
// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, RISING);          //функция обработки внешнего прерывания INT0
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderZ, RISING);        //функция обработки внешнего прерывания INT1
  Serial.begin (9600); 
  Serial.println("START READING");
}

void doEncoderA(){                                    // функция действие вызываемое прерыванием1
  // look for a low-to-high on channel A
  if ( PIND & (1 << 2) ) { 
    // check channel B to see which way encoder is turning
    if (   !(PIND & (1 << 7 ) ) ) {  
      encoder0Pos = encoder0Pos + 1;         // по Ч.С.
    } 
    else {
      encoder0Pos = encoder0Pos - 1;         // против Ч.С.
    }
  }
  else   // must be a high-to-low edge on channel A                                       
  { 
    // check channel B to see which way encoder is turning  
    if (PIND & (1 << 7 ) ) {   
      encoder0Pos = encoder0Pos + 1;          // по Ч.С.
    } 
    else {
      encoder0Pos = encoder0Pos - 1;          // против Ч.С
    }
  }
 
}   

void doEncoderZ()
  {
    if (PIND & (1 << 3 ) )
      {
      encoder0Pos = 0;
      encoder0Rotations = encoder0Rotations +1;
   //   delay(50);
      }
   }
void loop ()
{
Serial.print("  ");
Serial.print(encoder0Pos);
Serial.print("  ");
Serial.print(encoder0Rotations);
Serial.print(" \n ");
delay(10);
}

 

BeletskyAV
Offline
Зарегистрирован: 19.11.2013

Тоже купил энкодер для работы с NetIO . Может кому пригодится код для переделки.

Управление RGB диодом.

/*        _________
 BUTTON2 -|    _    |- BUTTON3
 GND -    |   |_|   |
 BUTTON1 -|_________|- GND
 */
// Подключаем библиотеку Bounce - (устраняет дребезг контактов)
#include <Bounce.h> //Адрес библиотеки http://www.pjrc.com/teensy/td_libs_Bounce.html

// Пины на книпки делайте как удобно Вам : BUTTON(1-6) (номер пина)
// Ротор
#define BUTTON1 8 
#define BUTTON2 7
// Кнопки
#define BUTTON3 2
#define BUTTON4 A0
#define BUTTON5 A1
#define BUTTON6 A2
// Устранение дребезга контактов - (подобраны опытным путём)
int boncR = 1; // Если больше то ошибки при быстром вращении
int boncK = 5; // Если меньше то ошибки при нажатии
Bounce bouncer1 = Bounce( BUTTON1,boncR ); 
Bounce bouncer2 = Bounce( BUTTON2,boncR ); 
Bounce bouncer3 = Bounce( BUTTON3,boncK ); 
Bounce bouncer4 = Bounce( BUTTON4,boncK ); 
Bounce bouncer5 = Bounce( BUTTON5,boncK ); 
Bounce bouncer6 = Bounce( BUTTON6,boncK ); 

long previousMillis = 0;        // храним время последнего опроса
long interval = 100;          // интервал между опросами

int Lamp = 1;           // Центральная кнопка
int Red = 0;
int Green = 0;
int Blue = 0;
int Step = 5;            // Величина шага
int Pin[3]={6,3,5};      // RGB диод (ШИМ выходы на плате)
int Vol[3]={0,0,0};      // Уровень яркости

void setup() {
//  Лень паять 10 кОм на кнопки
  digitalWrite(BUTTON1,HIGH);
  digitalWrite(BUTTON2,HIGH);
  digitalWrite(BUTTON3,HIGH);
  digitalWrite(BUTTON4,HIGH);
  digitalWrite(BUTTON5,HIGH);
  digitalWrite(BUTTON6,HIGH);
// И так понятно светодиод 3 ноги
  for (int i=0; i<3; i++){
    pinMode(Pin[i],OUTPUT);
  }
// Старт сом порта
  Serial.begin(9600);
}

void loop() {
// Уменьшаем яркость ( можно нажимать одну,две или три кнопки)
  if ( bouncer1.update()) {
    if ( bouncer1.read() == LOW & digitalRead(BUTTON2) == HIGH) {
// Кнопки не нажаты - 3 Цвета уменьшаем одновременно
      if (Red == 0 && Green == 0 && Blue == 0) { 
        Vol[0] = Vol[0] - Step;
        Vol[1] = Vol[1] - Step;
        Vol[2] = Vol[2] - Step;
      }
 // Только красный
      if (Red == 1) {
        Vol[0] = Vol[0] - Step;
      }
 // Только зелёный
      if (Green == 1) {
        Vol[1] = Vol[1] - Step;
      }
 // Только синий
      if (Blue == 1) {
        Vol[2] = Vol[2] - Step;
      }
 // Проверка и корекция перехода в минус
      for (int i=0; i<3; i++){
        if (Vol[i] < 0) {
          Vol[i] = 0;
        }
      }
  // Печать для наглядности
      Serial.print("  ");
      Serial.print(Vol[0]);
      Serial.print("  ");
      Serial.print(Vol[1]);
      Serial.print("  ");
      Serial.print(Vol[2]);
      Serial.println("  ");
    }
  }
  // Всё аналогично уменьшению только увеличение
  if ( bouncer2.update()) {
    if ( bouncer2.read() == LOW & digitalRead(BUTTON1) == HIGH) {
      if (Red == 0 && Green == 0 && Blue == 0) {
        Vol[0] = Vol[0] + Step;
        Vol[1] = Vol[1] + Step;
        Vol[2] = Vol[2] + Step;
      }       
      if (Red == 1) {  
        Vol[0] = Vol[0] + Step;
      }
      if (Green == 1) {
        Vol[1] = Vol[1] + Step;
      }
      if (Blue == 1) {
        Vol[2] = Vol[2] + Step;
      }
      for (int i=0; i<3; i++){
        if (Vol[i] > 255) {
          Vol[i] = 255;
        }
      }
      Serial.print("  ");
      Serial.print(Vol[0]);
      Serial.print("  ");
      Serial.print(Vol[1]);
      Serial.print("  ");
      Serial.print(Vol[2]);
      Serial.println("  ");
    }
  }
  // Кнопка включения диода
  if ( bouncer3.update() ) {
    if ( bouncer3.read() == LOW) {
      if ( Lamp == 0 ) {
        Lamp = 1;
      } 
      else {
        Lamp = 0;
      }
      Serial.print("Lamp - ");
      Serial.println(Lamp);
    }
  }
  // Кнопка Red
  if ( bouncer4.update() ) {
    if ( bouncer4.read() == LOW) {
      Red = 1;
    }
    else { 
      Red = 0;
    }
    Serial.print("Red - ");
    Serial.println(Red);
  }
  // Кнопка Green
  if ( bouncer5.update() ) {
    if ( bouncer5.read() == LOW) {
      Green = 1;
    }
    else { 
      Green = 0;
    }
    Serial.print("Green - ");
    Serial.println(Green);
  }
  // Кнопка Blue
  if ( bouncer6.update() ) {
    if ( bouncer6.read() == LOW) {
      Blue = 1;
    }
    else { 
      Blue = 0;
    }
    Serial.print("Blue - ");
    Serial.println(Blue);
  }
  // Проверка центральной кнопи и запись значения каждые 'interval' времени
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis; 
    if ( Lamp == 1) {
      analogWrite(Pin[0], Vol[0]);
      analogWrite(Pin[1], Vol[1]);
      analogWrite(Pin[2], Vol[2]);
    }
    else {
      analogWrite (Pin[0], 0);
      analogWrite (Pin[1], 0);
      analogWrite (Pin[2], 0);
    }
  }
}
  //---------Всё конец и в начало---------

 

sva1509
Offline
Зарегистрирован: 07.12.2012

Доброго времени суток !

Онотоле, попробуйте в вашей первоначальной программе 

attachInterrupt(0, doEncoderA, CHANGE);

второй attach вообще удалите

а оброботчик сделайте так:

void doEncoderA() {
         if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) encoder0Pos++; else encoder0Pos--;
}

И чуть не забыл - тип long целочисленный и устанавливается 0 а не 0.0

 

nkp
Offline
Зарегистрирован: 12.03.2014

Axell пишет:









#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//Объявляем переменные для счета
volatile int nul;// Кол-во импульсов нульметки
volatile int z_imp;// Кол-во импульсов датчика
volatile int nul_s;// Начало отсчета импульсов датчика

void setup(){
// здесь мы подкл.0-метку на внешнее прерывание INT0
    attachInterrupt(0, inerrupt_0, RISING);    // Разрешить прерывание(pin2) по фронту на подъём
 
    TCCR5A=0x00;//  выставляем
    TCCR5B=0x00;//  регистры
    TCCR5B=0x00;//  маски
    TIMSK5=0x00;//  и  
    TIFR5=0xFF;//  флаги
    TCNT5H=0x00;// для
    TCNT5L=0x00;// TMR5    
    SREG=0x80; // разрешаем глобальные
    nul=0;
    nul_s=0;
    z_imp=0;   
    pinMode(47,INPUT);
    digitalWrite(47, HIGH);   
}


 
void loop(){
  
    if (z_imp >=1 && nul_s==2){  // если поступило начало отсчета для датчика
      lcd.setCursor(0, 1);       // и есть импульсы, то выводим их на экран
      lcd.print("Zi=");          
      lcd.setCursor(3, 1);
      lcd.print(z_imp);}
 
void inerrupt_0(){
     nul++;                      // считаем 0-метки
     nul_s++;                    //
  if (nul==99){nul=1;}   
  if (nul_s==1){TCCR5B=0x07;}   // запускаем внешнее тактирование
  if (nul_s==2){TCCR5B=0x00;}   // после оборота дачика вводим их на экран и запрещаем тактирование  
  if (nul_s==3){ 
     TCNT5H=0x00; // для
     TCNT5L=0x00; // следующего
     TIFR5=0xFF; //  цикла
     nul_s=0;   //  отсчета
}}

Как то так...

У меня МЕГА R3 и все сделано под нее..Пины входов могут не совпадать с другими..

Начало отсчета(0-метку) повесили на внешнее прерывание(INT0)..По приходу события запускаем обработчик, в котором включаем таймер на внешнее тактирование(Т5) и по проходу одного оборота датчика отключаем внешнее тактирование..

объясните ,пожалуйста, начинающему)) - как проиходит отображение результата в коде(lcd.print(z_imp))?

если я правильно "нарыл" - то результат счетчика - в регистрах TCNT5L,TCNT5H 

???