Радиочастотное дистанционное управление с использованием VirtualWire на ATtiny85, работающем на частоте 8 МГц на внутреннем генераторе

attiny rf remote-control virtualwire

Я пытаюсь создать радиочастотный пульт дистанционного управления, используя ATtiny85, работающий на частоте 8 МГц на внутреннем генераторе, этот дешевый модуль передатчика RF 434MHz (как тот, что ниже) и VirtualWire lib, но пока безуспешно.

РЧ-передатчик

Итак, прежде чем я продолжу, можно ли использовать VirtualWire с ATtiny85, работающим на 3,6 В и 8 МГц с его внутренним генератором? Если да, то как?

Я нашел несколько разрозненных источников информации об этом в Сети, но не смог в них разобраться.

Я собрал версии приемника и передатчика ATMega328 с использованием VirtualWire без каких-либо серьезных проблем. Оба модуля взаимодействуют друг с другом, поэтому выпадает очень мало данных. Теперь, когда я попытался заменить передатчик ATMega328 на меньшую версию с использованием ATtiny85, у меня возникли проблемы.

Вот симптомы:

  1. Основная проблема заключается в том, что когда я добавляю включение VirtualWire, мой скетч сильно замедляется. Так что я полагаю, что это проблема времени. Но я не уверен, в чем проблема.

  2. Я подключил осциллограф к контакту 6 (PB1) ATtiny85, но ничего не передается. Кажется, я напутал с названиями контактов.

  3. Я могу заставить светодиод мигать, а также без проблем могу читать кнопки.

Вот схемы и дизайн платы для пульта дистанционного управления:

RC схема

Плата в настоящее время питается от 2 батареек AA NiMH с напряжением 2,6 В. Я могу заставить ATtiny85 мигать с этим напряжением. Я планирую питать RC от литиевой батареи CR2 3,6 В.

Подробнее о моей среде разработки:

  • IDE 1.05 для Arduino
  • Я использую ядра ATtiny отсюда
  • Я программирую ATtiny85, используя Arduino UNO R3 в качестве ISP, без проблем (он работает, так как я могу заставить ATtiny85 мигать светодиодом).

А вот мой код:

#include <VirtualWire.h>

#define VCC_PIN  A1
#define BTN_PIN  A2
#define LED_PIN  A3
#define TXD_PIN  PB1

#define BLINK_UP_TIME  25

#define BUTTON_NONE  0
#define BUTTON_1     1
#define BUTTON_2     2
#define BUTTON_3     3
#define BUTTON_4     4
#define BUTTON_5     5
#define BUTTON_6     6

void setup()
{
  pinMode(BTN_PIN, INPUT);
  pinMode(TXD_PIN, OUTPUT);     
  pinMode(VCC_PIN, OUTPUT);     
  pinMode(LED_PIN, OUTPUT);     

  // пока оставьте радиочастотный передатчик выключенным
  digitalWrite(VCC_PIN, LOW);

  // Инициализируем ввод-вывод и ISR
  vw_set_tx_pin(TXD_PIN);
  vw_setup(2000);    // Бит в секунду

  // сообщить пользователю, что мы живы: сердцебиение
  blink(3, 50);
  delay(1000);
}

uint8_t buf[VW_MAX_MESSAGE_LEN];
uint8_t buflen = VW_MAX_MESSAGE_LEN;

void loop()
{
  byte button = ReadButtons();
  if (button != BUTTON_NONE) {
    // кнопка передачи нажата
    digitalWrite(LED_PIN, HIGH); // Мигаем, чтобы показать передачу
    digitalWrite(VCC_PIN, HIGH); // включаем передатчик
    delay(10);
    buf[0] = button;
    vw_send(buf, 1);
    vw_wait_tx(); // Подождем, пока не исчезнет все сообщение
    digitalWrite(LED_PIN, LOW); // выключаем светодиод
    digitalWrite(VCC_PIN, LOW); // выключаем передатчик
  }
  delay(100);
}

