Генерация сигнала частотой 38 кГц без таймеров

В настоящее время я пытаюсь сгенерировать сигнал частотой 38 кГц для моего TSOP4838 (http://www.vishay.com /docs/82459/tsop48.pdf) с ATtiny84A.

Я знаю, что лучше всего было бы использовать таймер, но у ATtiny есть только 2 таймера (T0 и T1). Таймер1 использовать нельзя, поскольку он уже используется другими функциями.

Я не хочу передавать фактические данные на TSOP4838. Я хочу использовать его только в качестве светового барьера (я знаю, что для этого есть более качественные микросхемы, но я остановился на тех, которые у меня уже есть).

Итак, до сих пор я пробовал настроить таймер 0 для моего ШИМ-устройства, и все работало с помощью ИК-светодиодов. Функции задержки (micros(), millis() и т. д.) перестали работать и не позволили мне использовать последовательную связь. Я попытался изменить код в wiring.c, чтобы использовать прескалер 1 вместо 64, изменив определения, но это не сработало.

Я думаю, сейчас у меня есть два варианта:

О: каким-то образом изменить timer0, чтобы он генерировал сигнал частотой 38 кГц и при этом обеспечивал правильную работу функций синхронизации.

B: пусть timer0 будет инициализирован так, как этого хочет Arduino, и используйте функции задержки для мигания ИК-светодиода.

Я бы предпочел А, но Google не дал мне решения. Может быть, кто-нибудь из местных гуру сможет помочь?

Если вариант А невозможен, может ли кто-нибудь подсказать мне, как «замедлить» ИК-светодиод с задержкой? (Наверное, это глупый вопрос, но сейчас я не могу его понять :()

, 👍1

Обсуждение

Извините, но... вы действительно используете микроконтроллер для генерации прямоугольных волн частотой 38 кГц и ничего более?, @frarugi87


4 ответа


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

1

Прямоугольный сигнал частотой 38 кГц и коэффициентом заполнения 50 % будет переходом с частотой 38x2=76 кГц. Это 1/76 000 = 0,000013158 с, или 13,158 мкс. Это довольно высокая точность, которую можно попытаться достичь с помощью задержек на таком маленьком чипе, как 84A.

Я предполагаю, что вы используете его на частоте 8 МГц (внутренний калиброванный RC-генератор) — это означает, что каждый такт имеет длину 1/8000000 = 125 нс. поэтому 13156 нс будут соответствовать 105,248 тактовым циклам. Этого не может быть, поэтому самое близкое значение, которое вы получите, — это 105 тактовых циклов, что составит 13125 нс, или 76,190 кГц, или конечная частота 38,095 кГц. Так близко.

Итак, теперь вам нужно создать плотный цикл длиной ровно 105 тактовых циклов, включая переключение вывода ввода-вывода и сравнения и ветки для цикла. Для этого вам нужно тщательно подсчитать циклы команд и работать на языке ассемблера. На странице 268 технических данных подробно описаны тактовые циклы, используемые для каждой инструкции.

Самый простой (хотя и самый длинный) цикл — это инструкция OUT или SBR (в зависимости от порта) (1 такт) для установки вывода IO HIGH, затем 104 NOP (по 1 такт каждый), затем OUT или CBR для установите НИЗКИЙ уровень (1 такт), затем 102 NOP (по 1 такт каждый), затем RJMP, чтобы вернуться к началу (2 такта). В общей сложности это 210 тактовых циклов, или 1/(210*0,000000125) = 38,095 кГц.

Вы можете сами сжать все инструкции NOP в плотный цикл, установив в регистре заранее определенное значение счетчика цикла, уменьшая его при каждом проходе и повторяя, если оно не равно 0. Это означает еще более тщательный подсчет инструкций, чтобы получить это цикл, равный 104 или 102 тактам, необходимым для точного расчета времени.

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

Если вам все равно не нужна абсолютно надежная синхронизация, возможно, будет проще (хотя и гораздо менее точно) использовать функцию micros(), чтобы определить, когда прошло 13 мкс, и вместо этого сделать все это на C. сборки. Вам по-прежнему понадобится высокоскоростной прямой доступ к порту, а не использование digitalWrite(), поскольку digitalWrite() добавляет слишком много накладных расходов, чтобы можно было надежно выполнять такую синхронизацию. Что-то вроде:

uint32_t start = micros();
while(1) {
    if (micros() - start >= 13) {
        start = micros();
        PORTB ^= 1<<4; // переключение B4
    }
}

Обратите внимание на использование while (1) вместо того, чтобы полагаться на повторяющуюся функцию loop(). Использование автоматического повторения loop() сопряжено с довольно большими накладными расходами, которых можно избежать, зацикливая его с помощью while (1), подобного этому. Конечно, все остальное, что вы хотите, чтобы повторялось неоднократно, должно быть внутри этого цикла while и, скорее всего, несколько повлияет на время вывода.

Поэтому гораздо лучшим решением было бы использовать таймер, поскольку именно для этого он и существует. Но возня с таймерами нарушает такие функции, как millis() и delay(). Итак, ответ на этот вопрос: не используйте millis() и delay(). Вместо этого создайте свои собственные функции синхронизации, которые будут управляться вашим собственным прерыванием, которое выполняет ввод-вывод - вместо того, чтобы полагаться на предоставленный системой тик, вы создаете свои собственные. Если ваше прерывание срабатывает каждые 13 мкс, добавьте 13 к глобальному счетчику - тогда этот счетчик будет количеством прошедших микросекунд. Когда это значение превышает 1000, вы вычитаете 1000 (не обнуляйте) и добавляете 1 к счетчику миллисекунд. Теперь у вас есть эквивалент millis() в глобальной (не забудьте сделать ее изменчивой) переменной, которую вы можете использовать вместо millis(). .

,

Спасибо за подробный ответ! Я использую студию amtel с cire, который я скомпилировал сам, и поэтому я уже не использую методы цикла или настройки, такие как среда ia arduino. Думаю, я попробую еще раз, чтобы заставить таймер делать то, что я хочу. Плохо то, что мне тоже нужно изменить класс TinyDebugSerial, потому что он, похоже, использует функции Arduino Milli..., @traffiq

Я использую Arduino Nano в качестве генератора частоты, и он генерировал стабильную частоту с погрешностью менее 0,1 кГц. Но TSOP382 перестает получать данные через 200 мс. Я отключил все прерывания и сделал последовательность включения и выключения одинаковой., @ego2dot0


1

Было бы намного проще использовать специальную микросхему, такую как 555 или 556. Я построил такую систему на основе этих схем ИК-детектор обрыва леща

,

Использованная там комбинация R/C дает 41 кГц. Лучше подойдет 22 нФ/860 Ом (38,054 кГц) или 47 нФ/400 Ом (38,297 кГц)., @Simon A. Eugster

Изменить: поскольку для нестабильной версии необходимы два резистора, используйте Ra = 1,2 кОм, Rb = 12 кОм и C = 1,5 нФ, что дает 38,095 кГц. Ra/Rb/C см. [Технические данные NE555](http://www.ti.com/lit/ds/symlink/ne555.pdf)., @Simon A. Eugster


0

Возможно несколько подходов, некоторые из которых, но далеко не все, были перечислены в предыдущих ответах. Далее я предполагаю, что под «использовать его в качестве светового барьера» вы имеете в виду, что TSOP4838 будет частью системы обнаружения проникновения на объекты, а не для передачи данных. Как следствие, допустимо некоторое дрожание и/или неточность. Обратите внимание: все следующие методы предполагают, что код ядра Arduino установил таймер 0 для поддержки обычных вызовов micros() и millis().

Подход 1. Вычисление или калибровка простого цикла. В setup() запустите серию тестов, которые находят значение k, чтобы минимизировать ошибку, с помощью эквивалента кода C. к следующему Python:

for k in plausibleRange():
  err = genBurst19(k)
  if err < bestErr:
    bestErr = err
    bestK = k

где genBurst19() выглядит следующим образом:

def genBurst19(k):
  t1=micros()
  for i in range(38):
    setPortBit(i&1)
    for j in range(k):
      nop
  t2=micros()
  return abs(t2-t1-500)

Другими словами, genBurst19() отправляет пакет из 19 циклов, который при частоте 38 кГц должен занимать 500 микросекунд. Он возвращает меру ошибки вызывающей стороне.

Найдя bestK, вы можете впоследствии использовать его в качестве ограничения для других циклов задержки или просто использовать повторные вызовы genBurst19(bestK ) всякий раз, когда вы хотите сгенерировать какие-то «38КГц».

Обратите внимание: вы можете попробовать некоторые незначительные изменения, такие как добавление второго или третьего nop во внутренний цикл задержки или создание большего числа циклов, кратного 19. Хотя цикл задержки, содержащий одиночный nop (или вообще не содержащий nop, только пустой цикл), имеет более точное разрешение, чем цикл с несколькими nop s, может случиться так, что (в зависимости от кода цикла) произойдет меньшая ошибка при большем. [Но также см. подход 3 относительно точной настройки.]

Резюме: этот метод прост в использовании, имеет низкий джиттер, но ограничен в точности.

Подход 2. Используйте метод, аналогичный Брезенхема, для получения сигнала с периодом высокой точности, в среднем, но с большим джиттером, чем в подходе 1. Алгоритм Брезенхэма пытается нарисовать прямую линию на сетке пикселей xy; вы пытаетесь нарисовать прямую линию на сетке зависимости времени от циклов.

В этом методе вы отслеживаете, сколько микросекунд должно было пройти, и, если вы отстаете слишком сильно, задаете задержку с помощью k-1 вместо k, чтобы наверстать упущенное; или, если слишком далеко вперед, с помощью k+1, чтобы вернуться назад. См. мой ответ на stackoverflow 8113883, чтобы узнать немного больше о методе Брезенхема в контексте рисования линий.

Вывод: применяя метод Брезенхэма, вы можете получить среднюю частоту настолько близко, насколько вам нужно (при усреднении за несколько миллисекунд).

Подход 3. Это скорее дополнительная мера, чем целый подход. Он модифицирует подход 1, добавляя следующий код C перед циклом for j in range(k): nop. Ему также нужен второй параметр, l.

switch (l) {
  case 9: nop;
  case 8: nop;
  case 7: nop;
  case 6: nop;
  case 5: nop;
  case 4: nop;
  case 3: nop;
  case 2: nop;
  case 1: nop;
  case 0: ;
}

Чтобы использовать этот подход, сначала найдите лучший k, используя серию вызовов, например err = genBurst19(k,5). Затем найдите лучший l с помощью err = genBurst19(bestK,l) для l от 0 до 9. [Обратите внимание, что число меньше 9 может использоваться в приведенном выше примере, если цикл for j in range(k): nop использует меньше циклов.]

Резюме: этот шаг может настроить genBurst19() ближе к точности одиночного узла.

,

0

Я только что построил схему на базе NE555 для 38 кГц. Отправитель — ИК-диод 950 нм, приёмник — TSOP4838. Согласно Техническому описанию NE555, частота в нестабильном режиме составляет приблизительно

f ≈ 1,44 / (C (RA + 2 RB))

Нестабильная схема NE555

При значениях RA = 1,8 кОм, RB = 18 кОм, C = 1 нФ, NE555 должен генерировать частоту 38,095 кГц. Используя эти значения, моя схема генерировала частоту 34 кГц с небольшими изменениями в несколько 100 Гц при использовании разных резисторов/конденсаторов (из-за допуска), но она не приблизилась к 38 кГц. Вероятно, это связано с тем, что формула частоты является приближением. Самое простое решение – подключить переменный конденсатор параллельно C и использовать его для точной настройки или переменный резистор к RB.

Это может быть необходимо не во всех случаях, поскольку TSOP48xx использует полосовой фильтр и 34 кГц по-прежнему распознаются, только с более низкой чувствительностью. Обратите внимание, что этот ИК-приемник (как и другие) имеет некоторые ограничения по длине импульса, поэтому вы не можете постоянно отправлять 38 кГц, а только до 70/f, что составляет около 1,8 мс. После этого выход TSOP снова отключается, вероятно, из-за повышения внутреннего порога.

График частотной зависимости TSOP48

Измерения

Некоторые измерения логического анализатора (PulseView). Это из приведенной выше схемы NE555, которая должна давать частоту 38 кГц.

Скриншот логического анализатора

Я использовал микросхему AND (74HCT08) для модуляции сигнала, генерируемого Arduino. , поверх несущей 38 кГц. Частота дискретизации составляет 12 МГц. Зонды измеряют:

  • D0: выходной сигнал TSOP4838
  • D1: ИК-сигнал (несущая И сигнал)
  • D2: сигнал
  • Arduino
  • D3: несущая 38 кГц от NE555

модулированный ИК-сигнал 38 кГц

,