Серво тестер на ардуино

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

Решил разработку простого серво тестера вынести отдельным проектом.  (НУ НЕ ПРОПАДАТЬ ЖЕ ДОБРУ)
my very tks ЕвгенийП

Преамула:

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

Следующим постом будет приведен скетч в котом я попробую реализовать серво тестер имеющий канал входа, для измерения параметров входного сигнала PWM  и калибровки этого сигнала.
Для подключения планируется реализовать три канала управления.
1. Регулировка потенциометром и переключателями (плавная, ступенчатая, по заранее установленным показателям (1000, 1500, 2000 мксек)
2. Регулировка при помощи энкодера (ступенчатая, шаг регудировки 10)
3. Сигнал повторяет измеренный сигнал по входу тестера.

Последующий пост прошу не комментировать, дабы иметь возможность правки исходника!

/*  ПРОСТОЙ СЕРВОТЕСТЕР НА АРДУИНО НАНО  
 *  V-1.01
 *   
 */
// Подключаем библиотеку измерения PWM сигнала
// Измеряемый интервал - от 80 до 65534 тактов процессора
// при 20МГц это 4 - 3275 микросекунд, 
// при 16МГц это 5 - 4095 микросекунд
// Для измерения можно воспользоваться как 0 (PD2) пин 2
// так и первым (PD3) пин 3 ардуины прерыванием.
// Для задействования 1-го прерывания надо раскомментировать
// в файле TimeMeasure.cpp 7-ю строчку - #define USE_INT_1
// Если планируется использование функции tone()
// (tone(pin, frequency, duration)) то надо
// помнить, что она конфликрует с пинами 3 и 11 и не дает
// на этих пинах использовать режим PWM
// При вычислениях длительности импульса в микросекундах с точностью
// до десятых долей в качестве делителя используем константу 
// TICKS_PER_MICROSECOND 
// зависящую от частоты  кварца АРДУИНО (вычисляется в библиотеке)
// Функцию ticks2Microseconds(res) используем для вычисления
// длительности импульса с точностью до единиц микросекунд
// Для задания параметров на выходных портах серво-подключений
// используется стандартная библиотека SERVO.h имеющая функции:
// 

// Подключим библиотеку измерения длительности PWM
#include  "TimeMeasure.h"
float t_strobe = 0; // для измерения длительности с точность 0,0
int t_input = 0;

// Подключим библиотеку нашего дисплея

// Подключим библиотеку SERVO и назначаем переменные
#include <Servo.h>
#define PORT_PTM A0     // Порт подключения потенциометра
#define PORT_SW1 A1     // Порт подключения переключателя SW1
#define PORT_SW2 A2     // Порт подключения переключателя SW2
#define PORT_PWM1 5     // Порт подключения сервы (PWM1)           // Управляется потенциометром
#define PORT_PWM2 6     // Порт подключения сервы (PWM2)           // Управляется энкодером
#define PORT_PWM3 7     // Порт подключения сервы (PWM3)           // Управляется от входного сигнала

Servo servo_01;
Servo servo_02;
Servo servo_03;
int potenciometr = 1500;
int t_encoder = 1500;     // Начальное значение энкодера 1500 (в центр сервы) 
int enc_step = 10;        // Шаг перестройки с энкодера
int sw1 = 0;
int sw2 = 0;

// Энкодер
#define PORT_ENC_A 11 
#define PORT_ENC_B 12 
unsigned char encoder_A;
unsigned char encoder_B;
unsigned char encoder_A_prev=0;
unsigned long currentTime;
unsigned long loopTime;

void encoder(){
  currentTime = millis();
  if((currentTime-loopTime) >= 5){          // проверяем каждые 5мс (200  Гц)
  encoder_A =  digitalRead(PORT_ENC_A);     // считываем состояние  выхода А  энкодера
  encoder_B =  digitalRead(PORT_ENC_B);     // считываем состояние  выхода А  энкодера   
    if((!encoder_A) && (encoder_A_prev)){   // если  состояние изменилось с положительного  к нулю
      if(encoder_B) {                       // выход В в полож.  сост.,  значит вращение  по часовой стрелке
                                            // увеличиваем значение PWM в канале 3 шагом 10
      if(t_encoder + enc_step <= 2490) t_encoder += enc_step;              
      }  
      else {                                // выход В в 0 сост., значит вращение против асовой стрелки    
                                            // уменьшаем значение PWM, но не  ниже 500
        if(t_encoder - enc_step >=  510) t_encoder -= enc_step;              
       }  
     }  
    encoder_A_prev  = encoder_A;            // сохраняем зн ачение А для следующего цикла
    loopTime = currentTime;
  }                      
}


void setup() {
  pinMode(PORT_PTM, INPUT);
  pinMode(PORT_SW1, INPUT);
  pinMode(PORT_SW2, INPUT);
  pinMode(PORT_ENC_A, INPUT);
  pinMode(PORT_ENC_B, INPUT); 
  
  servo_01.attach(PORT_PWM1);   // Сигнал задается потенциометром и SW1, SW2
  servo_02.attach(PORT_PWM2);   // Сигнал задается энкодером
  servo_03.attach(PORT_PWM3);   // Повторяет сигнал на входе измерения
  
  potenciometr = analogRead(PORT_PTM);
  sw1 = analogRead(PORT_SW1);
  sw2 = analogRead(PORT_SW2);
  Serial.begin(57600);
  initTimeMeasuring();

  currentTime  = millis();
  loopTime = currentTime;
}


void loop() {
  potenciometr = analogRead(PORT_PTM);    // Получаем значение PWM с потенциометра
  potenciometr = map(potenciometr, 0, 1023, 800, 2200);        // Нормализуем
 // encoder();                              // Получаем значение PWM с энкодера
                                          // Получаем значение PWM со входа ихмерителя
  const uint16_t res = measureResult();
  
  if (res){ 
    t_strobe = res;
    t_input = ticks2Microseconds(res);
    t_strobe = t_strobe/TICKS_PER_MICROSECOND;
    Serial.print("PWM = ");
    Serial.println(t_input);
    Serial.print("Fine PWM = ");
    Serial.println(t_strobe);
    Serial.println("");
          }
   servo_01.writeMicroseconds(potenciometr);  
   servo_02.writeMicroseconds(t_encoder);  
               Serial.println(t_encoder);  // отладка - выводит 1500 при соединени со входом - не меряет
   servo_03.writeMicroseconds(t_input); 
}

 

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

Код в котором буду производить правки:

ПРОШУ не ЦИТИРОВАТЬ!!!

измерение от стороннего источника работает, завернул 10 пин на вход (пин 2) - не выходит каменный цветок, надо искать осциллограф. ( смотрел осциллографом, на выходе низкий уровень)
Может конфликт с библиотекой Servo.h???

/*  ПРОСТОЙ СЕРВОТЕСТЕР НА АРДУИНО НАНО  
 *  V-1.01
 *   
 */
// Подключаем библиотеку измерения PWM сигнала
// Измеряемый интервал - от 80 до 65534 тактов процессора
// при 20МГц это 4 - 3275 микросекунд, 
// при 16МГц это 5 - 4095 микросекунд
// Для измерения можно воспользоваться как 0 (PD2) пин 2
// так и первым (PD3) пин 3 ардуины прерыванием.
// Для задействования 1-го прерывания надо раскомментировать
// в файле TimeMeasure.cpp 7-ю строчку - #define USE_INT_1
// Если планируется использование функции tone()
// (tone(pin, frequency, duration)) то надо
// помнить, что она конфликрует с пинами 3 и 11 и не дает
// на этих пинах использовать режим PWM
// При вычислениях длительности импульса в микросекундах с точностью
// до десятых долей в качестве делителя используем константу 
// TICKS_PER_MICROSECOND 
// зависящую от частоты  кварца АРДУИНО (вычисляется в библиотеке)
// Функцию ticks2Microseconds(res) используем для вычисления
// длительности импульса с точностью до единиц микросекунд
// Для задания параметров на выходных портах серво-подключений
// используется стандартная библиотека SERVO.h имеющая функции:
// 

// Подключим библиотеку измерения длительности PWM
#include  "TimeMeasure.h"
float t_strobe = 0; // для измерения длительности с точность 0,0
int t_input = 0;

// Подключим библиотеку нашего дисплея

// Подключим библиотеку SERVO и назначаем переменные
#include <Servo.h>
#define PORT_PTM A0     // Порт подключения потенциометра
#define PORT_SW1 A1     // Порт подключения переключателя SW1
#define PORT_SW2 A2     // Порт подключения переключателя SW2
#define PORT_PWM1 9     // Порт подключения сервы (PWM1)           // Управляется потенциометром
#define PORT_PWM2 10    // Порт подключения сервы (PWM2)           // Управляется энкодером
#define PORT_PWM3 11    // Порт подключения сервы (PWM3)           // Управляется от входного сигнала

Servo servo_01;
Servo servo_02;
Servo servo_03;
int potenciometr = 1500;
int t_encoder = 1500;     // Начальное значение энкодера 1500 (в центр сервы) 
int enc_step = 10;        // Шаг перестройки с энкодера
int sw1 = 0;
int sw2 = 0;

// Энкодер
#define PORT_ENC_A 5 
#define PORT_ENC_B 6 
unsigned char encoder_A;
unsigned char encoder_B;
unsigned char encoder_A_prev=0;
unsigned long currentTime;
unsigned long loopTime;

void encoder(){
  currentTime = millis();
  if((currentTime-loopTime) >= 5){          // проверяем каждые 5мс (200  Гц)
  encoder_A =  digitalRead(PORT_ENC_A);     // считываем состояние  выхода А  энкодера
  encoder_B =  digitalRead(PORT_ENC_B);     // считываем состояние  выхода А  энкодера   
    if((!encoder_A) && (encoder_A_prev)){   // если  состояние изменилось с положительного  к нулю
      if(encoder_B) {                       // выход В в полож.  сост.,  значит вращение  по часовой стрелке
                                            // увеличиваем значение PWM в канале 3 шагом 10
      if(t_encoder + enc_step <= 2490) t_encoder += enc_step;              
      }  
      else {                                // выход В в 0 сост., значит вращение против асовой стрелки    
                                            // уменьшаем значение PWM, но не  ниже 500
        if(t_encoder - enc_step >=  510) t_encoder -= enc_step;              
       }  
     }  
    encoder_A_prev  = encoder_A;            // сохраняем зн ачение А для следующего цикла
    loopTime = currentTime;
  }                      
}


void setup() {
  pinMode(PORT_PTM, INPUT);
  pinMode(PORT_SW1, INPUT);
  pinMode(PORT_SW2, INPUT);
  pinMode(PORT_ENC_A, INPUT);
  pinMode(PORT_ENC_B, INPUT); 
  
  servo_01.attach(PORT_PWM1);   // Сигнал задается потенциометром и SW1, SW2
  servo_02.attach(PORT_PWM2);   // Сигнал задается энкодером
  servo_03.attach(PORT_PWM3);   // Повторяет сигнал на входе измерения
  
  potenciometr = analogRead(PORT_PTM);
  sw1 = analogRead(PORT_SW1);
  sw2 = analogRead(PORT_SW2);
  Serial.begin(57600);
  initTimeMeasuring();

  currentTime  = millis();
  loopTime = currentTime;
}


void loop() {
  potenciometr = analogRead(PORT_PTM);    // Получаем значение PWM с потенциометра
  potenciometr = map(potenciometr, 0, 1023, 800, 2200);        // Нормализуем
 // encoder();                              // Получаем значение PWM с энкодера
                                          // Получаем значение PWM со входа измерителя
  const uint16_t res = measureResult();
  
  if (res){ 
    t_strobe = res;
    t_input = ticks2Microseconds(res);
    t_strobe = t_strobe/TICKS_PER_MICROSECOND;
    Serial.print("PWM = ");
    Serial.println(t_input);
    Serial.print("Fine PWM = ");
    Serial.println(t_strobe);
    Serial.println("");
          }
   servo_01.writeMicroseconds(potenciometr);  
   servo_02.writeMicroseconds(t_encoder);  
               Serial.println(t_encoder);  // отладка - выводит 1500 при соединени со входом - не меряет
   servo_03.writeMicroseconds(t_input); 
}

Файл TimeMeasure.h

#ifndef  TimeMeasure_h
#define TimeMeasure_h
//
//  Измеряем время высокого уровня на пине 2 (aka PD2) или 3 (PD3)
//  (см. комментарий в начале файла TimeMeasure.cpp)
//
//  Измеряемый интервал - от 80 до 65534 тактов процессора
//  (при 20МГц это 4 - 3275 микросекунд, при 16МГц это 5 - 4095 микросекунд
//  Если высокий уровень держится 65535 тактов, то
//  считаем это ошибкой и выдаём результат ENDLESS_INTERVAL
//
//  Результат измерений - количество тактов процессора.
//  Чтобы получить время в микросекундах, нужно полученный результат
//  разделить на константу TICKS_PER_MICROSECOND
//  

//
// Константа для 
//    1. пересчёта тактов в микросекунды (нужно поделить количество тактов на неё)
//    2. пересчёта микросекунд в такты (нужно умножить количество микросекунд на неё)
static const uint8_t TICKS_PER_MICROSECOND = static_cast <uint8_t> (F_CPU / 1000000UL);
//
// Интервал (в тактах), который считаем ошибкой (слишком большой)
static const uint16_t ENDLESS_INTERVAL = UINT16_MAX;

//
//  Инициализация всего (нужно вызвать из setup)
//
extern void initTimeMeasuring(void);

//
//  Функция возвращает результат измерения, заодно обнуляя его
//
extern uint16_t measureResult(void);

//
//  Пересчёт тактов в целые микросекунды с округлением до ближайшего целого
//
inline uint16_t ticks2Microseconds(const uint16_t ticks) {
  return static_cast <uint16_t> ((static_cast <uint32_t> (ticks) + TICKS_PER_MICROSECOND / 2) / TICKS_PER_MICROSECOND);
}

//
#endif  //  TimeMeasure_h

Файл TimeMeasure.cpp

#include  <arduino.h>
#include  "TimeMeasure.h"

//
//  Измеряем время высокого уровня на пине 2 (aka PD2)
//  (чтобы измерять на пине 3 (PD3), нужно раскомментировать следующую строку)
//  #define USE_INT_1
//
//  Измеряемый интервал - от 80 до 65534 тактов процессора
//  (при 20МГц это 4 - 3275 микросекунд, при 16МГц это 5 - 4095 микросекунд
//  Если высокий уровень держится 65535 тактов, то
//  считаем это ошибкой и выдаём результат ENDLESS_INTERVAL
//
//  Результат измерений - количество тактов процессора.
//  Чтобы получить время в микросекундах, нужно полученный результат
//  разделить на константу TICKS_PER_MICROSECOND
//  

#ifdef  USE_INT_1 // используем пин 3

  #define PIN_NUMBER  3
  #define PIN_MASK  (bit(PD3))
  #define INT_FLAG  INT1
  #define MODE_BIT  ISC10
  #define INT_VECT  INT1_vect

#else // используем пин 2

  #define PIN_NUMBER  2
  #define PIN_MASK  (bit(PD2))
  #define INT_FLAG  INT0
  #define MODE_BIT  ISC00
  #define INT_VECT  INT0_vect
  
#endif

//
//  Инициализация таймера-счётчика 1
//  Состоит из начальной инициализации, которая выполняется один раз
//  и дополнительной инициализации, которая выполняется перед каждым 
//  измерением (stopTimerCounter1)
//
static void stopTimerCounter1(void) {
  TCCR1B = 0; //  остановим таймер
  TCNT1 = 0;  // считаем с 0
}
static void initTimerCounter1(void) {
  stopTimerCounter1();
  TIMSK1 = bit(TOIE1);  //  Разрешить прерывание по переполнению
  TCCR1A = 0;
  TCCR1C = 0;
}

//
//  Инициализация внешнего прерывания 0/1 на смену состояния
//
static void initInterrupt0(void) {
  EICRA = bit(MODE_BIT);  // на смену состояния
  EIMSK = bit(INT_FLAG);  // прерывание 0/1
}

//
//  Инициализация всего (нужно вызвать из setup)
//
void initTimeMeasuring(void) {
  pinMode(PIN_NUMBER, INPUT);
  initTimerCounter1();
  initInterrupt0();
}

static volatile uint16_t result = 0;

//
//  Функция возвращает результат измерения, заодно обнуляя его
//
uint16_t measureResult(void) {
  cli();
  const uint16_t res = result;
  result = 0;
  sei();
  return res;
}

//
//  Если случилось переполнение, значит истёк таймаут
//  Ставим таймаут результатом и реинициализируем таймер 
//  для следующегг измерения
//
ISR(TIMER1_OVF_vect) {
  result = ENDLESS_INTERVAL;
  stopTimerCounter1();
}

//
//  Прилетело прерывание
//  Если на пинe PD2/PD3 высокий уровень - начинаем отсчёт
//  Если на пине PD2/PD3 низкий уровень - заканчиваем отсчёт
//
//  Примечание: в этой функции количество тактов до включения таймера
//  и до запоминания результата одинаковое, т.е. поправки не нужны.
//  Теоретически одинаковость может пропасть при новой версии компилятора
//  или при других опциях. Если есть сомнения, можно её убрать, а вместо ней
//  использовать закомментированную (ниже) функцию с ассемблерным кодом.
//  Там то уж никто не нагадит.
//
ISR(INT_VECT) {
  if (PIND & PIN_MASK) {
    TCCR1B = 1; // запускаем таймер
  } else {
    result = TCNT1;
    stopTimerCounter1();
  }
}
/*
ISR(INT_VECT) {
asm volatile(
   // if (PIND & bit(PDx))
      "sbis %[Prt],%[Mask]  \r\n" 
      "rjmp SignalEnds    \r\n"
  // {
      "ldi  r24, 1      \r\n" //  Единицу пихаем в TCCRB1
      "sts  %[TCCRB], r24 \r\n" //  т.е. запускаем таймер
      "rjmp AllDone     \r\n"  
  // } else {
    "SignalEnds: \r\n"
      "lds  r24, %[TCNTL] \r\n" // младший байт TCNT1 читаем раньше старшего
      "lds  r25, %[TCNTH] \r\n"
      "sts  %[res]+1, r25 \r\n" //  Значение TCNT1 пихаем 
      "sts  %[res], r24   \r\n" //  в переменнуюresult
      "sts  %[TCCRB], r1  \r\n" //  Пихаем 0 в TCCRB1 - останавливаем таймер
      "sts  %[TCNTH], r1  \r\n" // Пихаем 0 в TCNT1 на будущее 
      "sts  %[TCNTL], r1  \r\n" //  старший байт раньше младшего
  // }    
    "AllDone: \r\n"
    :
    [res] "=m" (result)
  : 
    [Prt] "I" (_SFR_IO_ADDR(PIND)), 
    [Mask] "M" (PIN_NUMBER),
    [TCCRB] "M" (_SFR_MEM_ADDR(TCCR1B)),
    [TCNTH] "M" (_SFR_MEM_ADDR(TCNT1H)),
    [TCNTL] "M" (_SFR_MEM_ADDR(TCNT1L))
  :
    "r24", "r25"
  );
}
*/

//

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Вместо использования сигнала прерывания, имхо будет значительно точнее использовать сигнал захвата таймера T1, он есть в НАНО и др. мелких камнях - везде где есть 16-и разрядный таймер. Точность измерения входного сигнала станет просто обалденной и без .. "килорублей".

Прерывания также точно измерять не получится: во-первых реакция на него слегка "плавает" и во-вторых, измеряете Вы через "таймер времени", который изначально не лучше чем 4мксек., А ещё может и заблокировать ожидаемое прерывание от входного сигнала .. итого получить точность выше +- 8мксек (ошибка в 16мксек) - будет проблематично.

Тем временем, "мертвая зона" у хорошей сервы может быть меньше 2мсек., а измерять стоит "на порядок" точней - прибор все же..

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

Arhat109-2 пишет:

Вместо использования сигнала прерывания, имхо будет значительно точнее использовать сигнал захвата таймера T1, он есть в НАНО и др. мелких камнях - везде где есть 16-и разрядный таймер. Точность измерения входного сигнала станет просто обалденной и без .. "килорублей".

Прерывания также точно измерять не получится: во-первых реакция на него слегка "плавает" и во-вторых, измеряете Вы через "таймер времени", который изначально не лучше чем 4мксек., А ещё может и заблокировать ожидаемое прерывание от входного сигнала .. итого получить точность выше +- 8мксек (ошибка в 16мксек) - будет проблематично.

Тем временем, "мертвая зона" у хорошей сервы может быть меньше 2мсек., а измерять стоит "на порядок" точней - прибор все же..


Это Вы сейчас с кем разговаривали )))
То, что измерять надо прибором имеющим хотя бы на порядок точность лучше требуемого предела измерения - согласен...
Мой уровень - чуть более чем уровень начинающего, дата шит видимо не одолею, да и камень тогда лучше взять STM32F103

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

