Непонятки с энкодером

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Собрал схему. Оптический датчик положения от принтера с родным диском. Подключено к ардуино нано.
Код реализован через прерывания. Все считает. Но при слишком большой скорости вращения просто виснет. Это первое. Второе, при попытке рулить двигателем (просто изменить направление вращения), перестает считать.
Куда копать ?

Kakmyc
Offline
Зарегистрирован: 15.01.2018
/*
old_encoder - предыдущее значение каналов энкодера
new_encoder - новое значение каналов энкодера
*/
byte old_encoder, new_encoder;
/*
Энкодер с двумя каналами при вращении будет выдавать коды Грея, например:
00 (0)
10 (2)
11 (3)
01 (1)
Ячейки массива ns содержат значения следующего кода для заданного индекса массива.
Например если текущее значение каналов энкодера равно 00, то в ячейке массива ns[0]
содержится следующее значение равное 2 (в бинарном виде - 10).
*/
byte ns[4] = {2,0,3,1};
/*
counter - текущее значение счетчика энкодера
*/
long counter=0;
boolean Dir=0;
/*
tmp - предыдущее значение счетчика
*/
long tmp;

void setup()
{
  //start serial connection
  Serial.begin(115200);

  pinMode(2, INPUT);
  pinMode(3, INPUT);
pinMode(6,OUTPUT);
pinMode(8,OUTPUT);
// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderAB, CHANGE);
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderAB, CHANGE);
  counter = 0;
  tmp = 0;
// присваиваем текущие значения каналов энкодера
  old_encoder = PIND>>2;
  new_encoder = old_encoder;
}

void doEncoderAB()
{
  noInterrupts();
  new_encoder = PIND>>2;
  if (ns[old_encoder] == new_encoder)
  {
    counter++; 
  }
  else if (ns[new_encoder] == old_encoder)
  {
     counter--;
  }
  old_encoder = new_encoder;
  interrupts();
}

void loop()
{
  if (tmp != counter)
  {
    Serial.print(counter);
    Serial.print(' ');
    Serial.println(new_encoder);
    tmp = counter;

  }
	if(counter>=100000){!Dir;};	digitalWrite(6,!Dir);digitalWrite(8,Dir);
}

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Со строкой 73 отказывается работать.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Kakmyc пишет:

Со строкой 73 отказывается работать.

А что эта строка по-Вашему делает? По-моему, её первая часть "if(counter>=100000){!Dir;};" не деалет ничего, а Вы как считаете?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А по сути, у Вас коды расписаня неправильно. Так неоднозначно получается. Нужно учитывать не только текущее положение, но и предыдущее. Нарисуйте сдвинутые по фазе меандры энкодера и внимательно по ним пройдите.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

ЕвгенийП пишет:

Kakmyc пишет:

Со строкой 73 отказывается работать.

А что эта строка по-Вашему делает? По-моему, её первая часть "if(counter>=100000){!Dir;};" не деалет ничего, а Вы как считаете?

Я в курсе, что ничего не делает.

Только вот работать перестает. Изначально в коде писал просто

if(counter>=10000){digitalWrite(6,1);digitalWrite(8,0);}

И точно так же код переставал работать.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

ЕвгенийП пишет:

А по сути, у Вас коды расписаня неправильно. Так неоднозначно получается. Нужно учитывать не только текущее положение, но и предыдущее. Нарисуйте сдвинутые по фазе меандры энкодера и внимательно по ним пройдите.

 

Вот же учитываем и старое и новое значение, потом обновляем

