PWM каждый другой контакт не читается
Сценарий
Я подключил 6 контактов своего радиоуправляемого приемника к Arduino, чтобы считывать входящие значения.
Проблема
Я использую цифровой ШИМ~ контакты 2-6. Как ни странно, Arduino читает только нечетные каналы:
1480, 0, 1175, 0, 991, 0
Если я физически поменяю местами кабели (2–7, 3–6, 4–5), идущие к Arduino, я получу ВСЕ значения:
988, 983, 1511, 1174, 1490, 1487
Я обнаружил, что если я вернусь к исходной настройке выводов и просто переверну соседние точки (2-3, 4-5, 6-7), это тоже сработает.
Вопрос
Что может быть причиной этого?
Код
Here is the current code I've written
int ch1, ch2, ch3, ch4, ch5, ch6;
int ch1Pin = 2;
int ch2Pin = 3;
int ch3Pin = 4;
int ch4Pin = 5;
int ch5Pin = 6;
int ch6Pin = 7;
void setup() {
pinMode(ch1Pin, INPUT);
pinMode(ch2Pin, INPUT);
pinMode(ch3Pin, INPUT);
pinMode(ch4Pin, INPUT);
pinMode(ch5Pin, INPUT);
pinMode(ch6Pin, INPUT);
Serial.begin(9600);
}
void loop() {
ch1 = pulseIn(ch1Pin, HIGH, 25000);
ch2 = pulseIn(ch2Pin, HIGH, 25000);
ch3 = pulseIn(ch3Pin, HIGH, 25000);
ch4 = pulseIn(ch4Pin, HIGH, 25000);
ch5 = pulseIn(ch5Pin, HIGH, 25000);
ch6 = pulseIn(ch6Pin, HIGH, 25000);
String p = (String)ch1;
p += ", " + (String)ch2;
p += ", " + (String)ch3;
p += ", " + (String)ch4;
p += ", " + (String)ch5;
p += ", " + (String)ch6;
Serial.println(p);
// delay(100);
}
Фото
ИЗМЕНИТЬ
После публикации этого вопроса я обнаружил, что изменение значения chXpin
на противоположное дает тот же эффект, что и изменение местами физических контактов. Это заставляет меня думать, что это проблема программного обеспечения, поэтому я копаюсь дальше.
ВРЕМЕННОЕ РЕШЕНИЕ
Я не уверен, почему это работает, но изменение порядка соседей pulseIn
решило проблему:
/* OLD
ch1 = pulseIn(ch1Pin, HIGH, 25000);
ch2 = pulseIn(ch2Pin, HIGH, 25000);
ch3 = pulseIn(ch3Pin, HIGH, 25000);
ch4 = pulseIn(ch4Pin, HIGH, 25000);
ch5 = pulseIn(ch5Pin, HIGH, 25000);
ch6 = pulseIn(ch6Pin, HIGH, 25000);
*/
ch2 = pulseIn(ch2Pin, HIGH, 25000);
ch1 = pulseIn(ch1Pin, HIGH, 25000);
ch4 = pulseIn(ch4Pin, HIGH, 25000);
ch3 = pulseIn(ch3Pin, HIGH, 25000);
ch6 = pulseIn(ch6Pin, HIGH, 25000);
ch5 = pulseIn(ch5Pin, HIGH, 25000);
Почему это сработало? Для меня это не имеет смысла.
@Jacksonkr, 👍2
Обсуждение1 ответ
Лучший ответ:
pulseIn()
— это блокирующая функция: она ожидает, пока не получит полное
пульса или до тех пор, пока не истечет время ожидания. Пока он ждет, Arduino не может
что-нибудь еще, и он будет пропускать импульсы на других каналах.
Приемник RC посылает импульсы последовательно. Тогда, если вы попытаетесь прочитать
их в правильной последовательности, это может сработать. Если вы прочитаете их из
последовательность, вы пропустите несколько импульсов, и соответствующий pulseIn()
вызовы могут истечь, возвращая нули.
Для такого рода задач я бы использовал прерывание смены контакта на контактах 2–7. Это должно позволить непрерывно считывать шесть импульсов без блокировки. вся программа. Но это низкоуровневое (AVR-уровня) программирование, наверное невозможно со стандартными библиотеками Arduino.
Правка. Вот что я имел в виду, когда предлагал использовать контакт. изменить прерывание. Обратите внимание, что номера контактов «зашиты» в код и не может быть легко изменен. Это потому что:
- Выводы 2–7 совместно используют запрос прерывания на изменение одного контакта. (PCINT2_vect), поэтому для синхронизации шести каналов требуется один ISR.
- Они также имеют общий порт (порт D), что означает, что все они могут быть прочитаны ISR в одной инструкции.
Другим возможным вариантом, обладающим этими замечательными свойствами, могут быть контакты. 8–13 (PCINT0_vect, порт B) или A0–A5 (PCINT1_vect, порт C).
/*
* Чтение 6 сигналов ШИМ с радиоуправляемого приемника.
*
* Каналы 0-5 подключены к цифровым контактам 2-7 (т.е. PD2-PD7 = PCINT18-23).
*/
// Всегда содержит последнюю длину, измеренную на каждом канале.
volatile uint16_t last_pulse_lengths[6];
// Прерывание срабатывает при каждом изменении контактов 2-7.
ISR(PCINT2_vect)
{
static uint8_t last_pin_states;
static uint16_t start_times[6]; // время начала для каждого канала
uint8_t pin_states = PIND >> 2; // смещено в биты 0-5
uint16_t now = micros();
// Проверяем каждый измененный бит порта.
for (int i = 0; i < 6; i++) {
uint8_t mask = _BV(i);
// Время начала записи по переднему фронту.
if ((pin_states & mask) && !(last_pin_states & mask))
start_times[i] = now;
// Запись длины импульса на заднем фронте.
if (!(pin_states & mask) && (last_pin_states & mask))
last_pulse_lengths[i] = now - start_times[i];
}
last_pin_states = pin_states;
}
// Возвращаем длину импульса, избегая условий гонки.
static void get_pulse_lengths(uint16_t lengths[6])
{
for (int i = 0; i < 6; i++) {
noInterrupts();
uint16_t tmp = last_pulse_lengths[i];
interrupts();
lengths[i] = tmp;
}
}
void setup()
{
// Настройка прерывания смены контакта.
PCIFR = _BV(PCIF2); // сброс флага прерывания смены контакта
PCICR = _BV(PCIE2); // разрешить прерывание смены контакта
PCMSK2 = 0xfc; // воспринимать изменения на контактах 2-7 (т.е. PD2-PD7)
Serial.begin(9600);
}
void loop()
{
// Сообщить длину импульса на последовательной консоли.
uint16_t pulse_lengths[6];
get_pulse_lengths(pulse_lengths);
for (int i = 0; i < 6; i++) {
Serial.print(pulse_lengths[i]);
if (i < 5) Serial.print(", ");
else Serial.println();
}
delay(1000);
}
Будет ли учитываться такая интеграция? http://playground.arduino.cc/Main/PinChangeInterrupt, @Jacksonkr
Код, с которым вы связались, устанавливает отдельные биты в PCMSKn
, PCIFR
и PCICR
, а также определяет ISR(PCINTn_vect)
. Это как раз то «низкоуровневое» программирование, о котором я говорил. Обратите внимание, что при использовании контактов 2–7 вам нужно установить только один PCMSK
(а именно PCMSK2
) и определить только ISR(PCINT2_vect)
., @Edgar Bonet
Я вижу, вы добавили пример кода позже, я, должно быть, пропустил его в первый раз. Я подключил его, и с первого раза он работал безупречно с моим RC. Я изменил «задержку» с «1000» на «10», и все. Этот ответ заслуживает большего, чем +1, который я уже дал., @Jacksonkr
- Использовать выводы PWM в качестве обычных цифровых входов/выходов?
- Запись нескольких входов в последовательный порт с использованием цикла while
- Управление скоростью вентилятора с помощью библиотеки Arduino PID
- Как устранить шум от вентилятора 12 В с ШИМ-управлением на низкой скорости
- Генерация частоты ШИМ выше 125 кГц с помощью Arduino Uno
- Увеличить разрядность PWM
- Как вывести истинное аналоговое напряжение на выходной контакт
- ПИД-регулятор для управления скоростью двигателя
Конечно, это не связано с вашей текущей проблемой, но использование вами класса String в конце цикла ужасно расточительно, и известно, что чрезмерное использование оператора + в системах с ограниченной памятью, таких как Arduino, приводит к к сбоям. Нет причин собирать все это в одну строку для отправки. Вы можете просто использовать последовательные строки Serial.print, и вы будете использовать намного меньше памяти. Вероятно, это не проблема с этим небольшим тестовым кодом, но по мере того, как вы будете продвигаться вперед, у вас возникнут проблемы позже., @Delta_G