Автор Тема: Управление шаговым двигателем с помощью энкодера  (Прочитано 6566 раз)

0 Пользователей и 1 Гость просматривают эту тему.

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4773
Вот кино экспериментов. Мотор от каретки струйника, энкодер выдернут из мышевого колеса.



Сначала было 2 канала для 2-х моторов.
while(1||!1);
зберігай спокій

Speys

  • Глобальный модератор
  • .
  • *****
  • Сообщений: 941
(Нет темы)
« Ответ #1 : 13 Апрель 2012, 22:04:11 »
Влад,добавил  подтяжку по входам валкодера.Все детали правда в виде конденсаторов.

Speys

  • Глобальный модератор
  • .
  • *****
  • Сообщений: 941
(Нет темы)
« Ответ #2 : 12 Май 2012, 08:08:54 »
Влад приветствую! Вот соорудил,все работает,правда только в горизонтальной плоскости-360градус.Вертикальной не делал,нет редуктора. (Оказалось, и так достаточно).Один нашел у себя,довольно мощный,с двигателя (шагового??? вообщем проводов было много) .Дюраль корпус,шестерни бронза,подшипники.Вместо родного,прикрутил принтерный двиг,и закрыл колпаком.Поэтому вроди большой с виду.Сам двиг,как у тебя. Вообщем просьба такая. Подкорректируй прошиву,чтоб драйвер двига включался, только когда крутиш. Перестал вращать энкодер,питание на двиг прекратилось.  Причины две. Ток при удержании довольно большой,(удержание не нужно,через редуктор,руками не прокрутиш двиг) и нагреваясь микросхема драйвера,уходит в защиту...Перестает крутить,да и обмотка двига греется.. Можно управлять самим драйвером(вроди есть вывод на включение,или нужно управлять стабилизатором тока) чтоб при вращении,включался. Ток по любому ограничивать нужно.В паузе жрет 1.2А. Если это реализовать,то решено. И если можно,кнопку как у нас в Примусс,на включение самой камеры. Полевик добавлю, не хоца  с фиксацией городить.

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4773
(Нет темы)
« Ответ #3 : 12 Май 2012, 09:15:47 »
Цитата: "Speys"
Подкорректируй прошиву,чтоб драйвер двига включался, только когда крутиш. Перестал вращать энкодер,питание на двиг прекратилось.
 И если можно,кнопку как у нас в Примусс,на включение самой камеры. Полевик добавлю, не хоца  с фиксацией городить.
Включение камеры будет от кнопки энкодера.
 Второй канал и изменение скорости уберу.
while(1||!1);
зберігай спокій

Speys

  • Глобальный модератор
  • .
  • *****
  • Сообщений: 941
(Нет темы)
« Ответ #4 : 12 Май 2012, 09:36:18 »
Насчет включения, придумал отлично! Все равно кнопка не задействована  .Скорость остав большую,медленную не нужно,через редуктор. И напишиш изменения в схеме какие...

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4773
(Нет темы)
« Ответ #5 : 12 Май 2012, 11:02:08 »
в схеме менять практически ничего не надо.
Отключение мотора сделал программно.
Ключ питания камеры управляется ногой PB0
Фузы меги заводские (встроенный тактовый генератор 1 МГц).
while(1||!1);
зберігай спокій

Speys

  • Глобальный модератор
  • .
  • *****
  • Сообщений: 941
(Нет темы)
« Ответ #6 : 12 Май 2012, 14:35:39 »
Ок. Проверил,все как должно, работает. Двиг запитал от 5 вольт,без ограничения по току.При вращении жрет прим 650 мА.Остановка практически 0. Осталось ключ на включение допаять и в корпусок маленький. У меня схема в доме,управление и питание подаю по витой паре (4 пары жил),БП на чердаке. Для этого и просил откл самой камеры.СПАСИБО!

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4773
(Нет темы)
« Ответ #7 : 23 Август 2014, 17:36:40 »
по просьбам читателей выкладываю код


#include <avr/io.h>
#include <avr/interrupt.h>

#define u8 unsigned char
#define s8   signed char
#define u16 unsigned int


