Обсуждаем цифровые устройства...
Ответить

Ардуино намоточный станок

Пт июл 13, 2018 23:39:51

Уважаемые форумчане, прошу оценить идею и реализацию намоточного приспособления на базе контроллера ATmega328.
Сейчас думаю что улучшить в этом проекте.
Электроника в целом устраивает, хотя если есть дельные предложения то слушаю.
Механика требует доработки в плане перехода на типовые узлы типа муфты, резьбовые валы и т.д.
То есть стандартные детали которые применяются в 3D принтерах и различных дешевых ЧПУ.
Цель повысить надежность и уменьшить влияние сборки.
Код требует доработки в плане понятности, а так же есть моменты по управлению ШД через прерывания которые мне не нравятся.
Возможно кто скажет как такое управление реализуется в 3Д принтерах, граверах и т.д.
В целом интересны все Ваши предложения и замечания.

Изображение

Код:
/* Name: Winding machine   
   Description: Arduino ATmega 328P + Stepper motor control CNC Shield v3 DRV8825 + 1602 LCD I2C menu + Encoder KY-040
   Author:      TDA
   Ver:         1.0
   Date:        07/07/2018

       Arduino pinout diagram:
          _______________
         |      USB      |
         |           AREF|
         |            GND|
         |             13| DIR A
         |RESET        12| STEP A
         |3V3         #11|
         |5V          #10|
         |GND          #9|
         |VIN           8| EN STEP
         |               |
         |              7| DIR Z
         |             #6|
         |A0           #5| ENCODER DT
         |A1            4| STEP Z
         |A2      INT1 #3| ENCODER SW
         |A3      INT0  2| ENCODER CLK
 I2C LCD |A4 SDA     TX 1|
 I2C LCD |A5 SCL     RX 0|
         |_______________|               
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define ENC_CLK   2 // Деректива #define дает имя константе
#define ENC_SW    3
#define STEP_Z    4
#define ENC_DT    5
#define DIR_Z     7
#define EN_STEP   8
#define STEP_A    12
#define DIR_A     13

byte up[8] =   {0b00100,0b01110,0b11111,0b00000,0b00000,0b00000,0b00000,0b00000};   // Свой символ ⯅
byte down[8] = {0b00000,0b00000,0b00000,0b00000,0b00000,0b11111,0b01110,0b00100};   // Свой символ ⯆

volatile int Encoder_Dir;                                 // Направление вращения энкодера
volatile boolean Push_Button, Var_Set, DC, AutoWindStart; // Нажатие кнопки; режим установки значения; формирование сигнала STEP; работает подпрограмма автонамотки
volatile boolean Pause;                                   // Флаг паузы в режиме автонамотка   
volatile int i;                                           // Счетчик кол-ва заходов в прерывание таймера
char Str_Buffer[22];                                      // Буфер для функции sprintf
byte LCD_Column, LCD_Row, Symbol_Code, Motor_Num;         // Номер столбца и строки LCD; код символа https://i.stack.imgur.com/oZhjJ.gif; номер шагового двигателя
long int ActualShaftPos, ActualLayerPos;                  // Текущие позиции двигателей вала и укладчика
int Actual_Turn = 0, Actual_Layer = 0;                    // Текущий виток и слой при автонамотке
int Shaft_Pos, Lay_Pos, Set_Turns, Set_Step, Set_Speed=1, Set_Layers, Step_Mult=1;  // Переменные изменяемые на экране
byte Menu_Index = 0;                                      // Переменная хранит номер текущей строки меню

enum menu_states {Autowinding, PosControl, TurnsSet, StepSet, SpeedSet, LaySet, Start, Cancel, ShaftPos, LayPos, StepMul, PosCancel}; // Нумерованный список строк экрана

struct MenuType {                       // Структура описывающая меню
  byte Screen;                          // Индекс экрана
  byte string_number;                   // Номер строки на экране
  char format[22];                      // Формат строки
  char format_Set_var[6];               // Формат значения при вводе переменной
  int  *param;                          // Указатель на адрес текущей переменной изменяемой на экране
  int  var_Min;                         // Ограничение значения переменной снизу
  int  var_Max;                         // Ограничение значения переменной сверху
  byte param_coef;};                    // Размерный коэффициент значения переменной

const struct MenuType Menu[] = {        // Объявляем переменную Menu пользовательского типа MenuType и доступную только для чтения
  {0,  0,  "  AUTOWINDING        ", ""      ,NULL,        0,      0,      0 },    // "> AUTOWINDING   "
  {0,  1,  "  POS CONTROL        ", ""      ,NULL,        0,      0,      0 },    // "> POS CONTROL   "
  {2,  0,  "  TURNS:  %03d       ", "%03d"  ,&Set_Turns,  1,      999,    1 },    // "> TURNS: >000<  "
  {2,  1,  "  STEP: 0.%04d       ", "%04d"  ,&Set_Step,   1,      200,    35},    // "> STEP:>0.0000<↓" 
  {4,  0,  "  SPEED:  %03d       ", "%03d"  ,&Set_Speed,  1,      100,    1 },    // "> SPEED: >000< ↑"
  {4,  1,  "  LAYERS: %02d       ", "%02d"  ,&Set_Layers, 1,      99,     1 },    // "> LAYERS:>00<  ↓"
  {6,  0,  "  START              ", ""      ,NULL,        0,      0,      0 },    // "> START        ↑"
  {6,  1,  "  CANCEL             ", ""      ,NULL,        0,      0,      0 },    // "> CANCEL        "
  {8,  0,  "  SH POS: %+04d      ", "%+04d" ,&Shaft_Pos,  -200,   200,    1 },    // "> SH POS:>±000< "
  {8,  1,  "  LA POS: %+04d      ", "%+04d" ,&Lay_Pos,    -999,   999,    1 },    // "> LA POS:>±000<↓"
  {10, 0,  "  STPMUL: %03d       ", "%03d"  ,&Step_Mult,  1,      100,    1 },    // "> STPMUL:>000< ↑"
  {10, 1,  "  CANCEL             ", ""      ,NULL,        0,      0,      0 },    // "> CANCEL        "   
  {12, 0,  "T%03d/%03d L%02d/%02d", ""      ,NULL,        0,      0,      0 },    // "T000/000 L00/00 "
  {12, 1,  "SP%03d ST0.%04d      ", ""      ,NULL,        0,      0,      0 },    // "SP000 ST0.0000  "
  {14, 0,  "AUTOWINDING DONE     ", ""      ,NULL,        0,      0,      0 },    // "AUTOWINDING DONE"
  {14, 1,  "PRESS CONTINUE       ", ""      ,NULL,        0,      0,      0 }};   // "PRESS CONTINUE  "
 
LiquidCrystal_I2C lcd(0x3F,16,2); // 0x3F I2C адрес для PCF8574AT, дисплей 16 символов 2 строки

void setup() {
pinMode(ENC_CLK, INPUT);    // Инициализация входов/выходов 
pinMode(ENC_SW,  INPUT);
pinMode(STEP_Z,  OUTPUT);
pinMode(ENC_DT,  INPUT);
pinMode(DIR_Z,   OUTPUT);
pinMode(EN_STEP, OUTPUT);
pinMode(STEP_A,  OUTPUT);
pinMode(DIR_A,   OUTPUT);
digitalWrite(EN_STEP, HIGH); // Запрет управления двигателями   

lcd.init();                  // Инициализация LCD
lcd.backlight();             // Включение подсветки LCD
lcd.createChar(0, up);       // Записываем символ ⯅ в память LCD
lcd.createChar(1, down);     // Записываем символ ⯆ в память LCD

cli();                                                                        // Глобальный запрет прерываний
EICRA = (1<<ISC11)|(0<<ISC10)|(0<<ISC01)|(1<<ISC00);                          // Настройка срабатывания прерываний: INT0 по изменению сигнала, INT1 по спаду сигнала; ATmega328/P DATASHEET стр.89
EIMSK = (1<<INT0)|(1<<INT1);                                                  // Разрешение прерываний INT0 и INT1; ATmega328/P DATASHEET стр.90
EIFR = 0x00;                                                                  // Сбрасываем флаги внешних прерываний; ATmega328/P DATASHEET стр.91
TCCR1A=(0<<COM1A1)|(0<<COM1B1)|(0<<COM1A0)|(0<<COM1B0)|(0<<WGM11)|(0<<WGM10); // Настройка таймера/счетчика 1: нормальный режим работы порта, OC1A/OC1B отключены; ATmega328/P DATASHEET стр.170-172
TCCR1B=(0<<WGM13)|(1<<WGM12)|(0<<CS12)|(0<<CS11)|(1<<CS10);                   // Режим работы таймера/счетчика - CTC (очистить таймер при достижении значения в регистре сравнения OCR1A)
OCR1A = 1000;                                                                 // Значение в регистре OCR1A определяет частоту входа в прерывание таймера и устанавливает скрость вращения двигателей
sei();                                                                        // Глобальное разрешение прерываний

lcd.clear();                                                                             
sprintf(Str_Buffer, Menu[0].format);
lcd.print(Str_Buffer);                                                        // Выводим первую строку на экран
lcd.setCursor(0,1);
sprintf(Str_Buffer, Menu[1].format);
lcd.print(Str_Buffer);                                                        // Выводим вторую строку на экран
PrintSymbol(0,0,0x3E);}                                                       // Выводим символ ">" на 0,0 LCD

void loop() {
if (Encoder_Dir != 0) {                                                       // Проверяем изменение позиции энкодера
  switch (Menu_Index) {                                                       // Если позиция энкодера изменена то меняем Menu_Index и выводим экран
    case Autowinding:  Menu_Index = constrain(Menu_Index + Encoder_Dir, Autowinding, PosControl);   break;
    case PosControl:   Menu_Index = constrain(Menu_Index + Encoder_Dir, Autowinding, PosControl);   break;
    case TurnsSet:     Menu_Index = constrain(Menu_Index + Encoder_Dir, TurnsSet, Cancel);          break;
    case StepSet:      Menu_Index = constrain(Menu_Index + Encoder_Dir, TurnsSet, Cancel);          break; 
    case SpeedSet:     Menu_Index = constrain(Menu_Index + Encoder_Dir, TurnsSet, Cancel);          break;
    case LaySet:       Menu_Index = constrain(Menu_Index + Encoder_Dir, TurnsSet, Cancel);          break;         
    case Start:        Menu_Index = constrain(Menu_Index + Encoder_Dir, TurnsSet, Cancel);          break;
    case Cancel:       Menu_Index = constrain(Menu_Index + Encoder_Dir, TurnsSet, Cancel);          break;
    case ShaftPos:     Menu_Index = constrain(Menu_Index + Encoder_Dir, ShaftPos, PosCancel);       break;
    case LayPos:       Menu_Index = constrain(Menu_Index + Encoder_Dir, ShaftPos, PosCancel);       break;
    case StepMul:      Menu_Index = constrain(Menu_Index + Encoder_Dir, ShaftPos, PosCancel);       break;
    case PosCancel:    Menu_Index = constrain(Menu_Index + Encoder_Dir, ShaftPos, PosCancel);       break;}
    Encoder_Dir = 0; PrintScreen();}

if (Push_Button == true) {                                                     // Проверяем нажатие кнопки
  switch (Menu_Index)    {                                                     // Если было нажатие то выполняем действие соответствующее текущей позиции курсора
    case Autowinding:  Menu_Index = TurnsSet;                                                                                               break;
    case PosControl:   Menu_Index = ShaftPos;                                                                                               break;
    case TurnsSet:     SetQuote(9,13); Push_Button=false; Var_Set=true; while(!Push_Button){Set_Var();} Var_Set=false; ClearQuote(9,13);    break;
    case StepSet:      SetQuote(7,14); Push_Button=false; Var_Set=true; while(!Push_Button){Set_Var();} Var_Set=false; ClearQuote(7,14);    break; 
    case SpeedSet:     SetQuote(9,13); Push_Button=false; Var_Set=true; while(!Push_Button){Set_Var();} Var_Set=false; ClearQuote(9,13);    break;
    case LaySet:       SetQuote(9,12); Push_Button=false; Var_Set=true; while(!Push_Button){Set_Var();} Var_Set=false; ClearQuote(9,12);    break;                                   
    case Start:        Push_Button = false; AutoWindStart = true; AutoWindingPrg(); AutoWindStart = false;                                  break;
    case Cancel:       Menu_Index = Autowinding;                                                                                            break;
    case ShaftPos:     SetQuote(9,14); Push_Button=false; Var_Set=true; digitalWrite(EN_STEP, LOW); Motor_Num = 1;
                       while(!Push_Button){Set_Var(); ActualShaftPos=MotorMove(*Menu[Menu_Index].param, ActualShaftPos);}
                       Var_Set=false; digitalWrite(EN_STEP, HIGH); ClearQuote(9,14);                                                        break;   
    case LayPos:       SetQuote(9,14); Push_Button=false; Var_Set=true; digitalWrite(EN_STEP, LOW); Motor_Num = 2;
                       while(!Push_Button){Set_Var(); ActualLayerPos=MotorMove(*Menu[Menu_Index].param, ActualLayerPos);}
                       Var_Set=false; digitalWrite(EN_STEP, HIGH); ClearQuote(9,14);                                                        break;     
    case StepMul:      SetQuote(9,13);Push_Button=false;Var_Set=true; while(!Push_Button){Set_Var();} Var_Set=false; ClearQuote(9,13);      break;   
    case PosCancel:    Menu_Index = Autowinding; Shaft_Pos = 0; Lay_Pos = 0; Step_Mult = 1; ActualShaftPos = 0; ActualLayerPos = 0;         break;}
    Push_Button = false; PrintScreen();}}

ISR(INT0_vect) {  // Вектор прерывания от энкодера
byte Enc_Temp;    // Временная переменная для хранения состояния порта
Enc_Temp = PIND & 0b00100100;                                                                                              // Маскируем все пины порта D кроме PD2 и PD5
     if (Enc_Temp == 0b00000100 || Enc_Temp == 0b00100000) {Encoder_Dir =  1;}                                             // +1 - шаг по часовой
else if (Enc_Temp == 0b00000000 || Enc_Temp == 0b00100100) {Encoder_Dir = -1;}                                             // -1 - шаг против часовой
else    {Encoder_Dir = 0; return;}                                                                                         // Если случайно(?!) вошли в прерывание то выходим
     if (Var_Set == true && Encoder_Dir != 0) {                                                                            // Если находимся в режиме изменения переменной
        *Menu[Menu_Index].param += Encoder_Dir; Encoder_Dir = 0;                                                           // то меняем ее сразу и
        *Menu[Menu_Index].param = constrain(*Menu[Menu_Index].param, Menu[Menu_Index].var_Min, Menu[Menu_Index].var_Max);} // ограничиваем в диапазоне var_Min ÷ var_Max
     if (AutoWindStart == true && Encoder_Dir != 0) {Set_Speed = constrain(Set_Speed + Encoder_Dir, 1, 100);}}             // Если повернуть энкодер во время автонамотки
                                                                                                                           // то меняем значение скорости
ISR(INT1_vect){                               // Вектор прерывания от кнопки энкодера
  Push_Button = true;
  if (AutoWindStart == true) {Pause = true;}  // Если нажать кнопку энкодера во время автонамотки то выставляем флаг паузы
  else return;}

ISR(TIMER1_COMPA_vect) {                      // Вектор прерывания от таймера/счетчика 1
  i++;                                        // Счетчик кол-ва заходов в прерывание
  DC =! DC;                                   // Первое прерывание устанавливает STEP следующее - сбрасывает
    if      (Motor_Num == 1) {
      if (DC == true) {PORTD |= 0b00010000;}  // STEP_Z
      else            {PORTD &= 0b11101111;}}
    else if (Motor_Num == 2) {
      if (DC == true) {PORTB |= 0b00010000;}  // STEP_A
      else            {PORTB &= 0b11101111;}}}

void PrintScreen() {                          // Подпрограмма: Выводим экран на LCD
  static byte Prev_Screen;
  if (Menu[Menu_Index].Screen != Prev_Screen) {
  lcd.clear();
  sprintf(Str_Buffer, Menu[Menu[Menu_Index].Screen].format, *Menu[Menu[Menu_Index].Screen].param * Menu[Menu[Menu_Index].Screen].param_coef);
  lcd.print(Str_Buffer);
  lcd.setCursor(0, 1);
  sprintf(Str_Buffer, Menu[Menu[Menu_Index].Screen + 1].format, *Menu[Menu[Menu_Index].Screen + 1].param * Menu[Menu[Menu_Index].Screen + 1].param_coef);
  lcd.print(Str_Buffer);
  Prev_Screen = Menu[Menu_Index].Screen;}
  if      (Menu_Index & 1) {PrintSymbol(0,1,0x3E); PrintSymbol(0,0,0x20);}  // Если индекс меню нечетный выводим курсор на вторую строку
  else                     {PrintSymbol(0,0,0x3E); PrintSymbol(0,1,0x20);}  // Иначе выводим курсор на первую строку 
  switch (Menu_Index) {                                                     // Выводим стрелки ⯅⯆ на соответствующих строках меню
    case TurnsSet:     PrintSymbol(15,1,1);                      break;
    case StepSet:      PrintSymbol(15,1,1);                      break; 
    case SpeedSet:     PrintSymbol(15,1,1); PrintSymbol(15,0,0); break;
    case LaySet:       PrintSymbol(15,1,1); PrintSymbol(15,0,0); break;       
    case Start:        PrintSymbol(15,0,0);                      break;   
    case Cancel:       PrintSymbol(15,0,0);                      break;   
    case ShaftPos:     PrintSymbol(15,1,1);                      break;
    case LayPos:       PrintSymbol(15,1,1);                      break;
    case StepMul:      PrintSymbol(15,0,0);                      break;
    case PosCancel:    PrintSymbol(15,0,0);                      break;}}
                                                                                                         
void PrintSymbol(byte LCD_Column, byte LCD_Row, byte Symbol_Code) { // Подпрограмма: Выводим символ на экран
  lcd.setCursor(LCD_Column, LCD_Row);
  lcd.write(byte(Symbol_Code));}

void SetQuote   (int First_Cur, int Second_Cur) {                 // Подпрограмма: Выводим выделение изменяемой переменной на LCD
  PrintSymbol(First_Cur,  Menu[Menu_Index].string_number,0x3E);   // Выводим символ >
  PrintSymbol(Second_Cur, Menu[Menu_Index].string_number,0x3C);   // Выводим символ <
  PrintSymbol(0,          Menu[Menu_Index].string_number,0x20);}  // Стираем основной курсор

void ClearQuote (int First_Cur, int Second_Cur) {                 // Подпрограмма: Стираем выделение изменяемой переменной на LCD
  PrintSymbol(First_Cur,  Menu[Menu_Index].string_number,0x20);   // Стираем символ >
  PrintSymbol(Second_Cur, Menu[Menu_Index].string_number,0x20);   // Стираем символ <
  PrintSymbol(0,          Menu[Menu_Index].string_number,0x3E);}  // Выводим основной курсор     

void Set_Var() {                                                  // Подпрограмма: Выводим новое значение переменной на LCD
  static int Previous_Param;
    if (*Menu[Menu_Index].param != Previous_Param){
      lcd.setCursor(10, Menu[Menu_Index].string_number);
      sprintf(Str_Buffer, Menu[Menu_Index].format_Set_var, *Menu[Menu_Index].param * Menu[Menu_Index].param_coef);
      lcd.print(Str_Buffer);
      Previous_Param = *Menu[Menu_Index].param;}}

int MotorMove(int Move_Var, long Actual_Rot) {                    // Подпрограмма: Движение шагового двигателя до заданной координаты
long Rotation;       
  Rotation = Move_Var * Step_Mult - Actual_Rot;
  switch(Motor_Num) {
    case 1: if      (Rotation > 0) {PORTD |= 0b10000000; TCNT1=0; TIMSK1=2; while(i<32){} TIMSK1=0; TCNT1=0; Actual_Rot++; i=0; DC=false;}
            else if (Rotation < 0) {PORTD &= 0b01111111; TCNT1=0; TIMSK1=2; while(i<32){} TIMSK1=0; TCNT1=0; Actual_Rot--; i=0; DC=false;}
            else     TIMSK1 = 0; i = 0; DC = false; break;
    case 2: if      (Rotation > 0) {PORTB |= 0b00100000; TCNT1=0; TIMSK1=2; while(i<32){} TIMSK1=0; TCNT1=0; Actual_Rot++; i=0; DC=false;}
            else if (Rotation < 0) {PORTB &= 0b11011111; TCNT1=0; TIMSK1=2; while(i<32){} TIMSK1=0; TCNT1=0; Actual_Rot--; i=0; DC=false;}
            else     TIMSK1 = 0; i = 0; DC = false; break;}                   
return Actual_Rot;}

void MotorStep(int StepQuant, int Dir) {                          // Подпрограмма: Движение шагового двигателя на заданное число шагов
int Temp_Step = 0;
  switch(Motor_Num) {
    case 1:      if (Dir == 1)  {PORTD |= 0b10000000;}
            else if (Dir == -1) {PORTD &= 0b01111111;} 
    case 2:      if (Dir == 1)  {PORTB |= 0b00100000;} 
            else if (Dir == -1) {PORTB &= 0b11011111;}}                           
while (Temp_Step < StepQuant) {i=0; TCNT1=0; TIMSK1=2; while(i<32){} TIMSK1=0; TCNT1=0; DC=false; Temp_Step++;
    if (Encoder_Dir != 0) {sprintf(Str_Buffer, "%03d", Set_Speed); lcd.setCursor(2,1); lcd.print(Str_Buffer);
                           Encoder_Dir=0; OCR1A = 32767/Set_Speed;}}}

void AutoWindingPrg() {                                           // Подпрограмма автоматической намотки
  int Dir = 1;
  digitalWrite(EN_STEP, LOW);                                     // Разрешение управления двигателями
  OCR1A = 32767/Set_Speed;                                        // Записью в OCR1A устанавливаем скорость вращения двигателей
  PrintWendingScreen();
while (Actual_Layer < Set_Layers)                                 // Пока текущее кол-во слоев меньше заданного проверяем сколько сейчас витков
{
  while (Actual_Turn < Set_Turns)                                 // Пока текущее кол-во витков меньше заданного продолжаем мотать
    {
    Motor_Num = 1; MotorStep(200, Dir);
    Motor_Num = 2; MotorStep(Set_Step, Dir);
    Actual_Turn++;
    sprintf(Str_Buffer, "%03d", Actual_Turn);
    lcd.setCursor(1, 0);
    lcd.print(Str_Buffer);
      if (Pause == true)                                                // Проверяем не нужно ли сделать паузу
        {
          Push_Button = false;
          Pause = false;
          lcd.setCursor(0, 1);
          sprintf(Str_Buffer, Menu[15].format);                         // "PRESS CONTINUE  "
          lcd.print(Str_Buffer);
          while (Push_Button == false){}
          lcd.setCursor(0, 1);
          sprintf(Str_Buffer, Menu[13].format, Set_Speed, Set_Step*35); // "SPXXX ST0.XXXX  "
          lcd.print(Str_Buffer);           
          Push_Button = false;
          Pause = false;
        }
    }   
    Actual_Layer++;
    Actual_Turn = 0; 
    lcd.setCursor(10, 0);
    sprintf(Str_Buffer, "%02d", Actual_Layer);
    lcd.print(Str_Buffer);
    if (Actual_Layer == Set_Layers) continue;
    lcd.setCursor(0, 1);
    sprintf(Str_Buffer, Menu[15].format);                            // "PRESS CONTINUE  "
    lcd.print(Str_Buffer);
    while (Push_Button == false) {}
    Push_Button = false;
    Pause = false;
    if      (Dir ==  1) Dir = -1;
    else if (Dir == -1) Dir =  1;
    PrintWendingScreen();
  } 
  lcd.setCursor(0, 1);
  sprintf(Str_Buffer, Menu[14].format);                             // "AUTOWINDING DONE"
  lcd.print(Str_Buffer);
  digitalWrite(EN_STEP, HIGH);
  while (Push_Button == false) {}
  Push_Button = false;
  Pause = false;
  Menu_Index = Autowinding;
  Actual_Layer = 0;
}

void PrintWendingScreen() { // Подпрограмма вывода экрана автонамотки
  lcd.clear();
  sprintf(Str_Buffer, Menu[12].format, Actual_Turn, Set_Turns, Actual_Layer, Set_Layers);
  lcd.print(Str_Buffer);
  lcd.setCursor(0, 1);
  sprintf(Str_Buffer, Menu[13].format, Set_Speed, Set_Step*35);
  lcd.print(Str_Buffer);  }
 

Re: Ардуино намоточный станок

Сб июл 14, 2018 04:18:01

В 3д принтерах и прочих ЧПУ, ПО которых имеет родственные связи с grbl, управление движением происходит в главном цикле, путем счета времени и выдачи в нужные моменты счетных импульсов в контроллеры ШД. По прерываниям обрабатываются особые ситуации, вроде срабатывания концевых выключателей, щупов, аварийной кнопки.

Re: Ардуино намоточный станок

Сб июл 14, 2018 07:04:11

Всю эту сложнейшую хрень выгоднее заменить на 3 счетчика и 3 светодиодные матрицы. (механика прежняя).
Ради намотки одной катушечки 1 раз в 5 лет, строить сложнеюшую электронную систему нет никакого смысла! Глупо ...
Тем более: готовые катушечки с нужными параметрами продаются в магазинах радиодеталей!

Re: Ардуино намоточный станок

Сб июл 14, 2018 10:11:04

В 3д принтерах и прочих ЧПУ, ПО которых имеет родственные связи с grbl, управление движением происходит в главном цикле, путем счета времени и выдачи в нужные моменты счетных импульсов в контроллеры ШД

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

Сложность понятие относительное для кого то этот проект очень сложен, другому это детский лепет. Если делать механический со счетчиком витков то тогда укладчик также придется заменить на "хрень" с роликами и ремешками и подбором диаметров роликов и т.д.... проходили.
Готовые катушечки продаются ну-ну и на заказ делают и что?

Re: Ардуино намоточный станок

Сб июл 14, 2018 19:38:20

Можно. И даже g-код он тоже получает во время работы, регулярно вынимая байты из uart.

Re: Ардуино намоточный станок

Пт июл 20, 2018 08:57:20

Есть мнение что от I2C интерфейса стоит избавиться и перейти к полубайтовому режиму вывода на LCD. Последовательный работает медленно и использует прерывания, что не способствует многозадачности хоть и мнимой.

Re: Ардуино намоточный станок

Пт июл 20, 2018 20:20:26

Так тут станок, фактически, одноосевой. Ему не надо выдерживать точнные компоненты скорости, в отличие от фрезера, подающего режущий инструмент по сложной кривой. Вряд ли интерфейсные задержки могут чему-то помешать.

Ардуино намоточный станок

Ср авг 01, 2018 10:30:07

Печатная плата устройства, разработанная в Sprint Layout 6.0 одним хорошим человеком.
Изображение
Вложения
Winding Board.lay6
(138.65 KiB) Скачиваний: 1003
Ответить