С Вами. НАНО - вполне справится с вашей задачей. Возьмите первый таймер, у него есть вход "захват таймера", который собственно и предназначен для таких измерений "длительностей импульсов". На него в общем-то и надо вешать, в т.ч. и работу ультразвуковых датчиков тоже, а не гонять вхолостую pulseIn().

Там всё предельно просто: устанавливаете режим "захват таймера по входу ICP" и скажем высокий сигнал запускает замер, а низкий его останавливает (посмотрите даташит на этот момент, как там оно вточности) и всё. Получаете прерывание по входу ICP и в регистре ICP  - сколько насчитал таймер. Настройка делителя таймера дает как максимальный интервал так и соответственно точность измерений. На минимальном делителе получите предельную точность в 1-2 такта. Если нужна большая точность, и длительность не лезет в 16 бит, то используете прерывание переполнения счета таймера для увеличения разрядности счета .. делов-то.

Деталей даташита не помню, всегда когда надо что-то делать лезу заново и смотрю "как оно на самом деле", только в качестве идеи "как надо решать такие задачи" в части "как решение заточено разработчиками микроконтроллера", а не "как знаю", "как удобно", "как все (неучи) делают" .. оно ТАК ПОЛОЖЕНО, не более. :)

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

А разве сделано как-то иначе? - Файл TimeMeasure.cpp