void doEncoderAB()
48
{
49
  noInterrupts();
50
  new_encoder = PIND>>2;
51
  if (ns[old_encoder] == new_encoder)
52
  {
53
    counter++; 
54
  }
55
  else if (ns[new_encoder] == old_encoder)
56
  {
57
     counter--;
58
  }
59
  old_encoder = new_encoder;
60
  interrupts();
61
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, для начала Вы берете из порта 6 битов вместо двух ("new_encoder = PIND>>2;"). Сдвинули Вы на два и взяли оставшиеся шесть битов. 

А потом, что Вы учитываете? Я же Вам говорю, нарисуйте картинку и внимательно на неё посмотрите - все варианты рассмотрите.

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

Ну, или возьмите готовую библиотеку энкодера и не парьтесь.

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Да, действительно какая то херь переодически приходит с пинов значение бывает 15.

Подскажите как правильно прописать чтение двух первых битов с порта D ? Или все таки последних ?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Я не знаю какие именно порты Вы собираетесь читать. Но общее правило (допустим читаем порты 1 и 2):

1. считываем весь регистр: uint8_t pb = PORTB;
2. обнуляем ненужные биты: pb &= ~6;
3. сдвигаем вправо, pb >>= 1;

Пример, для битов 1 и 2, отсюда маска 6 и сдвиг на 1.

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Kakmyc пишет:

Да, действительно какая то херь переодически приходит с пинов значение бывает 15.

Подскажите как правильно прописать чтение двух первых битов с порта D ? Или все таки последних ?

 new_encoder = PIND  & 0x0C; // new_encoder = PIND 2;3; 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Мне надо считать порты прерываний PD2 и  PD3.

Что бы получилось двухбитное число, которое и будем сравнивать с прошлым таким числом.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vosara пишет:

 new_encoder = PIND  & 0x0C; // new_encoder = PIND 2;3; 

Ему нужно, чтобы значение было от 0 до 3, так что ещё сдвинуть надо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Kakmyc пишет:

Мне надо считать порты прерываний PD2 и  PD3.

Боюсь Вы неверно пjнимаете, что Вам на самом деле нужно.

Вам нужно обрабатывать прерывания, а не читать значения их пинов в loop.

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

Вы же пытаетесь читать в loop - когда очередь подойдёт.

для обработки прерываний используйте attachInterrupt.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

У меня в loop вообще только вывод на монитор значений.  Все остальное через прерывания и функции

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Попробуйте - прерываниена 2 пине. Енкодер подключен на 2й и 3й пин

int val = 0;
int val_temp;

void setup() {
  Serial.begin (9600);
  attachInterrupt(0, encoder, FALLING);
}

void loop() {
  if (val != val_temp) {
    val_temp = val;
    Serial.println(val);
  }
}

void encoder() {
  byte n = PIND  & 0x08;
  if (n) val++;
  else val--;
}

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

vosara пишет:

Попробуйте - прерываниена 2 пине. Енкодер подключен на 2й и 3й пин

int val = 0;
int val_temp;

void setup() {
  Serial.begin (9600);
  attachInterrupt(0, encoder, FALLING);
}

void loop() {
  if (val != val_temp) {
    val_temp = val;
    Serial.println(val);
  }
}

void encoder() {
  byte n = PIND  & 0x08;
  if (n) val++;
  else val--;
}

 

Вообще не вариант.  В одном случае пропускает первое изменение положения.

Точнее даже не в одном, а в трёх случаях из четырех.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

С битами не работал пока.  Пытаюсь постичь эту науку. Толком нигде не разжевано. Читаю дальше.

Пока понял, что мне надо при чтении с порта D 

PIND отбросить два старших бита , а остальное сдвинуть на 4 бита вправо.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Упс, выводы начинаются с младших битов. Значит мне надо отбросить 4 старших и сдвинуть вправо  на два бита.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

О великие гуру, подскажите я не ошибусь если сделаю так ?

new_encoder=(PIND&~4)>>2;

 

Т.е. мне надо считать значения входов PD2 PD3, я отбрасываю PD4 и выше, и сдвигаю то, что осталось.

 

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Ну я хотя бы уже понял, почему у меня работало, при незадействованных остальных пинах. :-)

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015
new_encoder = (PIND>>2)&0x03;

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Penni пишет:

new_encoder = (PIND>>2)&0x03;

 

