Ардуино про мини и 17 сервоприводов

lilik
Offline
Зарегистрирован: 19.10.2017

Собираю гуманоидного робота на 17 сервах. Для экономии использую про мини, hc-05. Как подправить библиотеку для корректной работы всех приводов? Тренировочно-проверочный скетч (реальный (часть строк закомментирована) и желаемый (исправлено число серв)) такой.

#include <Servo.h> // подключение библиотеки Servo
#define kolvoSV 17//количество сервоприводов 
Servo servo[kolvoSV];
int i=0;//счётчик элементов массивов
int n;
String inputString;// строки данных
void setup()
{
Serial.begin(57600);
 for(i=0;i<kolvoSV;i++){servo[i].attach(i+2);servo[i].write(90);delay(20);}//начальные установки углов 90 градусов (подкл. на выводы начиная со 2 по возр.)
 //если не трогать библиотеку...
/*servo[10].attach(14);servo[10].write(90);delay(200); 
servo[10].attach(15);servo[10].write(90);delay(200);
servo[10].attach(16);servo[10].write(90);delay(200); 
servo[10].attach(17);servo[10].write(90);delay(200);
servo[10].attach(18);servo[10].write(90);delay(200);*/     
}
void loop()
{
 CheckSerial();
}
////////////////////////////////////////////////////////////////////////////////////////////////
//ФУНКЦИИ ПРИЁМА И ОБРАБОТКИ КОМАНД-ДАННЫХ
void MakeCmd()
{
  int y = inputString.length(); // присваиваем переменной у число символов в строке
  if (y < 1 || y > 5){inputString = "";return;} //если их меньше 1 или больше 5, выходим из функции
  String cmd = inputString; // передаём строку другой переменной
  inputString = "";//"обнуляем переменную"
  //////
    if (cmd == "0") {                 //если получили ноль
    Serial.write(0x14);               // ответить avrdude.exe
    Serial.write(0x10);               //  для синхронизации 
    delay(10); 
    pinMode(19,OUTPUT);        //ЭТО ВЫЗОВЕТ аппаратный РЕСЕТ
  }
  ////////
  ////////
  if (cmd != "0"){ n=cmd.toInt();}   // преобразуем строку в  команды сервоприводам...
  for(i=0;i<kolvoSV;i++)
  {
   if(n>=(i+1)*1000&&n<(i+1)*1000+181){servo[i].attach(i+2);servo[i].write(n-(i+1)*1000);}
   }
   //если не трогать библиотеку...
  /* if(n>=13000&&n<13181){servo[10].attach(14);servo[10].write(n-13000);}//управление дополнительными сервоприводами (поворот качалок без удержания их под нагрузкой)
   if(n>=14000&&n<14181){servo[10].attach(15);servo[10].write(n-14000);}
   if(n>=15000&&n<15181){servo[10].attach(16);servo[10].write(n-15000);}
   if(n>=16000&&n<16181){servo[10].attach(17);servo[10].write(n-16000);}
   if(n>=17000&&n<17181){servo[10].attach(18);servo[10].write(n-17000);}*/
   //////
   //////
}

///////////////////////////////////////////////////////////////////////////////////////////////////////

void  CheckSerial()
{
  //////////////////////////////////////////////////////////////////////////////
  while (Serial.available())//считываем строку данных пока они поступают
  {
    char inChar = (char)Serial.read();
    if (inChar == '\n'||inChar == ' ')     //если окончание строки или запрос на загрузку
    {
      MakeCmd();//обрабатываем её данной функцией
      break;
    }
    else inputString += inChar;// иначе удлиняем строку на один символ
  }
}

/////////////////////////////////////////////////////////////////////////////////

 

b707
Offline
Зарегистрирован: 26.05.2017

lilik пишет:

Собираю гуманоидного робота на 17 сервах. Для экономии использую про мини,

простите, "для экономии" чего?

lilik
Offline
Зарегистрирован: 19.10.2017

Экономии всего - простая доступная версия робота. Схематехника - про мини, 17 sg90 (mg90), блютуз модуль, два 18650, dc-dc до 5 В понижающий, выключатель, шасси - пластиковые детальки печатные, максимум одинаковых с заполнением 40, заливка скетчей по воздуху.