Логично настроить микропроцессор на запуск счетчика по переднему фронту импульса, в IBM-XT крутизна фронта была от 30 до 120 наносекунд, то-есть при пороге срабатывания от 2.4 до 5 вольт погрешность счетчика составит порядка 0,1 микросекунды, задним фронтом по идее должен выставляться флаг прерывания. Счет естественно тактируется передним фронтом (JK-триггер).

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

Засада в том, что этот таймер использует необходимая мне библиотека Servo.h )))

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Понял - отстал. :)

Как понял ваши комментарии и суть прибора, Вы пытаетесь измеряемый "входящий сигнал" подвесить на обыкновенное, универсальное прерывание (INT) и измерять его длительности обыкновенным таймером, продолжая управлять серводвигателем через servo.h непонятно зачем: что является основной задачей прибора - управление сервой или ИЗМЕРЕНИЕ подаваемого сигнала и его точная настройка?

Я Вам предложил использовать для измерения то прерывание и режим таймера, которые специально предназначены для таких работ. А вот "как" вы будете управлять сервой (кстати, можно использовать и тот же 1-й таймер, промежду прочим) - вопрос "десятый". Но тут явно servo.h - не при делах должна оказаться.

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

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

Arhat109-2 пишет:

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

И какие это команды?
 

По таймингам:
Аналоговая серва - 20мсек (50 герц)
Цифровая серва - 8 мсек (125 герц)
Цифровая серва - 4 мсек (250гц)

