Радио модуль RFM70

RFM70
Радиомодули RFM70 фирмы HopeRF представляют из себя маломощные приёмопередатчики нелицензируемого диапазона 2.4 ГГц с неплохими параметрами и очень привлекательной ценой. Конструктивно, модули представляют из себя печатную плату 13х17 мм с микросхемой-«каплей» и печатной антенной.

Модуль работает в диапазоне 2400-2483 МГц, который разбит на 83 канала, при максимальной выходной мощности около 3.2 милливатт (5 dBm). Максимальная скорость передачи данных составляет 2 Мбит/с., что очень даже не плохо — можно передавать несжатый звук в обе стороны, причем с запасом. Практическая дальность связи при прямой видимости составляет около 40-50 метров, в условиях многоквартирного жилого дома — около 15-20 метров — через 4 кирпичных стены.

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

Напряжение питания модуля — 1.9 — 3.6 В. Однако, я по запарке включил его на напряжение питания 5 В. на несколько минут, ничего страшного не случилось и модуль продолжил исправно работать и при 5 вольтах. Но рисковать всё-же не стоит.

Электрический интерфейс модуля представляет вполне обычный SPI, плюс линия запроса прерывания (IRQ), линия CE — chip enable, общий и питание. Сразу скажу, что сэкономить ножку контроллера, подключив CE к питанию не выйдет — её, хоть и очень редко, но надо дёргать. А вот без IRQ можно обойтись, периодически опрашивая модуль.
Схема подключения
Модули организуют пакетную передачу данных с автоматическим подтверждением приёма и автоматической повторной отправкой в случае неудачи. Длинна пакета может быть как жестко фиксирована, так и задаваться динамически. Максимальная длинна пакета 32 байта. Соответственно имеются трёхуровневые буфер приёма и передачи, каждый по 32 байта. Если передавать пакеты меньшей длинны, то в буферах могут находится по несколько пакетов. RFM70 работает в полудуплексном режиме, или как приёмник или как передатчик. Передача данных адресная — каждый модуль имеет до 6 адресов(конечных точек) на приём и один на передачу. Это позволяет легко организовать сеть на базе этих модулей, например, для элементов «умного дома». Длинна адреса может быть от 3, 4, или 5 байт. Первые два адреса независимы и в них можно задавать все 5 байт, оставшиеся четыре делят старшие байты со вторым адресом, а независимо задавать позволяют только один младший байт. Модуль принимает пакет только, если адрес в его заголовке совпадает с одним из этих 6-ти адресов его конечных точек.

Каждый пакет дополняется контрольной суммой для контроля целостности данных. Если приёмник успешно принял пакет и контрольная сумма верна, он автоматически переключается в режим передатчика и передает со своим адресом приема специальный пакет, сигнализирующий об успешном получении данных. К этому пакету-подтверждению можно прицепить до 32 байт полезной информации, предварительно поместив её в буфер передачи (так, например, удобно организовать дуплексную передачу звука). При неверной контрольной сумме приёмник запрашивает повторную передачу того-же пакета.
Когда передатчик посылает пакет, он переключается в режим приёмника ждёт пакет подтверждения приема (ACK) с адресом удаленного модуля (того, кому мы передавали). И только при получении подтверждения сигнализирует успешную передачу низким уровнем на ножке IRQ. Если по прошествии определённого таймаута ответа нет, или пришел запрос на повторную передачу, передатчик снова посылает это-же пакет. Передатчик будет пытаться отправить пакет до 15 раз (можно настраивать) после, чего сигнализирует, что передача не удалась, опять-же низким уровнем на ножке IRQ и увеличит счетчик потерянных пакетов.

Подтверждение передачи, передачу/проверку контрольной суммы и повторную передачу можно, при желании выключить. Надо только помнить, что у приёмника и передатчика должны быть строго одинаковые настройки этих функций (за исключение числа попыток повторной передачи), видимо, от этого зависит формат пакета. Иначе приемник тихо ничего не примет.
Вообще, есть довольно много несовместимых конфигураций приёмника и передатчика, когда не удаётся настроить связь «непонятно почему» и приходится подолгу тупить в дампы банков регистров, с надеждой понять какой бит, в каком регистре надо поменять:) К счастью, при включении модули уже сконфигурированы параметрами по умолчанию и могут, в принципе, работать с минимальными телодвижениями.

Программный интерфейс.
Каждая команда модулю должна начинаться с выставления низкого уровня на ножке CSN (Chip Select Not) модуля, после чего можно слать данные по SPI. Первый записанный байт — непосредственно команда, последующие — её аргументы, их нужно передавать ровно столько, сколько команда ожидает, если записать хоть байт больше или меньше, команда будет молча проигнорирована, без выставления каких-либо флагов, или чего-то еще. Первый прочитанный байт — всегда содержимое статусного регистра. По окончанию передачи всей команды с аргументами нужно выставить на CSN высокий уровень, чтоб команда запустилась на исполнение.
Данные пишутся в формате MSB first — старший бит первым. А вот с порядком байт в много байтовых регистрах — разброд и шатания: в одни регистры сначала пишется старший байт, потом младший, в другие наоборот. Это знатные грабли о которых надо помнить, при конфигурировании модуля. О регистрах чуть позже, сначала про команды.

RFM70 реализует следующие SPI команды (в скобках указан код, см. таблицу 2 в даташите):

R_REGISTER — (000A AAAA ) читает регистр, заданный 5-ти битовым кодом AAAAA из текущего банка. Аргументов нет. Возвращает от 1 до 11 байт данных (да, да есть один 11 байтовый регистр).

W_REGISTER — (001A AAAA) пишет в регистр, заданный 5-ти битовым кодом AAAAA в текущем банке. После команды идёт значение, записываемое в регистр.

R_RX_PAYLOAD — (0110 0001) читает принятые данные из буфера приёма от 1 до 32 байт, в зависимости от того сколько получили. Данные из приёмного буфера удалятся, только если будет прочитано столько байт, сколько там есть(это можно узнать командой R_RX_PL_WID). Иначе в следующий раз мы будем читать тот-же самый пакет.

W_TX_PAYLOAD — (1010 0000) пишет данные в буфер передачи от 1 до 32 байт, в зависимости от того, какая длина пакета выбрана.

FLUSH_TX — (1110 0001) очищает буфер передачи. Нужно вызывать перед тем, как поместить данные для новой передачи после неудавшейся попытки и после смены адреса передатчика.