Если это и правильно, то понимания не даёт. На всякий случай спасибо.
0x03 это же 3 ? Зачем тогда столько сложностей ?

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Да 0х03 это 3. А в чем сложность? Сдвинули, наложили маску. Ну сделайте не сложно.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Penni пишет:

Да 0х03 это 3. А в чем сложность? Сдвинули, наложили маску. Ну сделайте не сложно.

Сложность в записи, 0x03 заместо 3

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Аааа вон оно что :) Да привычка просто. Хотел вообще 0b00000011 написать да подумал что сложно )

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Вроде разобрался. Подскажите
new_encoder=(PIND&12)>>2;
Будет иметь точно такой же результат, что и
new_encoder = (PIND>>2)&0x03; ?

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Да, тоже самое

SLOM
Offline
Зарегистрирован: 06.11.2014

Kakmyc  если у вас получилось покажите пожалуйста рабочий скетч, тоже ковыряю энкодер от старого принтера, все никак ненайду пример рабочего кода :(

Kakmyc
Offline
Зарегистрирован: 15.01.2018
/*
old_encoder - предыдущее значение каналов энкодера
new_encoder - новое значение каналов энкодера
*/
byte old_encoder, new_encoder;
/*
Энкодер с двумя каналами при вращении будет выдавать коды Грея, например:
00 (0)
10 (2)
11 (3)
01 (1)
Ячейки массива ns содержат значения следующего кода для заданного индекса массива.
Например если текущее значение каналов энкодера равно 00, то в ячейке массива ns[0]
содержится следующее значение равное 2 (в бинарном виде - 10).
*/
byte ns[4] = {2,0,3,1};
/*
counter - текущее значение счетчика энкодера
*/
long counter=0;
/*
tmp - предыдущее значение счетчика
*/
long tmp;

void setup()
{
  //start serial connection
  Serial.begin(9600);

  pinMode(2, INPUT);
  pinMode(3, INPUT);
// один выход энкодера к прерыванию 0 (pin 2)
  attachInterrupt(0, doEncoderAB, CHANGE);
// второй выход энкодера к прерыванию 1 (pin 3)
  attachInterrupt(1, doEncoderAB, CHANGE);
  counter = 0;
  tmp = 0;
// присваиваем текущие значения каналов энкодера
  old_encoder = (PIND&12)>>2;//считываем значения энкодера при старте
  new_encoder = old_encoder;
}

void doEncoderAB()
{
  noInterrupts();//отключаем прерывания на время проверки 
  new_encoder = (PIND&12)>>2;//записываем в переменную новое состояние енкодера
  if (ns[old_encoder] == new_encoder)/*сравниваем с предыдущим и либо инкрементируем положение если состояние пинов энкодера соответствует таблице положений , указанной в массиве*/
  {
    counter++; 
  }
  else if (ns[new_encoder] == old_encoder)/*либо декрементируем, если не соответствует*/
  {
     counter--;
  }
  old_encoder = new_encoder;//обновляем строе значение
  interrupts();//включаем прерывание
}

void loop()
{
  if (tmp != counter)//если были изменения выводим положение в монитор вместе с текущим состоянием энкодера
  {
    Serial.print(counter);
    Serial.print(' ');
    Serial.println(new_encoder);
    tmp = counter;
		

  }
	}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

SLOM, а что, с библиотекой Encoder что-то не устраивает?

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Ну меня в ней не устроило описание, там такое нагорожено, код нечитабелен.

Это я про встроенную.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А-а-а, ну тогда ладно.

SLOM
Offline
Зарегистрирован: 06.11.2014

Kakmyc

спасибо, буду вникать. 

ЕвгенийП  да дело в том что я полохо разбираюсь в ардуино и незнал что имеет значение на какие выводы подключать датчик.  оказхывается у ардуино на пинах 2,3 есть какойто прерыватель и там все считается четко. и быстро, чуток кручу энкодер и буквально каждую риску считает, без промаха от 0 до 9500 примерно, за 1 оборот энкодера. (0 1 2 3 4 5 6...9800....)

а вот когда я подключал на контакты 5,6  это был пипец, за один оборот считалось около 100 рисок и шел постоянный промах, числа повторялись (0 1 0 1 2 1 2 3...... )

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

такого плана 

фото не мое, пример из интернета, но энкодер похож. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

SLOM пишет:
оказхывается у ардуино на пинах 2,3 есть какойто прерыватель и там все считается четко.

Всё зависит от программы. Прерывания у Ардуины есть на всех пинах, просто те (не на 2-ом и 3-ем) надо по-другому использовать. Если их использовать, то можно вешать на любые пины.

SLOM
Offline
Зарегистрирован: 06.11.2014

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

/*
  HG7881_Motor_Driver_Example - Arduino sketch
   
  This example shows how to drive a motor with using HG7881 (L9110) Dual
  Channel Motor Driver Module.  For simplicity, this example shows how to
  drive a single motor.  Both channels work the same way.
   
  This example is meant to illustrate how to operate the motor driver
  and is not intended to be elegant, efficient or useful.
   
  Connections:
   
    Arduino digital output D10 to motor driver input B-IA.
    Arduino digital output D11 to motor driver input B-IB.
    Motor driver VCC to operating voltage 5V.
    Motor driver GND to common ground.
    Motor driver MOTOR B screw terminals to a small motor.
 
*/


/* Encoder Library - Basic Example
 * http://www.pjrc.com/teensy/td_libs_Encoder.html
 *
 * This example code is in the public domain.
 */

#include <Encoder.h>

// Change these two numbers to the pins connected to your encoder.
//   Best Performance: both pins have interrupt capability
//   Good Performance: only the first pin has interrupt capability
//   Low Performance:  neither pin has interrupt capability

Encoder myEnc(2, 3);
//   avoid using pins with LEDs attached


 
// wired connections
#define HG7881_B_IA 5 // D10 --> Motor B Input A --> MOTOR B +
#define HG7881_B_IB 6 // D11 --> Motor B Input B --> MOTOR B -
 
// functional connections
#define MOTOR_B_PWM HG7881_B_IA // Motor B PWM Speed
#define MOTOR_B_DIR HG7881_B_IB // Motor B Direction
 
// the actual values for "fast" and "slow" depend on the motor
#define PWM_SLOW 100  // arbitrary slow speed PWM duty cycle
#define PWM_FAST 200 // arbitrary fast speed PWM duty cycle
#define DIR_DELAY 1000 // brief delay for abrupt motor changes
 
void setup()
{
  Serial.begin( 9600 );
  pinMode( MOTOR_B_DIR, OUTPUT );
  pinMode( MOTOR_B_PWM, OUTPUT );
  digitalWrite( MOTOR_B_DIR, LOW );
  digitalWrite( MOTOR_B_PWM, LOW );

  Serial.println("Basic Encoder Test:");
}

long oldPosition  = -999;

 
void loop()
{
  boolean isValidInput;
  // draw a menu on the serial port
  Serial.println( "-----------------------------" );
  Serial.println( "MENU:" );
  Serial.println( "1) Fast forward" );
  Serial.println( "2) Forward" );
  Serial.println( "3) Soft stop (coast)" );
  Serial.println( "4) Reverse" );
  Serial.println( "5) Fast reverse" );
  Serial.println( "6) Hard stop (brake)" );
  Serial.println( "-----------------------------" );
  do
  {
    byte c;
    // get the next character from the serial port
    Serial.print( "?" );
    while( !Serial.available() )
      ; // LOOP...
    c = Serial.read();
    // execute the menu option based on the character recieved
    switch( c )
    {
      case '1': // 1) Fast forward
        Serial.println( "Fast forward..." );
        // always stop motors briefly before abrupt changes
        digitalWrite( MOTOR_B_DIR, LOW );
        digitalWrite( MOTOR_B_PWM, LOW );
        delay( DIR_DELAY );
        // set the motor speed and direction
        digitalWrite( MOTOR_B_DIR, HIGH ); // direction = forward
        analogWrite( MOTOR_B_PWM, 255-PWM_FAST ); // PWM speed = fast
        isValidInput = true;
        break;      
         
      case '2': // 2) Forward      
        Serial.println( "Forward..." );
        // always stop motors briefly before abrupt changes
        digitalWrite( MOTOR_B_DIR, LOW );
        digitalWrite( MOTOR_B_PWM, LOW );
        delay( DIR_DELAY );
        // set the motor speed and direction
        digitalWrite( MOTOR_B_DIR, HIGH ); // direction = forward
        analogWrite( MOTOR_B_PWM, 255-PWM_SLOW ); // PWM speed = slow
        isValidInput = true;
        break;      
         
      case '3': // 3) Soft stop (preferred)
        Serial.println( "Soft stop (coast)..." );
        digitalWrite( MOTOR_B_DIR, LOW );
        digitalWrite( MOTOR_B_PWM, LOW );
        isValidInput = true;
        break;      
 
      case '4': // 4) Reverse
        Serial.println( "Fast forward..." );
        // always stop motors briefly before abrupt changes
        digitalWrite( MOTOR_B_DIR, LOW );
        digitalWrite( MOTOR_B_PWM, LOW );
        delay( DIR_DELAY );
        // set the motor speed and direction
        digitalWrite( MOTOR_B_DIR, LOW ); // direction = reverse
        analogWrite( MOTOR_B_PWM, PWM_SLOW ); // PWM speed = slow
        isValidInput = true;
        break;      
         
      case '5': // 5) Fast reverse
        Serial.println( "Fast forward..." );
        // always stop motors briefly before abrupt changes
        digitalWrite( MOTOR_B_DIR, LOW );
        digitalWrite( MOTOR_B_PWM, LOW );
        delay( DIR_DELAY );
        // set the motor speed and direction
        digitalWrite( MOTOR_B_DIR, LOW ); // direction = reverse      
        analogWrite( MOTOR_B_PWM, PWM_FAST ); // PWM speed = fast
        isValidInput = true;
        break;
         
      case '6': // 6) Hard stop (use with caution)
        Serial.println( "Hard stop (brake)..." );
        digitalWrite( MOTOR_B_DIR, HIGH );
        digitalWrite( MOTOR_B_PWM, HIGH );
        isValidInput = true;
        break;      

        
      default:
        // wrong character! display the menu again!
        isValidInput = false;
        break;

 }
long newPosition = myEnc.read();
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    Serial.println(newPosition);


        if (newPosition = 30000){
     digitalWrite( MOTOR_B_DIR, LOW );
        digitalWrite( MOTOR_B_PWM, LOW );
}                              
        
    }

  } while( isValidInput == true );
 
  // repeat the main loop and redraw the menu...

  }

