Перейти к содержанию
Volmir

Электронная зубофрезерная делительная голова

Рекомендуемые сообщения

Дата: (изменено)

Общий вид LCD Keypad Shield (меню, кнопки управления):
large.LCD_Keypad_Shield.jpg.f9ad012b2d9c66e0f3d2bdc10601ec91.jpg

 

Меню "Нарезание шестеренок":

large.menu_gear.jpg.922cba07547f2ee3b858f33c6d992fee.jpg


Меню "Деление окружности на части":

large.menu_divider.jpg.e787689612619e8b770e8d97b0dab6fc.jpg

 

Процесс тестирования функции нарезания шестеренок:
large.IMG_20200304_213716.jpg.f712950853d0826217a0a27bb80ec8ea.jpg

Бумажный диск закреплен на валу ШД, маркер - на валу шпинделя. При синхронном вращении имитируем процесс нарезания шестеренок:
large.IMG_20200304_213919.jpg.ecee49f508602b217ceac60838e8df5a.jpg

Процесс тестирования функции деления окружностина части (карандаш, зажатый в губки патрона, является индикатором поворота на нужный угол):
large.IMG_20200303_194528.jpg.8a3cbb2472649776926714b7e08461ae.jpg


Результат "нарезания" шестеронок с разным количеством зубьев: 6, 13, 29, 49, 89 (специально брал соотношения, которые не делятся нацело).
Методика тестирования: карандаш в качестве однозубой фрезы, бумажный диск - в качестве заготовки.

large.IMG_20200305_200609.jpg.81131d0665a716cf3203f85869a31806.jpg

Изменено пользователем Volmir

Поделиться сообщением


Ссылка на сообщение

Схема подключения всех электроных компонентов вместе стандартная.
В качестве примера могу привести вот такую иллюстрацию:
Schema.jpg

В предлагаемом проекте достаточно одного сигнала от энкодера (на схеме указано два: А и B).
Резисторы при подключении к драйверу лично я не применял.

Прилагаю видео, на которых демонстрируется работа электронной делительной головы.

Тест №1: Нарезание зубьев.
"Нарезал" 24 зуба "фрезой" в автоматическом режиме.
"Фрезой" выступал маркер (им наносились риски на "заготовке"), а "заготовкой" - бумажный диск, закрепленный на валу ШД.
Все прошло успешно: связка "Энкодер (шпиндель)" - "Ардуино" - "Шаговый двигатель (УДГ)" работает синхронно друг с другом, пропуска шагов не наблюдается.



Тест №2: Деление окружности на равные части.
Карандаш использовал в качестве индикатора, маркера поворота заготовки на требуемый угол.
В качестве задания выбрал деление окуржности на две части (имитация нарезания лысок на детали).
После полного оборота заготовки карандаш занял первоначальное положение (т.е. деление прошло точно, погрешность не наблюдается).

 

Поделиться сообщением


Ссылка на сообщение

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

Поделиться сообщением


Ссылка на сообщение
15 часов назад, Volmir сказал:

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

Поправь в скетче и схеме соединения микропроцессара с драйвером шагового двигателя пины подключения. В скетче указаны пины 22,23,24 а на схеме  45,47,59. А то дилетанты, такие как я, будут писать письма "не работает".

Поделиться сообщением


Ссылка на сообщение
12 часов назад, orel.kom сказал:

Поправь в скетче и схеме соединения микропроцессара с драйвером шагового двигателя пины подключения. В скетче указаны пины 22,23,24 а на схеме  45,47,59. А то дилетанты, такие как я, будут писать письма "не работает".

Спасибо за замечание по деталам. Мне кажется, что теме подключения Арудино к остальной электронике, по-хорошему, надо посвятить отдельную детальную тему: там куча нюансов и мелочей, которые нужно учесть и настроить для работы.

Поделиться сообщением


Ссылка на сообщение
В 07.03.2020 в 22:27, Volmir сказал:

Спасибо за замечание по деталам. Мне кажется, что теме подключения Арудино к остальной электронике, по-хорошему, надо посвятить отдельную детальную тему: там куча нюансов и мелочей, которые нужно учесть и настроить для работы.

Я с вами полностью согласен. Может из-за этого меня постигла неудача. Вчера целый день пробовал ваш проект. В моем случае простая замена данных, не позволила производить нарезание зубьев и деление. К сожалению я не снимал видео, но поверьте на слово. Электросхема соединения такая как ваша, изменены данные, а именно: разрешение энкодера из расчета: 2000(разрешение самого энкодера) умноженное на передаточное число ременной передачи 4 (со шкива шпинделя на шкив энкодера), итого разрешение энкодера 8000 линий за один оборот червячной фрезы. Стандартное передаточное число универсальной делительной головки УДГ-160 1:40. Короче, ситуация такая. В представленном вами скетче и при установлении моих данных, скорость вращения заготовки не меняется в зависимости от количества нарезаемых зубьев, а в режиме деления на части шаговый двигатель вращается постоянно пока не отключишь питание, т.е. деления нет. Возвращаю назад данные в скетче, как было у вас,  начинает все работать, естественно не так как мне нужно. Причем деление работает до установки передаточного числа в скетче до 20, выше 20 - вал шагового двигателя начинается вращаться непрерывно, ничем не остановишь, кроме как выключением питания, скорость вращения заготовки не изменяется в зависимости от количества зубьев. Вывод: надо дорабатывать скетч под универсальные делительные головки типа УДГ с передаточным числом 1:40, под универсальные поворотные столы с передаточным отношением 1:90.

Поделиться сообщением


Ссылка на сообщение
Цитата

Вывод: надо дорабатывать скетч под универсальные делительные головки типа УДГ с передаточным числом 1:40

Попробую загрузить в скетч ваши параметры - и проверить результаты работы.
Это можно сделать удаленно.
Надеюсь, совместными усилиями удастся довести проект до рабочего состояния :)

Поделиться сообщением


Ссылка на сообщение
Дата: (изменено)
23 часа назад, Volmir сказал:

Попробую загрузить в скетч ваши параметры - и проверить результаты работы.
Это можно сделать удаленно.
Надеюсь, совместными усилиями удастся довести проект до рабочего состояния :)