byte ReadButtons()
{
  unsigned int buttonVoltage = analogRead(BTN_PIN);
  byte button = BUTTON_NONE;   // вернуть не нажатую кнопку, если приведенные ниже проверки не записывают в btn
  if      (buttonVoltage < 10)   {    button = BUTTON_1; } 
  else if (buttonVoltage < 200)  {    button = BUTTON_2; } 
  else if (buttonVoltage < 400)  {    button = BUTTON_3; } 
  else if (buttonVoltage < 550)  {    button = BUTTON_4; } 
  else if (buttonVoltage < 720)  {    button = BUTTON_5; } 
  else if (buttonVoltage < 900)  {    button = BUTTON_6; }
  else if (buttonVoltage > 1000) {    button = BUTTON_NONE; }
  return( button );
}

Итак, Что не так с моей настройкой?

, 👍6

Обсуждение

Вы знаете, что обычно вам нужно добавить антенну на модуль передатчика, верно? Кроме того, возможно, попробуйте уменьшить биты в секунду в настройке виртуального провода., @Connor Wolf

@FakeName ну, если честно, я еще даже не припаял радиочастотный модуль к плате, так как я даже не могу заставить MCU отправлять биты на вывод TX. Поэтому я подсоединяю щуп прицела к контакту TX, и если я увижу попытку передачи, то впаяю модуль., @Ricardo

А, упс, пропустил. Вам определенно нужно изучить правильное сопоставление контактов. Библиотека virtualwire заявляет об общей поддержке AVR8, а также имеет особое примечание о ATtiny84. Но мне любопытно. Некоторые из ваших контактов имеют номер A{n}, а контакт передатчика – PB{n}. Это правильная мнемоника контакта?, @Connor Wolf

Я не вижу PB1, определенного ни в одном из заголовков ATtiny, на которые вы ссылаетесь. Предполагая, что ваш передатчик подключен к ПОРТУ B, контакт 1, я *думаю*, что у вас должен быть #define TXD_PIN 1, так как доступ к порту B просто осуществляется с использованием традиционной мнемоники цифрового ввода-вывода., @Connor Wolf

@FakeName После долгой отладки последняя комбинация контактов «Ax» была той, которая сработала для меня (за исключением контакта TXD, очевидно). Я предполагаю, что вы можете ссылаться либо на PBx, либо на Ax, поскольку они являются макросами, которые в конечном итоге заменяются целыми числами (угадайте здесь)., @Ricardo

