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);
}

Фото

ШИМ-контакты передатчика на цифровой вход Arduino

ИЗМЕНИТЬ

После публикации этого вопроса я обнаружил, что изменение значения 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);

Почему это сработало? Для меня это не имеет смысла.

, 👍2

Обсуждение

Конечно, это не связано с вашей текущей проблемой, но использование вами класса String в конце цикла ужасно расточительно, и известно, что чрезмерное использование оператора + в системах с ограниченной памятью, таких как Arduino, приводит к к сбоям. Нет причин собирать все это в одну строку для отправки. Вы можете просто использовать последовательные строки Serial.print, и вы будете использовать намного меньше памяти. Вероятно, это не проблема с этим небольшим тестовым кодом, но по мере того, как вы будете продвигаться вперед, у вас возникнут проблемы позже., @Delta_G


1 ответ


Лучший ответ:

1

pulseIn() — это блокирующая функция: она ожидает, пока не получит полное пульса или до тех пор, пока не истечет время ожидания. Пока он ждет, Arduino не может что-нибудь еще, и он будет пропускать импульсы на других каналах.

Приемник RC посылает импульсы последовательно. Тогда, если вы попытаетесь прочитать их в правильной последовательности, это может сработать. Если вы прочитаете их из последовательность, вы пропустите несколько импульсов, и соответствующий pulseIn() вызовы могут истечь, возвращая нули.

Для такого рода задач я бы использовал прерывание смены контакта на контактах 2–7. Это должно позволить непрерывно считывать шесть импульсов без блокировки. вся программа. Но это низкоуровневое (AVR-уровня) программирование, наверное невозможно со стандартными библиотеками Arduino.

Правка. Вот что я имел в виду, когда предлагал использовать контакт. изменить прерывание. Обратите внимание, что номера контактов «зашиты» в код и не может быть легко изменен. Это потому что:

  1. Выводы 2–7 совместно используют запрос прерывания на изменение одного контакта. (PCINT2_vect), поэтому для синхронизации шести каналов требуется один ISR.
  2. Они также имеют общий порт (порт 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