Мне  загрузить загрузить ваш скретч с моими данными? Вот он

Спойлер

 

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

//---------- Переменные для вывода текста на экран ----------
char lcdRow1[16];
char lcdRow2[16];

unsigned long uptime;               // Переменная хранит время работы в миллисекундах

//---------- Параметры которые можно задать в меню ----------
unsigned long gearTooth = 24;

int dividerTotal = 4;
int dividerCurrent = 0;

boolean runGear = false;
boolean runDivider = false;

int rotateDirection = 1;            // Направление вращения заготовки

//----------  Названия кнопок ----------
#define BUTTON_RESET   0
#define BUTTON_SELECT  1
#define BUTTON_PREV    2
#define BUTTON_NEXT    3
#define BUTTON_UP      4
#define BUTTON_DOWN    5

//----------  Нажатие кнопок ----------
#define buttonAnalogPin   0         // Пин с которого считываются нажатия кнопок
int const buttonInterval = 200;      // Интервал срабатывания кнопки при удержании
int buttonPress = 0;                // Код нажатой кнопки, или 0 если не нажата
unsigned long buttonPressTime = 0;  // Время на устройстве в которое была нажата кнопка

//----------  Меню ----------
int menuCurrent = 0;                // Выбранный пункт меню
int menuCount = 3;                  // Количество пунктов меню

#define MENU_GEAR       0           // Пункт меню "Нарезание зубьев"
#define MENU_DIVIDER    1           // Пункт меню "Деление окружности на части" 
#define MENU_SETTINGS   2           // Пункт меню "Выбор направления вращения заготовки (CW/CCW). Непрерывное вращение" 

//---------- Настройки шагового двигателя ----------
#define motorStepPin   45           // Output signal to step the motor
#define motorDirPin    47           // Output signal to set direction
#define motorEnablePin 49           // Output pin to power up the motor

#define stepsPerRevolution 200      // Number of steps it takes the motor to do one full revolution
#define microsteps 8                // Depending on your stepper driver, it may support microstepping
#define gearRatio 40                 // Gear ratio "Motor" : "Dividing head"

// There are 1,000 microseconds in a millisecond and 1,000,000 microseconds in a second
int pulseWidth = 100;               // Length of time for one step pulse (microseconds)

#define CW HIGH                     // Define direction of rotation
#define CCW LOW                     // If rotation needs to be reversed, swap HIGH and LOW here

unsigned long motorSteps;           // Количество импульсов ШД на один оборот детали

//---------- Энкодер, синхронизация ----------
#define interruptPin           21           // Контакт к которому подключен датчик энкодера
#define encoderStepsPerTurn    8000          // Разрешение энкодера (количество линий на полный оборот)
volatile unsigned long encoderCounter = 0;  // Счетчик шагов энкодера

//---------- Подсчет количества оборотов шпинделя ----------
#define turnsCalcInterval     200           // Интервал в миллисекундах за который подсчитываются обороты шпинделя   
unsigned int turnsPerMinute = 0;            // Обороты шпинделя (в минуту)
unsigned long turnsCounterPrev = 0;
unsigned long turnsTimeLast = 0;

//---------- Нарезание шестеренок ----------
#define multiplicator    1000                          // Вводим для замены работы с float, double 
volatile unsigned long gearCoefficient = 0;            // Итоговый коэффициент: на сколько линий должен провернуться энкодер для того, чтобы шаговый двигатель смог сделать один шаг
volatile unsigned long gearCoefficientFraction = 0;    // Итоговый коэффициент: дробная часть
volatile unsigned long encoderLinesMove = 0;           // Количество линий, на которые повернулся энкодер


//---------- Загрузка ----------
void setup() {
  lcd.begin(16, 2); // Инициализируем дисплей
  printMenuGear();

  pinMode(motorStepPin, OUTPUT);
  pinMode(motorDirPin, OUTPUT);
  pinMode(motorEnablePin, OUTPUT);

  digitalWrite(motorEnablePin, LOW);
  digitalWrite(motorDirPin, CW);

  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), encoderTick, RISING);

  motorSteps = stepsPerRevolution * microsteps * gearRatio;
  rotateDirection = CW;
}

//---------- Главный цикл ----------
void loop() {
  uptime = millis(); // Сохраняем время работы каждый цикл

  if (runGear) {
    calcTurnsPerMinute();
  }

  if (buttonPress == 0) { // Если кнопки не были нажаты ранее
    int buttonPinValue = analogRead(buttonAnalogPin); // Проверяем значение, не нажата ли кнопка
    if (buttonPinValue < 60)    buttonPress = BUTTON_UP; // Нажата [+]
    else if (buttonPinValue < 200)  buttonPress = BUTTON_PREV; // Нажата [Prev]
    else if (buttonPinValue < 400)  buttonPress = BUTTON_NEXT; // Нажата [Next]
    else if (buttonPinValue < 600)  buttonPress = BUTTON_DOWN; // Нажата [-]
    else if (buttonPinValue < 800)  buttonPress = BUTTON_SELECT; // Нажата [Menu]
  } else { // Кнопка была нажата ранее
    if (buttonPressTime == 0) { // Если не замеряли интервал нажатия кнопки
      buttonPressTime = uptime; // Засекаем когда была нажата кнопка
      ButtonClick(buttonPress); // Вызывается функция обработки нажатия на кнопку
    }
    if (buttonPressTime + buttonInterval < uptime) { // Если кнопка была нажата раньше чем buttonInterval ms назад
      buttonPressTime = 0; // Сбрасываем время
      buttonPress = 0; // Отжимаем кнопку (это имитирует многократное нажатие с интервалом buttonInterval если кнопку держать)
    }
  }

}

