Ви не увійшли.
Всім міцного!
Хлопці потребую допомоги у написанні скетчу.
Задача:
Маємо PWM сигнал з іншого пристрою, треба з нього по команді (умова (average < 20)), перетворити на логічну одиницю і якщо через деякий час ще буде такий саме імпульс, на нуль. Використовую порт з LED, щоб бачити як відпрацьовує логіка.
#define NUM_READINGS 500
int average;
int outPin = 13;
void setup() {
digitalWrite(13, LOW);
Serial.begin(9600);
}
void loop() {
long sum = 0; // локальна змінна sum
for (int i = 0; i < NUM_READINGS; i++) { // згідно з кількістю усереднень
sum += analogRead(0); // сумуємо значення з датчика змінної sum
}
average = sum / NUM_READINGS; // знаходимо середнє арифметичне, поділивши суму на число вимірів
Serial.println(average); // для прикладу виводимо в порт
if (average < 20) { // умова на потрібний імпульс (шим)
digitalWrite(outPin, HIGH); // робимо високий рівень
} else {
digitalWrite(13, LOW);
}
}
Є нюанс, справа в тому, що імпульс пробігає декілька разів і в мене, порт падає в LOW. Можливо, я все роблю не правильно, є інший алгоритм...
Підскажіть як це можна реалізувати.
Дякую!
Неактивний
Маємо PWM сигнал з іншого пристрою
Яка частота PWM? Якщо не фіксована, то який допустимий діапазон: мінімум та максимум?
Якщо це PWM, чому ви читаєте аналогове значення, а не дискретне? Чи PWM попередньо фільтрується, і на вході аналоговий сигнал? Тоді яка різниця, як він формується, PWM чи іншим способом?
треба з нього по команді (умова (average < 20))
Вважаючи, що ви робите по 500 виборок, це значить коефіцієнт заповнення 4%? Чи якась фіксована напруга у випадку аналогового сигналу?
перетворити на логічну одиницю і якщо через деякий час ще буде такий саме імпульс, на нуль.
Тобто умовою перемикання виходу на протилежне значення є перехід коефіцієнту заповнення від значення 4% і вище до значення нижче 4%, чи як?
EDIT: про 4% писав до того як звернув увагу, що там analogRead(), а не digitalRead(). Тоді ще менше зрозуміло, яка саме умова перемикання вихода. Бо значення 20 від analogRead() - це десь 2% від Vcc, тобто приблизно 0.1 вольт для 5-вольтової ардуіни або 0.06 вольт для 3.3-вольтової.
імпульс пробігає декілька разів і в мене, порт падає в LOW
У вашій спробі реалізації стан виходу залежить тільки від поточного середнього значення, без врахування поточного стану виходу.
Також, частота виборки залежить від тактової частоти процесора та інших сторонніх факторів, таких як версія компілятора та опції збірки.
Можливо, я все роблю не правильно, є інший алгоритм... Підскажіть як це можна реалізувати.
Ви не могли б описати умову задачі, а не своє бачення її реалізації?
Остання редакція dimich (2025-06-08 01:21:25)
Неактивний
Звичайно PWM з польотного контроллера має частоту 50 гц і довжину імпульсу 1..2 мс. Якщо вам для цього, то простіше за все налаштувати переривання по зміні рівня і вимірювати час.
Конкретно що не так з вашим кодом. Ви 500 разів підряд читаєте значення з АЦП і рахуєте середнє значення. Зазвичай так роблять, коли інтервал часу, на якому усереднюють, відомий. У вашому випадку - ні. 500 відліків - невідомо скільки мікросекунд.
Далі. АЦП вертає значення від 0 до 1024, 1024 відповідає, якщо не помиляюсь, 3.3 В. Що таке 20 незрозуміло, але це дуже близько до рівня шуму. Скоріше за все, воно ловить завади від живлення.
Неактивний
Схоже, автор хотів зробити щось типу фільтра ковзаючим вікном з прямокутною віконною функцією. Але в загальному випадку для цього потрібно зберігати попередні значення в буфері. При кожному новому семплі найстаріше значення віднімати від акумульованої суми, замість нього записувати нове значення і додавати його до акумульованої суми.
Для періодичного сигналу можна і без збереження, але похибка залежить від співвідношення тривалості вікна та періоду сигналу.
Для бінарного сигналу можна замість самих значень 0/1 зберігати тривалості між фронтами, і по них рахувати середнє.
В будь-якому разі, як уже зазначили, потрібно знати, за який проміжок часу середнє.
Але це здогадки. Що насправді хоче автор - відомо тільки автору, чекаєм на зрозумілий опис задачі.
500 відліків - невідомо скільки мікросекунд.
Можна приблизно оцінити. В ардуіно тактова частота АЦП встановлюється 125 кГц, тобто при 10 бітах на семпл та буфері в 500 семплів це десь 40 мс на буфер. Це не враховуючи часу на виконання коду.
АЦП вертає значення від 0 до 1024, 1024 відповідає, якщо не помиляюсь, 3.3 В.
До 1023 В ардуіно АЦП atmega328p за замовчуванням вимірює відносно Vcc (тобто 5 або 3.3 В), але можна перемкнути на Vref або внутрішнє джерело 1.1 В.
Неактивний
#define PIN_INPUT 2 // Arduino Nano - pin2 or pin3 only
#define PWM_WIDTH_FALSE 1400
#define PWM_WIDTH_TRUE 1600
unsigned long pulse_start = 0;
unsigned long pulse_width = 0;
bool state_from_pwm = false;
void catch_int()
{
if (digitalRead(PIN_INPUT))
{
pulse_start = micros();
} else {
pulse_width = micros() - pulse_start;
if (pulse_width < PWM_WIDTH_FALSE)
{
state_from_pwm = false;
} else if (pulse_width > PWM_WIDTH_TRUE)
{
state_from_pwm = true;
};
digitalWrite(LED_BUILTIN, state_from_pwm);
};
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PIN_INPUT, INPUT);
attachInterrupt(digitalPinToInterrupt(PIN_INPUT), catch_int, CHANGE);
interrupts();
}
void loop() {
}
Десь так. Очікуються імпульси на вході D2. Якщо ваші 20 - це 1/20 від 50 гц, тобто 1 мс, то варто взяти за межу середину допустимого інтервалу (1.5мс), і додати гістерезис +-100 мкс, для захисту від спрацьовувань близько до межі. Сподіваюсь, вгадав.
Хоча, якщо я вгадав, то мені здається, що ваша задача вирішується і без додаткового контроллера.
Неактивний
Конкретно що не так з вашим кодом. Ви 500 разів підряд читаєте значення з АЦП і рахуєте середнє значення. Зазвичай так роблять, коли інтервал часу, на якому усереднюють, відомий. У вашому випадку - ні. 500 відліків - невідомо скільки мікросекунд.
Далі. АЦП вертає значення від 0 до 1024, 1024 відповідає, якщо не помиляюсь, 3.3 В. Що таке 20 незрозуміло, але це дуже близько до рівня шуму. Скоріше за все, воно ловить завади від живлення.
Згоден!
Так, замало. Але мені цього вистачає, щоб зловити команду на запуск. Нажаль осцила нема, щоб зробити точні заміри імпульсів. Ціль - Продублювати запуск умовного сервомотору, тіки з логічною одиницею або нулем.
Слухаю порт, де значення можуть бути різними від 0 до 1023. Коли інший пристрий знаходится в режимі очікування, то сигнал PWM йде приблизно зі значеннями 69-74, а коли йде команда на виконання, то маємо десь 18-21. (умовні значення). і роблю порт (13) HIGH. Через деякий час поступає таж сама команда з умовним значенням 18-20 мені потрібно зробити порт (13) LOW. При тому коді, що скинув вишче, світлодіод блимкае приблизно 4 рази, та тухне. Та якщо убрати
} else {
digitalWrite(13, LOW);
}
світлодіод світиться постіно. Потрібно щоб він тух при подальшому, коли середне аріфмітичне (18-21) з'явиться в виборці.
Остання редакція Kvins (2025-06-08 11:42:38)
Неактивний
#define PIN_INPUT 2 // Arduino Nano - pin2 or pin3 only #define PWM_WIDTH_FALSE 1400 #define PWM_WIDTH_TRUE 1600 unsigned long pulse_start = 0; unsigned long pulse_width = 0; bool state_from_pwm = false; void catch_int() { if (digitalRead(PIN_INPUT)) { pulse_start = micros(); } else { pulse_width = micros() - pulse_start; if (pulse_width < PWM_WIDTH_FALSE) { state_from_pwm = false; } else if (pulse_width > PWM_WIDTH_TRUE) { state_from_pwm = true; }; digitalWrite(LED_BUILTIN, state_from_pwm); }; } void setup() { pinMode(LED_BUILTIN, OUTPUT); pinMode(PIN_INPUT, INPUT); attachInterrupt(digitalPinToInterrupt(PIN_INPUT), catch_int, CHANGE); interrupts(); } void loop() { }
Десь так. Очікуються імпульси на вході D2. Якщо ваші 20 - це 1/20 від 50 гц, тобто 1 мс, то варто взяти за межу середину допустимого інтервалу (1.5мс), і додати гістерезис +-100 мкс, для захисту від спрацьовувань близько до межі. Сподіваюсь, вгадав.
Хоча, якщо я вгадав, то мені здається, що ваша задача вирішується і без додаткового контроллера.
Дякую за відповідь. Протестив вашу задумку. Але навіть в режимі очікування спрацовує
state_from_pwm = true;
Остання редакція Kvins (2025-06-08 12:09:17)
Неактивний
Якась незвичайна серва у вас
#define PIN_INPUT 2 // Arduino Nano - pin2 or pin3 only
#define PWM_WIDTH_FALSE 1400
#define PWM_WIDTH_TRUE 1600
unsigned long pulse_start = 0;
unsigned long pulse_width = 0;
bool state_from_pwm = false;
void catch_int()
{
if (digitalRead(PIN_INPUT))
{
pulse_start = micros();
} else {
pulse_width = micros() - pulse_start;
if (pulse_width < PWM_WIDTH_FALSE)
{
state_from_pwm = false;
} else if (pulse_width > PWM_WIDTH_TRUE)
{
state_from_pwm = true;
};
digitalWrite(LED_BUILTIN, state_from_pwm);
};
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PIN_INPUT, INPUT);
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(PIN_INPUT), catch_int, CHANGE);
interrupts();
}
unsigned long ts_next = 0;
unsigned long pw;
void loop() {
if (millis() > ts_next)
{
ts_next = millis() + 1000;
noInterrupts();
pw = pulse_width;
interrupts();
Serial.println(pw);
};
}
Ну давайте спробуємо виміряти, що у вас там за PWM.
Неактивний
щось це не схоже на pwm. Або десь земля відірвана.
Розказуйте, якщо це не секретна бімба
щось це не схоже на pwm. Або десь земля відірвана.
Розказуйте, якщо це не секретна бімба
Інший пристрій це STM32F103 яка управляє сервомоторами зі зворотнім зв'язком у виді микроперемекача. Я вимикаю конектор мотора та туди ціпляюсь портом D2 (два пина: сигнал та GND)
Неактивний
Трохи дивно виходить. Ну давайте поміряємо період імпульсів.
#define PIN_INPUT 2 // Arduino Nano - pin2 or pin3 only
#define PWM_WIDTH_FALSE 1400
#define PWM_WIDTH_TRUE 1600
unsigned long pulse_start = 0;
unsigned long pulse_width = 0;
unsigned long period = 0;
unsigned long pulse_prev_start = 0;
bool state_from_pwm = false;
void catch_int()
{
if (digitalRead(PIN_INPUT))
{
pulse_prev_start = pulse_start;
pulse_start = micros();
} else {
pulse_width = micros() - pulse_start;
if (pulse_width < PWM_WIDTH_FALSE)
{
state_from_pwm = false;
} else if (pulse_width > PWM_WIDTH_TRUE)
{
state_from_pwm = true;
};
digitalWrite(LED_BUILTIN, state_from_pwm);
};
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PIN_INPUT, INPUT);
Serial.begin(115200);
attachInterrupt(digitalPinToInterrupt(PIN_INPUT), catch_int, CHANGE);
interrupts();
}
unsigned long ts_next = 0;
unsigned long pw;
void loop() {
if (millis() > ts_next)
{
ts_next = millis() + 1000;
noInterrupts();
pw = pulse_width;
period = pulse_start - pulse_prev_start;
interrupts();
Serial.print(period);
Serial.print(" ");
Serial.println(pw);
};
}
Неактивний
if (average < 20) // умова на потрібний імпульс (шим)
{
digitalWrite(outPin, HIGH); // робимо високий рівень
} else if digitalRead(outPin,HIGH) { // i якщо імпульс був знов через деякий час , де був HIGH
digitalWrite(outPin, LOW); // вимикаємо
Наразі не компілюється, щось с синтаксисом..
Неактивний
Можливо. Дивіться попередження компіляції.
Але спочатку розберіться що у вас там за сигнал. Не схоже це на pwm. .
>> сигнал PWM йде приблизно зі значеннями 69-74,
що це за значення, звідки ви його взяли?
Можливо. Дивіться попередження компіляції.
Але спочатку розберіться що у вас там за сигнал. Не схоже це на pwm. .>> сигнал PWM йде приблизно зі значеннями 69-74,
що це за значення, звідки ви його взяли?
Я дивився у мониторі порта зміни при натисканні команди серво
Неактивний
Ну, що ми тут бачимо? Коли вимірюється період 20 мс - довжина імпульсу 2 мс. Схоже на правду. А інше якесь дивне. Ви там серву не від USB живите?
Ну, покрутіть той stm32. Якщо з нього в іншому положенні вдасться отримати імпульси 1 мс - можна просто ігнорувати імпульси з неправильним періодом.
Хлопці все запрацювало
ось...
#define NUM_READINGS 600
int average;
int outPin = 13;
int inPWM = A0;
void setup() {
digitalWrite(outPin, LOW);
pinMode(13, OUTPUT);
Serial.begin(9600);
}
void loop() {
long sum = 0; // локальна змінна sum
for (int i = 0; i < NUM_READINGS; i++) { // згідно з кількістю усереднень
sum += analogRead(A0); // сумуємо значення з датчика змінної sum
}
average = sum / NUM_READINGS; // знаходимо середнє арифметичне, поділивши суму на число вимірів
Serial.println(average); // для прикладу виводимо в порт
delay(1000);
if (average < 22 && digitalRead(outPin) == LOW) // умова на потрібний імпульс (шим)
{
digitalWrite(outPin, HIGH); // робимо високий рівень
}
else if (average < 22 && digitalRead(outPin) == HIGH) // і якщо імпульс був раніше
{
digitalWrite(outPin, LOW); // вимикаємо
}
}
Неактивний