FLUSH_RX — (1110 0010) очищает буфер приёмника, если мы не хотим читать принятое сообщение.

REUSE_TX_PL — (1110 0011) передать последнее переданное сообщение еще раз.

ACTIVATE — (0101 0000) если вызвать эту команду с «магическим» параметром 0x73, то у нас включается режим переменной длинны пакетов. Если с параметром 0x53 у нас переключится текущий банк регистров, с которым работают R_REGISTER и W_REGISTER. Еспользовать ее можно только в power
down и stand by режимах, при линии низком уровне на линии CE(именно по этому ее нельзя цеплять напрямую на шину питания).

R_RX_PL_WID — (0110 0000) возвращает длину первого в очереди принятого пакета.

W_ACK_PAYLOAD — (1010 1PPP) записывает данные, которые нужно отправить вместе с пакетом подтверждения приёма. PPP — номер конечной точки от 000 до 101. Можно записать для трёх конечных точек одновременно.

W_TX_PAYLOAD_NOACK — (1011 0000) пишет данные в буфер передачи от 1 до 32 байт. Для этого пакета не будет выслано подтверждение о получении и передатчик на будет его ждать.

NOP — (1111 1111) Ничего не делает. Может использоваться для чтения статусного регистра.

Регистры.
В RFM70 имеется два банка регистров. В нулевом 26 регистров, используемых для конфигурирования модуля. Здесь находится статусный регистр, регистры, хранящие адреса приёма и передачи, радиочастотный канал и много всего другого, В основном работаем именно с этим банком. К счастью, регистры в этом банке всегда нужно записывать с одним порядком следования байт: от младшего байта к старшим. Все регистры в этом банке можно писать и читать. А читать их бывает очень полезно в отладочных целях, чтоб видеть в каком состоянии находится модуль и как он реагирует на команды.

В первом банке большая часть регистров хранит какие-то непонятные калибровочные константы и должны быть инициализированы значениями, указанными в даташите. Здесь надо учитывать, что в регистры с 0 по 8 в этом банке байты пишутся в порядке от старшего к младшему, то есть с обратным порядком байт. Зачем это сделано — непонятно. Большинство регистров, кроме нескольких, только для записи и всегда читаются как 0. Надо сказать, что несмотря на то, что в даташите сказано эти регистры инициализировать определёнными значениями, если этого не делать и ничего не писать в банк 1, модуль всё работает, но несколько снижается дальность связи. Зато можно сэкономить немного памяти программ. В этом банке находится самый длинный регистр, имеющий номер 0xE, длинной аж 11 байт. В нем видимо хранится какая-то хитрая калибровочная кривулька и его значение очень сильно влияет на дальность и стабильность связи. Хотя со значением по-умолчанию(его можно прочитать), модуль тоже неплохо работает.

Теперь перейдём непосредственно к программированию модуля. На сайте производителя есть пример программирования модуля на Си для какого-то Pic-а, вполне рабочий, но на мой взгляд слишком кучерявый.

Поэтому я написал драйвер модуля для себя сам и конечно-же на С++.
В качестве примера возьмём такую систему:

Тестовый полигон
передатчик — MSP430 Launchpad с контроллером G2231 читает температуру со встроенного датчика и, естественно, передаёт ее;

приёмник — Mega16 принимает сообщение и выводит его в USART.

Код примеров для mspgcc и avr-gcc соответственно, но его не составляет сложности подправить и под IAR. Среды разработки для MSP430 — Codeblocks, для AVR — AVRStudio 4. Многое из исходников я буду опускать, а приводить только ключевые моменты работы с модулем. Полный текст примеров в приложенном архиве.

Начнем с записи и чтения 8-ми битных регистров модуля:

// выполнить команду с одно-байтовым аргументом, или возвращающую один байт
// cmd - выполняемая команда
// value - аргумент команды, 0 - если для команд чтения.
static uint8_t ReadWriteCmd(uint8_t cmd, uint8_t value)
{
    SlaveSelectPin::Clear();
    Spi::ReadWrite(cmd);
    uint8_t result = Spi::ReadWrite(value);
    SlaveSelectPin::Set();
    return result;
}
// записать одно-байтовый регистр в текущем банке.
// reg - номер регистра от 0 до 31
static void WriteReg(uint8_t reg, uint8_t value)
{
    ReadWriteCmd(reg | WriteRegCmd, value);
}
// читать одно-байтовый регистр в текущем банке.
// reg - номер регистра от 0 до 31
static uint8_t ReadReg(uint8_t reg)
{
    return ReadWriteCmd(reg | ReadRegCmd, 0);
}

Здесь Spi — это класс реализующий интерфейс SPI модуля, для каждой платформы нужна своя реализация этого класса. Собственно, в этом классе должна быть только функция ReadWrite, осуществляющая запись-чтение по SPI.
Далее, функции записи и чтения произвольного буфера, они не сильно сложнее:
// читает результат выполнения команды в буфер
// command - выполняемая команда
// buffer - буфер, куда читаем результат
// length - сколько байт читаем.
static void ReadBuffer(uint8_t command, uint8_t *buffer, uint8_t length)
{
    SlaveSelectPin::Clear();
    Spi::ReadWrite(command);
    for(uint8_t *end = buffer + length; buffer != end; ++buffer)
        *buffer = Spi::ReadWrite(0);
    SlaveSelectPin::Set();
}
// передаёт содержимое буфера в модуль
// command - выполняемая команда
// buffer - буфер, откуда читаем данные
// length - сколько байт читаем.
static void WriteBuffer(uint8_t command, const uint8_t *buffer, uint8_t length)
{
    SlaveSelectPin::Clear();
    Spi::ReadWrite(command);
    for(const uint8_t *end = buffer + length; buffer != end; ++buffer)
        Spi::ReadWrite(*buffer);
    SlaveSelectPin::Set();
}
// пишем 32-ти битный регистр
static void WriteReg32(uint8_t reg, uint32_t value)
{
    WriteBuffer(reg | WriteRegCmd, (uint8_t*)&value, 4);
}