//---------- Обработка нажатия кнопки ----------
void ButtonClick(int buttonId) {

  // Клик [Select]
  if (buttonId == BUTTON_SELECT) {
    if (menuCurrent == MENU_GEAR) {
      toggleGearOption();
    } else if (menuCurrent == MENU_DIVIDER) {
      runDividerOption();
    } else if (menuCurrent == MENU_SETTINGS) {
      runRotateOption();
    }
  }

  if (buttonId == BUTTON_PREV) {
    menuCurrent--;
  }
  if (buttonId == BUTTON_NEXT) {
    menuCurrent++;
  }
  menuCurrent = constrain(menuCurrent, 0, menuCount - 1); // Ограничиваем меню

  // Клик [+] Увеличиваем значение выбранного параметра
  if (buttonId == BUTTON_UP) {
    if (menuCurrent == MENU_GEAR) {
      setGearTooth(1);
    } else if  (menuCurrent == MENU_DIVIDER) {
      setDividerTotal(1);
    } else if (menuCurrent == MENU_SETTINGS) {
      changeRotateDirection();
    }
  }

  // Клик [-] Уменьшаем значение выбранного параметра
  if (buttonId == BUTTON_DOWN) {
    if (menuCurrent == MENU_GEAR) {
      setGearTooth(-1);
    } else if (menuCurrent == MENU_DIVIDER) {
      setDividerTotal(-1);
    } else if (menuCurrent == MENU_SETTINGS) {
      changeRotateDirection();
    }
  }

  // Отрисовка пунков меню
  if (menuCurrent == MENU_GEAR) {
    printMenuGear();
  } else if (menuCurrent == MENU_DIVIDER) {
    printMenuDivider();
  } else if (menuCurrent == MENU_SETTINGS) {
    printMenuRotate();
  }
}

void printMenuGear() {
  sprintf(lcdRow1, "Gear: %s %4d", (runGear == true) ? "[ON] " : "[OFF]", turnsPerMinute);
  sprintf(lcdRow2, "Tooth: %3d", gearTooth);
  printLcd();
}

void printMenuDivider() {
  sprintf(lcdRow2, "Parts: %3d | %3d", dividerTotal, dividerCurrent);
  sprintf(lcdRow1, "Divider: %s", (runDivider == true) ? "[ON] " : "[OFF]");
  printLcd();
}

void printMenuRotate() {
  sprintf(lcdRow1, "Settings");
  sprintf(lcdRow2, "Direction: %s", (rotateDirection == CW) ? "CW " : "CCW");
  printLcd();
}

void printLcd() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(lcdRow1);
  lcd.setCursor(0, 1);
  lcd.print(lcdRow2);
}

void setGearTooth(int concat) {
  if (!runGear) {
    gearTooth = constrain(gearTooth + concat, 6, 240);
  }
}

void setDividerTotal(int concat) {
  if (!runDivider) {
    dividerTotal = constrain(dividerTotal + concat, 2, 240);
  }
}

void changeRotateDirection() {
  if (rotateDirection == CW) {
    rotateDirection = CCW;
    digitalWrite(motorDirPin, CCW);
  } else {
    rotateDirection = CW;
    digitalWrite(motorDirPin, CW);
  }
}

void toggleGearOption() {
  runGear = !runGear;
  if (runGear) {
    digitalWrite(motorEnablePin, HIGH);

    gearCoefficient = gearTooth * multiplicator * encoderStepsPerTurn / motorSteps;

    encoderLinesMove = 0;
    gearCoefficientFraction = 0;
  } else {
    digitalWrite(motorEnablePin, LOW);
  }
}

void runDividerOption() {
  unsigned long moduleOfDivision;
  unsigned long partOfDividerTotal;
  static int j = 0;
  unsigned long i;
  unsigned long stepsPerDiv;

  if (dividerCurrent < dividerTotal) {
    dividerCurrent++;
    runDivider = true;
    digitalWrite(motorEnablePin, HIGH);

    /**
       Округляем последовательно в большую и меньшую сторону
       Это позволит избежать накопления погрешности деления окружности
    */
    moduleOfDivision = (motorSteps * 100 / dividerTotal) - ((motorSteps / dividerTotal) * 100);
    j++;

    partOfDividerTotal = (dividerTotal * moduleOfDivision) / 100;
    if (partOfDividerTotal >= j) {
      stepsPerDiv = (motorSteps / dividerTotal) + 1;
    } else {
      stepsPerDiv = (motorSteps / dividerTotal);
    }

    if (j == dividerTotal) {
      j = 0;
    }

    for (i = 0; i < stepsPerDiv; i++) {
      moveMotor();
    }
  } else {
    dividerCurrent = 0;
    runDivider = false;
    j = 0;
    digitalWrite(motorEnablePin, LOW);
  }

  menuCurrent = MENU_DIVIDER;
  printMenuDivider();
}

void runRotateOption() {
  int breakFlag = 0;
  int buttonPinValue;
  digitalWrite(motorEnablePin, HIGH);

  while (breakFlag == 0) {
    moveMotor();

    buttonPinValue = analogRead(buttonAnalogPin);
    if (buttonPinValue > 800) {
      breakFlag = 1;
      digitalWrite(motorEnablePin, LOW);
    }
  }
}

void moveMotor() {
  digitalWrite(motorStepPin, HIGH);
  delayMicroseconds(pulseWidth);
  digitalWrite(motorStepPin, LOW);
  delayMicroseconds(pulseWidth);
}

void encoderTick() {
  encoderCounter++;

  if (runGear) {
    encoderLinesMove++;

    if ((encoderLinesMove * multiplicator) >= gearCoefficient) {
      moveMotor();

      gearCoefficientFraction += ((encoderLinesMove * multiplicator) - gearCoefficient);
      encoderLinesMove = 0;
    }

    if (gearCoefficientFraction >= multiplicator) {
      encoderLinesMove++;
      gearCoefficientFraction = gearCoefficientFraction - multiplicator;
    }
  }
}

void calcTurnsPerMinute() {
  // если прошло turnsCalcInterval мс или более, то начинаем расчёт
  if ((millis() - turnsTimeLast) >= turnsCalcInterval) {
    turnsPerMinute = 60 * (1000 / turnsCalcInterval) * (encoderCounter - turnsCounterPrev) / encoderStepsPerTurn;

    turnsCounterPrev = encoderCounter; // запоминаем количество шагов
    turnsTimeLast = millis(); // запоминаем время расчёта

    menuCurrent = MENU_GEAR;
    printMenuGear();
  }
}

 