Проблема в том, что похоже, что статические переменные, на которые ссылается PBx, *не* определены библиотеками ATtiny, поэтому они могут просто *случайно* работать. Вы убедились, что можете нормально управлять ПОРТОМ B 1 (например, с помощью простых команд digitalWrite()? В качестве альтернативы, просто поменяйте местами выводы LED и TX и используйте вывод LED (который, как вы знаете, работает) для вывода данных., @Connor Wolf

Действительно, PBn определяется AVR-Libc. Так уж получилось, что arduino-tiny сопоставляет PBn с Dn, и каждый PBn определяется как имеющий значение n для использования с _BV()., @Ignacio Vazquez-Abrams

Вы упомянули, что используете Arduino IDE, определили ли вы новую плату для своего ATtiny? Это можно добавить в ARDUINO_INSTALL_DIR/harware/arduino/boards. В противном случае вам пришлось бы #define VW_PLATFORM VW_PLATFORM_GENERIC_AVR и использовать дополнительные вызовы, необходимые для универсальных платформ AVR., @jfpoilpret

@jfpoilpret Пакет attiny-master устанавливает целую кучу новых плат ATtiny в IDE. Он поддерживает несколько комбинаций ATtiny45/84/85 с 1/8/16 МГц с внутренними и внешними генераторами. Я могу запрограммировать 85 со всеми тремя часами, когда я закомментирую VW., @Ricardo

Есть ли у вас способ проверить, совпадает ли частота MCU с частотой F_CPU, используемой для компиляции вашего кода? Как правило, когда программа кажется медленнее, чем должна, это часто происходит из-за несоответствия между F_CPU и реальной частотой (которая, как я полагаю, устанавливается фьюзами в вашем случае)., @jfpoilpret

Происходит ли замедление при использовании 3,6 В по сравнению с использованием 5 В?, @jfpoilpret

Замедление происходит, когда я включаю VirtualWire и вызовы функций. Если я их прокомментирую, время вернется в норму., @Ricardo

Вы пытались просто включить VirtualWire, но без вызова функции? Это устранит возможность того, что включения переопределяют некоторые макросы., @jfpoilpret

@jfpoilpret - я комментирую включение И входящие и исходящие вызовы в целом, туда и обратно при отладке. Но одни только вызовы не будут компилироваться без включения., @Ricardo

На самом деле моя идея заключалась в том, чтобы сохранить включение, но удалить вызовы (которые должны компилироваться), а затем проверить, экспериментируете ли вы с замедлением или нет. Если да, это означает, что во включении есть что-то, что перезаписывает один макрос, например F_CPU, или статическую переменную (экземпляр класса), которая при построении изменяет что-то важное., @jfpoilpret

@jfpoilpret Понял. Попробую, пока не отказался от платы. Я, вероятно, сделаю шаг назад и сделаю версию для ATmega328., @Ricardo

У меня были некоторые проблемы с часами с ATtiny85. Однажды я пытался подключить к нему ЖК-дисплей, и он никогда не работал, только несколько раз мне удавалось разобрать искаженный текст., @TheDoctor


4 ответа


3

Я попробовал те ядра Tiny, которые вы используете, и столкнулся с множеством проблем. Я переключился на http://code.google.com/p/arduino-tiny/, и мне кажется, лучше. Создайте вторую папку установки Arduino и добавьте ядра arduino-tiny. Константы имени контакта: PIN_A0, PIN_A1,...,PIN_B1 и т. д. Итак, попробуйте.

Я бегло взглянул на код VirtualWire, и там есть ifdefs специально для Attiny85, так что это должно работать. Вот производный проект с рабочими примерами ATTINY85: http://cosa-arduino. blogspot.de/2013/03/news-virtual-wire-interface.html

,

+1 Отлично, спасибо! Я попробую и дам вам знать., @Ricardo


1

Вместо этого попробуйте библиотеку manchester. В этом посте есть вся необходимая информация, кроме схем подключения: Манчестерская библиотека не компилируется для Attiny85

,

1

Я потратил некоторое время на то, чтобы VirtualWire работал на attiny84. Я вижу две проблемы с вашим кодом:

  1. Поскольку у tiny85 есть только один таймер, он используется как VirtualWire, так и millis/micros. Таким образом, ваши задержки не будут работать должным образом.
  2. VirtualWire использует значения по умолчанию для vw_ptt_pin (10) и vw_rx_pin (11). Эти контакты не существуют на attiny85. Когда vwsetup вызывает pinMode с этими значениями, это приводит к записи в произвольные адреса памяти. Это, вероятно, заставит ваш скетч вести себя непредсказуемо. Чтобы исправить, установите vw_set_ptt_pin() и vw_set_rx_pin() на контакты, которые существуют на tiny85, или измените источник VirtualWire.
,

Спасибо за Ваш ответ. У меня были заметные симптомы пункта 1 выше: время сбилось, когда я попытался заставить VirtualWire работать. Пункт 2 кажется веским основанием для того, чтобы все это не работало. Мой обходной путь для этой проблемы заключался в том, чтобы придерживаться ATmega328 для радиочастот. Я посмотрю и посмотрю, смогу ли я вернуться к использованию VW с моими ATTinies., @Ricardo


3

Обновлено 26 августа 2018 г.: добавлено tx(false); в конце отправки. Без него вывод TX мог бы оставаться высоким, затопляя диапазон 433 МГц и делая любую другую связь на нем практически невозможной!!!

Несмотря на то, что все это довольно старые сообщения, я все еще изо всех сил пытался заставить VirtualWire или его преемника RadioHead работать над Attiny85. Обычно проблема заключается в том, что VirtualWire использует таймер 0 и, таким образом, нарушает работу других компонентов.

Теперь, для RX вы, как правило, не хотите тратить все свои циклы обработки на ожидание поступления битов, поэтому вы хотите, чтобы он управлялся прерываниями, но для TX вы часто можете обойтись без этого... Итак, Я немного переработал код VirtualWire TX и реализовал его как простую процедуру отправки, которую можно использовать без специального прерывания по таймеру. Использование приведенного ниже кода на Attiny85 и Arduino Uno в качестве приемника с обычной библиотекой VirtualWire и той же скоростью передачи данных отлично работает для меня, поэтому, если кто-то еще может извлечь из этого пользу, то вот он (включая некоторый код для считывания ультразвукового сигнала). датчик расстояния и управление некоторыми светодиодами - читатель может вырезать то, что ему не нужно):