Важно отметить, что при выполнении команды, которая ожидает какое-то количество байт, её нужно передать в точности это количество, иначе она молча не выполнится. Если мы хотим записать 4-х байтовый регистр, то после команды WriteReg, нам нужно выкинуть в SPI ровно 4 байта перед тем, как снова установим линию SlaveSelectPin в высокое состояний. При чтении — всё равно, если мы прочитаем меньше, чем нам хочет отдать модуль, то он не обидется, если будем читать больше чем нужно — лишнии байты прочитаются как нули.
Теперь перейдём к инициализации модуля:

static void Init()
{
// конфигурируем интересующие нас линии ввода-вывода
	SlaveSelectPin::Set();
	EnablePin::Clear();
	SlaveSelectPin::SetDirWrite();
	EnablePin::SetDirWrite();
	IrqPin::SetDirRead();
	IrqPin::Clear();
// активируем модуль
	Util::delay_ms<50, F_CPU>();
	ReadWriteCmd(ActivateCmd, 0x73);
// инициализируем регистры банка 1 согласно даташиту
	InitBank1Regs();
// далее работаем с регистрами только из нулевого банка
	SwitchBank(0);
// конфигурация - включаем CRC
	WriteReg(ConfigReg, EnableCrc | Crc2bytes | PowerUpBit);
// после установки PowerUpBit нужно чуток подождать
	Util::delay_ms<50, F_CPU>();
// устанавливаем ширину адреса
	WriteReg(SetupAdressWidthReg, AddressWidth);
// скорость 1Mbps, максимальная выходная мощность и чувствительность 
RfSetup(DataRate1Mbps | OutputPower5dBm | LnaHighGain);
// 15 попыток повторной передачи с периодом ожидания 
// между попытками в 1000 микро секунд
	WriteReg(SetupRetryReg, Wait1000us | 15);
// включаем динамическую длинну сообщений
	EnableDinamicPayload();
// или устанавливаем фиксированную для интересующих конечных точек
	//WriteReg(RxDataLength0, 32);
//WriteReg(RxDataLength1, 5);
// устанавливаем линию CE, хотя она уже была установлена в SwitchBank.
	EnablePin::Set();
}
// переключает текущий банк регистров модуля
// bank - false или true - нулевой или первый банк соответственно.
static void SwitchBank(bool bank)
{
// переключение банков должно осуществляться при линии CE (EnablePin) 
// установленной в низкий уровень
    EnablePin::Clear();
// после чего нужно немного подождать
    Util::delay_ms<50, F_CPU>();
    bool isBank0 = (ReadReg(StatusReg) & RegBank) != 0;
    if(bank != isBank0)
    {
        ReadWriteCmd(ActivateCmd, 0x53);
    }
    EnablePin::Set();
}
// конфигурация регистров в первом банке
static void InitBank1Regs()
{
    SwitchBank(1);
    WriteBuffer(WriteRegCmd | 15, Bank1_Reg15, Reg15Size);
    WriteReg32(0x00, 0xE2014B40);
    WriteReg32(0x01, 0x00004BC0);
    WriteReg32(0x02, 0x028CFCD0);
    WriteReg32(0x03, 0x41390099);
    WriteReg32(0x04, 0x0B869ED9);
    WriteReg32(0x05, 0xA67F0624);
    WriteReg32(0x06, 0x0B869ED9);
    WriteReg32(0x0c, 0x00731200);
    WriteReg32(0x0d, 0x0080B436);
}


Как я уже говорил, инициализацию регистров в первом банке можно опустить и оставить всё со значениями «по умолчанию», сэкономив при этом немного памяти программ, но при этом дальность связи несколько падает.
Теперь перейдём непосредственно к приёму и передачи данных:
// записывает данные в буфер передачи модуля
static bool Write(const void * buffer, uint8_t size)
{
// на всякий случай переключаемся в режим передачи
    SwitchToTxMode();
// читаем статус
    uint8_t fifoStatus = ReadReg(FifoStatusReg);
// если буфер передачи не полон
    if(!(fifoStatus & FifoTxFull))
    {
// пишем наши данные в буфер передачи
        WriteBuffer(WriteTxDataCmd, (uint8_t*)buffer, size);
        return true;
    }
    return false;
}

// читает полученные данные
static bool Recive(void * buffer)
{
// получаем длину полученного пакета
    uint8_t length = RecivedDataLength();
// если данные полученны
    if(Status() & RxDataReady)
    {
// читаем 
        ReadBuffer(ReadRxDataCmd, (uint8_t*)buffer, length);
// очищаем флаги, чтоб модуль мог получить новую порцию данных
        ClearInterruptStatus();
        return true;
    }
    return false;
}


А теперь код для передатчика. Некоторые вещи, такие, как калибровку встроенного генератора от часового кварца и отладочный вывод через soft USART я пропущу для ясности, — они непосредственно к работе с модулем не относятся.
// читаем температуру со встроенного датчика 
//в десятых долях градуса Цельсия
// позаимствовано из TI-шных примеров
static int AdcGetTemp()
{
    ADC10CTL0 |= ENC + ADC10SC;
    __bis_SR_register(CPUOFF + GIE);   // LPM0 with interrupts enabled
    long temp = ADC10MEM;
    long degC = ((temp - 673) * 423*10) / 1024;
    return degC;
}
// иницивализация АЦП на работу с датчиком температуры
static void AdcInitTempSence()
{
    ADC10CTL1 = INCH_10 + ADC10DIV_3;
    ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;
}

// софтварный SPI - с имеющимся USI со SPI режимом лень было разбираться
typedef SoftSpi<P1_4, P1_5, P1_2> Spi;
// собственно определение нашего модуля
typedef Rfm70<Spi, P1_7, P1_6, NullPin> transceiver;
// красный светодиодик
typedef P1_0 Led;
uint16_t temp;

int main()
{
	// выключаем собаку
WDTCTL = WDTPW + WDTHOLD;
// настраиваем тактирование
SetUpClock();
// конфигурируем линию с диодом на выход
Led::SetConfiguration(Led::Port::Out);

AdcInitTempSence();
// инициализируем модуль
transceiver::Init();
// задаём адрес по которому передаём - 5 байт
transceiver::SetTxAddress(0x3456789a, 0x12);
// переключаемся на 10 канал
transceiver::SetRfChannel(10);
// режим передатчика
transceiver::SwitchToTxMode();
while(1)
{
// получили температуру
        temp = AdcGetTemp();
// чистим флаги прерываний
        transceiver::ClearInterruptStatus();
// передаём 2   байта 
        transceiver::Write(&temp, sizeof(int));
	// ждём немного
        Util::delay_ms<200, F_CPU>();
// если данные успешно отправились и их кто-то получил,
// зажигаем красный светодиод
        Led::Set(transceiver::Status() & TxDataSent);
}
return 0;
}
// прерывание от АЦП - нужно, чтоб разбудить 
// проц из спячки после измерения температуры
interrupt(ADC10_VECTOR) adc_service_routine()
{
    __bic_SR_register_on_exit(CPUOFF);
}