В вашем скетче я заменил только две цифры, разрешение энкодера с 500 на 8000 и передаточное число с 16 на 40.  Схема не заработала. В тоже время доработанный проект Олега "электронная гитара" работает и если подобрать скорость вращения фрезы и выполнить  вышеуказанные рекомендации по системе СПИД, то вполне можно добиться приемлемого результата. Вот его скетч, доработанный усилиями igorkawa, ну я там тоже слегка поковырялся, следуя рекомендациям Олега

 

Спойлер

 

#include <avr/pgmspace.h>


// ***** LCD *****
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
char LCD_Row_1[17];
char LCD_Row_2[17];


// ***** Stepper Motor *****
#define Motor_X_Step_Per_Revolution          64000

#define MotorPort                    PORTL                    // под моторы выделен весь порт "L",
#define MotorInitialization()        DDRL=B11111111           // сконфигурирован как выход
#define Motor_X_SetPulse()           MotorPort &= ~(1<<0)     // Pin49 1
#define Motor_X_RemovePulse()        MotorPort |= (1<<0)      // Pin49 0

#define Motor_X_Forward()            MotorPort &= ~(1<<2)      // Pin47 0
#define Motor_X_Reverse()            MotorPort |= (1<<2)     // Pin47 1

#define Motor_X_Enable()             MotorPort |= (1<<4)      // Pin45 0
#define Motor_X_Disable()            MotorPort &= ~(1<<4)     // Pin45 1

boolean Step_On_flag = false;                                 // Флаг разрешаем/запрещаем счет до "шагать"
boolean Mode_On_flag = false;                                 // Флаг On/Off режим


// ***** Taho *****
#define TahoPort                      PORTL
#define TahoSetPulse()                TahoPort |= (1<<6)             // Pin43 1
#define TahoRemovePulse()             TahoPort &= ~(1<<6)            // Pin43 0


// ***** Encoder *****
#define Enc_Line_per_Revolution       8000                           // Кол-во линий энкодера
#define Enc_Line                      Enc_Line_per_Revolution*2      // Рабочее кол-во тиков
#define EncoderPort                   PORTD
#define EncoderInitialization()       DDRD=B00000000                 // Под энкодер выделен весь порт "D", сконфигурирован на вход
#define Enc_Read()                    (PIND & B00000010)
volatile int Enc_Pos = 0;                                            // Счетчик положения энкодера
volatile byte Ks_Count = 0;                                          // Счетчик для "Подача", "Резьба" целая часть
volatile int Km_Count = 0;                                           // Счетчик для "Подача", "Резьба" дробная часть
byte Ks_Divisor = 0;                                                 // Делитель для "Подача", "Резьба" целая часть
int Km_Divisor = 0;                                                  // Делитель для "Подача", "Резьба" дробная часть
int Enc_Pos_tmp = 0;
long Spindle_Angle = 0;


//***** Sensor *****
#define SensorPort                     PORTD
#define Sensor                         PIND
#define Sensor_Left_Mask B00001000
#define Sensor_Right_Mask B00000100
char Sensor_Mask = B00000000;


enum Pressed_Key
{
  Key_None,
  Key_Right,
  Key_Up,
  Key_Down,
  Key_Left,
  Key_Select
};
byte Pressed_Key = Key_None;
boolean key_flag = false;                                            // флаг нажатой/отжатой кнопки


// ***** Mode *****
enum Mode
{
  Mode_Thread_Left = 1,
  Mode_Feed_Left,
  Mode_Divider,
  Mode_Feed_Right,
  Mode_Thread_Right
};
byte Mode = Mode_Divider;


// ***** Feeds *****
// Enc_Line/(Step_Per_Revolution/Feed_Screw*Feed_mm)
#define Total_Feeds                   1                  // Кол-во подач
typedef struct
{
  byte  s_Divisor;                                        // Делитель для "Подача" целая часть
  char  Text[7];
}
FEED_INFO;
FEED_INFO Feed_Info[Total_Feeds] =
{
  { 180, "0.02" },
};
byte Feed_Step = 0;                                      // выборка из массива по умолчанию (0.08mm)


