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

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

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

Реализация электронной зубофрезерной делительной головы на Ардуино.
Принцип работы: Арудино выступает в качестве электронного редуктора между шпинделем и УДГ. Импульсы с энкодера (установленного на шпинделе) обрабатываются и преобразуются (согласно требуемого коэффициента редукции) в сигналы для шагового двигателя (установленного на УДГ).
Для успешного запуска проекта необходима настройка и соединение вместе трех компонентов: механических, электронных и программных.

Код программы: https://github.com/volmir/dividing-device/blob/master/dividing_device/dividing_device.ino

Основной функционал:

  • Нарезание шестеренок червячной фрезой
  • Деление окружности на части (для нарезания шестенок дисковой фрезой и/или получения квадрата, шестигранника, снятия лысок и т.п.)

Вспомогательный функционал:

  • Индикация текущих оборотов шпинделя
  • Смена направления вращения заготовки
  • Вращение заготовки в делительной головке (необходимое для настройки, центрирования, проворота и т.п.)

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

  • энкодер
  • УДГ (или четвертая ось для станков с ЧПУ)
  • шаговый двигатель
  • STEP/DIR драйвер управления ШД
  • Arduino Mega или Arduino Uno
  • LCD Keypad Shield
  • питание для драйвера ШД
  • питание для Ардуино, энкодера

Порядок настройки и запуска проекта:

  1. Собрать все механические и электронные компоненты вместе
  2. Скачать последнюю версию кода ПО с Github https://github.com/volmir/dividing-device/blob/master/dividing_device/dividing_device.ino
  3. Прописать параметры вашей конфигурации в скетче для Ардуино.
  4. Загрузить код, скомпилировать программу, залить прошивку в Ардуино
  5. Проверить функционал в работе (нет ли ошибок, неточностей, некорретной работы отдельных элементов)
  6. При необходимости повторять п. 1. - 5. до достижения нужного результата
  7. Собирать все в прочный постоянный защищенный корпус
  8. Использовать в работе

Перемещение по меню, ввод параметров реализованы на LCD Keypad Shield. Доступно пять кнопок (шестая - "Reset", перезагрузка контроллера), нажимая на них задаем нужные параметры. Кнопки "Вверх", "Вниз" отвечают за выбор пунктов меню, "Вправо", "Влево" - уменьшение/увеличение/смена параметра, "Select" - запуск/остановка выбранной программы.

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

Синхронность вращения (без потери шагов) зависит от:

  • Разрешения энкодера (количество линий на полный оборот)
  • Передаточного соотношения в УДГ
  • Установленном числе микрошагов ШД
  • Скорости вращения шпинделя
  • Требуемого числа зубьев на шестеренке

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

Ограничения в электронике, механике

Учет этих факторов позволит добиться устойчивого сихронного вращения заготовки и шпинделя; избежать брака при нарезении заготовки; исключить пропуск шагов ШД при длительной работе:

  • Быстродействие Ардуино: ограничения частоты обработки сигналов, получаемых от энкодера.
  • Жесткость и надежность крепления заготовки: смещение детали в процессе обработки приведет к срезанию зубьев червячной фрезой.
  • В процессе нарезания зубьев фрезой возникают ударные нагрузки. При малом крутящем моменте на ШД он может кратковременно останавливаться, вал его может смещаться (соответственно, это приведет к браку).
  • Чем больше нарезаемый модуль и тверже материал заготовки - тем большие нагрузки возникают в системе СПИД.
  • Чем меньше обороты фрезы, тем больше вероятность успешной обработки детали (Ардуино успевает обработать все входящие и исходящие сигналы).
  • Коэффициент редукции на УДГ: чем меньше этот коэффициент, тем более мощный ШД следует применять (для устойчивого удержания заготовки).
  • Большой коэффициент редукции на УДГ (1:40, как на типовых решениях) позволяет удерживать заготовку от проворота меньшим усилием на ШД. Но, при этом должна возрастать скорость вращения шагового двигателя (упираемся в ограничение частоты обработки сигналов в Ардуино).
  • Максимальная скорость вращения шагового двигателя обычно ограничена 400-600 об./мин.
  • Требуемая скорость вращения ШД падает при увеличении числа нарезаемых зубьев (за один момент времени шаговому двигателю нужно провертуть вал на меньший угол).

Возможно частичное (без подсоединения энкодера) использование делительной головы. Нарезание шестеренок в этом случае возможно путем простого деления окружности на части. В случае, когда число требуемых шагов ШД (необходимых для одного полного оборота заготовки) без остатка делится на число частей, на которые делится окружность - то деление будет точным. В случае деления с остатком эта погрешность тем меньше, чем больше передаточное соотношение между ШД и УДГ; чем более мелкий микрошаг выставлен на драйвере.