Если данные успешно передаются, то на Launchpad-е будет гореть красный светодиод, как только связь прервётся — он погаснет. Да, чтоб этот пример работал, надо впаять в Launchpad часовой кварц на его законное место.
Теперь приёмник.


// модуль подключен к аппаратному SPI меги16
typedef Rfm70<Spi, Pb4, Pb3, Pb2> transceiver;
// буферезированный USART 
typedef Usart<16, 16> MyUsart;
// обработчики прерываний USART
ISR(USART_RXC_vect)
{
	MyUsart::RxHandler();
}

ISR(USART_UDRE_vect)
{
	MyUsart::TxHandler();
}
// класс адпаптер для потока вывода через USART
template<class Src>
class MyStream
{
    public:
    void put(char c)
    {
    	if(c == '\n')
			Src::Putch('\r');
        Src::Putch(C);
    }

    void write(const char *ptr, size_t size)
    {
        for(size_t i=0; i<size; i++)
            put(ptr[i]);
    }
};
// форматированный поток вывода, привязанный к USART
typedef FormatWriter<MyStream<MyUsart> > Output;
Output out;

uint16_t temp;

__attribute__((OS_main))
int main()
{
// инициализируем USART
	MyUsart::Init(19200);
// инициализируем SPI
	Spi::Init(Spi::Div16);

	sei();
// приветствие
	out << "Rfm70 module at AtMega16\n";
// инициализируем модуль
	transceiver::Init();
// включаем 10 канал
	transceiver::SetRfChannel(10);
// преключаемся в режим приёмника
	transceiver::SwitchToRxMode();
// ставим адрес приёма для нулевой конечной точки такой-же как адрес у передатчика
	transceiver::SetRxAddress<0>(0x3456789a, 0x12);

	while(1)
	{
	// если что-то получили
	// выводим это в USART 
		if(transceiver::Recive(&temp))
			out.Format("Temp = % C\n") % temp;
		Util::delay_ms<100, F_CPU>();
	}
}


Ссылки.
Даташит:
www.hoperf.com/upload/rf/RFM70.pdf
Страница с примерами производителя:
www.hoperf.com/pro/rf/24g/RFM70.htm

  • +10
  • 11 июня 2011, 01:42
  • neiver
  • 1
Файлы в топике: rfm70sample.zip

Комментарии (97)

RSS свернуть / развернуть
И сколько такая няма стоит? И как у нее с потреблением?
0
  • avatar
  • PRC
  • 11 июня 2011, 11:57
И где.
0
18 страниц, на первой нет, при попытке заюзать параметрический поиск посылает регистрироваться.
0
В наличии есть 1 шт RFM70S по цене 145.98р.
Цена мне нравится, осталось почитать про потребление и можно использовать.
0
Хорошая, годная цена. Только доставка у них дорогая.
Никто из default city не хочет для меня парочку взять? :)
0
Сейчас их в наличии целых 1 штук. Как появятся буду брать себе парочку, могу и еще парочку прихватить:)
0
блин, вечно я забываю про регистрацию. хотя ща попробовал выйти, все равно нормально показывает..., можно у них заказать по 150 рэ
0
Пришла мне пара этих модулей с www.futurlec.com. Вместе с доставкой вышло около 15 баксов
0
Я их покупал в Терре где-то в феврале, они тогда были по 130 р.
0
в имраде 49 грн ~ 6 уе
0
в космодроме та же цена, но в наличии пока нет.
0
старичок TR24P — те же денеги, передача чуть ниже — 1Мбс, зато мощность почти в 2,5 раза выше, чувствительность тоже чуть получше, тоже SPI.
0
TR24P кажется привлекательнее, но у него похоже нет антенны
а от неё и подключения многое зависит
0
12dBm на печатке рассеять — многовато получится, EMC не потянет имхо,
Обещанная дальность для TR24P — 1200м, антенна как расплата за фичу))
Печатная антенна только в A и B. для TR24P есть антенны: приемной и передающей.
для работы с общей антенной можно поставить простой антенный коммутатор PIN-диодах.

теоритически конечно можно поиграться с 1/4 штырем к самой плате,
а можно и навесить МС или MMCX разъем и купить такое:

ANT 2.4 ANTSMT001
ANT 2.4 A24-HABUF-P5I UFL-F
ANT 2.4 BY-2400-01-03

2.4GHz MINI RUBBER DUCKY WIRELESS ANTENNA -MC CONNECTOR

в любом (не наобум) случае, будет получше чем 5dBm, поскольку антенну
все равно ведь выносить придется, если клиенту не с коленок отдавать.

Если честно, с 2.4ГГц — не пробовал, но 900/1900 МГц направленную
на дачу приятелю — пару лет назад собирал, реально работала.
0
на первой картинке это керамическая антена?
0
Ага.
0
valio, о антеннах это вы интересно написали, вы бы статью написали о TR-24Р и его плюсах, а то инфы о TR-24А мало, а о TR-24Р и то меньше. ДОКа на них как китайский код, фиг разберешь без гуру. И модули то года три назад как появились, «новье», мало кто работал, а описания работы просто шаманство.
0
Ы, вон они какие внутри, антенны от роутеров. А я думал, чего они такие длинные, хотя четверть волны вроде всего 3 см. Хотя почему они именно такие — все равно не понятно.
0
Спасибо, почитал с интересом.
Конечно хочется попробовать, но как-бы приобрести до кучи с чем-нибудь…
0
Начинал читать статью, была мысль, что можно на таких модулях соорудить радиохвост к своей мышаке.
(с недавних пор ношу с ноутом любимую х7 вместо беспроводного китайца, но провод как то мешает)
место под батарею в ней навалом, да и еще одну плату она потерпит.
Есть ли смысл в затее и какой модуль стоит взять?(этот, как я понимаю из loic по воробьям)
0
На него хоть цена разумная. А вообще, почему бы не взять беспроводную X7? У меня например A4 XFar X-630. Правда, говорят, она быстро жрет батарейки и она уменьшенного размера (средняя между десктопной и и чисто ноутбучной, которая размером с серую мышь). По цене — ненамного дороже двух модулей, а там еще придется городить свой контроллер, причем с обеих сторон.
P.S. A4 тоже китайцы) Или около того.
0
Всё китайцы, не хочется расставаться с этой мышкой.
Да и шило в пятой точке так и тянет, что-нибудь погородить)
0
Отличная статья, спасибо, как раз искал хороший RF модуль по приемлимой цене. Собираюсь заказывать в терраэлектронике (145 руб).