// ***** Threads *****
// Enc_Line/(Step_Per_Revolution/Feed_Screw*Thread_mm)
#define Total_Threads                 400                // Кол-во резьб
typedef struct
{
  byte s_Divisor;                                       // Делитель для "Резьба" целая часть
  int  m_Divisor;                                       // Делитель для "Резьба" дробная часть
  char Text[7];
}
THREAD_INFO;
THREAD_INFO Thread_Info[Total_Threads] =
{
  { 1, 5000, " Z=6  " },
  { 1, 7500, " Z=7  " },
  { 2, 0000, " Z=8  " },
  { 2, 2500, " Z=9  " },
  { 2, 5000, " Z=10 " },
  { 2, 7500, " Z=11 " },
  { 3, 0000, " Z=12 " },
  { 3, 2500, " Z=13 " },
  { 3, 5000, " Z=14 " },
  { 3, 7500, " Z=15 " },
  { 4, 0000, " Z=16 " },
  { 4, 2500, " Z=17 " },
  { 4, 5000, " Z=18 " },
  { 4, 7500, " Z=19 " },
  { 5, 0000, " Z=20 " },
  { 5, 2500, " Z=21 " },
  { 5, 5000, " Z=22 " },
  { 5, 7500, " Z=23 " },
  { 6, 0000, " Z=24 " },
  { 6, 2500, " Z=25 " },
  { 6, 5000, " Z=26 " },
  { 6, 7500, " Z=27 " },
  { 7, 0000, " Z=28 " },
  { 7, 2500, " Z=29 " },
  { 7, 5000, " Z=30 " },
  { 7, 7500, " Z=31 " },
  { 8, 0000, " Z=32 " },
  { 8, 2500, " Z=33 " },
  { 8, 5000, " Z=34 " },
  { 8, 7500, " Z=35 " },
  { 9, 0000, " Z=36 " },
  { 9, 2500, " Z=37 " },
  { 9, 5000, " Z=38 " },
  { 9, 7500, " Z=39 " },
  { 10, 0000, " Z=40 " },
  { 10, 2500, " Z=41 " },
  { 10, 5000, " Z=42 " },
  { 10, 7500, " Z=43 " },
  { 11, 0000, " Z=44 " },
  { 11, 2500, " Z=45 " },
  { 11, 5000, " Z=46 " },
  { 11, 7500, " Z=47 " },
  { 12, 0000, " Z=48 " },
  { 12, 2500, " Z=49 " },
  { 12, 5000, " Z=50 " },
  { 12, 7500, " Z=51 " },
  { 13, 0000, " Z=52 " },
  { 13, 2500, " Z=53 " },
  { 13, 5000, " Z=54 " },
  { 13, 7500, " Z=55 " },
  { 14, 0000, " Z=56 " },
  { 14, 2500, " Z=57 " },
  { 14, 5000, " Z=58 " },
  { 14, 7500, " Z=59 " },
  { 15, 0000, " Z=60 " },
  { 15, 2500, " Z=61 " },
  { 15, 5000, " Z=62 " },
  { 15, 7500, " Z=63 " },
  { 16, 0000, " Z=64 " },
  { 16, 2500, " Z=65 " },
  { 16, 5000, " Z=66 " },
  { 16, 7500, " Z=67 " },
  { 17, 0000, " Z=68 " },
  { 17, 2500, " Z=69 " },
  { 17, 5000, " Z=70 " },
  { 17, 7500, " Z=71 " },
  { 18, 0000, " Z=72 " },
  { 18, 2500, " Z=73 " },
  { 18, 5000, " Z=74 " },
  { 18, 7500, " Z=75 " },
  { 19, 0000, " Z=76 " },
  { 19, 2500, " Z=77 " },
  { 19, 5000, " Z=78 " },
  { 19, 7500, " Z=79 " },
  { 20, 0000, " Z=80 " },
  { 20, 2500, " Z=81 " },
  { 20, 5000, " Z=82 " },
  { 20, 7500, " Z=83 " },
  { 21, 0000, " Z=84 " },
  { 21, 2500, " Z=85 " },
  { 21, 5000, " Z=86 " },
  { 21, 7500, " Z=87 " },
  { 22, 0000, " Z=88 " },
  { 22, 2500, " Z=89 " },
  { 22, 5000, " Z=90 " },
  { 22, 7500, " Z=91 " },
  { 23, 0000, " Z=92 " },
  { 23, 2500, " Z=93 " },
  { 23, 5000, " Z=94 " },
  { 23, 7500, " Z=95 " },
  { 24, 0000, " Z=96 " },
  { 24, 2500, " Z=97 " },
  { 24, 5000, " Z=98 " },
  { 24, 7500, " Z=99 " },
  { 25, 0000, " Z=100" },
  { 25, 2500, " Z=101" },
  { 25, 5000, " Z=102" },
  { 25, 7500, " Z=103" },
  { 26, 0000, " Z=104" },
  { 26, 2500, " Z=105" },
  { 26, 5000, " Z=106" },
  { 26, 7500, " Z=107" },
  { 27, 0000, " Z=108" },
  { 27, 2500, " Z=109" },
  { 27, 5000, " Z=110" },
  { 27, 7500, " Z=111" },
  { 28, 0000, " Z=112" },
  { 28, 2500, " Z=113" },
  { 28, 5000, " Z=114" },
  { 28, 7500, " Z=115" },
  { 29, 0000, " Z=116" },
  { 29, 2500, " Z=117" },
  { 29, 5000, " Z=118" },
  { 29, 7500, " Z=119" },
  { 30, 0000, " Z=120" },
  { 30, 2500, " Z=121" },
  { 30, 5000, " Z=122" },
  { 30, 7500, " Z=123" },
  { 31, 0000, " Z=124" },
  { 31, 2500, " Z=125" },
  { 31, 5000, " Z=126" },
  { 31, 7500, " Z=127" },
  { 32, 0000, " Z=128" },
  { 32, 2500, " Z=129" },
  { 32, 5000, " Z=130" },
  { 32, 7500, " Z=131" },
  { 33, 0000, " Z=132" },
  { 33, 2500, " Z=133" },
  { 33, 5000, " Z=134" },
  { 33, 7500, " Z=135" },
  { 34, 0000, " Z=136" },
  { 34, 2500, " Z=137" },
  { 34, 5000, " Z=138" },
  { 34, 7500, " Z=139" },
  { 35, 0000, " Z=140" },
  { 35, 2500, " Z=141" },
  { 35, 5000, " Z=142" },
  { 35, 7500, " Z=143" },
  { 36, 0000, " Z=144" },
  { 36, 2500, " Z=145" },
  { 36, 5000, " Z=146" },
  { 36, 7500, " Z=147" },
  { 37, 0000, " Z=148" },
  { 37, 2500, " Z=149" },
  { 37, 5000, " Z=150" },
  { 37, 7500, " Z=151" },
  { 38, 0000, " Z=152" },
  { 38, 2500, " Z=153" },
  { 38, 5000, " Z=154" },
  { 38, 7500, " Z=155" },
  { 39, 0000, " Z=156" },
  { 39, 2500, " Z=157" },
  { 39, 5000, " Z=158" },
  { 39, 7500, " Z=159" },
  { 40, 0000, " Z=160" },
};
byte Thread_Step = 14;                      // выборка из массива по умолчанию (1.0mm)


// ***** Interrupts *****
#define EnableINT0()          EIMSK |= (1 << INT0)
#define DisableINT0()         EIMSK &= ~(1 << INT0)
#define Init_INT0_Any()       EICRA = B00000001


//*********************************************************
void setup()
{
  TIMSK0 = 0;                               // !Отключаем таймер! (он что-то свое делает в фоновом режиме)

  EncoderInitialization();
  PORTD = B00001111;                        // подтяжка PIN_21, 20, 19, 18

  MotorInitialization();
  Init_INT0_Any();
  EnableINT0();

  lcd.begin(16, 2);
}


//**********************************************************
void loop()
{
  Enc_Pos_tmp = Enc_Pos;                         // в "void Divider" читаем повторно и сравниваем

  if ((Mode == Mode_Divider) || !Mode_On_flag)
  {
    Motor_X_Disable();
    //    Motor_Y_Disable();
  }
  else
  {
    Motor_X_Enable();
    //    Motor_Y_Enable();
  }

  menu();
  Sensors();
}