Технические детали (по коду программы):

  • Функция слежения за положением заготовки (в зависимости от получаемых импульсов с энкодера) реализована на прерываниях.
  • При делении окружности на части происходит программное подстраивание, округление числа шагов для достижения полного оборота заготовки (с минимально возможной погрешностью).
  • Управление шагами ШД реализовано путем подачи сигнала STEP на контроллер; скорость вращения (обороты в минуту) зависит от задержек между посылаемыми сигналами (этот параметр настраивается в конфигурации прошивки).

 

Примеры расчетов (для выбора энкодера и связки УДГ с ШД).

------------------------------------------------------------------------------------------------------------------------------------------------------

Пример 1: Расчет минимально возможного количества зубьев в шестерне

Коэффициент (К) = "Количество линий энкодера" * "Требуемое количество зубьев в шестерне" / "Количество шагов ШД на один оборот заготовки"

Для устойчивой работы "К" должен быть больше (или равен) 1 (единице).

Исходные данные:

  • Энкодер на 500 линий
  • Число требуемых шагов ШД, для одного полного оборота заготовки = 200 * 4 * 6 = 4800 шагов (шаговый двигатель - 200 импульсов на оборот; выставлен микрошаг 1:4; соотношение шкивов ШД:УДГ = 1:6)
  • Число нарезаемых зубьев: 30
500 * 30 / 4800 = 3.125

Выводы:

3.125 > 1 - это значит, что на 3.125 шага энкодера необходимо сделать 1 (один) шаг ШГ.

3.125 - рациональное число (деление произошло нацело, без остатка в периоде, как, например: 10 / 3 = 3,333333...). Нарезание зубьев будет происходить успешно, без накопления погрешности.

В случае необходимости нарезать 6 зубьев расчеты будут следующими:

500 * 6 / 4800 = 0.75

Выводы: 0.75 < 1 - нарезание зубьев не может быть произведено стабильно.

------------------------------------------------------------------------------------------------------------------------------------------------------

Пример 2: Расчет возможности точного (без погрешности) нарезания зубьев заготовки

Чтобы не набегала погрешность при сихронном вращении заготовки и шпинделя - энкодер надо брать кратный количеству шагов ШД на оборот заготовки.

Исходные данные:

  • Энкодер на 500 линий
  • Число требуемых шагов ШД, для одного полного оборота заготовки = 200 * 4 * 6 = 4800 шагов (шаговый двигатель - 200 импульсов на оборот; выставлен микрошаг 1:4; соотношение шкивов ШД:УДГ = 1:6)
  • Число нарезаемых зубьев: 13
500 * 13 / 4800 = 1,3541666...

Выводы: 1,3541666... - рациональное число с остатком в периоде. При нарезании шестеренок будет накапливаться погрешность, необходимо изменить исходные параметры.

------------------------------------------------------------------------------------------------------------------------------------------------------

Пример 3: Изменим передаточное соотношение ШД : УДГ с "1 : 6" на "1 : 2"

Исходные данные:

  • Энкодер на 500 линий
  • Число требуемых шагов ШД, для одного полного оборота заготовки = 200 * 4 * 2 = 1600 шагов (шаговый двигатель - 200 импульсов на оборот; выставлен микрошаг 1:4; соотношение шкивов ШД:УДГ = 1:2)
  • Число нарезаемых зубьев: 13
500 * 13 / 1600 = 4.0625

Выводы: 4.0625 - рациональное число (деление произошло нацело, без остатка в периоде). Нарезание зубьев будет происходить успешно, без накопления погрешности.

------------------------------------------------------------------------------------------------------------------------------------------------------

Пример 4: Деление окружности на части

Исходные данные:

  • шаговый двигатель - 200 импульсов на оборот
  • выставлен микрошаг 1:4
  • соотношение шкивов ШД : УДГ = 1 : 6

Число требуемых шагов ШД, для одного полного оборота заготовки = 200 * 4 * 6 = 4800 шагов

4800 / 24 = 200

Выводы: 200 - целое число, без остатка. Значит, деление (нарезание зубьев шестерни) будет точным.

Точность зубьев в случае не целых соотношений (например: 17, 19, 27, 35, 49 зубьев) получается с небольшой погрешностью. Эта погрешность тем меньше, чем больше передаточное соотношение между ШД и УДГ; чем более мелкий микрошаг выставлен на драйвере.

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


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

Общий вид 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 для шагового мотора

 

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


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

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

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


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

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

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

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

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

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

Войти

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

Войти

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

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


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

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

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