задумка была такая что включив тест мотор крутит до тех пор пока энкодер ненасчитает 30 000, но минуло 80 000 а он и не думал останавливатся. 

а вообще пытаюсь добится результата, при котором можно написать чтото типа таблицы со значениями по которым долен работать покрутил до значения 20 000 потом остановился на время задержки, потом покрутил до -10 000 и снова остановился, потом до 40 000 и тд.... 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Нет, это так не делается.

1.
Сравнение в строке 166 может не сработать никогда. newPosition на одном шаге может быть 29999, а на следующеи уже успело тикнуть два раза и стало 30001 - всё, Вы пропустили и движок крутится дальше.
Вообще, это сравнение лучше оставить в обработчике прерывания, тогда оно будет проверять точно когда сменилось значение, а не "когда придётся", как это сделано сейчас.

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

SLOM
Offline
Зарегистрирован: 06.11.2014

ЕвгенийП а у вас нет таких примеров? 

 

сделал так 

 if (newPosition >= 30000){
     digitalWrite( MOTOR_B_DIR, LOW );
        digitalWrite( MOTOR_B_PWM, LOW ); 
 
тоже несрабатывает
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

а что печатает в строке 163?

И вообще, давайте сюда протокол.

SLOM
Offline
Зарегистрирован: 06.11.2014

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

происходит это так: 

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

Basic Encoder Test:
-----------------------------
MENU:
1) Fast forward
2) Forward
3) Soft stop (coast)
4) Reverse
5) Fast reverse
6) Hard stop (brake)
-----------------------------
?