// ********** Функция обработки событий в главном меню **********
void menu()
{
  int ADC_value = analogRead(A0);
  if (ADC_value < 100) Pressed_Key = Key_Right;
  else if (ADC_value < 220) Pressed_Key = Key_Up;
  else if (ADC_value < 390) Pressed_Key = Key_Down;
  else if (ADC_value < 600) Pressed_Key = Key_Left;
  else if (ADC_value < 870) Pressed_Key = Key_Select;
  else Pressed_Key = Key_None;

  if (!key_flag)
  {
    switch (Pressed_Key)
    {
      case Key_Left:
        MenuKeyLeftPressed();
        break;
      case Key_Right:
        MenuKeyRightPressed();
        break;
      case Key_Up:
        MenuKeyUpPressed();
        break;
      case Key_Down:
        MenuKeyDownPressed();
        break;
      case Key_Select:
        MenuKeySelectPressed();
        break;
    }
  }
  if (Pressed_Key == Key_None) key_flag = false;

  SelectWorkMode();                                // вызов выбранного рабочего режима
}

// ********** Обработчик нажатия кнопки Select **********
void MenuKeySelectPressed()
{
  switch (Mode)
  {
    case Mode_Thread_Left:
    case Mode_Thread_Right:
    case Mode_Feed_Left:
    case Mode_Feed_Right:
      Step_On_flag = false;
      Mode_On_flag = !Mode_On_flag;              // переворачиваем значение на противоположное
      Ks_Count = 0;
      Km_Count = 0;
      break;
    case Mode_Divider:
      Enc_Pos = 0;
      break;
  }
  key_flag = true;
}

// ********** Обработчик нажатия кнопки Up **********
void MenuKeyUpPressed()
{
  switch (Mode)
  {
    case Mode_Thread_Left:
    case Mode_Thread_Right:
      if (Thread_Step < Total_Threads - 1)
      {
        Mode_On_flag = false;
        Step_On_flag = false;
        Ks_Count = 0;
        Km_Count = 0;
        Thread_Step++;
      }
      break;
    case Mode_Feed_Left:
    case Mode_Feed_Right:
      if (Feed_Step < Total_Feeds - 1)
      {
        Ks_Count = 0;
        Feed_Step++;
      }
      break;
  }
  key_flag = true;
}

// ********** Обработчик нажатия кнопки Down **********
void MenuKeyDownPressed()
{
  switch (Mode)
  {
    case Mode_Thread_Left:
    case Mode_Thread_Right:
      if (Thread_Step > 0)
      {
        Mode_On_flag = false;
        Step_On_flag = false;
        Ks_Count = 0;
        Km_Count = 0;
        Thread_Step--;
      }
      break;
    case Mode_Feed_Left:
    case Mode_Feed_Right:
      if (Feed_Step > 0)
      {
        Ks_Count = 0;
        Feed_Step--;
      }
      break;
  }
  key_flag = true;
}

// ********** Обработчик нажатия кнопки Left **********
void MenuKeyLeftPressed()
{
  switch (Mode)
  {
    case Mode_Feed_Left:
    case Mode_Divider:
    case Mode_Feed_Right:
    case Mode_Thread_Right:
      Mode_On_flag = false;
      Step_On_flag = false;
      Ks_Count = 0;
      Km_Count = 0;
      Mode--;
      break;
  }

  key_flag = true;
}

// ********** Обработчик нажатия кнопки Right **********
void MenuKeyRightPressed()
{
  switch (Mode)
  {
    case Mode_Thread_Left:
    case Mode_Feed_Left:
    case Mode_Divider:
    case Mode_Feed_Right:
      Mode_On_flag = false;
      Step_On_flag = false;
      Ks_Count = 0;
      Km_Count = 0;
      Mode++;
      break;
  }

  key_flag = true;
}

// ********** Выбор режима работы **********
void SelectWorkMode()
{
  switch (Mode)
  {
    case Mode_Thread_Left:
      Thread_Left();
      break;
    case Mode_Divider:
      Divider();
      break;
    case Mode_Thread_Right:
      Thread_Right();
  }
}


//***************************************
void Thread_Left()
{
  Sensor_Mask = Sensor_Left_Mask;
  Ks_Divisor = Thread_Info[Thread_Step].s_Divisor;
  Km_Divisor = Thread_Info[Thread_Step].m_Divisor;
  snprintf(LCD_Row_1, 17, (Mode_On_flag == true) ? "Mode:  ON       " : "Mode:  OFF      ");
  snprintf(LCD_Row_2, 17, "Gear <=  %s", Thread_Info[Thread_Step].Text);
  Print();
}

void Feed_Left()
{
  Sensor_Mask = Sensor_Left_Mask;
  Ks_Divisor = Feed_Info[Feed_Step].s_Divisor;
  snprintf(LCD_Row_1, 17, (Mode_On_flag == true) ? "Mode:  ON       " : "Mode:  OFF      ");
  snprintf(LCD_Row_2, 17, "Feed   <= %s", Feed_Info[Feed_Step].Text);
  Print();
}

void Divider()
{
  if (Enc_Pos == Enc_Pos_tmp)
  {
    Spindle_Angle = (Enc_Pos * 360000 / (Enc_Line));
  }
  snprintf(LCD_Row_1, 17, "Mode: OFF    ");                                 // нарезка зубьев выключена. моя редакция//
  snprintf(LCD_Row_2, 17, "Set kn.1: %3ld.%03ld  ", Spindle_Angle / 1000, Spindle_Angle % 1000);           //нажать левую первую кнопку два раза-  включение нарезания зубьев ,вращение заготовки  влево, при нажатии второй  левой кнопки дважды - включение режима нарезания зубьев, вращение заготовки вправо. моя редакция//
  Print();
}

