Как выбрать входной контакт 600 кГц, разделить его на 4 и сгенерировать низкочастотный сигнал на выходном контакте Arduino

Я пытаюсь разделить входящий высокочастотный сигнал 600 кГц на 4 и выходную волну на цифровом выводе Arduino MEGA2560.

  • Ввод в Arduino поступает от инкрементального энкодера, который подключен к двигателю, работающему со скоростью 9000 об/мин и генерирующему 1024 импульса за оборот.
  • Я пытаюсь понизить частоту, которую я получаю от Arduino, до 250 кГц или меньше.
  • Я попробовал следующие подходы:

    а. Я подсчитал нарастающий фронт входящей волны, и прерывание срабатывает, когда количество нарастающих фронтов достигает определенного значения, а ISR переключает выходной контакт. б. Я пытался заставить аппаратный счетчик работать на выводе T0, но мне не удалось выполнить ISR, когда счетчик достиг определенного значения.

Проблема подхода 1 заключается в следующем: по мере увеличения частоты входящей волны выходная волна начинает пропускать некоторые импульсы и генерирует один полупериод для 6/8/10 импульсов входящей волны. Фактически он должен генерировать один полупериод для 4 импульсов.

Вот код подхода 2:

#define outPin1 10
volatile short encoder = 4;

void setup ()
{
   pinMode (outPin1, OUTPUT);
   Serial.begin(9600);
   cli();
   // сброс Таймера 0
   TCCR0A = 0;
   TCCR0B = 0;

 }  // конец настройки

 ISR(TIMER0_COMPA_vect)
 {
     digitalWrite(outPin1, !digitalRead(outPin1));

 }

 void loop ()
 {
    TCNT0 = 0;      //счетчик до нуля

    // устанавливаем регистр сравнения совпадений на желаемое значение таймера:
   OCR0A = encoder;
    // включаем режим CTC:

   bitSet(TCCR0A, WGM01);
   // Таймер 0 — считает события на выводе D4

  bitSet(TIMSK0, OCIE0A);    // прерывание по таймеру 0 CTC
   // запускаем Таймер 0
  // Внешний источник синхронизации на выводе T0 (D4). Часы на подъеме.

   bitSet(TCCR0B, CS02);
   bitSet(TCCR0B, CS01);
   bitSet(TCCR0B, CS00);
}

Пожалуйста, помогите мне с этой проблемой

, 👍1

Обсуждение

Зачем вам тратить на это Arduino, если вы можете использовать один чип стоимостью 10 центов? http://www.ti.com/product/SN74HC163, @Majenko

Я думаю, что код, который вы помещаете в «цикл», должен находиться в «setup». В противном случае ваш циклический код будет постоянно сбрасывать счетчик обратно в ноль (TCNT0 = 0;), @Gerben

Не могли бы вы просто подключить прерывание к входному контакту, считать if каждый раз, когда оно вызывается, и установить низкий уровень выхода, когда счетчик равен 4. 600 кГц, вероятно, достаточно медленный, чтобы иметь достаточно времени для запуска ISR это код., @Gerben


2 ответа


3

Возможно, это слишком быстро, чтобы в цикле участвовало программное обеспечение. Вам следует попробовать использовать только аппаратное решение. Обратите внимание на таймеры Arduino. должны использоваться в качестве аппаратных делителей частоты:

  • настройте таймер для отсчета входного сигнала, чтобы он ведет себя как счетчик.
  • настройте его для подачи ШИМ-выхода с периодом таймера 4 тактовые циклы и рабочий цикл 50 %.

Вы можете попробовать это, но будьте осторожны: я это не проверял:

void setup()
{
    // Настраиваем Таймер 5 как делитель частоты:
    // вход: цифровой вывод 47 = PL2 = T5
    // выход: цифровой вывод 46 = PL3 = OC5A
    DDRL  |= _BV(PL3);    // устанавливаем вывод PL3 как выход
    TCCR5A = 0;           // отменяем конфигурацию ядра Arduino
    TCCR5B = 0;
    ICR5   = 4 - 1;       // период = 4 такта таймера
    OCR5A  = 2 - 1;       // вывод HIGH для 2 тактов таймера
    TCCR5A = _BV(COM5A1)  // неинвертирующий ШИМ на выводе OC5A
           | _BV(WGM51);  // режим 14: быстрый ШИМ, TOP = ICR5
    TCCR5B = _BV(WGM52)
           | _BV(WGM53)
           | _BV(CS50)    // тактовый сигнал по нарастающему фронту сигнала T5
           | _BV(CS51)
           | _BV(CS52);
}

void loop(){}
,

