изменить скетч с управления 8 шаговыми двигателями на 8 соленоидов
Я хочу посмотреть, смогу ли я изменить прикрепленный скетч, который управляет 8 шаговыми двигателями, на простое переключение 8 реле для ткацкого станка, который у нас есть, к сожалению, внесение этого изменения намного выше моей зарплаты, лол. Я использую arduino mega и 8-канальную релейную плату, которая, в свою очередь, отключает соленоиды, которые уже подключены к ткацкому станку, спасибо за любую помощь
Это скетч, который я пытаюсь изменить
// Интерфейс ткацкого станка для интерфейса Ashford Dobby (с использованием 8 шаговых двигателей):
// Линии Arduino 0 & 1 к интерфейсу USB
// строка 13 — педаль
// 14-43 — управление двигателем
// Общие определения:
#define MAX_CHARS 80
#define DEBUG 0
#define MOTORS 8
#define IDENTITY "Generic Dobby Interface (Jones Stepper Motor Dobby)\n"
// Данные, управляющие позициями моторов, состояниями и т. д.:
const long MOTOR_DELAY=900; // Временная задержка между шагами двигателя (уменьшайте, пока валы не будут двигаться надежно)
const long SHAFT_UP=8*170, SHAFT_DOWN=0 ; // Количество шагов двигателя, необходимое для подъема валов
unsigned long shaft_state=0x00000000, shaft_cue=0xffffffff; // отслеживание текущего & следующие желаемые состояния управления валом
long MotorActual[MOTORS], MotorWant[MOTORS]; // Текущее значение & желаемое положение для каждого из двигателей
// Определение того, какие выходные контакты подключаются к какому двигателю
char MotorPin[MOTORS][4]= // Моторы 1,3,5,7 справа, 2,4,6,8 слева: левая сторона меняет направление
{ {14,15,16,17}, {18,21,20,19}, {22,23,24,25}, {26,29,28,27}, // Двигатели 1-4
{30,31,32,33}, {34,37,36,35}, {38,39,40,41}, {42,45,44,43}}; // Двигатели 5-8
unsigned long NextMotorUpdate=0; // Таймер для следующего шага двигателя
const int ADVANCE =13; // входной контакт для ножного переключателя (имеет подтяжку, только контакт переключателя)
// Последовательный (USB) интерфейс
// Последовательный интерфейс является "Общим" Добби интерфейс:
// Хост отправляет, какие валы должны быть активированы для текущего выстрела
// Валы поднимутся при активации ножного переключателя
// Валы возвращаются, когда ножной переключатель отпущен, и кнопка "Вперед" команда возвращена хосту
// Также отвечает на "?" с "ИДЕНТИЧНОСТЬ" указано выше, показывает хозяина, который здесь
int serial_chars=0; // Количество символов в буфере последовательного ввода
char serial_string[MAX_CHARS]=""; // Буфер последовательного ввода
char Message[MAX_CHARS]=""; // Выходной буфер сообщений
long serial_time=0; // Используется для закрытия последовательной строки по тайм-ауту (таймер)
const int serial_timeout=100; // Дайджест последовательной строки после истечения времени ожидания
char serial_idle=1; // Отслеживает активность на последовательной шине
char HostPresent = 1; // устанавливаем, когда какой-то хост идентифицирован
int Button_state=1; // отслеживает изменения состояния переключателя этажей
////////////////////////////////////////////////// ///////////////////////
// Отправляем управление валом на оборудование
////////////////////////////////////////////////// ///////////////////////
void set_motor(char m,char state) // Функция самого низкого уровня для обработки шагов в двигателях
{
// sprintf(Message,"M %d @ state %d\n",m,state);
// Serial.write(Сообщение);
switch(state)
{
case 0: // Установить двигатель в неактивное состояние
digitalWrite(MotorPin[m][0], 0); digitalWrite(MotorPin[m][1], 0);
digitalWrite(MotorPin[m][2], 0); digitalWrite(MotorPin[m][3], 0);
break;
case 1: // Установить мотор в состояние 1
digitalWrite(MotorPin[m][0], 1); digitalWrite(MotorPin[m][1], 0);
digitalWrite(MotorPin[m][2], 0); digitalWrite(MotorPin[m][3], 0);
break;
case 2: // Установить состояние двигателя 2
digitalWrite(MotorPin[m][0], 1); digitalWrite(MotorPin[m][1], 1);
digitalWrite(MotorPin[m][2], 0); digitalWrite(MotorPin[m][3], 0);
break;
case 3: // Установить состояние двигателя 3
digitalWrite(MotorPin[m][0], 0); digitalWrite(MotorPin[m][1], 1);
digitalWrite(MotorPin[m][2], 0); digitalWrite(MotorPin[m][3], 0);
break;
case 4: // Установить состояние двигателя 4
digitalWrite(MotorPin[m][0], 0); digitalWrite(MotorPin[m][1], 1);
digitalWrite(MotorPin[m][2], 1); digitalWrite(MotorPin[m][3], 0);
break;
case 5: // Установить состояние двигателя 5
digitalWrite(MotorPin[m][0], 0); digitalWrite(MotorPin[m][1], 0);
digitalWrite(MotorPin[m][2], 1); digitalWrite(MotorPin[m][3], 0);
break;
case 6: // Установить состояние двигателя 6
digitalWrite(MotorPin[m][0], 0); digitalWrite(MotorPin[m][1], 0);
digitalWrite(MotorPin[m][2], 1); digitalWrite(MotorPin[m][3], 1);
break;
case 7: // Установить состояние двигателя 7
digitalWrite(MotorPin[m][0], 0); digitalWrite(MotorPin[m][1], 0);
digitalWrite(MotorPin[m][2], 0); digitalWrite(MotorPin[m][3], 1);
break;
case 8: // Установить состояние двигателя 8
digitalWrite(MotorPin[m][0], 1); digitalWrite(MotorPin[m][1], 0);
digitalWrite(MotorPin[m][2], 0); digitalWrite(MotorPin[m][3], 1);
break;
}
}
char set_motors() // Установить все двигатели в новое состояние двигателя - вызывается для каждого шага двигателя на любом двигателе
{ // возврат указывает, активен ли еще какой-либо двигатель
int m=0;
long setting, entry_setting;
char active=0;
while(m<MOTORS)
{
// Проверяем необходимое направление движения
entry_setting=setting=MotorActual[m];
if(setting==MotorWant[m]) setting=setting;
else if(setting<MotorWant[m]) setting++;
else setting--;
// Отправляем элементы управления настройками обновления
if((entry_setting!=MotorWant[m]))
{
active=1;
MotorActual[m]=setting; // обновить актуальное для текущего перемещения
setting=(setting&7)+1; // состояние set_motor 0 неактивно, 1-8 активны
set_motor(m,setting); // Установить новое состояние мотора
}
m++; // Следующий мотор
}
if(active==0)
{ // Если сделано, отключите питание двигателей, которые вернулись в пассивное состояние.
m=0;
while(m<MOTORS)
{
if(MotorActual[m]==SHAFT_DOWN)set_motor(m,0); // установка мотора в неактивное состояние
m++;
}
}
return active;
}
char run_motors() // Функция более высокого уровня для установки моторов в нужное положение
{ // возврат указывает, активны двигатели или простаивают
char active=1;
if(micros()>=NextMotorUpdate)
{ // время для обновления позиций двигателей
active=set_motors(); // Обновить все моторы
NextMotorUpdate=micros()+MOTOR_DELAY; // устанавливаем часы для следующего обновления
}
return active;
}
void set_shafts(unsigned long shafts) // Вызывается для установки желаемых положений двигателя в зависимости от того, какие валы были запрошены
{ // Это предполагает, что все валы перемещаются на одинаковую величину, на самом деле им не нужно
unsigned long bit_mask=1;
int i=0;
while(i<MOTORS)
{
if(shafts&bit_mask)MotorWant[i]=SHAFT_UP;
else MotorWant[i]=SHAFT_DOWN;
// sprintf(Message,"M %d @ хочу %d\n",i,MotorWant[i]>>8);
// Serial.write(Сообщение);
i++;
bit_mask=bit_mask<<1;
}
}
////////////////////////////////////////////////// ///////////////////////
// Preset-вызывается при инициализации
////////////////////////////////////////////////// ///////////////////////
void preset() // Может поднять все валы, чтобы показать, что он работает и т. д.
{ // Эта версия ничего не делает, по умолчанию все валы поднимаются при нажатии ножного переключателя до того, как будет запрошено что-либо еще
}
//////////////////////////////////////////////////
// ФУНКЦИИ ПОСЛЕДОВАТЕЛЬНОГО ИНТЕРФЕЙСА (USB)
//////////////////////////////////////////////////
// Команды ввода USB:
// 1,2,3,n Установить валы 1,2,3,n
// ? Запрос идентификатора, ответ с идентификатором
unsigned long get_shafts(char* command) // Ввод представляет собой серию чисел, разделенных запятыми, для поднятия валов
{ // возвращает слово, указывающее, какие валы были идентифицированы в строке
int chars=strlen(command); // Количество символов во входной строке
int i=0;
unsigned long shafts=0, bits; // Устанавливает биты для указанных валов
char shaft=0, digit=0, character;
while(i<=chars) // цикл по всем символам в команде
{
character=command[i];
if((character==',')||(i==chars))
{ // есть разделитель полей или конец строки, а не вал
if((shaft>0)&&(shaft<=32))
{ // 'shafts' отмечает, что этот вал необходим
bits=1<<shaft-1;
shafts|=bits;
shaft=0;
}
}
else if(('0'<=character)&&(character<='9'))
{ // есть цифра
character&=0x0f; // уменьшить до десятичного числа
shaft=shaft*10+character;
}
else if((character==' ')||(character=='\n')); // игнорировать пробелы/возвраты
else
{
Serial.println("Invalid Character detected in command:"); // отладочное отображение извлеченной команды
Serial.println(command); // отладочное отображение извлеченной команды
i=chars; // выходим, дерьмо
}
i++;
}
return shafts;
}
void digest_serial(char* command) // Основная функция последовательного ответа
{
int chars=strlen(command); // Количество символов в команде
if(chars>0)
{ // Если что-то делать, переварить ввод
HostPresent=1; // обратите внимание, что там что-то говорит со мной
if(('0'<=command[0])&&(command[0]<='9')) // идем число, должны быть валы
shaft_cue=get_shafts(command); // преобразовать строку ASCII в двоичное слово
else if((command[0]=='c')||(command[0]=='t')) // тестовая команда для переключения клапанов
preset(); // циклически переключаться между клапанами (для этого ткацкого станка ничего не определено, можно реализовать самопроверку вала и т. д.)
else if(command[0]=='?')
{ // У вас есть вопрос "Кто вы?" своего рода команда, скажите хозяину, кто мы
char message[256]=IDENTITY; // Команда создания первичной идентификации
message[strlen(message)-1]=0; // удалить возврат в строке
sprintf(message,"%s [Solenoid state = 0x%3x]\n",message,shaft_state); // обратите внимание, что мы будем делать при нажатии кнопки
Serial.write(message); // Отправляем идентификатор с текущим состоянием коммутатора на хост (для устранения неполадок)
}
}
}
void serial_input()
{ // Последовательная обработка высокого уровня
char inChar;
char command[MAX_CHARS]="";
while((Serial.available()>0))
{ // Получить последовательную команду от хоста (посимвольно)
inChar=char(Serial.read());
serial_string[serial_chars]=inChar;
serial_chars++;
serial_string[serial_chars]=0;
serial_time=millis();
}
if((serial_chars>0)&&(serial_idle)&&((serial_string[serial_chars-1]=='\n')||(millis()>(serial_time+serial_timeout))))
{ // Если есть команда, переварить ее
serial_idle=0;
strcpy(command,serial_string);
serial_chars=0;
serial_string[0]=0;
// Serial.println(команда); // отладочное отображение извлеченной команды
digest_serial(command);
// выполнено с последней командой, очищаем входные буферы
serial_idle=1; // готов к дальнейшему последовательному вводу
}
}
////////////////////////////////////////////////// //////////// /
// Монитор ножного переключателя - установить валы/указать хосту двигаться вперед
////////////////////////////////////////////////// //////////// /
void monitor_button()
{ // проверка нажатия кнопки для продвижения вперед, отправляется только тогда, когда кнопка нажата (не удерживается)
char state=digitalRead(ADVANCE); // чтение состояния ножного переключателя (0=активно, 1=неактивно)
if(state==0)
{ // кнопка нажата,
set_shafts(shaft_cue); // Установите требуемые состояния двигателя из списка желаемых валов для этого кадра
run_motors(); // Запустим моторы, чтобы это произошло
}
else
{ // кнопка не нажата,
set_shafts(0); // Устанавливаем требуемые состояния двигателя обратно в состояние сброса
run_motors(); // Сделай это
if(Button_state!=state)
Serial.write("ADVANCE\n"); // Отправить "Доступно" когда ножной переключатель отпущен
}
if(state!=Button_state) // Проверяем, изменилось ли состояние кнопки
delay(500); // Подождать полсекунды после изменения переключателя устранения дребезга
Button_state=state; // Отслеживание текущего состояния кнопки для поиска изменений
}
//////////////////////////////////////////////////
// ФУНКЦИИ ВЕРХНЕГО УРОВНЯ - НАСТРОЙКА & ПЕТЛЯ //
//////////////////////////////////////////////////
void setup()
{
char m; // Индикатор двигателя
// USB-интерфейс для хост-компьютера
Serial.begin(9600); // запускаем последовательный порт на скорости 9600 бит/с:
Serial.print(IDENTITY); // вывести сообщение на серийный монитор
// Управление двигателем
// устанавливаем управление мотором для всех моторов
m=0;
while(m<MOTORS)
{
digitalWrite(MotorPin[m][0], LOW); // установить низкий уровень
digitalWrite(MotorPin[m][1], LOW); // установить низкий уровень
digitalWrite(MotorPin[m][2], LOW); // установить низкий уровень
digitalWrite(MotorPin[m][3], LOW); // установить низкий уровень
pinMode(MotorPin[m][0], OUTPUT); // Установить как выходной бит
pinMode(MotorPin[m][2], OUTPUT); // Порядок смешивания, чтобы двигатель не двигался
pinMode(MotorPin[m][1], OUTPUT); // Установить как выходной бит
pinMode(MotorPin[m][3], OUTPUT); // Установить как выходной бит
MotorWant[m]=MotorActual[m]=0; //
m++;
}
pinMode(ADVANCE, INPUT_PULLUP); // Установить как входной контакт для управления педальным переключателем
preset(); // "самопроверка" - обратите внимание, что он жив
unsigned long T0=millis(); // Обратите внимание на время запуска
}
void loop()
{
serial_input(); // проверяем последовательный интерфейс, обрабатываем, если он приходит
monitor_button(); // проверка нажатия кнопки
}
@Vaughan Ratahi, 👍2
Обсуждение2 ответа
Предполагаемая цель – автоматизация ткацкого станка с использованием соленоидов вместо шаговых двигателей.
В В этом видео утверждается, что необходимо тянуть 3 кг. Может быть трудно найти подходящий / экономичный соленоид с таким тяговым усилием, ходом и усилием. рассчитан на непрерывную работу (удерживать свое положение в течение длительного периода времени без вреда для себя из-за проблем, связанных с нагревом).
Если найти такой соленоид не проблема, в приведенном выше коде есть только две функции, которые управляют шаговыми двигателями. "установить_двигатель" и "настройка". Измените их, чтобы активировать/деактивировать соленоиды.
Кроме того, это может быть сложнее, удалите код, который циклически переводит шаговые двигатели в положение / из положения. Вам это не нужно с соленоидами.
Я хотел бы сначала выяснить, что делает каждый сегмент кода шагового двигателя, в общих чертах. Подумайте о том, чтобы наблюдать за процессом, стоя в стороне от него, и опишите причину существования сегмента кода, а не то, что конкретно он делает — каково его отношение к процессу, а не к двигателю.
Переместите эти моторные сегменты в функции и поместите вызовы функций там, где раньше были моторные сегменты.
Теперь замените код двигателя внутри каждой функции кодом соленоида. Вот почему мы хотели получить абстрактный обзор этих двигательных сегментов. В каждой функции вы напишете код для соленоида, который имеет такое же отношение к общему процессу, как и код двигателя, но вы адаптируете специфику к соленоидам.
- Как инициализировать цифровой выходной контакт как LOW
- Несколько кнопок для управления реле
- Ищу ссылку на двухполюсное однопозиционное реле (DPST) для Arduino.
- Вопрос новичка - Biltong Box Project
- Выходное напряжение цифрового вывода падает при управлении реле
- Регулятор напряжения перегревается
- управление 2 датчиками и 3 насосами с помощью millis
- Использование millis вместо задержки перезагрузки реле
невозможно делать то, что вы хотите, не зная, как действие соленоида заменяет действие двигателя ... в основном, что делают двигатели, в какое время они это делают и как соленоиды заменяют функцию, которую выполняют двигатели., @jsotola
комментарии не являются частью кода... они не должны вплетаться в код... почистил для вас листинг программы, @jsotola
Привет, Jsotola, спасибо за ваш ответ, все, что соленоид будет делать, это активировать, когда двигатель работает нормально, то есть вместо того, чтобы двигатель включался и делал x число оборотов, соленоид просто включается, это запускается программным обеспечением, которое работает в Windows для создания и запуска заданного шаблона, затем при срабатывании педального переключателя соленоид отключится, и программное обеспечение ПК перейдет к следующему шагу шаблона и задействует правильные соленоиды. извините за все комментарии, так как они уже были в скетче, который я нашел., @Vaughan Ratahi
много комментариев - это очень хорошо, но они не должны загромождать код... вот и все, что я хочу сказать, @jsotola
Я всегда думал, что лучшее место для комментариев — начало функций. Хорошие комментарии указывают на то, что мысль была направлена на то, чтобы разделить задачу на хорошо организованные функции. Однако даже такие гиганты, как NXP и STMicro, не делают этого регулярно в своем коде. Кроме того, комментарии типа «Установить состояние двигателя 1» бесполезны. Опытные программисты тут же прокомментируют: «Это очевидно… но что такое «Состояние 1»… вот настоящий вопрос/комментарий, который нужно сделать»., @st2000