#include <util/crc16.h>

#define LED_PIN           3
#define LED2_PIN          4

#define TX_PIN            2
#define TX_BAUDRATE       2000
#define TX_MICROINTERVAL  (1000000UL/TX_BAUDRATE)
#define TX_MAXLEN         12

#define TRIGGER_PIN       0
#define ECHO_PIN          1

inline void led(const bool onOff) { digitalWrite(LED_PIN, onOff ? HIGH : LOW); }
inline void led2(const bool onOff) { digitalWrite(LED2_PIN, onOff ? HIGH : LOW); }
inline void tx(const bool onOff) { digitalWrite(TX_PIN, onOff ? HIGH : LOW); }
inline void trigger(const bool onOff) { digitalWrite(TRIGGER_PIN, onOff ? HIGH : LOW); }

const uint8_t header[8] = { 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x38, 0x2C };
const uint8_t conv4to6[16] = { 0x0D, 0x0E, 0x13, 0x15, 0x16, 0x19, 0x1A, 0x1C,
                               0x23, 0x25, 0x26, 0x29, 0x2A, 0x2C, 0x32, 0x34 };

void rawSend(const uint8_t * p, uint8_t len) {
  while (len--) {
    const uint8_t val = *p++;
    for (uint8_t mask = 1; mask != 0x40; mask <<= 1) {
      tx(val & mask);
      delayMicroseconds(TX_MICROINTERVAL);
    }
  }
}

void send(const uint8_t * buf, const uint8_t len) {
  // Сначала создадим полезную нагрузку...
  static uint8_t payload[(TX_MAXLEN+3)*2];
  uint8_t * p = payload;
  uint16_t crc = 0xFFFF;
  uint8_t v = len + 3;
  crc = _crc_ccitt_update(crc, v);
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];
  for (uint8_t l = len; l--;) {
    v = *buf++;
    crc = _crc_ccitt_update(crc, v);
    *p++ = conv4to6[v >> 4];
    *p++ = conv4to6[v & 0x0F];
  }
  crc = ~crc;
  v = (uint8_t)crc;
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];
  v = (uint8_t)(crc >> 8);
  *p++ = conv4to6[v >> 4];
  *p++ = conv4to6[v & 0x0F];

  // Теперь передаем заголовок и полезную нагрузку...
  rawSend(header, sizeof(header));
  rawSend(payload, (len + 3)*2);
  tx(false);
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  led(false);
  led2(false);

  pinMode(TX_PIN, OUTPUT);
  tx(false);

  pinMode(TRIGGER_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  trigger(false);
}

int measure() {
  trigger(false); delay(10); trigger(true); delay(10); trigger(false);

  // Мы поддерживаем до 6 м, т.е. 6000 мм, так что потребуется
  // 6000 мм * 2 / 0,34 мм/мс = 35294 мс, чтобы добраться до конца
  // и обратно, поэтому давайте использовать тайм-аут вдоль этих
  // строки...
  unsigned long duration = pulseIn(ECHO_PIN, HIGH, 36000);
  if (!duration || (duration > 35500)) return 0;
  return (int)(duration * 34 / 200);
}

int bufferedMeasure() {
  static int lastVal = 0;
  static bool toggle = true;
  int newVal = measure();
  if (newVal) {
    led2(toggle);
    toggle = !toggle;
    lastVal += (newVal-lastVal)/10;
  }
  return lastVal;
}

// процедура цикла выполняется снова и снова навсегда:
void loop() {
    // Измеряем расстояние...
    int val = bufferedMeasure();
    if (val) {
      static unsigned long data;
      led(true);
      data = 0xD1570000UL|val;
      send((const uint8_t *)&data, sizeof(data));
      led(false);
    } else {
      led(true); delay(100); led(false); delay(100);
      led(true); delay(100); led(false); delay(100);
      led(true); delay(100); led(false);
    }
    delay(1000);
}
,