b707
Offline
Зарегистрирован: 26.05.2017

и в чем у вас проблема? Зачем править библиотеку?

rkit
Offline
Зарегистрирован: 23.11.2016

lilik пишет:

17 sg90 (mg90)

два 18650, dc-dc до 5 В понижающий

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

lilik
Offline
Зарегистрирован: 19.10.2017

rkit пишет:

lilik пишет:

17 sg90 (mg90)

два 18650, dc-dc до 5 В понижающий

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

Опыта хватает, это лишь продолжение после 2, 4,8,12 серв дешёвых на разных типах ходунов из пластика печатного. Вы знаете как поправить библиотеку?  

lilik
Offline
Зарегистрирован: 19.10.2017

b707 пишет:

и в чем у вас проблема? Зачем править библиотеку?

Дело в том, что не все качалки будут держать нагрузку, для рук это терпимо, для ног нет. Просто хочется знать знать предел элементарного использования простых вещей). Кстати щёлкающие и клинящие sg90 легко чинятся.

 

b707
Offline
Зарегистрирован: 26.05.2017

lilik пишет:

Вы знаете как поправить библиотеку?  

Зачем ее править? Она что, ошибку выдает? - если да - выкладывайте сообщение об ошибке

lilik
Offline
Зарегистрирован: 19.10.2017

))))

Она не выдаёт ошибку, она 13,14,15,16,17 серву не вращает, если просто так взять и задать в скетче вместо 12 - 17

kalapanga
Offline
Зарегистрирован: 23.10.2016

lilik пишет:

Она не выдаёт ошибку, она 13,14,15,16,17 серву не вращает, если просто так взять и задать в скетче вместо 12 - 17

Вы ошибочно считаете, что число поддерживаемых моторов 12 поставлено в библиотеке "от балды" разработчиками, которые пожмотились написать 17. Это не так. Возможности микроконтроллера не резиновые.

Берите мегу, там поддерживаются 48 моторов.

lilik
Offline
Зарегистрирован: 19.10.2017

Ящик Пандоры открывался легко )

В файле Servo.h

строку

#define SERVOS_PER_TIMER       12    // the maximum number of servos controlled by one timer

меняем на

#define SERVOS_PER_TIMER       17     // the maximum number of servos controlled by one timer

Rumata
Rumata аватар
Offline
Зарегистрирован: 29.03.2019

lilik пишет:
Ящик Пандоры открывался легко )

По Фрейду

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Rumata пишет:

lilik пишет:
Ящик Пандоры открывался легко )

По Фрейду

"сколько будет дважды два - семь учитель...семь, восемь, где-то так, но не сорок же..."

MaksVV
Offline
Зарегистрирован: 06.08.2015

разогнал ведь сцуко библиотеку... сначала железо модно было гнать - оверклоцкеры, теперь софт гонят - овербиблеры

b707
Offline
Зарегистрирован: 26.05.2017

интересно, насколько оно у него работать будет? - зависит от углов размаха ногами, видимо :)

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

 Ипользуется 16-битный таймер с делителем 8, при 16 МГЦ это 2 тика за микросекунду. Диапазон сигналов серв - до 2400 мкс

Итого получаем 0xFFFF / (2400*2) = 13.6 серв на таймер

То есть гарантировано успеет обработать 13 серв, дальше как повезет :)

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

Хотя не.. пинов не хватит :).

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

b707 пишет:
Хотя не.. пинов не хватит :).

ото ж. Это тоже спокойно гонится. Ищешь свободное место на плате, сверлишь дырочки... В этом плане уна лучше гонится по сравнению с промини. 

ЗЫ. Мы в свое время мониторы CRT с 15 до 17 разгоняли. Особые экземпляры до 19 гнались. Но это канало только с выпуклым экраном. Плоские к разгону были не чувствительны. 

lilik
Offline
Зарегистрирован: 19.10.2017

b707 пишет:

интересно, насколько оно у него работать будет? - зависит от углов размаха ногами, видимо :)

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

 Ипользуется 16-битный таймер с делителем 8, при 16 МГЦ это 2 тика за микросекунду. Диапазон сигналов серв - до 2400 мкс

Итого получаем 0xFFFF / (2400*2) = 13.6 серв на таймер

То есть гарантировано успеет обработать 13 серв, дальше как повезет :)

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

Хотя не.. пинов не хватит :).

 

Проверял таким аналогичным скетчем. Три сервы перетыкивал на уно по выходам разным, вроде работает справно. Из вашей подсказки понял, что библиотека штатно рассчитана на 13 приводов. И для "надёжности более глубокой" надо изменить делитель таймера на 64?, влезая в дикие дебри регистров.

//СКЕТЧ ПЛАВНЫХ ПЕРЕХОДОВ МЕЖДУ ЗАДАННЫМИ ПОЗАМИ РОБОТА
#include <Servo.h>// в библиотеку внесены изменения (В файле Servo.h строку #define SERVOS_PER_TIMER       12    // the maximum number of servos controlled by one timer
//                                  меняем на                               #define SERVOS_PER_TIMER       17     // the maximum number of servos controlled by one timer )
#define kolvoKAD 2//количество кадров-тактов
#define kolvoSV 17//количество сервоприводов 

Servo servo[kolvoSV];

byte ishod_ugolServ [kolvoSV]={90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90};//массив начальных углов серв
byte ugolServ [kolvoKAD][kolvoSV]={{180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180},{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};//массив-таблица хранения значений углов серв по кадрам
int tabl_da[kolvoSV];//массив переменных приращения углов серв
int tabl_ga[kolvoSV];//массив переменных длительностей между переключениями серв
long tabl_Ya[kolvoSV];//массив переменных хранения моментов времени


int n=1000;//длительность общая шага-цикла в мс
int i=0;//счётчик элементов массивов
int j=0;//счётчик элементов массивов
boolean fl_B=false;//флаг состояний

String inputString;// строки данных

void setup() {
  
 Serial.begin(57600); 
  for(i=0;i<kolvoSV;i++){servo[i].attach(i+2);servo[i].write(ishod_ugolServ [i]);delay(100); }//начальные установки углов 90 градусов (подкл. на выводы начиная со 2 по возр.)  


} 
void loop() {
 CheckSerial();
 ispol_();//функция на исполнение записанных в массив кадров
  
}
///////////////////////////////////////////////////////
//запускается цикл ходьбы
void ispol_()
{

     for (j=0;j<kolvoKAD;j++)//проверяем все углы по кадрам-тактам
   
   {
    
       for(i=0;i<kolvoSV;i++)
       {
       
        
        if(ugolServ [j][i]-servo[i].read()>0){tabl_da[i]=1;}//определяем есть ли приращение и его знак
        if(ugolServ [j][i]-servo[i].read()<0){tabl_da[i]=-1;}
        if(ugolServ [j][i]-servo[i].read()==0){tabl_da[i]=0;}
        if(tabl_da[i]!=0){tabl_ga[i]=n/abs(ugolServ [j][i]-servo[i].read());}//определяем временную паузу между приращениями шага
        if(tabl_da[i]==0){tabl_ga[i]=0;}
        } 
      /////////////////
      do
       {
        fl_B=true;
            for (i=0; i<kolvoSV ; i++) {
              if (ugolServ [j][i]!=servo[i].read()) { 
               fl_B=false; 
                break; 
                  }
                  }
         for(i=0;i<kolvoSV;i++)
         {
         if(millis()- tabl_Ya[i]>tabl_ga[i]&&ugolServ [j][i]!=servo[i].read()){tabl_Ya[i]=millis(); servo[i].write(servo[i].read()+tabl_da[i]);} 
      
         }
      }while(!fl_B); // пока все сервы не придут к табличному углу поворота...
     
   }

  
}
/////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//ФУНКЦИИ ПРИЁМА И ОБРАБОТКИ КОМАНД-ДАННЫХ
void MakeCmd()
{
  int y = inputString.length(); // присваиваем переменной у число символов в строке
  if (y < 1 || y > 5){inputString = "";return;} //если их меньше 1 или больше 5, выходим из функции
  String cmd = inputString; // передаём строку другой переменной
  inputString = "";//"обнуляем переменную"
  //////
    if (cmd == "0") {                 //если получили ноль
    Serial.write(0x14);               // ответить avrdude.exe
    Serial.write(0x10);               //  для синхронизации 
    delay(10); 
    pinMode(19,OUTPUT);        //ЭТО ВЫЗОВЕТ аппаратный РЕСЕТ
  }
  ////////
  
}

///////////////////////////////////////////////////////////////////////////////////////////////////////

void  CheckSerial()
{
  //////////////////////////////////////////////////////////////////////////////
  while (Serial.available())//считываем строку данных пока они поступают
  {
    char inChar = (char)Serial.read();
    if (inChar == '\n'||inChar == ' ')     //если окончание строки или запрос на загрузку
    {
      MakeCmd();//обрабатываем её данной функцией
      break;
    }
    else inputString += inChar;// иначе удлиняем строку на один символ
  }
}

/////////////////////////////////////////////////////////////////////////////////

 

 

b707
Offline
Зарегистрирован: 26.05.2017

lilik пишет:

Из вашей подсказки понял, что библиотека штатно рассчитана на 13 приводов. И для "надёжности более глубокой" надо изменить делитель таймера на 64?, влезая в дикие дебри регистров.

нет, "штатно" она рассчитана на 12. Но на 13-ти гарантировано будет работать. Все что выше - на ваш страх и риск.

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

OK0
Offline
Зарегистрирован: 06.03.2020

Можно посмотреть в сторону LGT8F328P - ничего не меняя, имеем 32 МГц на платах типа pro mini, nano, да и дешевле. 

lilik
Offline
Зарегистрирован: 19.10.2017

OK0 пишет:

Можно посмотреть в сторону LGT8F328P - ничего не меняя, имеем 32 МГц на платах типа pro mini, nano, да и дешевле. 

Ну тут надо заказать, дождаться, поставить расширение, научиться пользоваться новой платой...

а тут убиться, ища где поправить файл :)