Это сработало для одного выхода кодера, но у меня также есть другой выход кодера, для которого у меня нет входного контакта для mega2560, поскольку его можно использовать на T5, а T0 не имеет функции захвата входа. У меня есть одна идея, могу ли я использовать один вход энкодера и генерировать две волны на выходном выводе со сдвигом фазы 90 градусов. В зависимости от флага я должен иметь возможность изменить, какая волна является ведущей или отстающей., @Vishal777

@ Vishal777: Re «_T0 не имеет функции захвата ввода_»: вам не нужен захват ввода. Вы можете использовать T0, если не против потерять функции синхронизации Arduino (millis() и другие), которые зависят от таймера 0. Или вы можете использовать T1, если можете припаять разъем к незаполненному отпечатку JP5. Повторная генерация двух волн под углом 90° с помощью одного таймера: я не думаю, что это возможно., @Edgar Bonet

Я пытаюсь использовать таймер 0 в режиме CTC и запускать ISR, который переключает вывод, когда TCNT0 достигает 4. Но без действительного входа я получаю волну на цифровом выходе 10. Я думаю, что он использует внутренние часы в качестве эталона вместо внешние часы. Я сделал все биты CS высокими, чтобы выбрать часы из PIN-кода T0. Это странное поведение., @Vishal777

@ Vishal777: Позвольте мне настаивать: «** Вероятно, это слишком быстро, чтобы в цикле участвовало программное обеспечение. **» ISR — это часть программного обеспечения, поэтому забудьте о нем., @Edgar Bonet

Я мог бы успешно разделить на 4, используя два таймера T0 и T5. Согласно моей существующей настройке, я получаю входную волну менее 200 кГц. Я не тестировал входную волну 600 кГц., @Vishal777

@ EdgarBonet вот ссылка на код http://collabedit.com/e3xth, @Vishal777


1

Я пишу еще один ответ, потому что то, что я предлагаю здесь, радикально отличается от предыдущего ответа. В этом ответе я написал, что это слишком быстро для того, чтобы это можно было сделать программно, и здесь я собираюсь возразить сам. :-/

При частоте 600 кГц входной сигнал остается низким примерно в течение 13 циклов процессора. затем высокий уровень еще 13 циклов. За 13 циклов многого не сделать. Примечательно, что пролог ISR нередко может потребовать большего. Кроме того, всякий раз, когда срабатывает ISR переполнения Таймера 0 из ядра Arduino, он может задержать ваш собственный ISR более чем на 13 циклов. Вот почему это обычно иллюзорно ожидать, что какая-либо работа будет надежно выполнена за это время рамка. Особенно если задействованы прерывания.

При этом, если процессору совершенно больше нечего делать. И особенно если его никогда не прерывать, тогда он сможет справиться с этим шаг. Вот программа, которая должна делать то, что вы ожидаете. я изначально думал написать на ассемблере, но можно и на C написать, поскольку компилятор переводит его довольно простым способом. Если в сомневаюсь, однако, проверьте сгенерированную сборку:

/*
 * Из одной прямоугольной волны генерируют две квадратурные волны на 1/4 от
 * исходная частота.
 *
 * https://arduino.stackexchange.com/questions/63202
 *                               _   _   _   _   _   _
 *  input:  digital 13 = PB7    / \_/ \_/ \_/ \_/ \_/ \_/
 *                               _______         _______
 *  output: digital 10 = PB4    /       \_______/       \
 *                                   _______         ____
 *  output: digital 11 = PB5    ____/       \_______/
 */

int main(void)
{
    DDRB = _BV(PB4) | _BV(PB5);  // PB4 и PB5 как выходы
    for (;;) {
        loop_until_bit_is_clear(PINB, PB7);  // ждем увеличения входных данных
        loop_until_bit_is_set(PINB, PB7);
        PORTB |= _BV(PB4);                   // поднимаем PB4
        loop_until_bit_is_clear(PINB, PB7);  // ждем увеличения входных данных
        loop_until_bit_is_set(PINB, PB7);
        PORTB |= _BV(PB5);                   // повышение PB5
        loop_until_bit_is_clear(PINB, PB7);  // ждем увеличения входных данных
        loop_until_bit_is_set(PINB, PB7);
        PORTB &= ~_BV(PB4);                  // падение PB4
        loop_until_bit_is_clear(PINB, PB7);  // ждем увеличения входных данных
        loop_until_bit_is_set(PINB, PB7);
        PORTB &= ~_BV(PB5);                  // падение PB5
    }
}

Обратите внимание: поскольку здесь не используется ядро Arduino, я написал main() вместо setup() и loop(). Это переопределяет предоставленный main(). ядром, поэтому у вас нет инициализаций, которые идут с ним и, в частности, у вас нет прерывания Таймера 0.

,