далее жму 1 и энтер. получаю:

Basic Encoder Test:
-----------------------------
MENU:
1) Fast forward
2) Forward
3) Soft stop (coast)
4) Reverse
5) Fast reverse
6) Hard stop (brake)
-----------------------------
?Fast forward...
0
?

мотор крутит и ничего в мониторе не происходит, жму 3 + энтер и получаю: 

Basic Encoder Test:
-----------------------------
MENU:
1) Fast forward
2) Forward
3) Soft stop (coast)
4) Reverse
5) Fast reverse
6) Hard stop (brake)
-----------------------------
?Fast forward...
0
?Soft stop (coast)...
501862
?

вот както так этот код и работает. 

а у меня есть сканер от принтера. тоже кэнон и судя по фото внутрянка сканера идентична.

вот потипу такого 

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

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

SLOM
Offline
Зарегистрирован: 06.11.2014

нашел интересную ссылку 

http://homofaciens.de/technics-base-circuits-encoder-disc_en.htm

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

так вот там есть ссылка на архив в котором есть скетч printer_motor :


//Printer motor
//Source & info at: www.HomoFaciens.de/technics-base-circuits-encoder-disc_en_navion.htm

#include <util/delay.h>

int stepStatus = 0;
//int stepStatusOld = 0;
byte sensorStatusA = 0;
byte oldSensorStatusA = 0;
byte sensorStatusB = 0;
int dutyCycle = 0;
signed long setPoint = 0;
signed long actualPoint = 0;
byte readByte = 0;