То-есть для 50 герц можно уложиться, в 8 уже проблематично, а на 4 - не укладываемся

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

SG90 - цифровая, но на 50гц: http://www.ee.ic.ac.uk/pcheung/teaching/de1_ee/stores/sg90_datasheet.pdf

Тут больше от модели (начинки) зависит.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

sadman41 пишет:

SG90 - цифровая, но на 50гц: http://www.ee.ic.ac.uk/pcheung/teaching/de1_ee/stores/sg90_datasheet.pdf

Тут больше от модели (начинки) зависит.

Мои тесты показывали что тайминг в 12 тиков МК эта серва замечает очень даже прекрасно. А это .. 0,75мсек .. ВСЕГО-то.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

ua6em пишет:

И какие это команды?

Где-то в arhat.h ещё делал макрос настройки 16-и битного таймера в режим "серводвигатель", далее управляем как обычно через analogWrite() просто указывая величину ШИМ (оттуда же). Там кажется ещё и пример был как управлять.. щаз под рукой нет.

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

sadman41 пишет:

SG90 - цифровая, но на 50гц: http://www.ee.ic.ac.uk/pcheung/teaching/de1_ee/stores/sg90_datasheet.pdf

Тут больше от модели (начинки) зависит.

И диапазон по даташиту правильный 1000 -1500 -2000

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Так я не берусь доказывать, что SG90 не работает при частом пульсе. Насколько я читал - в ней начинаются разные явления типа нагрева. 