Вопрос: разница между D (dip) и S (smd) только в том, что в дипе PLS-разъём припаян, а в SMD нет? судя по чертежам в даташите, контактные площадки с отверстиями одинаковые и там и сям.

Еще вопрос: возможен ли уверенный обмен данными на расстоянии около 100 м при _почти_ прямой видимости? Скорость высокая не нужда, один 4-байтный пакет в секунду. И реально ли с наименьшими усилиями немного увеличить дальность и/или уверенность приёма, например, прикрутив антенну от WiFi-роутера? (там ведь, если не ошибаюсь, тоже 2.4 ГГц)
0
Да, разница между D (dip) и S (smd) только в припаянном разъёме pls c шагом 1.27 мм.
На счет 100 м, даже при прямой видимости, я не уверен. У меня получилось максимум ~70 м, но уверенность связи сильно зависила от взаимной ориентации модулей.
Внешню антенну я не пробовал, но думаю, что возможно, нужно будет только новые согласующие цепи подобрать…
0
взаимной ориентации модулей? они должны быть что ли еще и строго вертикально расположены? ну или иначе, но одинаково? Жуть. Пульт управления придется держать определенным образом…
0
Антеннка-то несимметричная и диаграмма направленности у нее, видимо, на совсем круговая…
Так, что граница зоны связи от ориентации модулей зависит.
Вот, например, в даташите на tr24a есть диаграмма на 3-й странице:
mdfly.com/newmdfly/products/RF2.4G/nRF24L01/TR24A.pdf
У rfm70 — что-то сопоставимое.
0
HopeRF известна своей любовью брать чужие чипы, сажать их под blob и говорить «все маё». В данном случае, судя по всему, чип — Nordic nRF24L01 (с плюсом или без) www.nordicsemi.com/kor/Products/2.4GHz-RF/nRF24L01P.

Действительно, один из самых «вкусных» на сегодняшний момент чипов по цене при адекватных возможностях строить распределенные сетки.

А вообще господа, забахайте проект — беспроводная сенсорная нода стоимостью до $10. Собственно, в этой статье как раз и решение — MSP430 Launchpad + nRF24L01+ breakout. Но «пузато» получается. Идеальное решение было бы одночиповое, но самый дешевый nRF24LE1 breakout, что я видел — $13.95, при левом питче и отсутствии бутлоадера…
www.mdfly.com/index.php?main_page=product_info&cPath=8_52&products_id=659
0
в rfm-70 скорее чип от беккена, bk2491, аналог bk2421, что стоят в rf-2400, упоминаемые каментом ниже, одно из доказательств — у нордика однобанковый конфиг, а тут двух
0
А чем RF-2400P хуже? Вот тут cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&item=320715876578#ht_5191wt_1163 идут по $3 с учётом доставки :) Уже заказал себе несколько штук для экспериментов. А про ноду — серия STM32W — там в чипе сразу всё зашито, и по цене как раз укладывается.
0
Доброе время суток.

Взяли 2 модуля. Инициализация похожа на ту которая тут приведенна. Но вот проблема: модули ничего в эфир не выдают.
Кто нибудь с таким сталкивался?
0
Да много чего может быть: линии SS и/или CE не установлены, модуль в режиме приемника, PowerUp бит не установлен в регистре конфигурации, в буфер записывается неправильное количество байт. Это так, на вскидку. А вообще эти модули практически никак не извещают об ошибках конфигурации, бывает сложно определить почему не работает.
0
Здравствуйте, та же засада что и у gem4410 провели инициализацию регистров 0- го и 1-го банков, записали регистры, в эфире пустота вот инит регистров, посоветуйте плиз куда копать?

//************ Bank0 register initialization commands
uint8_t bank0Init[][2]=
{
// address data
{ (0x20|0x00), 0x72 }, //Disable CRC ,CRC=1byte, POWER UP, TX
{ (0x20|0x01), 0x01 }, //Enable auto acknowledgement data pipe0
{ (0x20|0x02), 0x01 }, //Enable RX Addresses pipe0
{ (0x20|0x03), 0x03 }, //RX/TX address field width 5byte
{ (0x20|0x04), 0x00 }, //auto retransmission disabled
{ (0x20|0x05), 0x0B }, //channel = 11
{ (0x20|0x06), 0x34 }, //air data rate-1M,out power 0dbm,setup LNA gain-20dB
{ (0x20|0x0C), 0x0b }, //LSB Addr pipe 2
{ (0x20|0x0D), 0x0c }, //LSB Addr pipe 3
{ (0x20|0x0E), 0x0d }, //LSB Addr pipe 4
{ (0x20|0x0F), 0x0e }, //LSB Addr pipe 5
{ (0x20|0x1C), 0x01 }, //Enable dynamic payload legth data pipe0
{ (0x20|0x1D), 0x06 } //Enables Dynamic Payload Length,Enables Payload with ACK
};

//************ Bank1 initialization commands
uint8_t bank1Init[][5]= {
// address data
{ (0x20|0x00), 0x40, 0x4B, 0x01, 0xE2 }, //0xE2014B40 Address 0
{ (0x20|0x01), 0xC0, 0x4B, 0x00, 0x00 }, //0x00004BC0 Address 1
{ (0x20|0x02), 0xD0, 0xFC, 0x8C, 0x02 }, //0x028CFCD0 Address 2
{ (0x20|0x03), 0x99, 0x00, 0x39, 0x41 }, //0x41390099 Address 3
{ (0x20|0x04), 0xD9, 0x9E, 0x86, 0x0B }, //0x0B869ED9 Address 4
{ (0x20|0x05), 0x24, 0x06, 0x7F, 0xA6 }, //0xA67F0624 Address 5
{ (0x20|0x0C), 0x00, 0x73, 0x12, 0x00 }, //0x00127300 Address C
{ (0x20|0x0D), 0x00, 0x80, 0xB4, 0x36 } //0x36B48000 Address D
};