//порт и выводы к которым подключен энкодер
#define PORT_Enc PORTC
#define PIN_Enc PINC
#define DDR_Enc DDRC
#define Pin1_Enc 0
#define Pin2_Enc 1
/*
//1
#define A0 0b0001
#define B0 0b0010
#define A1 0b0100
#define B1 0b1000
*/
//2
#define A0 0b0010
#define B0 0b0001
#define A1 0b0100
#define B1 0b1000
/*
//3
#define A0 0b0100
#define B0 0b0001
#define A1 0b0010
#define B1 0b1000

//4
#define A0 0b1000
#define B0 0b0001
#define A1 0b0010
#define B1 0b0100

//5
#define A0 0b1000
#define B0 0b0010
#define A1 0b0001
#define B1 0b0100

//6
#define A0 0b1000
#define B0 0b0100
#define A1 0b0001
#define B1 0b0010

//7
#define A0 0b0100
#define B0 0b1000
#define A1 0b0001
#define B1 0b0010

//8
#define A0 0b0100
#define B0 0b1000
#define A1 0b0010
#define B1 0b0001

//9
#define A0 0b0010
#define B0 0b1000
#define A1 0b0100
#define B1 0b0001

//10
#define A0 0b0010
#define B0 0b0100
#define A1 0b1000
#define B1 0b0001

//11
#define A0 0b0001
#define B0 0b0100
#define A1 0b1000
#define B1 0b0010

//12
#define A0 0b0001
#define B0 0b0010
#define A1 0b1000
#define B1 0b0100
*/

volatile u16 timer;
volatile u16 timer1;
volatile u8 cmd =0;

#define speed 1
#define kanal 2

//const u8 WaveDriv[4] ={A0, B0, A1, B1};

const u8 FullStep[4] =
{
A0|B0,
  B0|A1,
     A1|B1,
        B1|A0
};

const u8 HalfStep[8] =
{
A0|B0,
  B0,
  B0|A1,
 A1,
 A1|B1,
B1,
B1|A0,
A0
};


//##############
ISR(SIG_OVERFLOW0)
{
timer++;
timer1++;

static u8 stateEnc;
u8 currentState = 0;

if(PIN_Enc & (1<<Pin1_Enc)) currentState |=2;
if(PIN_Enc & (1<<Pin2_Enc)) currentState |=1;

//если состояние энкодера не изменилось выходим
if(currentState == (stateEnc & 0b00000011)) return;

timer1 = 0;

stateEnc <<=2;
stateEnc |= currentState;

static u8 tick;

if(stateEnc == 0b11100001) tick++;
if(stateEnc == 0b11010010) tick--;

PORTD = FullStep[tick % 4];
/*
u8 out;

if(cmd & speed)out = HalfStep[tick % 8];
else   out = FullStep[tick % 4];

if(cmd & kanal)out <<=4;
PORTD = out;

for(u8 i=0;i<8;i++)
{
PORTB &=~((1<<PB0)|(1<<PB1));

if(out & 1)PORTB |= (1<<PB0); //data

PORTB |= (1<<PB1); //clk
out >>=1;
}
*/
}

//##############
int __attribute__((naked)) main(void)
{
//вкл подтягивающий резистор
PORT_Enc = (1<<Pin1_Enc)|(1<<Pin2_Enc)|(1<<PC2);
DDRD = 0xff;
DDRB = (1<<PB0);

TIMSK = (1<<TOIE0);

TCCR0 = 0b000001;
sei();

while(1)
{
if(timer1 > 3000) PORTD = 0;

static u8 flag;

if((PINC & (1 << PC2))==0)  //проверяем кнопку
{
if(timer > 300 && flag == 0)
{
PORTB ^= 1;
flag = -1;
}
}
else
{
timer =0;
flag = 0;
}

/*
static u8 flags;

if((PINC & (1 << PC2))==0) //проверяем кнопку
{
if(timer >  200 && (flags & speed) ==0)
{
cmd ^= speed;
flags |= speed;
}

if(timer > 3000 && (flags & kanal) ==0)
{
cmd ^= kanal;
flags |= kanal;
}
}
else
{
timer =0;
flags =0;
}
*/

// PORTB &=~((1<<PB2)|(1<<PB3));
// if(cmd & kanal)PORTB |= (1<<PB2);
};
}
while(1||!1);
зберігай спокій

intruderok

  • .
  • *
  • Сообщений: 1
на ардуино бы замутить

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4773
Цитата: "intruderok"
на ардуино бы замутить
мути, кто мешает?
Написано на Си. Всего кода фиг-да-нифига.

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

#include <avr/io.h>
#include <avr/interrupt.h>