Просто я видел как-то сводную таблицу серв на wiki-сайте RC-шников. Допустимые частоты там разные, но все сервы цифровые.

UPD: типа такой табличка https://www.vstabi.info/en/tailservos

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

"пульс" там вообще-то "стандартный" от 50 до 60 герц. Серва управляется длительностью "1" ШИМ сигнала с этой частотой. В зависимости от ширины ШИМа она тупо встает и держит требуемый угол. Диапазон ширины ШИМ - нормирован от .. до, но: "как выясняется" он индивидуален от сервы к серве и вот какой нужен конкретно этой (или её подгонка под правильный угол в зависимости от ШИМ) и есть задача такого "тестера" - выдать величину ошибки угла в зависимости от ширины ШИМ. И тут как раз важен такой параметр сервы как "мертвая зона" - изменение ШИМ, которое серва может "игнорить".

Согласно даташиту, оригинальная (не китайская) SG-90 в районе нуля имеет мертвую зону в 10 микросекунд .. (Dead band width: 10 µs). Мои, китайские давали другие чиселки .. вплоть до 12 тиков таймера (выше ошибся) по 4мксек = 48 тактов = 3-6мксек. В любом случае, чтобы измерять надежно даже эти 10мксек, прибор должен иметь чувствительность на порядок ниже .. ну хотябы раза в 4 .. т.е. 1-2мксек ошибку он ловит просто обязан. А там ещё может быть косяк в крайних положениях и их соответствии стандарту по ширине ШИМ ..