Просто следующий аппаратный зигзаг - включить две про мини на один блютуз, поочерёдно заливать по воздуху скетчи уже для 34 серв :)

lilik
Offline
Зарегистрирован: 19.10.2017

Для правки нашёл такую строку для первого таймера (тот, не тот микроконтроллер?) 

TCCR1B = _BV(CS11);     // set prescaler of 8

и ещё две подозрительные на исправление :

#define usToTicks(_us)    (( clockCyclesPerMicrosecond()* _us) / 8)     // converts microseconds to tick (assumes prescale of 8)  // 12 Aug 2009
#define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ну я не знаю, 2400мксек это стандарт на максимальную длительность  сигнала PWM видимо

Upper
Offline
Зарегистрирован: 23.06.2020

b707 пишет:

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

Заинтересовала эта тема. До этого с сервами не работал, так что могу ошибаться. Но (на первый взляд) переполнение таймера не должно прервать или сбить обслуживание "старших" серв. Период только может превысить 20 мс.

Дополнение. Если не вносить в код изменения, то при переполнении таймера, период возрастет сразу в два раза до 40 мс. Если отслеживать факт переполнения, то можно уменьшить период до реально необходимого значения. Но пока не проверял, не будет ли сбоев, если в результате вычислений OCR1A станет = 0, но вроде не должно.

lilik
Offline
Зарегистрирован: 19.10.2017

Интересная гипотеза. Пока из особенностей 17 вместо 12 заметил , что при подходе качалок к 180 градусам плавность хода слегка заменяется скачками, появляется шелест.  Поменял в библиотеке период повтора упр. импульсов на 10, 15,25 мс.  Ничего не изменилось, но при наименьшем значении шелест стал существенно тише.

Надо бы попробовать всёж поставить таймер1 на делитель 64, но чего то страшно:)

b707
Offline
Зарегистрирован: 26.05.2017

lilik пишет:

Надо бы попробовать всёж поставить таймер1 на делитель 64, но чего то страшно:)

а чего боятся... это ж не обрезание. если не заработает - вернете обратно :)