#define u8 unsigned char

//порт и выводы к которым подключен энкодер
#define PORT_Enc PORTC
#define PIN_Enc PINC
#define DDR_Enc DDRC
#define Pin1_Enc 0
#define Pin2_Enc 1
//ноги управления мотором
#define A0 0b0001
#define B0 0b0010
#define A1 0b0100
#define B1 0b1000

const u8 FullStep[4] =
{
A0|B0,
  B0|A1,
     A1|B1,
        B1|A0
};
//прерывание где всё шаманство происходит
ISR(SIG_OVERFLOW0)
{
static u8 stateEnc;
u8 currentState = 0;
if(PIN_Enc & (1<<Pin1_Enc)) currentState |=2;
if(PIN_Enc & (1<<Pin2_Enc)) currentState |=1;

//если состояние энкодера не изменилось выходим
if(currentState == (stateEnc & 0b00000011)) return;

stateEnc <<=2;
stateEnc |= currentState;

static u8 tick;
if(stateEnc == 0b11100001) tick++;
if(stateEnc == 0b11010010) tick--;

PORTD = FullStep[tick % 4];
}

int __attribute__((naked)) main(void)
{
//вкл подтягивающий резистор
PORT_Enc = (1<<Pin1_Enc)|(1<<Pin2_Enc);
DDRD = 0xff;
TIMSK = (1<<TOIE0);
TCCR0 = 0b000001;
sei();
while(1 || !1);
}
while(1||!1);
зберігай спокій

Alehandroz

  • .
  • *
  • Сообщений: 1
Повторил  схему- всё работает! Автору спасибо.
У меня стоит энкодер на 20 импульсов и шаговик 1.8 градусов. Чтобы прокрутить шаговик на оборот-два нужно очень  долго вращать энкодер. Вопрос к гуру- как модернизировать прошивку чтобы за один импульс энкодера  движок вращался не на один шаг а пару десятков.
п.с. сильно прошу не ругать, с++ только начал изучать, даётся торудновато...

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4773
(Нет темы)
« Ответ #11 : 20 Декабрь 2017, 21:42:20 »
принцип реализации вижу примерно такой:
сделать счетчик импульсов движка, который тактируется генератором скорости и всё время стремится к нулю,
и к этому счетчику прибавлять импульсы от энкодера умноженные на желаемый коэффициент передачи.
Вот и всё.
while(1||!1);
зберігай спокій

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4773
(Нет темы)
« Ответ #12 : 22 Декабрь 2017, 06:34:40 »
кстати, в сорцах выше корявенькая реализация энкодера, которую я когда-то где-то подсмотрел.
Вот тут это сделано правильно.
Прерывание переписывается:
ISR(SIG_OVERFLOW0)
{
   static u8 enc_tick, old;
   u8 clk=PIN_Enc&(1<<Pin1_Enc), dir=PIN_Enc&(1<<Pin2_Enc);//смотрим ноги
   if(old&&clk==0){//ловим задний фронт
      if(dir)enc_tick++;//тудом
      else   enc_tick--;//сюдом
   }
   old=clk;
   
   PORTD = FullStep[enc_tick % 4];
}
Мысли, написанные в предыдущем посте, тоже легко вписываются в это прерывание
while(1||!1);
зберігай спокій

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4773
(Нет темы)
« Ответ #13 : 25 Декабрь 2017, 09:29:51 »
вот так это примерно может выглядить:
просто, как 3 копейки
/* writing by slav0n */

#define DIV_MOTOR_CLK 10 //делитель частоты прерывания для тактирования мотора
#define GEAR 3 //предаточное число энкодер-шаги мотора

ISR(SIG_OVERFLOW0)
{
static int motor_cnt;
static u8 old, tmp;

u8 clk=PIN_Enc&(1<<Pin1_Enc), dir=PIN_Enc&(1<<Pin2_Enc);//смотрим ноги

if(old && clk==0){//ловим задний фронт
if(dir)motor_cnt+=GEAR;//тудом
else   motor_cnt-=GEAR;//сюдом
}
old=clk;
   
   tmp++;
if((tmp % DIV_MOTOR_CLK)==0){
if(motor_cnt<0)motor_cnt++;
if(motor_cnt>0)motor_cnt--;

PORTD = FullStep[motor_cnt % 4];
}
}
while(1||!1);
зберігай спокій