void Feed_Right()
{
  Sensor_Mask = Sensor_Right_Mask;
  Ks_Divisor = Feed_Info[Feed_Step].s_Divisor;
  snprintf(LCD_Row_1, 17, (Mode_On_flag == true) ? "Mode:  ON       " : "Mode:  OFF      ");
  snprintf(LCD_Row_2, 17, "Feed   => %s", Feed_Info[Feed_Step].Text);
  Print();
}

void Thread_Right()
{
  Sensor_Mask = Sensor_Right_Mask;
  Ks_Divisor = Thread_Info[Thread_Step].s_Divisor;
  Km_Divisor = Thread_Info[Thread_Step].m_Divisor;
  snprintf(LCD_Row_1, 17, (Mode_On_flag == true) ? "Mode:  ON       " : "Mode:  OFF      ");
  snprintf(LCD_Row_2, 17, "Gear =>  %s", Thread_Info[Thread_Step].Text);
  Print();
}


//******************************************************************
void Print()
{
  lcd.setCursor(0, 0);
  lcd.print(LCD_Row_1);
  lcd.setCursor(0, 1);
  lcd.print(LCD_Row_2);
}


//******************************************************************
void Sensors()
{
  if (!(Sensor & Sensor_Mask) )
  {
    Mode_On_flag = false;
  }
}


//******************************************************************
ISR(INT0_vect)
{
  TahoRemovePulse();
  Motor_X_RemovePulse();

  if (!Enc_Read())                                     // Вращение шпинделя вправо
  {
    Enc_Pos++;
    if (Enc_Pos == Enc_Line)                          // полный оборот
    {
      Enc_Pos = 0;
      TahoSetPulse();                                // при проходе 0 генерим сигнал Taho
      Step_On_flag = (Mode_On_flag == true);         // проверка режима на ON/OFF, только! после прохода 0 разрешаем счет до к.деления
    }


    if (Step_On_flag)
    {
      if (!(Sensor & Sensor_Mask) )
      {
        Step_On_flag = false;
        return;
      }

      if (Mode == Mode_Feed_Left)
      {
        if (Ks_Count == Ks_Divisor)
        {
          Motor_X_SetPulse();
          Ks_Count = 0;
        }
        Ks_Count++;
      }

      else if (Mode == Mode_Feed_Right)
      {
        if (Ks_Count == Ks_Divisor)
        {
          Motor_X_SetPulse();
          Ks_Count = 0;
        }
        Ks_Count++;
      }

      else if (Mode == Mode_Thread_Left)
      {
        if (Ks_Count > Ks_Divisor)
        {
          Motor_X_Reverse();
          Motor_X_SetPulse();
          Km_Count = Km_Count + Km_Divisor;
          if (Km_Count > Km_Divisor)
          {
            Km_Count = Km_Count - 10000;
            Ks_Count = 0;
          }
          else
          {
            Ks_Count = 1;
          }
        }
        Ks_Count++;
      }

      else if (Mode == Mode_Thread_Right)
      {
        if (Ks_Count > Ks_Divisor)
        {
          Motor_X_Forward();
          Motor_X_SetPulse();
          Km_Count = Km_Count + Km_Divisor;
          if (Km_Count > Km_Divisor)
          {
            Km_Count = Km_Count - 10000;
            Ks_Count = 0;
          }
          else
          {
            Ks_Count = 1;
          }
        }
        Ks_Count++;
      }
    }
  }


  else
  { // Вращение шпинделя влево
    Enc_Pos--;
    if (Enc_Pos < 0)
    {
      Enc_Pos = Enc_Line - 1;
      TahoSetPulse();
      Step_On_flag = (Mode_On_flag == true);     // проверка режима на ON/OFF, только! после прохода 0 разрешаем счет до к.деления
    }

    if (Step_On_flag)
    {
      if (!(Sensor & Sensor_Mask) )
      {
        Step_On_flag = false;
        return;
      }

      if (Mode == Mode_Feed_Left)
      {
        if (Ks_Count == 0)
        {
          Motor_X_SetPulse();
          Ks_Count = Ks_Divisor;
        }
        Ks_Count--;
      }

      if (Mode == Mode_Feed_Right)
      {
        if (Ks_Count == 0)
        {
          Motor_X_SetPulse();
          Ks_Count = Ks_Divisor;
        }
        Ks_Count--;
      }

      if (Mode == Mode_Thread_Left)
      {
        if (Ks_Count == 0)
        {
          Motor_X_Forward();
          Motor_X_SetPulse();
          Km_Count = Km_Count - Km_Divisor;
          if (Km_Count < 1)
          {
            Km_Count = Km_Count + 10000;
            Ks_Count = Ks_Divisor + 1;
          }
          else
          {
            Ks_Count = Ks_Divisor;
          }
        }
        Ks_Count--;
      }

      if (Mode == Mode_Thread_Right)
      {
        if (Ks_Count == 0)
        {
          Motor_X_Reverse();
          Motor_X_SetPulse();
          Km_Count = Km_Count - Km_Divisor;
          if (Km_Count < 1)
          {
            Km_Count = Km_Count + 10000;
            Ks_Count = Ks_Divisor + 1;
          }
          else
          {
            Ks_Count = Ks_Divisor;
          }
        }
        Ks_Count--;
      }
    }
  }
}

//*******************************************************************

 

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

Изменено пользователем orel.kom

Поделиться сообщением


Ссылка на сообщение
Дата: (изменено)
Цитата

универсальные поворотные столы с передаточным отношением 1:90

А это идея! Использовать поворотный стол в качестве УДГ (для нарезания шестеренок).

За скетчи спасибо.
orel.kom можно задать вам вопрос?
Какой микрошаг установлен в драйвере для вашего ШД (1:2, 1:4, 1:8, ...)?

Сегодня брал разные ШД и пробовал раскрутить их на максимальную скорость (путем установки минимальной задержки между шагами, между сигналами STEP).
На самом "быстром" ШД удалось добиться стабильной работы с задержкой мешду шагами в 100 микросекунд. Т.е. это получается 10000 шагов ШД в секунду. На двигателе был выставлен микрошаг 1:8, 200 полных шагов на оборот.
Количество шагов на один полный оброт двигателя: 200*8 = 1600 шагов на оборот.
Количество обротов в секунду: 10000 / 1600 = 6,25 об./сек.
Количество оборотов в минуту: 6.25 * 60 = 375 об./мин.