lilik
Offline
Зарегистрирован: 19.10.2017

Так с таймером и не получилось, исправлял- не работала библиотека. Но в целом 17 серв работают как и 12 :)

b707
Offline
Зарегистрирован: 26.05.2017

Класс

Upper
Offline
Зарегистрирован: 23.06.2020

lilik пишет:

Поменял в библиотеке период повтора упр. импульсов на 10, 15,25 мс.  Ничего не изменилось, но при наименьшем значении шелест стал существенно тише.

При периоде повтора 20 мс, после обслуживания последнего серво проверяется - отсчитал ли таймер 20 мс. Если больше 20 мс, то таймер сбрасывается и начинается новый период. Если меньше 20 мс то ждет пока досчитает до 20 мс и тогда сбрасывает и начинаем новый период. Но если серв много, и произойдет переполнение таймера (32 мс), то после обслуживания последнего серво информации о переполнении не учитывается и ждет очередные 20 мс. Итого период становится 32+20=52 мс. Если добавить проверку флага переполнения таймера после обслуживания последнего серво, то при переполнении можно начинать новый период сразу (не забыв сбросить флаг). Когда вы ставили период повтора 10 мс, то вы уменьшали период в случае переполнения с 32+20=52 до 32+10=42.

lilik
Offline
Зарегистрирован: 19.10.2017

Upper пишет:

lilik пишет:

Поменял в библиотеке период повтора упр. импульсов на 10, 15,25 мс.  Ничего не изменилось, но при наименьшем значении шелест стал существенно тише.

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

Найти бы это ещё :)

 Пока уловил, что период повтора надо снизить до 2-0 мс - хотя это неграмотное решение.

sadman41
Offline
Зарегистрирован: 19.10.2016

Не все сервы хорошо относятся к снижению частоты ШИМ.

Upper
Offline
Зарегистрирован: 23.06.2020

lilik пишет:

 Пока уловил, что период повтора надо снизить до 2-0 мс - хотя это неграмотное решение.

Если использовать этот вариант ТОЛЬКО для большого числа серв, то проблем быть не должно (по моему). Есть сомнения в моменте подключения, т.к. если как у вас в коде стоят задержки после подключения каждой сервы delay(200); то некоторое время, до подключения всех серв, они могут работать с сильно повышенной частотой, что может быть вредно.

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

Вариант с проверкой переполнения универсальный, но надо править код. Повторюсь - для 17 серв ваш вариант выглядит вроде нормальным. Только снижать не до 0. Какой допустимый минимум не считал, но меньше (например) #define REFRESH_INTERVAL    200   выигрыш пренебрежимо мал.

Upper
Offline
Зарегистрирован: 23.06.2020

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

static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t *TCNTn, volatile uint16_t* OCRnA)
{
  if( Channel[timer] < 0 )
    *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
  else{
    if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && SERVO(timer,Channel[timer]).Pin.isActive == true )
      digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,LOW); // pulse this channel low if activated
  }

  Channel[timer]++;    // increment to the next channel
  if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
    *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
    if(SERVO(timer,Channel[timer]).Pin.isActive == true)     // check if activated
      digitalWrite( SERVO(timer,Channel[timer]).Pin.nbr,HIGH); // its an active channel so pulse it high
  }
  else {
    // finished all channels so wait for the refresh period to expire before starting over
    if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL) )  // allow a few ticks to ensure the next OCR1A not missed
      *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
    else
      *OCRnA = *TCNTn + 4;  // at least REFRESH_INTERVAL has elapsed
    Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
  }
} 

В 18 строке добавить проверку, что количество серв меньше 17. Тогда если их меньше 17, то (как и было) будет вставлено ожидание, а если нет, то сразу else с подготовкой к новому периоду.

lilik
Offline
Зарегистрирован: 19.10.2017

Попробую добавить условие и посмотреть в макете на уно с парой серв.

-NMi-
Offline
Зарегистрирован: 20.08.2018

Upper пишет:

При периоде повтора 20 мс, после обслуживания последнего серво проверяется - отсчитал ли таймер 20 мс.

Это понятно.

Upper пишет:

Если больше 20 мс, то таймер сбрасывается и начинается новый период.

Соответственно "опоздав" на время = (время исполнения вектора isr) - 20мс (или 32), верно?

Upper пишет:

Если меньше 20 мс то ждет пока досчитает до 20 мс и тогда сбрасывает и начинаем новый период.

В таком случае код "успевает" за тиками таймера и тики происходят без задержек и в строго отведённые временные рамки, верно?

Upper пишет:

Но если серв много, и произойдет переполнение таймера (32 мс), то после обслуживания последнего серво информации о переполнении не учитывается и ждет очередные 20 мс.

В таком случае выполнение вектора isr от таймера будет совершенно случайным и никак не привязанным ко времени тиков, верно?

========

В таком случае есть ли какой-нибудь метод определения, что таймер (isr) "успеет" посчитать и не "наехать" на другие события, например от других таймеров.

Например, имеем некую программу, в которой есть всего 3 вектора и они выполняются за определённый % времени. В таком случае имеем 3 разные модели поведения:

1 - (1 isr = 10%), (2 isr = 20%), (3 main < 70%); 10 + 20 + <70 = <100% === всё прекрасно работает.

2 - (1 isr = 10+-5%), (2 isr = 20+_5%), (3 main < 70+-10%); ~10+-5  +  ~20+-5  + ~70+-10  = ~100+-20% === имеем "плавающий" непостоянный глюк.

3 - (1 isr > 10%), (2 isr > 20%), (3 main > 70%);  >10 + >20 + >70 = >100% === нихрена ничего не работает так как нужно.

Например, вот так.

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

b707 пишет:

lilik пишет:

Из вашей подсказки понял, что библиотека штатно рассчитана на 13 приводов. И для "надёжности более глубокой" надо изменить делитель таймера на 64?, влезая в дикие дебри регистров.

нет, "штатно" она рассчитана на 12. Но на 13-ти гарантировано будет работать. Все что выше - на ваш страх и риск.

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

правильно (2.4мсек) х (на количество серв)?

Upper
Offline
Зарегистрирован: 23.06.2020

-NMi- пишет:

В таком случае есть ли какой-нибудь метод определения, что таймер (isr) "успеет" посчитать и не "наехать" на другие события, например от других таймеров.

Например, имеем некую программу, в которой есть всего 3 вектора и они выполняются за определённый % времени. В таком случае имеем 3 разные модели поведения:

1 - (1 isr = 10%), (2 isr = 20%), (3 main < 70%); 10 + 20 + <70 = <100% === всё прекрасно работает.

2 - (1 isr = 10+-5%), (2 isr = 20+_5%), (3 main < 70+-10%); ~10+-5  +  ~20+-5  + ~70+-10  = ~100+-20% === имеем "плавающий" непостоянный глюк.

3 - (1 isr > 10%), (2 isr > 20%), (3 main > 70%);  >10 + >20 + >70 = >100% === нихрена ничего не работает так как нужно.

Например, вот так.

Я не понял, что вы имеете в виду. Здесь используется обработчик по совпадению OCR1A, он приведен в #31.

Время выполнения обработчика примерно одинаково для любых условий (внутри обработчика ничего не ждут). 

lilik
Offline
Зарегистрирован: 19.10.2017

Да, с подсказкой вы оказались правы качалки серв в окрестностях 180 градусов практически перестали ступенчато двигаться.