планируется использовать только 2 пайпа из 6-ти.

Заранее весьма признателен
0
Вот в приведенной таблице инициализации регистров банка 1 регистры с 0х00 до 0х05 (вообще до 0х08) нужно запихивать в модуль начиная с MSB, т.е. так как они уже стоят в массиве ( { (0x20|0x00), 0x40, 0x4B, 0x01, 0xE2 }, ибо в даташите предлагают запихнуть в этот регистр 0x404B01E2). Для регистров 0х09 до 0х0E пихать их нада начиная с LSB, т.е например для 0х0С в такой последовательности слать в SPI: (0x20|0x0C), 0x00, 0x12, 0x73, 0x00(Значение из даташита: 0x00731200). Для 0x0D: (0x20|0x0D), 0x36, 0xB4,0x80,0x00 (Значение из даташита: 0x0080B436)
The serial shifting SPI commands is in the following format:
0
чето както криво встала часть из даташита. параграф 6.3.1
The serial shifting SPI commands is in the following format:
— <Command word: MSB bit to LSB bit (one byte)>
— <Data bytes: LSB byte to MSB byte, MSB bit in each byte first> for all registers at bank 0 and register 9 to register 14 at bank 1
— <Data bytes: MSB byte to LSB byte, MSB bit in each byte first> for register 0 to register 8 at bank 1

Мой затык был как раз в этом. Поменял порядок, все заработало как надо
0
Линии CE и SS ставятся правильно. PU бит установлен. Динамический буфер. В принципе конфигурация такаяже как описали в следуюшем посте. А в эфире как и у mr_Z пустота.
0
А как узнали, что в именно эфире пустота? Может просто приемник не слушает? У вас auto retransmission выключен, передача занимает всего несколько микросекунд, как её ловите именно в эфире? В статусном регистре Data send бит устанавливается через некоторое время после записи в буфер? После записи в буфер Slave Select устанавливается в единичку?
У меня телепатические способности кончились :)
0
Проверяли эфир какимто навороченным сканером. Да и второй модуль стоит в тежиме прийома. Auto Retransmission я включал и выключал. Безрезультатно. Data send должен устанавливаться, тут честно надо еще проверится. SS переключается из 0 в 1 правильно. У меня тоже все идеи закончились 8-(
0
У меня тоже затык был с этими модулями… Дней наверно 5 с ними любился… Оказалось что я регистры не в той последовательности записываю… Там для некоторых нужно чтоб запись начиналась со старшего байта, в некоторых с младьшего… Короче легко запутаться. И у меня при этой ошибке тоже ничего не передавалось. Конечно навароченного оборудования чтоб эфир послушать у меня не было, но посылки не проходили, IRQ пин модуля тоже не дергался…
Как разобрался с последовательностью записи значений в регистры — сразу все заработало во всех режимах
0
вот последовательность
static unsigned char bank0[][2]=
{
// address data
{ 0x00, 0x7A }, // Enable CRC, CRC = 1 byte, POWER UP, TX
{ 0x01, 0x03 }, // Enable auto acknowledgement data pipe0 and pipe1
{ 0x02, 0x03 }, // Enable RX Addresses pipe0 and pipe1
{ 0x03, 0x03 }, // RX/TX address field width 5 byte
{ 0x04, 0x00 }, // Auto retransmission disabled
{ 0x05, 0x00 }, // Channel = 0 (valid 0 — 63)
{ 0x06, 0x36 }, // Air data rate = 1M, out power 5 dbm, setup LNA gain -20dB
{ 0x0C, 0x0B }, // LSB Addr pipe 2
{ 0x0D, 0x0C }, // LSB Addr pipe 3
{ 0x0E, 0x0D }, // LSB Addr pipe 4
{ 0x0F, 0x0E }, // LSB Addr pipe 5
{ 0x11, 0x01 }, // RX_PW_P0 = 1 bytes
{ 0x12, 0x01 }, // RX_PW_P1 = 1 bytes
{ 0x1C, 0x03 }, // Enable dynamic payload legth data pipe0 and pipe1
{ 0x1D, 0x07 } // Enable dynamic payload length, enable payload with ACK
};

Естественно что 1D b 1C записываются в конце. перед банком 1.
IRQ я не пользую.
0
Да забыл сказать что все инитится до последних двух в порядке следования.

Что Вы сможете посоветовать?
0
В первую очередь избавиться от магических констант в конфигурации модуля где только можно, использовать именованые значения для битов. Тогда может будет видно, есть ли проблемы в конфиге.
0
Можно, конечно. Только результат возможен такой же (проверил и переповерил). К сожаленю я сегодня не в том месте где есть модуль. Проверить все изменения я смогу только завтра.
0
Я, собственно, больше ничем помочь не могу, все свои грабли я уже рассказал.
0
Спасибо за указания и за помощь. Завтрева попробуем как это поможет. Еще раз спасибо.
0
Гляньте выше мой коментарий к сообщению mr_Z с массивами значений регистров
0
Да, кстати, регистры 0x1C и 0x1D имеет смысл писать токо тогда, когда фишки переменной длины пакета активированы командой ACTIVATE с параметром 0х73. До этого «a write has no effect, a read only results in zeros on MISO.».
Вот мой кусок кода для этого:
i=SPI_Read_Reg(0x1D);//read Feature Register .
	if(i==0) // i!=0 showed that chip has been actived.so do not active again.
		SPI_Write_Reg(ACTIVATE_CMD,0x73);// Activate
	for(i=8;i>=7;i--) // засылаем сначала 0х1D,затем 0x1C
	{
		data_to_send[0]=Bank0_Reg0_29[i][0];
		data_to_send[1]=Bank0_Reg0_29[i][1];
		SPI_master_send(data_to_send, 2);
		while(SPI_action!=0);
	}
0
Все правильно. TX_DS не ставится никогда.
0
подскажите! что бы прочитать значение регистра по адресу, скажем 0х07 нужно:
1) Прижать CSN к земле
2) Отправить по SPI такой байт: 0b00000111
3) Дернуть CSN к плюсу питания не небольшой промежуток времени и опять прижать к земле
4) А дальше ждать что модуль побайтно пришлет содержимое регистра?
0
Для чтения нужно:
1 прижать CSN к земле
2 отправить по SPI код команды
3 отправлять ноль по SPI для каждого байта результата и читать значение из SPI
4 подтянуть CSN к питанию.
0
Спасибо! передатчик вроде заработал, буду начинать разбиратся с приемником.
0
не ноль а 1 надо отправлять (0xFF)
0
  • avatar
  • x893
  • 01 ноября 2011, 01:31