Изменено пользователем Volmir

Поделиться сообщением


Ссылка на сообщение
Дата: (изменено)
20 часов назад, Volmir сказал:

А это идея! Использовать поворотный стол в качестве УДГ (для нарезания шестеренок).

За скетчи спасибо.
orel.kom можно задать вам вопрос?
Какой микрошаг установлен в драйвере для вашего ШД (1:2, 1:4, 1:8, ...)?

Сегодня брал разные ШД и пробовал раскрутить их на максимальную скорость (путем установки минимальной задержки между шагами, между сигналами STEP).
На самом "быстром" ШД удалось добиться стабильной работы с задержкой мешду шагами в 100 микросекунд. Т.е. это получается 10000 шагов ШД в секунду. На двигателе был выставлен микрошаг 1:8, 200 полных шагов на оборот.
Количество шагов на один полный оброт двигателя: 200*8 = 1600 шагов на оборот.
Количество обротов в секунду: 10000 / 1600 = 6,25 об./сек.
Количество оборотов в минуту: 6.25 * 60 = 375 об./мин.

Микрошаг 1:8. Один оборот вала шагового двигателя равен 1600 шагов. Одному  обороту вала делительной головки соответствует 200х800х40=64000 микрошагов шагового двигателя. При загрузки моего приспособленного скетча нарезка шестерни Z=6 зубьев, произвожу при оборотах фрезы 50-60 об./ мин, можно раза в два повысить , но на грани  срыва вращение шагового двигателя и, больше 60 об/мин фрезы, проявляется слабость шагового двигателя вплоть  до возможности остановить рукой вращение заготовки. При большем количестве зубьев можно увеличить обороты фрезы. Я не знаю как сюда загрузить видео, можно было бы показать как все работает, но модератор блокирует мое сообщение, когда использую яндекс диск. 

Изменено пользователем orel.kom

Поделиться сообщением


Ссылка на сообщение

Вариант сервы на поворотку, 3000 оборотов 

Не новая , но еще поработает

http://directlot.ru/lot.php?id=495103

Поделиться сообщением


Ссылка на сообщение
Дата: (изменено)

#define motorStepPin   45           // Output signal to step the motor
#define motorDirPin    47           // Output signal to set direction
#define motorEnablePin 49           // Output pin to power up the motor

_______ степпин и энеблпин в схеме наоборот. Помоему в скече не верно в вашем. Пины нужно местами поменять в скече( у кого не работает)

Ко всему нужно понимать, что при уменьшении кол-ва зубов нужно занижать сильно кол-во оборотов шпиндиля, так как при кол-ва шагов 32000 и более шд на максималках уже

 

Изменено пользователем Nixon777

Поделиться сообщением


Ссылка на сообщение
6 часов назад, Nixon777 сказал:

степпин и энеблпин в схеме наоборот. Помоему в скече не верно в вашем. Пины нужно местами поменять в скече( у кого не работает)

Спасибо!
В оригинальном скетче я так же поменял пины (чтобы было однозначное толкование, как на схеме подключения):
 

#define motorStepPin   49           // Пин выходящего сигнала STEP для шагового мотора
#define motorDirPin    47           // Пин выходящего сигнала DIR для шагового мотора
#define motorEnablePin 45           // Пин выходящего сигнала ENABLE для шагового мотора

 

Поделиться сообщением


Ссылка на сообщение

Столкнулся с проблемой мощности, шд держится рукой. Как на это повлиять? 

Поделиться сообщением


Ссылка на сообщение

1566382902_.thumb.png.cf82c3fbca6af920f79ea20091949582.png

Крутящий момент шд  сильно падает с оборотами, по факту максимально без потерь можно получить всего 250 оборотов в минуту

Поделиться сообщением


Ссылка на сообщение

У меня такой вопрос : как прописать в скетче другой экран, у меня просто есть TFT LCD 3.5" с  тачскрином как его приспособить что бы коректно все показывал?

Поделиться сообщением


Ссылка на сообщение

У меня все заработало с ходу и схавало и 40 делительная головка и 90 стол

Поделиться сообщением


Ссылка на сообщение
В 25.08.2020 в 21:43, Proffkrivbass сказал:

У меня такой вопрос : как прописать в скетче другой экран, у меня просто есть TFT LCD 3.5" с  тачскрином как его приспособить что бы коректно все показывал?

В данной версии прошивки (программы на Ардуино) код отвечающий за управление шаговым двигателем находится вместе с кодом, отвечающим за вывод информации на экран и обработку сигналов с кнопок управления.
Для подключения другого экрана надо кардинально переработать проект:
 - подключить новый вид экрана
 - подключить новое управление (в случае если будете отказываться от кнопок LCD Keypad Shield)
 - обеспечить форматирование данных и вывод информации на новый дисплей

Поделиться сообщением


Ссылка на сообщение

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

 

Поделиться сообщением


Ссылка на сообщение

Один из пользователей форума поставил на свою делительную голову (1:40) код программного обеспечения из этой темы.
У него удалось как нарезать шестеренки, так и делить окружность на части.
Шестеренки он нарезал на скорости 80 оборотов заготовки в минуту, делал несколько размеров зубьев: 70, 43; модуль - 0,4.

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

large.IMG_20200325_162326.jpg.e87bb2d9f87f7424b7f7633adde215ca.jpg.6f175117296b2f7d4ebfe6d65e5cafd6.jpg

large.52906151_2020-03-2709-31-55.png.f5974640087eec5e441461ca92ba341a.png

large.IMG_20200325_162311.jpg.b2a9f36fddda824931546a5c29eaabb6.jpg.9514ca1c5b4bf4bd9dd26e9696552987.jpg

large.1691386671_2020-03-2709-31-16.png.47b22c985d83dc29d9b9490673fb1d06.png

Поделиться сообщением


Ссылка на сообщение

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу


×
×
  • Создать...

Важная информация

Мы разместили cookie-файлы на ваше устройство, чтобы помочь сделать этот сайт лучше. Вы можете изменить свои настройки cookie-файлов, или продолжить без изменения настроек.