В файле Servo.cpp ( папка avr)
строку
if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL))  // allow a few ticks to ensure the next OCR1A not missed
меняем на
if( ((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL)&&SERVOS_PER_TIMER <=12)  // allow a few ticks to ensure the next OCR1A not missed

тест скетч:

//СКЕТЧ ПЛАВНЫХ ПЕРЕХОДОВ МЕЖДУ ЗАДАННЫМИ ПОЗАМИ РОБОТА ()
#include <Servo.h>// в библиотеку внесены изменения (В файле Servo.h строку #define SERVOS_PER_TIMER       12    // the maximum number of servos controlled by one timer
//                                  меняем на                               #define SERVOS_PER_TIMER       17     // the maximum number of servos controlled by one timer )
#define kolvoKAD 2//количество кадров-тактов
#define kolvoSV 17//количество сервоприводов 

Servo servo[kolvoSV];

byte ishod_ugolServ [kolvoSV]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//массив начальных углов серв
byte ugolServ [kolvoKAD][kolvoSV]={{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180,180}};//массив-таблица хранения значений углов серв по кадрам
int tabl_da[kolvoSV];//массив переменных приращения углов серв
int tabl_ga[kolvoSV];//массив переменных длительностей между переключениями серв
long tabl_Ya[kolvoSV];//массив переменных хранения моментов времени


int n=1000;//длительность общая шага-цикла в мс
int i=0;//счётчик элементов массивов
int j=0;//счётчик элементов массивов
boolean fl_B=false;//флаг состояний

String inputString;// строки данных

void setup() {
  
 Serial.begin(57600); 
  for(i=0;i<kolvoSV;i++){servo[i].attach(i+2);servo[i].write(ishod_ugolServ [i]);delay(100); }//начальные установки углов 90 градусов (подкл. на выводы начиная со 2 по возр.)  
 

} 
void loop() {
 CheckSerial();// проверяем нет ли новых данных
 ispol_();//функция на исполнение записанных в массив кадров
  
}
///////////////////////////////////////////////////////
//запускается цикл ходьбы
void ispol_()
{

     for (j=0;j<kolvoKAD;j++)//проверяем все углы по кадрам-тактам
  
   {
    
       for(i=0;i<kolvoSV;i++)
       {
       
        
        if(ugolServ [j][i]-servo[i].read()>0){tabl_da[i]=1;}//определяем есть ли приращение и его знак
        if(ugolServ [j][i]-servo[i].read()<0){tabl_da[i]=-1;}
        if(ugolServ [j][i]-servo[i].read()==0){tabl_da[i]=0;}
        if(tabl_da[i]!=0){tabl_ga[i]=n/abs(ugolServ [j][i]-servo[i].read());}//определяем временную паузу между приращениями шага
        if(tabl_da[i]==0){tabl_ga[i]=0;}
        } 
      /////////////////
      do
       {
        fl_B=true;
            for (i=0; i<kolvoSV ; i++) {
              if (ugolServ [j][i]!=servo[i].read()) { 
               fl_B=false; 
                break; 
                  }
                  }
         for(i=0;i<kolvoSV;i++)
         {
         if(millis()- tabl_Ya[i]>tabl_ga[i]&&ugolServ [j][i]!=servo[i].read()){tabl_Ya[i]=millis(); servo[i].write(servo[i].read()+tabl_da[i]);} 
         CheckSerial();// проверяем нет ли новых данных
         }
      }while(!fl_B); // пока все сервы не придут к табличному углу поворота...
     
   }
 
  
}
/////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//ФУНКЦИИ ПРИЁМА И ОБРАБОТКИ КОМАНД-ДАННЫХ
void MakeCmd()
{
  int y = inputString.length(); // присваиваем переменной у число символов в строке
  if (y < 1 || y > 5){inputString = "";return;} //если их меньше 1 или больше 5, выходим из функции
  String cmd = inputString; // передаём строку другой переменной
  inputString = "";//"обнуляем переменную"
  //////
    if (cmd == "0") {                 //если получили ноль
    Serial.write(0x14);               // ответить avrdude.exe
    Serial.write(0x10);               //  для синхронизации 
    delay(100); 
    pinMode(19,OUTPUT);        //ЭТО ВЫЗОВЕТ аппаратный РЕСЕТ
  }
  ////////
  
}

///////////////////////////////////////////////////////////////////////////////////////////////////////

void  CheckSerial()
{
  //////////////////////////////////////////////////////////////////////////////
  while (Serial.available())//считываем строку данных пока они поступают
  {
    char inChar = (char)Serial.read();
    if (inChar == '\n'||inChar == ' ')     //если окончание строки или запрос на загрузку
    {
      MakeCmd();//обрабатываем её данной функцией
      break;
    }
    else inputString += inChar;// иначе удлиняем строку на один символ
  }
}

/////////////////////////////////////////////////////////////////////////////////