не ноль а 1 надо отправлять (0xFF). Я их использую как дешевую замену blutooth. Там обычно Nordic стоит — можно брать все тулзы с сайта nordic'a и пользовать (для регистров и настройки)
0
  • avatar
  • x893
  • 01 ноября 2011, 01:33
Там на самом деле всё равно, что отправлять при чтении, после байта команды чтения остальные просто игнорируются.
0
будет ли достаточно для проверки связи двух модулей:
1) один сконфигурировать как ТХ (PWR_UP=1, PRIM_RX=0), больше ничего не менять. Подать СЕ=1.
2) второй сконфигурировать как РХ (PWR_UP=1, PRIM_RX=1), больше ничего не менять. Подать СЕ=1.
Потом на первый отправить по SPI команду «отправить» и один байт информации, а на другом считывать значение бита RX_FULL что бы понять что сообщение пришло?
0
Не знаю, надо проверять…
0
а нету нигде больше примеров использования этих модулей с АВР, т.к с параметрами настройки все очень запутано… или как хотя бы запустить обмен в простейшем режиме
0
А вот как грамотно оценить состояние радиоканалов во всём диапазоне для данного модуля? Для выбора наиболее «чистого».
Я подозреваю, что наиболее оптимальный вариант — посылка 15-ти пакетов на каждом канале, с мониторингом на передающей стороне количества потерянных, а далее выбор каналов с наименьшим кол-вом потерь, (аналог BER) кто как думает?
P.S. Видел решение одного поляка к-рый предлагает играться трешхолдом по уровню входного сигнала (мне кажется гемор ещё тот)
0
  • avatar
  • mr_Z
  • 07 ноября 2011, 22:04
Подбор канала довольно сложно реализовать надёжно. Как вариант такой алгоритм:
1 устанавливаем связь между модулями на канале по умолчанию;
2 ведущий инициирует подбор канала;
3 запоминаем канал на котором удалось установить связь
4 ведущий выбирает следующий канал и передает по «последнему удачному» каналу его номер ведомому
5 пытаемся установить связь на новом канале
6 если связь лучше чем на «последнем удачном» канале, то текущий канал становится последнем удачном
7 пока каналы не кончились идина 4

В результате «последним удачным» каналом станет самый лучший. Оценивать качество канала можно как по количеству повторов, так и по силе сигнала. Подбор можно прервать досрочно если передача передается без повторов.
Вот только если канал по умолчанию будет забит, то связь всё равно не удастся установить, даже если на остальных каналах это возможно.
0
Еще вариант:
Приемник последовательно перебирает все каналы и слушает каждый на протяжении фиксированного промежутка времени.
Передатчик тоже последовательно перебирает все каналы и передает сигналы на протяжении другого периода времени, не равного периоду ожидания приемника, например, вдвое быстрее.
Можно рассчитывать, что за O(N^2) попыток, где N число перебираемых каналов, будут предприняты попытки установить связь на всех каналах. Если перебирать все 83 канала, то это долго может занять несколько минут, модуль перестраивается с канала на канал, примерно 60 мс. А если, например, каждый четвертый-пятый, то может оказаться вполне приемлемо. Надо попробовать…
0
В результате «последним удачным» каналом станет самый лучший. Оценивать качество канала можно как по количеству повторов, так и по силе сигнала. Подбор можно прервать досрочно если передача передается без повторов.
Вот только если канал по умолчанию будет забит, то связь всё равно не удастся установить, даже если на остальных каналах это возможно.

Согласен, но я спрашивал как раз о критериях выбора так как RSSI функциональность отсутствует. Чтобы оценить состояние на конкретном канале, хотя бы на уровне да/нет.
В любом случае спасибо за советы, алгоритмы;)
0
Ребята! у кого какая дальность внутри момещений?
0
Дальность уж очень сильно зависит от всего: настройки модулей, их взаимной ориентации в пространстве, от конкретных экземпляров модулей. Вообще, у меня практическая дальность в помещении получилась не менее 15 м.
0
У меня проблема. Настроил один модуль на прием, а второй на передача. Но вот данные передаются только если касаюсь антенну передатчика. В чем причина такого странного поведения?
0
Возможно выходные/согласующие цепи передатчика пробиты статикой. Пикофарадные SMD конденсаторы, на которых сделана согласующая цепь, очень легко пробить статикой. А поскольку модель хоть как-то передаёт значит сама микросхема всё-таки жива.
0
Когда поставил минимальную выходную мощность передатчика то связь наладилась. Не понимаю почему так.
Подскажите как быстро модуль переключается с режима передачи в режим приема и обратно.
0
Я не измерял. Но если учесть, что на каждый принятый пакет модуль переключается в режим передатчика и посылает ответный ACK пакет, то довольно бастро.
0
Сегодня запаяю другой модуль, может и на самом деле пробил статикой.
Поделитесь кто где использует эти модули.
Я вот хочу сделать радио удлинитель для СОМ порта.
0
в виде радиокнопки к дверному звонку. Статейку с передатчиком где-то тут выкладывал…
0
Хотел по шустрику проверить работу модулей, больно уж давно они у меня валяются, взял код со статьи и не в какую, открывал и в 4 и в 5 студии но четно мейкфайл переписывал но все равно куча всевозможных граблей… что делать?
0
Какие конкретно грабли?
0
Сначала что 4 что 5 ругалась на то что не может найти подключаемые файлы *.h. Я перепробовал все и на XP и на 7 не было ни пробелов ни русских букв в адресе к папкам с файлами нив в какую не подключал решил проблему прописав полные адреса у кучи инклюд файлов. Дальше пошли другие грабли с мейк файлом, АВРстудия напрочь не хотела его правильно создавать создал ВИНавэром. И наконец компилятор начал писать
0
Проблема в том, что нужно указывать пути к каталогам с заголовками.