#define SENSOR_A 2
#define SENSOR_B 3

#define MOTOR_DIRECTION 5
#define MOTOR_PWM 6


#define P_FRACTION 1.0     //Proportional factor of control loop 0.001 - 10.0 (1.0)
#define STEP_MARGIN 4L     //10 - 1000 (1)

#define MIN_DUTYCYCLE 150   //0 - 255 (125)
#define MAX_DUTYCYCLE 250  //0 - 255 (255)



void establishContact() {
  while (Serial.available() <= 0) {
    Serial.print('X');     // send a capital X to indicate that Arduino is running
    delay(300);
  }
}

void change_step(){
  sensorStatusA = digitalRead(SENSOR_A);

   if ((oldSensorStatusA == LOW) && (sensorStatusA == HIGH)) {
     if (sensorStatusB == 0) {
       actualPoint++;
     }
     else{
       actualPoint--;
     }
   } 
   oldSensorStatusA = sensorStatusA;
}

void change_sensor_A(){
  change_step();
}

void change_sensor_B(){
  sensorStatusB = digitalRead(SENSOR_B);
}


void setup() {
  
  digitalWrite(MOTOR_DIRECTION, 0);
  analogWrite(MOTOR_PWM, MAX_DUTYCYCLE);
  delay(1000);
  analogWrite(MOTOR_PWM, 0);
  delay(100);
  
  attachInterrupt(0, change_sensor_A, CHANGE);
  attachInterrupt(1, change_sensor_B, CHANGE);
  
  sensorStatusA = digitalRead(SENSOR_A);
  sensorStatusB = digitalRead(SENSOR_B);
  
  pinMode(MOTOR_DIRECTION, OUTPUT);  
  pinMode(MOTOR_PWM, OUTPUT);    
  

  // start serial port at 115200 bps:
  Serial.begin(115200);
  establishContact();  // send a byte to establish contact until receiver responds 
}