sadman41
Онлайн
Зарегистрирован: 19.10.2016

А, вон вы о чем. Я думал, что "сервотестер" - это типа просто покрутить серву. Ну, коробочки такие на али с крутилкой - посмотреть, как серва ходит туды-сюды. А вам еще ШИМ-параметры мерять надо, оказывается. 

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

sadman41 пишет:

А, вон вы о чем. Я думал, что "сервотестер" - это типа просто покрутить серву. Ну, коробочки такие на али с крутилкой - посмотреть, как серва ходит туды-сюды. А вам еще ШИМ-параметры мерять надо, оказывается. 

 

Хотелось бы

В идеале не просто мерять, а загнать аппаратуру в сервисный режим и настроить )))
А так , если покруть серву, то на али есть, в ценах начинаются от 1 американских.
То-же самое и регуляторы ESC с моторами настраивать.

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

По "супер"-сервам - это вот такой прожект был: https://github.com/SterlingPeet/OpenServo 

Там и обратная связь и командование по I2C...

https://hackaday.io/project/158267-openservo-20

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

sadman41 пишет:

По "супер"-сервам - это вот такой прожект был: https://github.com/SterlingPeet/OpenServo 

Там и обратная связь и командование по I2C...

https://hackaday.io/project/158267-openservo-20

Так это вроде по управлению сервами по шине I2C, мне без надобности, мне только калибровать уже готовые устройства, чтобы я чужие аппараты об землю не приземлял )))