Страница 1 из 2

Перемещение, ограниченное датчиками

Добавлено: 13 окт 2017, 00:08
Eugene
Здравствуйте,

Ставится след задача:
перемещение с заданной потенциометром скоростью (отображение значения на экране 1602 I2C)
старт по нажатию кнопки, перемещение до правого датчика и обратное перемещение до левого датчика
останов и ожидание следующего нажатия кнопки старт

перемещение должно быть плавным, без рывков

В наличии:
движок 17HD4420 size 17 1,8° bipolar 0,2A
Arduino Uno
1602 I2C


на подходе:
Н-мост SN754410NA
драйвер TB6560

А теперь вопросы:
- хватит ли для решения данной задачи одного лишь моста (при подключении, как указано здесь: http://wikihandbk.com/wiki/Arduino:%D0% ... ed_Control)
- какие библиотеки использовать для управление двигателем, с учетом необходимости реверса и останова


И спасибо Вам, Эдуард, за уроки и возможность консультации.

Re: Перемещение, ограниченное датчиками

Добавлено: 13 окт 2017, 09:47
Эдуард
Здравствуйте!
А зачем вам мост SN754410NA, если вы используете драйвер TB6560. Достаточно только драйвера.
Посмотрите урок 35 на сайте.
Также, возможно, вам поможет эта тема форума. Там выложена программа управления сверлильным станком. Шпиндель сверлильного станка поднимается до датчика, а затем перемещается на нужное число шагов с нужной скоростью. Возьмите за основу структуру этой программы.

Re: Перемещение, ограниченное датчиками

Добавлено: 14 окт 2017, 04:02
Eugene
Спасибо огромное, очень вовремя. Голову сломал, как задать значение Divider.

Пока получилось так:

Код: Выделить всё

#include <TimerOne.h>
#include <StepDirDriver.h>
#include <Wire.h>                                                              //  Подключаем библиотеку для работы с шиной I2C
#include <LiquidCrystal_I2C.h>                                                 //  Подключаем библиотеку для работы с LCD дисплеем по шине I2C

StepDirDriver myMotor(10, 11, 12); // создаем объект типа StepDirDriver, задаем выводы для сигналов

unsigned int timeCounter; // счетчик времени
byte md; // режим: 0 - вращение против ч.с., 1 - пауза, 2 - вращение против ч.с., 3 - пауза

LiquidCrystal_I2C lcd(0x27,16,2);                        //  Объявляем  объект библиотеки, указывая параметры дисплея (адрес I2C = 0x27, количество столбцов = 16, количество строк = 2)
uint8_t           symbol[8] = {31,31,31,31,31,31,31,31}; //  Определяем массив который содержит полностью закрашенный символ
const uint8_t     pinSensor = A0;                        //  Определяем константу для хранения номера аналогового входа с которого будут браться данные
uint16_t          valSensor;                             //  Объявляем  переменную для хранения данных с аналогового входа pinSensor

int               left_Button = 4;
int               right_Button = 3;
int               start_Button = 2;
int               led_pin = 13;

void setup() {

  pinMode(start_Button, INPUT);
  pinMode(led_pin, OUTPUT);

Timer1.initialize(1200); // инициализация таймера 1, период 250 мкс
Timer1.attachInterrupt(timerInterrupt, 1200); // задаем обработчик прерываний
myMotor.setMode(0, false); // шаговый режим, без фиксации при остановке
myMotor.setDivider(1); // делитель частоты 10 (1 оборот в сек)
md= 0; // начальный режим
myMotor.step(0); // начальный запуск

lcd.init();                                          //  Инициируем работу с LCD дисплеем
    lcd.backlight();                                     //  Включаем подсветку LCD дисплея
    lcd.createChar(1, symbol);                           //  Загружаем символ из массива symbol в первую ячейку ОЗУ дисплея

}

void loop() {

    valSensor = analogRead(pinSensor);                   //  Читаем данные с аналогового входа pinSensor в переменную valSensor
    lcd.setCursor(6, 1); lcd.print("    ");              //  Устанавливаем курсор дисплея на 6 символ 1 строки и выводим 4 пробела начиная с той позиции, куда ранее был установлен курсор.
    lcd.setCursor(6, 1); lcd.print(valSensor);           //  Устанавливаем курсор дисплея на 6 символ 1 строки и выводим значение переменной valSensor на дисплей (начиная с той позиции, куда ранее был установлен курсор)
    lcd.setCursor(0, 0);                                 //  Устанавливаем курсор дисплея на 0 символ 0 строки для вывода шкалы, дальнейший вывод символов начнётся именно с этой позиции
    uint8_t j=map(valSensor,0,100,0,17);                //  Определяем переменную j которой присваиваем значение valSensor преобразованное от диапазона 0...1023 к диапазону 0...17
    for(uint8_t i=0; i<16; i++){                         //  Выполняем цикл 16 раз для вывода шкалы из 16 символов начиная с позиции в которую ранее был установлен курсор
    lcd.write(j>i? 1:32);                            //  Выводим на дисплей символ по его коду, либо 1 (символ из 1 ячейки ОЗУ дисплея), либо 32 символ пробела)
                               }

    myMotor.setDivider(map(valSensor, 0, 1023, 0, 100));

if (digitalRead(start_Button) == HIGH) {                              //если кнопка нажата ...
//  digitalWrite(led_pin, HIGH);
  myMotor.step(-20000);                  }

else if (digitalRead(right_Button) == HIGH)  {
  myMotor.step(20000);                        }

 else if (digitalRead(left_Button) == HIGH)  {
  myMotor.step(0);                           }
                                                   
}

//-------------------------------------- обработчик прерывания 0,25 мс
void timerInterrupt() {
myMotor.control(); // управвление двигателем
timeCounter++; // счетчик времени
}


и в общем, все работает как заказывали. Спасибо!!!
Но есть вопросы:
- отображаемое на экране значение даже не знаю как назвать, а хотелось бы все таки скорость, подскажите
- как инвертировать значение для ползунка, а то он растет, а "скорость" падает
- при значениях величины больше 50, двигатель фактически стоит, можно ли уменьшить кол-во входных значений

Re: Перемещение, ограниченное датчиками

Добавлено: 14 окт 2017, 18:40
Эдуард
Здравствуйте!

В уроке 29 описана связь между divider и реальной скоростью вращения.
Rpm = 60 000 / ( divider * Tcontrol * Nдвигателя )
    Rpm – скорость вращения в оборотах в минуту;
    Tcontrol – период вызова метода control() в мс;
    Nдвигателя – число шагов двигателя на полный оборот. Если используется микрошаговый режим, то имеется в виду число микрошагов на оборот.
Можно пересчитать так:

Код: Выделить всё

lcd.setCursor(6, 1); lcd.print( 60000. / ((float)divider * 0.25 * 400.) ); // Устанавливаем курсор дисплея на 6 символ 1

divider – то что используем в качестве аргумента myMotor.setDivider();
0.25 – период вызова control();
400. число шагов двигателя на оборот.

Чтобы ползунок на экране двигался в другую сторону надо сделать так:

Код: Выделить всё

uint8_t j=17 - map(valSensor,0,100,0,17); // Определяем переменную j которой присваиваем значение valSensor преобразованное от диапазона 0...1023 к диапазону 0...17

При 0 j будет равна 17, а при 17 j=0.

А по поводу уменьшения входных значений, вы же используете функцию map. В ней и поменяйте выходной диапазон.

Re: Перемещение, ограниченное датчиками

Добавлено: 17 окт 2017, 17:35
Eugene
Спасибо, где-то на форуме видел вопрос о не целых значениях divider, не могу вспомнить. Это возможно? И библиотека StepDirDriverL.h - она есть в свободном доступе?

Re: Перемещение, ограниченное датчиками

Добавлено: 17 окт 2017, 18:24
Эдуард
Здравствуйте!
Аргумент divider может быть только целым.
Библиотеку StepDirDriverL давно обещал выложить, все забываю. Выложу в уроке 35. Вот специально для вас.
StepDirDriverL.zip
(2.58 КБ) 76 скачиваний

Re: Перемещение, ограниченное датчиками

Добавлено: 18 окт 2017, 17:51
Eugene
Cпасибо!

Столкнулся с тем, что значения, заданный потенциометром, скачут, вызывая в свою очередь биения в движке. Как лечить, не подскажите, помимо прецензионного?

Re: Перемещение, ограниченное датчиками

Добавлено: 18 окт 2017, 17:54
Eugene
И еще вопрос: получается что я не могу установить промежуточные значения скорости, между 600 - 400 - 300 (Tcontrol, мс 25)?

Re: Перемещение, ограниченное датчиками

Добавлено: 18 окт 2017, 19:10
Эдуард
Здравствуйте!
Чтобы значения, заданные потенциометром были стабильными поставьте конденсатор между аналоговым входом и землей. И еще необходимо считывать несколько значений АЦП и усреднять. Пример есть в уроке 13.
Время переключения фаз двигателя, а значит и скорость вращения, задается количеством периодов прерывания. Изменение скорости нелинейное. В уроке 29 есть подробные пояснения.

Re: Перемещение, ограниченное датчиками

Добавлено: 18 окт 2017, 19:20
Eugene
Спасибо, какого номинала конденсатор

Re: Перемещение, ограниченное датчиками

Добавлено: 18 окт 2017, 19:43
Eugene
Извините, туго доходит ;

Re: Перемещение, ограниченное датчиками

Добавлено: 18 окт 2017, 22:34
Эдуард
Например, 1-2 мкф. Лучше электролитический не использовать.

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 08:54
Eugene
Спасибо.

Для усреднения значения аналогового входа (урок 13) используется библиотека MsTimer2.h и вызывается через void timerInterupt(), а в программе шагового двигателя TimerOne.h и тоже void timerInterupt(). Не могу сообразить как их совместить, подскажите плиз?

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 10:07
Эдуард
У вас есть прерывание по таймеру, заданное библиотекой TimerOne. В обработчике этого прерывания и вызывайте измерение АЦП и усреднение. Только имейте в виду что измерение аналогового входа занимает довольно много времени (более 100 мкс). Надо, чтобы суммарное время обработки прерывания не превысило время периода прерывания. В вашем случае (период 250 мкс), например, 2 измерения аналоговых входов может не потянуть. По хорошему надо измерение АЦП делать в основном цикле, синхронно с прерыванием. Попробуйте пока в прерывании, потом напишите, я расскажу, как это сделать.

Кстати, можете посмотреть, как это делается в уроке 32. Во второй части урока есть программа следящего электропривода с управлением от потенциометра. Там в в прерывании измеряется напряжение потенциометра и усредняется.

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 10:44
Eugene
Спасибо!
Пока так:

// программа управления шаговым двигателем с помощью библиотеки StepDirDriver

#include <TimerOne.h>
#include <StepDirDriverL.h>
#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C
#include <LiquidCrystal_I2C.h> // Подключаем библиотеку для работы с LCD дисплеем по шине I2C




StepDirDriverL myMotor(10, 11, 12); // создаем объект типа StepDirDriver, задаем выводы для сигналов

unsigned int timeCounter; // счетчик времени
byte md; // режим: 0 - вращение против ч.с., 1 - пауза, 2 - вращение против ч.с., 3 - пауза


LiquidCrystal_I2C lcd(0x27,16,2); // Объявляем объект библиотеки, указывая параметры дисплея (адрес I2C = 0x27, количество столбцов = 16, количество строк = 2)
uint8_t symbol[8] = {31,31,31,31,31,31,31,31}; // Определяем массив который содержит полностью закрашенный символ
const uint8_t pinSensor = A0; // Определяем константу для хранения номера аналогового входа с которого будут браться данные
uint16_t valSensor; // Объявляем переменную для хранения данных с аналогового входа pinSensor

int left_Button = 4;
int right_Button = 3;
int start_Button = 2;
int led_pin = 13;

#define MEASURE_PERIOD 80 // время периода измерения
int timeCount; // счетчик времени
long sumU; // переменные для суммирования кодов АЦП
long averageU; // сумма кодов АЦП (среднее значение * 500)
boolean flagReady; // признак готовности данных измерения




void setup() {

pinMode(left_Button, INPUT);
pinMode(right_Button, INPUT);
pinMode(start_Button, INPUT);
pinMode(pinSensor, INPUT);
pinMode(led_pin, OUTPUT);

//Serial.begin(9600); // инициализируем порт, скорость 9600
//MsTimer2::set(1, timerInterupt); // прерывания по таймеру, период 1 мс
//MsTimer2::start(); // разрешение прерывания


Timer1.initialize(250); // инициализация таймера 1, период 250 мкс
Timer1.attachInterrupt(timerInterrupt1, 250); // задаем обработчик прерываний
myMotor.setMode(0, false); // шаговый режим, без фиксации при остановке
myMotor.setDivider(5); // делитель частоты 100 (1 оборот в сек)
md= 0; // начальный режим
myMotor.step(0); // начальный запуск

lcd.init(); // Инициируем работу с LCD дисплеем
lcd.backlight(); // Включаем подсветку LCD дисплея
lcd.createChar(1, symbol); // Загружаем символ из массива symbol в первую ячейку ОЗУ дисплея




}

void loop() {

valSensor = constrain((averageU / MEASURE_PERIOD), 2, 50);

//valSensor = constrain(analogRead(pinSensor), 2, 50); // Читаем данные с аналогового входа pinSensor в переменную valSensor

//sensVal = constrain(sensVal, 10, 150);

myMotor.setDivider(valSensor);

lcd.setCursor(6, 1); lcd.print(" "); // Устанавливаем курсор дисплея на 6 символ 1 строки и выводим 4 пробела начиная с той позиции, куда ранее был установлен курсор.


lcd.setCursor(6, 1); lcd.print( 60000. / ((float)valSensor * 0.25 * 200.) ); // Устанавливаем курсор дисплея на 6 символ 1

//lcd.setCursor(6, 1); lcd.print(valSensor); // Устанавливаем курсор дисплея на 6 символ 1 строки и выводим значение переменной valSensor на дисплей (начиная с той позиции, куда ранее был установлен курсор)
lcd.setCursor(0, 0); // Устанавливаем курсор дисплея на 0 символ 0 строки для вывода шкалы, дальнейший вывод символов начнётся именно с этой позиции

uint8_t j=map(( 60000. / ((float)valSensor * 0.25 * 200.) ),0,600,0,17);
// uint8_t j=map(valSensor,0,1023,0,17); // Определяем переменную j которой присваиваем значение valSensor преобразованное от диапазона 0...1023 к диапазону 0...17
for(uint8_t i=0; i<16; i++){ // Выполняем цикл 16 раз для вывода шкалы из 16 символов начиная с позиции в которую ранее был установлен курсор
lcd.write(j>i? 1:32); // Выводим на дисплей символ по его коду, либо 1 (символ из 1 ячейки ОЗУ дисплея), либо 32 символ пробела)
}



if (digitalRead(start_Button) == HIGH) { //если кнопка нажата ...
// digitalWrite(led_pin, HIGH);
myMotor.step(-32000000); }

else if (digitalRead(right_Button) == HIGH) {
myMotor.step(32000000); }

else if (digitalRead(left_Button) == HIGH) {
myMotor.step(0); }



}



//-------------------------------------- обработчик прерывания 0,25 мс
void timerInterrupt1() {
myMotor.control(); // управвление двигателем
timeCounter++; // счетчик времени

sumU += analogRead(A0); // суммирование кодов АЦП
timeCount++; // +1 счетчик выборок усреднения

// проверка числа выборок усреднения
if ( timeCount >= MEASURE_PERIOD ) {
timeCount= 0;
averageU = sumU; // перегрузка среднего значения
sumU= 0;
}
}



Биение вылечено, показания на экране стабильны, спасибо. Весьма трудно выставить необходимую скорость, вращая потенциометр

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 10:51
Eugene
Поспешил, на 300 об.мин - стабильно, 400 - уже скачет.

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 11:03
Эдуард
Может быть увеличить MEASURE_PERIOD 80 // время периода измерения. Только надо, чтобы оно было кратным периоду сети 20 мс.
А скачет у вас на 1 единицу?
Может вам промассштабировать диапазон значения потенциометра (0...1023) в нужный вам диапазон divider. Тогда скорость выставлять будет легче. Например, y= map(x, 0, 1023, 1, 20);

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 11:18
Eugene
Скачет от 400 до 600 (об/мин)

Я обрезаю значения divider ,ни больше, ни меньше не нужно
valSensor = constrain((averageU / MEASURE_PERIOD), 2, 50);
Соответственно масштабирую экран
uint8_t j=map(( 60000. / ((float)valSensor * 0.25 * 200.) ),0,600,0,17);

попробую хотя бы проволочный потенциометр

а 20 мс - что за величина, есть 250 мкс - период вызова

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 11:28
Эдуард
MEASURE_PERIOD умножить на время периода прерывания 250 мкс будет время усреднения 80 * 0,25 = 20 мс. Считается, что оно должно быть кратным периоду сети, для того чтобы эффективнее душить помехи сети.
Так у вас значения потенциометра на 1 скачет или больше?
В принципе любой АЦП скачет на 1. Если нужна высокая стабильность, то надо как-то обрабатывать.

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 11:36
Eugene
На одно значение divider, 2 - это 600, 3- это 400, вот в этих значениях и скачет
Сети электрической, 50 Гц? И какое значение тогда лучше поставить

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 12:10
Эдуард
Любой АЦП скачет на 1. Надо отрабатывать изменения уровня АЦП только на значение более 1. Может энкодер будете использовать?

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 12:16
Eugene
Эта проблема решилась, старый советский СП-1

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 12:28
Eugene
[quote="Эдуард"]По хорошему надо измерение АЦП делать в основном цикле, синхронно с прерыванием. Попробуйте пока в прерывании, потом напишите, я расскажу, как это сделать.

Расскажите, хотелось бы и кнопки-датчики обрабатывать.

И нет ли у вас примера для задания параметров с помощью кнопок, увеличения или выбора из имеющихся?

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 13:17
Эдуард
Есть контроллер элемента Пельтье, начиная с урока 36. Там все задается кнопками. Но удобнее всего энкодером. У меня есть урок по энкодеру, библиотека.

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 15:10
Eugene
Как только приобрету, Вы обещали рассказать как делать измерения АЦП в основном цикле, и как мне отфильтровать кнопки

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 15:55
Эдуард
Кнопки отфильтровать - возьмите мою библиотеку Button.h. В уроках 6 и 8 рассказывается об алгоритмах фильтрации. В уроке 9 есть библиотека. Работает в том же прерывании по таймеру.

По поводу АЦП в основном цикле. У вас задача с определенным периодом измерять АЦП.
Сделайте программный таймер. Для этого в прерывании по аппаратному таймеру прибавляйте 1 к счетчику и при достижении определенного числа, сбрасывайте его и взводите флаг. Получится, что через заданные промежутки времени у вас будет взводится флаг. Т.е. появится еще один таймер, но уже программный. Например вот таймер с периодом 2 мс:

Код: Выделить всё

//-------------------------------------- обработчик прерывания 0,25 мс
void timerInterrupt1() {
  myMotor.control(); // управвление двигателем

  timeCounter++; // счетчик времени
  if(timeCounter >= 8 ) {
    timeCounter=0;
    flagTimer2mc= true;
  }
}

Теперь в основном цикле вам надо проверять состояние флага flagTimer2mc. Если он стал равным true, то надо его сбросить и выполнить нужное действие. Таким образом в основном асинхронном цикле у вас будут выполнятся определенные действия, с заданным периодом, не зависящим от времени выполнения цикла. И эти действия не будут блокировать прерывания.

Код: Выделить всё

void loop() {

  if(flagTimer2mc == true) {
    // вызов программного таймера каждые 2 мс
    flagTimer2mc= false;
    sumU += analogRead(A0); // суммирование кодов АЦП
    timeCount++; // +1 счетчик выборок усреднения
    // проверка числа выборок усреднения
    if ( timeCount >= MEASURE_PERIOD ) {
    timeCount= 0;
    averageU = sumU; // перегрузка среднего значения
    sumU= 0;
    }   
  }
 
}

Заметьте, что этот программный блок не подвешивает основной цикл. В нем можно выполнять другие задачи. Таким образом можно создать несколько таймеров с разными временами периодов. Единственное условие время их периодов должно быть больше времени аппаратного прерывания по таймеру.

Хороший стиль программы это одно прерывание по аппаратному таймеру. Остальные процессы отсчитываются от него.

Посмотрите еще структуру программы для контроллера элемента Пельтье (начиная с урока 36). Там более сложная структура, но может вам нужна именно такая.

Re: Перемещение, ограниченное датчиками

Добавлено: 19 окт 2017, 18:20
Eugene
Спасибо,
могу ли я посоветоваться с Вами относительно библиотеки accelstepper.h?

Не думали о том, чтобы завести кнопку Donate, я почти готов. Это я к тому, что очень благодарен.

Re: Перемещение, ограниченное датчиками

Добавлено: 20 окт 2017, 00:07
Эдуард
Нет, я эту библиотеку никогда не использовал. Обычно у подобных библиотек время переключения фаз отсчитывается постоянным вызовом функции micros() и сравнением значений времени этой функции. На все остальное уже ресурсов микроконтроллера не хватает. Если же в основном цикле выполняются какие-либо другие задачи, то реже анализируется micros() и точность и равномерность отработки фазных импульсов падает. В моей библиотеке импульсы строго привязаны к прерыванию по таймеру. Из-за этого достаточно грубая дискретность изменения скорости, особенно на высоких скоростях. Можно увеличить частоту вызова прерывания по таймеру, но я бы не советовал на простых платах Ардуино делать период прерывания менее 100 мкс.
В общем я бы рекомендовал использовать accelstepper.h только в случае, если в основном цикле не выполняются ресурсоемкие задачи.

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

Re: Перемещение, ограниченное датчиками

Добавлено: 20 окт 2017, 12:31
Eugene
Можно ли считать ресурсоемкой задачей операции с lcd по I2C? Я столкнулся с тем, что существенно падает скорость. И не могу понять как остановить движок по датчику, он продолжает двигаться и после срабатывания

Re: Перемещение, ограниченное датчиками

Добавлено: 20 окт 2017, 13:21
Эдуард
Я думаю, да. Обычно функции работы с LCD требуют 2-3 мс времени (http://mypractic-forum.ru/viewtopic.php?t=17). На это время у вас могут растянуться фазные импульсы двигателя.
Кстати, в моей библиотеке StepDirDriver можно попробовать регулировать скорость вращения плавно, если менять период прерывания по таймеру.