void loop() {    

      dutyCycle = (double)(abs(setPoint - actualPoint)) * (double)P_FRACTION;

      if(dutyCycle < MIN_DUTYCYCLE){
        dutyCycle = MIN_DUTYCYCLE;
      }
      if(dutyCycle > MAX_DUTYCYCLE){
        dutyCycle = MAX_DUTYCYCLE;
      }
      
      if(dutyCycle < MIN_DUTYCYCLE){
        dutyCycle = MIN_DUTYCYCLE;
      }
      if(dutyCycle > MAX_DUTYCYCLE){
        dutyCycle = MAX_DUTYCYCLE;
      }
      if(abs(setPoint - actualPoint) < STEP_MARGIN){
        analogWrite(MOTOR_PWM, 0);
        digitalWrite(MOTOR_DIRECTION, 0);
      }
      else{
        if(actualPoint < setPoint){
          digitalWrite(MOTOR_DIRECTION, 1);
          analogWrite(MOTOR_PWM, 255 - dutyCycle);
        }
        if(actualPoint > setPoint){
          digitalWrite(MOTOR_DIRECTION, 0);
          analogWrite(MOTOR_PWM, dutyCycle);
        }
      }
          
  
  
  readByte = 0;
  if (Serial.available() > 0){//if we get a valid byte, read from serial:
    //get incoming byte:
    readByte = Serial.read();
    Serial.print('r');   // send a 'r' to initiate next data from computer
  
    if(readByte > 0){
      if(readByte == 20){
        setPoint++;
      }
      if(readByte == 19){
        setPoint--;
      }
      if(readByte == 18){
        setPoint+=2875;
      }
      if(readByte == 17){
        setPoint-=2875;
      }
      if(readByte == 16){
        setPoint+=100;
      }
      if(readByte == 15){
        setPoint-=100;
      }
      if(readByte == 14){
        setPoint+=10;
      }
      if(readByte == 13){
        setPoint-=10;
      }
    }//if(readByte > 0){

  }//while (Serial.available() > 0){
   
}

поидее, как я понял из этого

 if(readByte > 0){
      if(readByte == 20){
        setPoint++;
      }
      if(readByte == 19){
        setPoint--;
      }
      if(readByte == 18){
        setPoint+=2875;
      }
      if(readByte == 17){
        setPoint-=2875;
      }
      if(readByte == 16){
        setPoint+=100;
      }
      if(readByte == 15){
        setPoint-=100;
      }
      if(readByte == 14){
        setPoint+=10;
      }
      if(readByte == 13){
        setPoint-=10;

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

но оно работает както не так. 

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, так понятно. у Вас же блокировка стоит в строке 85.

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

Удаляйте блокировку и делайте аккуратную проверку - пришло что-нибудь - читаем, нет - идём контролировать двигатель.

SLOM
Offline
Зарегистрирован: 06.11.2014

в каком именно коде? что именно удалить и что вместо него поставить? 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы что, этот код не сами писали? 

SLOM
Offline
Зарегистрирован: 06.11.2014

увы сам неумею, все что смог найти в инете, пытаюсь по возможности переделать. 

SLOM
Offline
Зарегистрирован: 06.11.2014

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

неужели никто их неиспользует. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Причём тут вообще энкодер? Вы стали в мёртвом цикле в строке 85 и ничего не делаете, наплевав на то, что мотор у Вас крутитс и за ним надо следить. Вам нужно убрать этот мёртвый цикл, а поставить аккуратную, неблокирующую проверку пришло что-то или нет. Делайте, пытайтесь, учитесь.

SLOM
Offline
Зарегистрирован: 06.11.2014

были бы примеры былоб легче. 

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

 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Я планировал заняться парой энкодер/двигатель. Но к сожалению спалил датчик(как оказалось датчики все разные и перед подключением их надо изучать.).
Сейчас пока времени нет, появится куплю очередной принтер и займусь.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

SLOM
Offline
Зарегистрирован: 06.11.2014

Kakmyc я тоже сжег 2 штуки, подключал 5в потом подключил 3,3в и норм. :) 

Евгений

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

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