Серво тестер на ардуино
- Войдите на сайт для отправки комментариев
Решил разработку простого серво тестера вынести отдельным проектом. (НУ НЕ ПРОПАДАТЬ ЖЕ ДОБРУ)
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);
}
Код в котором буду производить правки:
ПРОШУ не ЦИТИРОВАТЬ!!!
измерение от стороннего источника работает, завернул 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" ); } */ //Вместо использования сигнала прерывания, имхо будет значительно точнее использовать сигнал захвата таймера T1, он есть в НАНО и др. мелких камнях - везде где есть 16-и разрядный таймер. Точность измерения входного сигнала станет просто обалденной и без .. "килорублей".
Прерывания также точно измерять не получится: во-первых реакция на него слегка "плавает" и во-вторых, измеряете Вы через "таймер времени", который изначально не лучше чем 4мксек., А ещё может и заблокировать ожидаемое прерывание от входного сигнала .. итого получить точность выше +- 8мксек (ошибка в 16мксек) - будет проблематично.
Тем временем, "мертвая зона" у хорошей сервы может быть меньше 2мсек., а измерять стоит "на порядок" точней - прибор все же..
Вместо использования сигнала прерывания, имхо будет значительно точнее использовать сигнал захвата таймера T1, он есть в НАНО и др. мелких камнях - везде где есть 16-и разрядный таймер. Точность измерения входного сигнала станет просто обалденной и без .. "килорублей".
Прерывания также точно измерять не получится: во-первых реакция на него слегка "плавает" и во-вторых, измеряете Вы через "таймер времени", который изначально не лучше чем 4мксек., А ещё может и заблокировать ожидаемое прерывание от входного сигнала .. итого получить точность выше +- 8мксек (ошибка в 16мксек) - будет проблематично.
Тем временем, "мертвая зона" у хорошей сервы может быть меньше 2мсек., а измерять стоит "на порядок" точней - прибор все же..
Это Вы сейчас с кем разговаривали )))
То, что измерять надо прибором имеющим хотя бы на порядок точность лучше требуемого предела измерения - согласен...
Мой уровень - чуть более чем уровень начинающего, дата шит видимо не одолею, да и камень тогда лучше взять STM32F103
С Вами. НАНО - вполне справится с вашей задачей. Возьмите первый таймер, у него есть вход "захват таймера", который собственно и предназначен для таких измерений "длительностей импульсов". На него в общем-то и надо вешать, в т.ч. и работу ультразвуковых датчиков тоже, а не гонять вхолостую pulseIn().
Там всё предельно просто: устанавливаете режим "захват таймера по входу ICP" и скажем высокий сигнал запускает замер, а низкий его останавливает (посмотрите даташит на этот момент, как там оно вточности) и всё. Получаете прерывание по входу ICP и в регистре ICP - сколько насчитал таймер. Настройка делителя таймера дает как максимальный интервал так и соответственно точность измерений. На минимальном делителе получите предельную точность в 1-2 такта. Если нужна большая точность, и длительность не лезет в 16 бит, то используете прерывание переполнения счета таймера для увеличения разрядности счета .. делов-то.
Деталей даташита не помню, всегда когда надо что-то делать лезу заново и смотрю "как оно на самом деле", только в качестве идеи "как надо решать такие задачи" в части "как решение заточено разработчиками микроконтроллера", а не "как знаю", "как удобно", "как все (неучи) делают" .. оно ТАК ПОЛОЖЕНО, не более. :)
А разве сделано как-то иначе? - Файл TimeMeasure.cpp
Логично настроить микропроцессор на запуск счетчика по переднему фронту импульса, в IBM-XT крутизна фронта была от 30 до 120 наносекунд, то-есть при пороге срабатывания от 2.4 до 5 вольт погрешность счетчика составит порядка 0,1 микросекунды, задним фронтом по идее должен выставляться флаг прерывания. Счет естественно тактируется передним фронтом (JK-триггер).
Если все так то точность должна быть лучше одной микросекунды, что и требуется.
Засада в том, что этот таймер использует необходимая мне библиотека Servo.h )))
Понял - отстал. :)
Как понял ваши комментарии и суть прибора, Вы пытаетесь измеряемый "входящий сигнал" подвесить на обыкновенное, универсальное прерывание (INT) и измерять его длительности обыкновенным таймером, продолжая управлять серводвигателем через servo.h непонятно зачем: что является основной задачей прибора - управление сервой или ИЗМЕРЕНИЕ подаваемого сигнала и его точная настройка?
Я Вам предложил использовать для измерения то прерывание и режим таймера, которые специально предназначены для таких работ. А вот "как" вы будете управлять сервой (кстати, можно использовать и тот же 1-й таймер, промежду прочим) - вопрос "десятый". Но тут явно servo.h - не при делах должна оказаться.
По совмещению двух функций таймера тоже всё тривиально: перенастраиваете таймер на измерение - получаете точную длительность интервала, затем перенастраиваете его на управление (3 команды!) и ставите серву куда сказано, затем снимаете сигнал с сервы и она останется в том же положении, но уже без упр. сигнала. Никаких проблем.. :)
По совмещению двух функций таймера тоже всё тривиально: перенастраиваете таймер на измерение - получаете точную длительность интервала, затем перенастраиваете его на управление (3 команды!) и ставите серву куда сказано, затем снимаете сигнал с сервы и она останется в том же положении, но уже без упр. сигнала. Никаких проблем.. :)
И какие это команды?
По таймингам:
Аналоговая серва - 20мсек (50 герц)
Цифровая серва - 8 мсек (125 герц)
Цифровая серва - 4 мсек (250гц)
То-есть для 50 герц можно уложиться, в 8 уже проблематично, а на 4 - не укладываемся
SG90 - цифровая, но на 50гц: http://www.ee.ic.ac.uk/pcheung/teaching/de1_ee/stores/sg90_datasheet.pdf
Тут больше от модели (начинки) зависит.
SG90 - цифровая, но на 50гц: http://www.ee.ic.ac.uk/pcheung/teaching/de1_ee/stores/sg90_datasheet.pdf
Тут больше от модели (начинки) зависит.
И какие это команды?
Где-то в arhat.h ещё делал макрос настройки 16-и битного таймера в режим "серводвигатель", далее управляем как обычно через analogWrite() просто указывая величину ШИМ (оттуда же). Там кажется ещё и пример был как управлять.. щаз под рукой нет.
SG90 - цифровая, но на 50гц: http://www.ee.ic.ac.uk/pcheung/teaching/de1_ee/stores/sg90_datasheet.pdf
Тут больше от модели (начинки) зависит.
И диапазон по даташиту правильный 1000 -1500 -2000
Так я не берусь доказывать, что SG90 не работает при частом пульсе. Насколько я читал - в ней начинаются разные явления типа нагрева.
Просто я видел как-то сводную таблицу серв на wiki-сайте RC-шников. Допустимые частоты там разные, но все сервы цифровые.
UPD: типа такой табличка https://www.vstabi.info/en/tailservos
"пульс" там вообще-то "стандартный" от 50 до 60 герц. Серва управляется длительностью "1" ШИМ сигнала с этой частотой. В зависимости от ширины ШИМа она тупо встает и держит требуемый угол. Диапазон ширины ШИМ - нормирован от .. до, но: "как выясняется" он индивидуален от сервы к серве и вот какой нужен конкретно этой (или её подгонка под правильный угол в зависимости от ШИМ) и есть задача такого "тестера" - выдать величину ошибки угла в зависимости от ширины ШИМ. И тут как раз важен такой параметр сервы как "мертвая зона" - изменение ШИМ, которое серва может "игнорить".
Согласно даташиту, оригинальная (не китайская) SG-90 в районе нуля имеет мертвую зону в 10 микросекунд .. (Dead band width: 10 µs). Мои, китайские давали другие чиселки .. вплоть до 12 тиков таймера (выше ошибся) по 4мксек = 48 тактов = 3-6мксек. В любом случае, чтобы измерять надежно даже эти 10мксек, прибор должен иметь чувствительность на порядок ниже .. ну хотябы раза в 4 .. т.е. 1-2мксек ошибку он ловит просто обязан. А там ещё может быть косяк в крайних положениях и их соответствии стандарту по ширине ШИМ ..
А, вон вы о чем. Я думал, что "сервотестер" - это типа просто покрутить серву. Ну, коробочки такие на али с крутилкой - посмотреть, как серва ходит туды-сюды. А вам еще ШИМ-параметры мерять надо, оказывается.
А, вон вы о чем. Я думал, что "сервотестер" - это типа просто покрутить серву. Ну, коробочки такие на али с крутилкой - посмотреть, как серва ходит туды-сюды. А вам еще ШИМ-параметры мерять надо, оказывается.
Хотелось бы
В идеале не просто мерять, а загнать аппаратуру в сервисный режим и настроить )))
А так , если покруть серву, то на али есть, в ценах начинаются от 1 американских.
То-же самое и регуляторы ESC с моторами настраивать.
По "супер"-сервам - это вот такой прожект был: https://github.com/SterlingPeet/OpenServo
Там и обратная связь и командование по I2C...
https://hackaday.io/project/158267-openservo-20
По "супер"-сервам - это вот такой прожект был: https://github.com/SterlingPeet/OpenServo
Там и обратная связь и командование по I2C...
https://hackaday.io/project/158267-openservo-20
Так это вроде по управлению сервами по шине I2C, мне без надобности, мне только калибровать уже готовые устройства, чтобы я чужие аппараты об землю не приземлял )))