Попробуйте распаковать архив с примером как есть, полностью, и открыть проект RFM70.aps в 4 студии. Он должен собираться и работать.
0
В том то и дело что не работает выдает:

Я уже и с мейкфайлом мудрил и заново создавал проэкт все четно…
В чем может быть проблема? В гугле уже искал…
0
Картинка не читаема…
0
Странно картинки аплоудятся и маштабируються… Короче компилятор пишет:
make: *** No rule to make target `main.o', needed by `RFM70.elf'. Stop.
0
Странно. У меня в студии 4.17 + WinAVR и в студии 4.19 + AvrToolchain всё собирается без проблем как есть.
Пути к заголовкам точно прописаны? И заголовки по этим путям лежат?
0
Да правильно если убрать то компилятор начинает ругаться я пробую собрать 4.13+ WinAVR и на 5 с тулчайном.
Пятерка вообще не хочет в никакую видеть путь к заголовкам… может попробовать переставить 4ку на 4.17 хотя странно все равно компилятором служит ВинАВР а он последний что выпускался 10го года…
0
переставил на 4.19 + AvrToolchain две новые проблемы ))))
D:\rfm70sample\AvrRFM70\..\mcucpp/containers.h:102:14: error: 'bool RingBuffer<SIZE, DATA_T>::IsEmpty() const
[with int SIZE = 16, DATA_T = unsigned char]' is inaccessible
D:\rfm70sample\AvrRFM70\..\mcucpp\AVR/usart.h:141:3: error: within this context
0
Точно.
Надо в файле containers.h немного подправить:
template<int SIZE, class DATA_T=unsigned char>
class Queue :public RingBuffer<SIZE, DATA_T>
{
public:
	using typename RingBuffer<SIZE, DATA_T>::INDEX_T;
	using RingBuffer<SIZE, DATA_T>::IsFull;
	using RingBuffer<SIZE, DATA_T>::IsEmpty;
0
Слава вам… Спасибо запустилось буду дальше мучать код под себя ))))
0
вроде все работает но муляет один вопрос почему это не работает в 5 студии там жешь вроде тоже тулчайн и не хочет ни в какую подрубать файлы как бы они не указывались в чем может быть дело???
0
Прошу прощение за сказанное но по моему тут опечатка, не соответствие схеме подключения модуля:
typedef Rfm70<Spi, Pb4, Pc3, Pb2> transceiver; (по идее должен быть же Pb3 а не Pc3)????
Я понимаю можно указать какие угодно порты но все же не соответствие схеме в верху топика…
0
Видимо очепятка :)
Поправил.
0
Моя наглость не имеет границ. За что я прошу прощение но возникла новая проблема решил перекинуть проект на мегу8 без проблем скомпилил зашил два дня мучался не работает осциллографом тыкаю нет ни клока ни изминений уровня на моси уже даж пробовал напрямую 8 бит отправить Spi::ReadWrite(temp); Вроде SPI.h проверил вроде там все ок в чем может быть беда?
0
Не знаю, у меня на меге8 этот код работал без изменений. Может порт палёный? Или в мотнаже что-то коротит?
0
Хз проверил же у меня отдельно от всего мега в паяная тупо в макетку, отказалась что либо выводить на spi осциллографом тыкал смотрел… Главное компилю простейший код для передачи 8 битного числа через spi на кодвижене все работает и клок(делитель16) я вижу на осцыле и моси биты кидает либо у меня руки кривые либо я не выкупаю почему так криво библиотеки работают или компилятор идиот в 4 студии. Главное что uart работает, а spi нет…
0
спс разобрался наткнулся на первоисточник
github.com/KonstantinChizhov/AvrProjects/tree/master/RFM70
0
Куда все пропали? У кого сначала не пошло, а потом заработало: поделитесь опытом. Копаюсь третью неделю, перепаял модули, а радиосвязи нет. Исходники взял с сайта производителя. SPI работает, могу писать в регистры и читать из регистров. Инициализация у приемника и передатчика одинаковые. Особенности записи банков 0 и 1 учел. МК использую ATMega8A. Где еще порыться?
0
У меня код отсюда не заработал, сколько бы я ни пытался. А вот после переноса примеров с оф. сайта 1 в 1 с пика на авр, всё это дело завелось. Хотя, конечно, диаграмма направленности у них та ещё.
0
Всем доброго дня, интересная тема, озадачился одной схемкой, но в ней стоит RFM12, отзыв есть только одного человека и кажется не хватает скорости RFM12, большая задержка на передачу байта в течении где-то 2,0-3мс + ещё что-то там… трудно сказать точно, интересно попробывать RFM70 или NRF24L01, дальность приёма не критична (квартира, улица — максимум метров 20-30 а то и меньше). На ebay.com цены на них смотрятся замечательно, только ждать надо :( и смотрю NRF24L01 там очень популярен. Сориентируйте меня пожалуйста на конкретный трансивер…
Спасибо.
0
скажите, а зачем у вас в коде инициализируется 6-й регистр банка1, да еще и значеним для 4-го?
WriteReg32(0x06, 0x0B869ED9);
Тогда как даташит говорит, что его вобще трогать не надо.
0
Похоже на издержки технологии Copy-Paste :)
0
Слыхал, на замену оному вышел RFM73. Какие у него отличия кроме дополнительной пониженной скорости передачи данных? Как с диаграммой направленности, где её посмотреть или снять?

У 70 мне не понравилось (хотя, может, это проблемы иного плана), что стоило лишь немного повернуть модули от их оптимального взаимного расположения, так даже на 5-7 метрах пакеты уже почти не доходили.
0
Кому нужны радиомодули пишите icq 603809986, в наличии весь модельный ряд.
0
Подскажите. Хотел настроить элементарно: отправка 1 байт. длинна передаваемого пакета фиксирована — 1 байт (Dynamic Payload отключено). Auto Acknowledgment — тоже отключено — по проще же хочу. Приемник не включен. Итог: TX_DS выставляется всегда. и когда отправляю байт командой W_TX_PAYLOAD_NOACK и когда отправляю байт командой W_TX_PAYLOAD. тоесть невозможно командой W_TX_PAYLOAD без Auto Acknowledgment просто контролировать есть ли передатчик на том конце?
0
Без Acknowledgment-а не возможно узнать слушает ли нашу передачу кто-то или нет. По этому TX_DS выставляется сразу как данные отправлены, а принял из кто или нет мы не знаем.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.