From cb09165d3eb0a8df562fc5d30707327387ff8f15 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 14 Jun 2021 18:36:58 +0300 Subject: [PATCH] v1.1 --- README.md | 67 +++- examples/demo_rx/demo_rx.ino | 36 ++ examples/demo_tx/demo_tx.ino | 37 ++ examples/isr_rx/isr_rx.ino | 28 ++ examples/raw_rx/raw_rx.ino | 16 + examples/raw_tx/raw_tx.ino | 24 ++ examples/rx433/rx433.ino | 22 -- examples/rx433_lcd/rx433_lcd.ino | 16 +- examples/rx433_struct/rx433_struct.ino | 14 +- examples/tx433/tx433.ino | 18 - examples/tx433_struct/tx433_struct.ino | 9 +- keywords.txt | 10 +- library.properties | 2 +- src/FastIO.h | 109 ++++++ src/Gyver433.h | 453 +++++++++++++------------ 15 files changed, 560 insertions(+), 301 deletions(-) create mode 100644 examples/demo_rx/demo_rx.ino create mode 100644 examples/demo_tx/demo_tx.ino create mode 100644 examples/isr_rx/isr_rx.ino create mode 100644 examples/raw_rx/raw_rx.ino create mode 100644 examples/raw_tx/raw_tx.ino delete mode 100644 examples/rx433/rx433.ino delete mode 100644 examples/tx433/tx433.ino create mode 100644 src/FastIO.h diff --git a/README.md b/README.md index 6cdfeda..6388f9e 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,12 @@ ![author](https://img.shields.io/badge/author-AlexGyver-informational.svg) # Gyver433 Библиотека для радиомодулей 433 МГц и Arduino -- Не использует прерывания и таймеры (кроме нулевого, читает micros()) -- Встроенный CRC контроль целостности +- Супер лёгкая либа, заведётся даже на тини13 (отправка) +- Поддержка кривых китайских модулей +- Интерфейс Manchester Coding (v1.1) +- Встроенный CRC контроль целостности (CRC8 или XOR) - Ускоренный алгоритм IO для AVR Arduino +- Опционально работа в прерывании (приём данных) ### Совместимость Совместима со всеми Arduino платформами (используются Arduino-функции) @@ -33,22 +36,40 @@ ## Инициализация ```cpp -// указать пин -Gyver433_RX rx(2); -Gyver433_TX tx(2); +//Классы: +Gyver433_RX // приёмник +Gyver433_TX // передатчик + +Gyver433_xx<пин, буфер, CRC> xx; +// пин: цифровой пин +// буфер: размер буфера в байтах. На "ручную" отправку буфер не нужен (пример raw_tx). По умолч. 64 байта +// CRC: проверка целостности данных: G433_CRC8 (надёжный), G433_XOR (лёгкий), G433_NOCRC (отключено). По умолч. G433_CRC8 + +// Дефайны-настройки перед подключением библиотеки +#define G433_SLOW_MODE // "медленный режим" для синих модулей SYN480R +#define G433_SPEED 1000 // скорость 100-8000 бит/с, по умолч. 2000 бит/с ``` ## Использование ```cpp // ========= Gyver433_TX ========= -void sendData(T &data); // отправить данные любого типа +void sendData(T &data); // отправить данные любого типа (CRC добавится автоматически) +void write(uint8_t* buf, uint8_t size); // отправить массив байт указанного размера (CRC не добавляется) +uint8_t buffer[]; // доступ к буферу для отладки // ========= Gyver433_RX ========= uint8_t tick(); // неблокирующий приём, вернёт кол-во успешно принятых байт uint8_t tickWait(); // блокирующий приём, вернёт кол-во успешно принятых байт bool readData(T &data); // прочитает буфер в любой тип данных (в указанную переменную) int getSize(); // получить размер принятых данных +bool gotData(); // вернёт true при получении корректных данных (если tick опрашивается в другом месте) +uint8_t buffer[]; // доступ к буферу для отладки + +// ============= CRC ============= +// можно использовать встроенные функции для генерации байта CRC для ручной упаковки пакетов +uint8_t G433_crc8(uint8_t *buffer, uint8_t size); // ручной CRC8 +uint8_t G433_crc_xor(uint8_t *buffer, uint8_t size); // ручной CRC XOR ``` @@ -56,26 +77,41 @@ int getSize(); // получить размер принятых д Остальные примеры смотри в **examples**! ### Отправка ```cpp -// мелкий передатчик 3.6V SYN115 - -#define G433_BUFSIZE 50 // размер буфера -#define G433_SPEED 2000 // скорость бит/сек (минимальная) - +// ======== ПЕРЕДАТЧИК ========= +#define G433_SLOW_MODE #include -Gyver433_TX tx(2); // указали пин +Gyver433_TX<2, 20> tx; // указали пин и размер буфера void setup() { } -char data[] = "Hello from #xx"; -byte count = 0; +char data[] = "Hello from #xx"; // строка для отправки +byte count = 0; // счётчик для отправки + void loop() { + // добавляем счётчик в строку data[12] = (count / 10) + '0'; data[13] = (count % 10) + '0'; if (++count >= 100) count = 0; - tx.sendData(data); + tx.sendData(data); delay(100); } + +// ======== ПРИЁМНИК ========= +#define G433_SLOW_MODE +#include +Gyver433_RX<2, 20> rx; // указали пин и размер буфера + +void setup() { + Serial.begin(9600); +} + +void loop() { + if (rx.tickWait()) { + Serial.write(rx.buffer, rx.size); + Serial.println(); + } +} ``` ### Приём @@ -107,6 +143,7 @@ void loop() { ## Версии - v1.0 +- v1.1 - оптимизация, новый интерфейс, поддержка дешёвых синих модулей, работа в прерывании ## Баги и обратная связь diff --git a/examples/demo_rx/demo_rx.ino b/examples/demo_rx/demo_rx.ino new file mode 100644 index 0000000..f676f0c --- /dev/null +++ b/examples/demo_rx/demo_rx.ino @@ -0,0 +1,36 @@ +// крупный приёмник SYN480R [VCC: 3.3-5.5V, logic: VCC] +// или MX-RM-5V (RF-5V) [VCC: 5V, logic: 5V] + +// "медленный режим" для синих модулей SYN480R +// Объявляется перед подключением библиотеки +// Зелёным модулям не нужен! +#define G433_SLOW_MODE + +// можно указать скорость, по умолч. стоит 2000 бит/с +// рабочий диапазон: 100-8000 бит/с +//#define G433_SPEED 1000 + +#include +// Gyver433_TX<пин, буфер, CRC> +// пин: цифровой пин +// буфер: размер приёмного буфера в байтах +// CRC: проверка целостности данных: G433_CRC8 (надёжный), G433_XOR (лёгкий), G433_NOCRC (отключено). По умолч. G433_CRC8 +Gyver433_RX<2, 20> rx; // указали пин и размер буфера + +void setup() { + Serial.begin(9600); +} + +void loop() { + // "тик" опрашивает радио и вернёт количество успешно принятых байт + // tick принимает асинхронно, но может ловить ошибки при загруженном коде + // tickWait блокирует выполнение, но принимает данные чётко + + //if (rx.tick()) { + if (rx.tickWait()) { + // принятые данные доступны в .buffer + // и имеют размер .size + Serial.write(rx.buffer, rx.size); + Serial.println(); + } +} \ No newline at end of file diff --git a/examples/demo_tx/demo_tx.ino b/examples/demo_tx/demo_tx.ino new file mode 100644 index 0000000..848fd2f --- /dev/null +++ b/examples/demo_tx/demo_tx.ino @@ -0,0 +1,37 @@ +// мелкий передатчик SYN115 [VCC: 1.8-3.6V, logic: VCC] +// или FS1000A [VCC: 3-12V, logic: 5V] + +// "медленный режим" для синих модулей SYN115 +// Объявляется перед подключением библиотеки +// зелёным модулям не нужен! +#define G433_SLOW_MODE + +// можно указать скорость, по умолч. стоит 2000 бит/с +// рабочий диапазон: 100-8000 бит/с +//#define G433_SPEED 1000 + +#include +// Gyver433_TX<пин, буфер, CRC> +// пин: цифровой пин +// буфер: размер буфера в байтах. На "ручную" отправку буфер не нужен. По умолч. 64 +// CRC: проверка целостности данных: G433_CRC8 (надёжный), G433_XOR (лёгкий), G433_NOCRC (отключено). По умолч. G433_CRC8 +Gyver433_TX<2, 20> tx; // указали пин и размер буфера + +void setup() { +} + +char data[] = "Hello from #xx"; // строка для отправки +byte count = 0; // счётчик для отправки + +void loop() { + // добавляем счётчик в строку + data[12] = (count / 10) + '0'; + data[13] = (count % 10) + '0'; + if (++count >= 100) count = 0; + + // отправка данных любого типа. Блокирующая на период отправки + tx.sendData(data); + + // отправка 10 раз в сек + delay(100); +} \ No newline at end of file diff --git a/examples/isr_rx/isr_rx.ino b/examples/isr_rx/isr_rx.ino new file mode 100644 index 0000000..b59039b --- /dev/null +++ b/examples/isr_rx/isr_rx.ino @@ -0,0 +1,28 @@ +// приём в прерывании. Отправляет пример demo_tx +#define G433_SLOW_MODE + +#include +Gyver433_RX<2, 20> rx; // указали пин и размер буфера + +void setup() { + Serial.begin(9600); + // взводим прерывания по CHANGE + attachInterrupt(0, isr, CHANGE); +} + +void isr() { + rx.tick(); // тикер вызывается в прерывании +} + +void loop() { + // .gotData() вернёт true при получении корректных данных + // и сам сбросится до следующего приёма + // внутри gotData() встроен тик! + if (rx.gotData()) { + Serial.write(rx.buffer, rx.size); + Serial.println(); + } + + // имитация загруженного кода + delay(200); +} diff --git a/examples/raw_rx/raw_rx.ino b/examples/raw_rx/raw_rx.ino new file mode 100644 index 0000000..cdc4317 --- /dev/null +++ b/examples/raw_rx/raw_rx.ino @@ -0,0 +1,16 @@ +// обмен сырыми данными без CRC и буфера на отправку + +#define G433_SLOW_MODE +#include +Gyver433_RX<2, 20, G433_NOCRC> rx; // буфер на приём нужен! + +void setup() { + Serial.begin(9600); +} + +void loop() { + if (rx.tickWait()) { + Serial.write(rx.buffer, rx.size); + Serial.println(); + } +} \ No newline at end of file diff --git a/examples/raw_tx/raw_tx.ino b/examples/raw_tx/raw_tx.ino new file mode 100644 index 0000000..043327f --- /dev/null +++ b/examples/raw_tx/raw_tx.ino @@ -0,0 +1,24 @@ +// обмен сырыми данными без CRC и буфера на отправку + +#define G433_SLOW_MODE +#include +Gyver433_TX<2, 0, G433_NOCRC> tx; + +void setup() { +} + +char data[] = "Hello from #xx"; // строка для отправки +byte count = 0; // счётчик для отправки + +void loop() { + // добавляем счётчик в строку + data[12] = (count / 10) + '0'; + data[13] = (count % 10) + '0'; + if (++count >= 100) count = 0; + + // отправка данных типа byte* + tx.write(data, sizeof(data)); + + // отправка 10 раз в сек + delay(100); +} diff --git a/examples/rx433/rx433.ino b/examples/rx433/rx433.ino deleted file mode 100644 index eb3c918..0000000 --- a/examples/rx433/rx433.ino +++ /dev/null @@ -1,22 +0,0 @@ -// крупный приёмник 5.0 SYN480R - -#define G433_BUFSIZE 50 // размер буфера -#define G433_SPEED 2000 // скорость бит/сек (минимальная) - -#include -Gyver433_RX rx(2); - -void setup() { - Serial.begin(9600); -} - - -void loop() { - // tick принимает асинхронно, но может ловить ошибки при загруженном коде - // tickWait блокирует выполнение, но принимает данные чётко - if (rx.tickWait()) { - byte buf[64]; - rx.readData(buf); - for (byte i = 0; i < rx.size; i++) Serial.write(buf[i]); - } -} diff --git a/examples/rx433_lcd/rx433_lcd.ino b/examples/rx433_lcd/rx433_lcd.ino index 5ceb5be..3262288 100644 --- a/examples/rx433_lcd/rx433_lcd.ino +++ b/examples/rx433_lcd/rx433_lcd.ino @@ -1,28 +1,20 @@ -// крупный приёмник 5.0V SYN480R -#define G433_BUFSIZE 50 // размер буфера -#define G433_SPEED 2000 // скорость бит/сек (минимальная) +// выводим данные на дисплей. Отправляет пример demo_tx #include -Gyver433_RX rx(2); // указали пин +Gyver433_RX<2> rx; // указали пин -#include #include LiquidCrystal_I2C lcd(0x3f, 16, 2); // или 0x27 void setup() { - Serial.begin(9600); lcd.init(); lcd.backlight(); } void loop() { - // tick принимает асинхронно, но может ловить ошибки при загруженном коде - // tickWait блокирует выполнение, но принимает данные чётко if (rx.tick()) { - byte buf[64]; - rx.readData(buf); // прочитать в buf lcd.clear(); - lcd.home(); - for (byte i = 0; i < rx.size; i++) lcd.write(buf[i]); + lcd.home(); + for (byte i = 0; i < rx.size; i++) lcd.write(rx.buffer[i]); } } diff --git a/examples/rx433_struct/rx433_struct.ino b/examples/rx433_struct/rx433_struct.ino index 8b6cf00..316cfef 100644 --- a/examples/rx433_struct/rx433_struct.ino +++ b/examples/rx433_struct/rx433_struct.ino @@ -1,10 +1,8 @@ -// передача структуры данных -// крупный приёмник 5.0V SYN480R -#define G433_BUFSIZE 50 // размер буфера -#define G433_SPEED 2000 // скорость бит/сек (минимальная) +// приём структуры данных +#define G433_SLOW_MODE #include -Gyver433_RX rx(2); // указали пин +Gyver433_RX<2, 10> rx; // указали пин и размер буфера // формат пакета для приёма (такой же как отправляется) struct dataPack { @@ -19,11 +17,9 @@ void setup() { } void loop() { - // tick принимает асинхронно, но может ловить ошибки при загруженном коде - // tickWait блокирует выполнение, но принимает данные чётко if (rx.tick()) { - dataPack data; - rx.readData(data); // прочитать в buf + dataPack data; // "буферная" структура + rx.readData(data); // переписываем данные в неё Serial.println("Received:"); Serial.println(data.counter); diff --git a/examples/tx433/tx433.ino b/examples/tx433/tx433.ino deleted file mode 100644 index a5f8a69..0000000 --- a/examples/tx433/tx433.ino +++ /dev/null @@ -1,18 +0,0 @@ -// мелкий передатчик 3.6V SYN115 -#define G433_BUFSIZE 50 // размер буфера -#define G433_SPEED 2000 // скорость бит/сек (минимальная) -#include -Gyver433_TX tx(2); // указали пин - -void setup() { -} - -char data[] = "Hello from #xx"; -byte count = 0; -void loop() { - data[12] = (count / 10) + '0'; - data[13] = (count % 10) + '0'; - if (++count >= 100) count = 0; - tx.sendData(data); - delay(100); -} diff --git a/examples/tx433_struct/tx433_struct.ino b/examples/tx433_struct/tx433_struct.ino index 91dae0e..5c33d28 100644 --- a/examples/tx433_struct/tx433_struct.ino +++ b/examples/tx433_struct/tx433_struct.ino @@ -1,10 +1,8 @@ // передача структуры данных -// мелкий передатчик 3.6V SYN115 -#define G433_BUFSIZE 50 // размер буфера -#define G433_SPEED 2000 // скорость бит/сек (минимальная) +#define G433_SLOW_MODE #include -Gyver433_TX tx(2); // указали пин +Gyver433_TX<2, 10> tx; // указали пин и размер буфера // формат пакета для отправки struct dataPack { @@ -25,6 +23,8 @@ void loop() { data.analog = analogRead(0); // тут ацп data.time = millis(); // тут миллис + tx.sendData(data); + Serial.println("Transmit:"); Serial.println(data.counter); Serial.println(data.randomNum); @@ -32,6 +32,5 @@ void loop() { Serial.println(data.time); Serial.println(); - tx.sendData(data); delay(1000); } diff --git a/keywords.txt b/keywords.txt index ee6890d..0e7eb9a 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,14 +13,22 @@ Gyver433_RX KEYWORD1 # Methods and Functions (KEYWORD2) ####################################### sendData KEYWORD2 +write KEYWORD2 tick KEYWORD2 tickWait KEYWORD2 readData KEYWORD2 size KEYWORD2 +buffer KEYWORD2 getSize KEYWORD2 +gotData KEYWORD2 +G433_crc8 KEYWORD2 +G433_crc_xor KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### +G433_CRC8 LITERAL1 +G433_XOR LITERAL1 +G433_NOCRC LITERAL1 G433_SPEED LITERAL1 -G433_BUFSIZE LITERAL1 \ No newline at end of file +G433_SLOW_MODE LITERAL1 \ No newline at end of file diff --git a/library.properties b/library.properties index 073a5af..a48f5d0 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Gyver433 -version=1.0 +version=1.1 author=AlexGyver maintainer=AlexGyver sentence=Simple library for 433 MHz radio diff --git a/src/FastIO.h b/src/FastIO.h new file mode 100644 index 0000000..7bba4a5 --- /dev/null +++ b/src/FastIO.h @@ -0,0 +1,109 @@ +// Быстрый IO для AVR (для остальных будет digitalxxxxx) +// v1.0 + +#ifndef FastIO_h +#define FastIO_h +#include + +bool fastRead(const uint8_t pin); // быстрое чтение пина +void fastWrite(const uint8_t pin, bool val); // быстрая запись +uint8_t fastShiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); // быстрый shiftIn +void fastShiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t data); // быстрый shiftOut + +// ================================================================ +bool fastRead(const uint8_t pin) { +#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + if (pin < 8) return bitRead(PIND, pin); + else if (pin < 14) return bitRead(PINB, pin - 8); + else if (pin < 20) return bitRead(PINC, pin - 14); + +#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__) + return bitRead(PINB, pin); + +#elif defined(AVR) + uint8_t *_pin_reg = portInputRegister(digitalPinToPort(pin)); + uint8_t _bit_mask = digitalPinToBitMask(pin); + return bool(*_pin_reg & _bit_mask); + +#else + return digitalRead(pin); + +#endif + return 0; +} + + +void fastWrite(const uint8_t pin, bool val) { +#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) + if (pin < 8) bitWrite(PORTD, pin, val); + else if (pin < 14) bitWrite(PORTB, (pin - 8), val); + else if (pin < 20) bitWrite(PORTC, (pin - 14), val); + +#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__) + bitWrite(PORTB, pin, val); + +#elif defined(AVR) + uint8_t *_port_reg = portInputRegister(digitalPinToPort(pin)); + uint8_t _bit_mask = digitalPinToBitMask(pin); + _port_reg = portOutputRegister(digitalPinToPort(pin)); + _bit_mask = digitalPinToBitMask(pin); + if (val) *_port_reg |= _bit_mask; // HIGH + else *_port_reg &= ~_bit_mask; // LOW + +#else + digitalWrite(pin, val); + +#endif +} + + +uint8_t fastShiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) { +#if defined(AVR) + volatile uint8_t *_clk_port = portOutputRegister(digitalPinToPort(clockPin)); + volatile uint8_t *_dat_port = portInputRegister(digitalPinToPort(dataPin)); + uint8_t _clk_mask = digitalPinToBitMask(clockPin); + uint8_t _dat_mask = digitalPinToBitMask(dataPin); + uint8_t data = 0; + for (uint8_t i = 0; i < 8; i++) { + *_clk_port |= _clk_mask; + if (bitOrder == MSBFIRST) { + data <<= 1; + if (bool(*_dat_port & _dat_mask)) data |= 1; + } else { + data >>= 1; + if (bool(*_dat_port & _dat_mask)) data |= 1 << 7; + } + *_clk_port &= ~_clk_mask; + } + return data; +#else + return shiftIn(dataPin, clockPin, bitOrder); +#endif +} + + +void fastShiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t data) { +#if defined(AVR) + volatile uint8_t *_clk_port = portOutputRegister(digitalPinToPort(clockPin)); + volatile uint8_t *_dat_port = portOutputRegister(digitalPinToPort(dataPin)); + uint8_t _clk_mask = digitalPinToBitMask(clockPin); + uint8_t _dat_mask = digitalPinToBitMask(dataPin); + for (uint8_t i = 0; i < 8; i++) { + if (bitOrder == MSBFIRST) { + if (data & (1 << 7)) *_dat_port |= _dat_mask; + else *_dat_port &= ~_dat_mask; + data <<= 1; + } else { + if (data & 1) *_dat_port |= _dat_mask; + else *_dat_port &= ~_dat_mask; + data >>= 1; + } + *_clk_port |= _clk_mask; + *_clk_port &= ~_clk_mask; + } +#else + shiftOut(dataPin, clockPin, bitOrder, data); +#endif +} + +#endif \ No newline at end of file diff --git a/src/Gyver433.h b/src/Gyver433.h index 1449a73..69d63e0 100644 --- a/src/Gyver433.h +++ b/src/Gyver433.h @@ -6,6 +6,7 @@ - Не использует прерывания и таймеры (кроме нулевого, читает micros()) - Встроенный CRC контроль целостности - Ускоренный алгоритм IO для AVR Arduino + - Работает с хорошими и плохими 433 МГц модулями AlexGyver, alex@alexgyver.ru https://alexgyver.ru/ @@ -13,59 +14,67 @@ Версии: v1.0 - релиз + v1.1 - оптимизация, новый интерфейс, поддержка дешёвых синих модулей, работа в прерывании */ #ifndef Gyver433_h #define Gyver433_h #include +#include "FastIO.h" -/* - Передатчик: - Gyver433_TX tx(пин) - создать объект - sendData(data) - отправить, любой тип данных - - Приёмник: - Gyver433_RX rx(пин) - создать объект - tick() - вызывать постоянно для чтения. Асинхронный. Вернёт количество принятых байт - tickWait() - тож самое, но блокирует выполнение, принимает более четко - readData(data) - прочитать, любой тип данных - size - количество принятых байтов -*/ +// настройки из скетча: +// #define G433_SLOW_MODE - включить "медленный режим" для плохих модулей +// #define G433_SPEED n - скорость, бит/сек. Рекомендуется 2000. Работает вплоть до 6000 +uint8_t G433_crc8(uint8_t *buffer, uint8_t size); // ручной CRC8 +uint8_t G433_crc_xor(uint8_t *buffer, uint8_t size); // ручной CRC XOR +#define TRAINING_TIME_SLOW (500000ul) // время синхронизации для SLOW_MODE + +// ========================================================================= #ifndef G433_SPEED -#define G433_SPEED 2000 // скорость бит/сек (минимальная) -#endif -#ifndef G433_BUFSIZE -#define G433_BUFSIZE 64 // размер буфера приёма и отправки +#define G433_SPEED 3000 // скорость по умолчанию #endif -// тайминги интерфейса (компилятор посчитает) -#define HIGH_PULSE (1000000ul/G433_SPEED) -#define LOW_PULSE (HIGH_PULSE/2) -#define START_PULSE (HIGH_PULSE*2) -#define PULSE_HYST (LOW_PULSE/2) -#define START_MIN (START_PULSE-PULSE_HYST) -#define START_MAX (START_PULSE+PULSE_HYST) -#define LOW_MIN (LOW_PULSE-PULSE_HYST) -#define LOW_MAX (LOW_PULSE+PULSE_HYST) -#define HIGH_MIN (HIGH_PULSE-PULSE_HYST) -#define HIGH_MAX (HIGH_PULSE+PULSE_HYST) +// тайминги интерфейса +#define FRAME_TIME (1000000ul / G433_SPEED) // время фрейма +#define HALF_FRAME (FRAME_TIME / 2) // полфрейма +#define START_PULSE (FRAME_TIME * 2) // стартовый импульс +#define TRAINING_AMOUNT_SLOW (TRAINING_TIME_SLOW / FRAME_TIME / 2) // количество импульсов для SLOW_MODE -// crc -uint8_t G433_crc(uint8_t *buffer, uint8_t size); -void G433_crc_byte(uint8_t &crc, uint8_t data); +// количество импульсов в зависимости от SLOW_MODE +#ifdef G433_SLOW_MODE +#define TRAINING_AMOUNT 40 +#else +#define TRAINING_AMOUNT 10 +#endif + +// окно времени для обработки старта и фрейма +#define START_MIN (START_PULSE * 3 / 4) +#define START_MAX (START_PULSE * 5 / 4) +#define FRAME_MIN (FRAME_TIME * 3 / 4) +#define FRAME_MAX (FRAME_TIME * 5 / 4) + +// жоский delay для avr +#ifdef AVR +#define G433_DELAY(x) _delay_us(x) +#else +#define G433_DELAY(x) delayMicroseconds(x) +#endif + +// режимы CRC +#define G433_CRC8 0 +#define G433_XOR 1 +#define G433_NOCRC 2 + +// crc8 один байт +void G433_crc8_byte(uint8_t &crc, uint8_t data); // ============ ПЕРЕДАТЧИК ============ +template class Gyver433_TX { public: - Gyver433_TX(uint8_t pin) { -#if defined(__AVR__) - _port_reg = portOutputRegister(digitalPinToPort(pin)); - _bit_mask = digitalPinToBitMask(pin); -#else - _pin = pin; -#endif - pinMode(pin, OUTPUT); + Gyver433_TX() { + pinMode(TX_PIN, OUTPUT); } // отправка, блокирующая. Кушает любой тип данных @@ -73,195 +82,203 @@ public: void sendData(T &data) { const uint8_t *ptr = (const uint8_t*) &data; for (uint16_t i = 0; i < sizeof(T); i++) buffer[i] = *ptr++; - buffer[sizeof(T)] = G433_crc(buffer, sizeof(T)); // CRC последним байтом - bool flag = 0; // флаг дрыга - for (uint8_t i = 0; i < 30; i++) { // 30 импульсов для синхронизации - flag = !flag; - fastDW(flag); - delayMicroseconds(HIGH_PULSE); + if (CRC_MODE == G433_CRC8) { + buffer[sizeof(T)] = G433_crc8(buffer, sizeof(T)); + write(buffer, sizeof(T) + 1); + } else if (CRC_MODE == G433_XOR) { + buffer[sizeof(T)] = G433_crc_xor(buffer, sizeof(T)); + write(buffer, sizeof(T) + 1); + } else { + write(buffer, sizeof(T)); } - fastDW(1); // старт бит - delayMicroseconds(START_PULSE); // старт бит - for (int n = 0; n < sizeof(T) + 1; n++) { // буфер + CRC + } + + // отправка сырого набора байтов + void write(uint8_t* buf, uint16_t size) { + #ifdef G433_SLOW_MODE + for (uint16_t i = 0; i < ((millis() - tmr > 400) ? TRAINING_AMOUNT_SLOW : TRAINING_AMOUNT); i++) { + #else + for (uint16_t i = 0; i < TRAINING_AMOUNT; i++) { + #endif + fastWrite(TX_PIN, 1); + G433_DELAY(FRAME_TIME); + fastWrite(TX_PIN, 0); + G433_DELAY(FRAME_TIME); + } + fastWrite(TX_PIN, 1); // старт + G433_DELAY(START_PULSE); // ждём + fastWrite(TX_PIN, 0); // старт бит + G433_DELAY(HALF_FRAME); // ждём + for (uint16_t n = 0; n < size; n++) { + uint8_t data = buf[n]; for (uint8_t b = 0; b < 8; b++) { - fastDW(flag); - flag = !flag; - if (bitRead(buffer[n], b)) delayMicroseconds(HIGH_PULSE); - else delayMicroseconds(LOW_PULSE); + fastWrite(TX_PIN, !(data & 1)); + G433_DELAY(HALF_FRAME); + fastWrite(TX_PIN, (data & 1)); + G433_DELAY(HALF_FRAME); + data >>= 1; } } - fastDW(0); // передача окончена - } - -private: - void fastDW(bool state) { -#if defined(__AVR__) - if (state) *_port_reg |= _bit_mask; // HIGH - else *_port_reg &= ~_bit_mask; // LOW -#else - digitalWrite(_pin, state); -#endif - } - uint8_t buffer[G433_BUFSIZE]; - -#if defined(__AVR__) - volatile uint8_t *_port_reg; - volatile uint8_t _bit_mask; -#else - uint8_t _pin; -#endif -}; - - -// ============ ПРИЁМНИК ============ -class Gyver433_RX { -public: - Gyver433_RX(uint8_t pin) { -#if defined(__AVR__) - _pin_reg = portInputRegister(digitalPinToPort(pin)); - _bit_mask = digitalPinToBitMask(pin); -#else - _pin = pin; -#endif - } - - // неблокирующий приём, вернёт кол-во успешно принятых байт - uint8_t tick() { - bool newState = fastDR(); // читаем пин - if (newState != prevState) { // ловим изменение сигнала - uint32_t thisUs = micros(); - uint32_t thisPulse = thisUs - tmr; - if (parse == 1) { // в прошлый раз поймали фронт - if (thisPulse > START_MIN && thisPulse < START_MAX) { // старт бит? - parse = 2; // ключ на старт - tmr = thisUs; - byteCount = 0; - bitCount = 0; - size = 0; - for (uint8_t i = 0; i < G433_BUFSIZE; i++) buffer[i] = 0; - } else { // не старт бит - parse = 0; - } - } else if (parse == 2) { // идёт парсинг - if (thisPulse > LOW_MIN && thisPulse < LOW_MAX) { // low бит - // просто пропускаем (в буфере уже нули) - tmr = thisUs; - bitCount++; - if (bitCount == 8) { - bitCount = 0; - byteCount++; - if (byteCount > G433_BUFSIZE) parse = 0; // оверфлоу - } - } else if (thisPulse > HIGH_MIN && thisPulse < HIGH_MAX) { // high бит - bitSet(buffer[byteCount], bitCount); // ставим бит единичку - tmr = thisUs; - bitCount++; - if (bitCount == 8) { - bitCount = 0; - byteCount++; - if (byteCount > G433_BUFSIZE) parse = 0; // оверфлоу - } - } else { // ошибка или конец передачи - tmr = thisUs; - parse = 0; - // проверяем, есть ли данные и целые ли они - if (byteCount > 0 && G433_crc(buffer, byteCount) == 0) { - size = byteCount - 2; // длина даты (минус crc) - return size; - } - else return 0; - } - } - - if (newState && !prevState && parse == 0) { // ловим фронт - parse = 1; // в следующий раз ждём флаг - tmr = thisUs; - } - prevState = newState; + fastWrite(TX_PIN, 0); // конец передачи + #ifdef G433_SLOW_MODE + tmr = millis(); + #endif } - return 0; - } - - // блокирующий приём, вернёт кол-во успешно принятых байт - uint8_t tickWait() { - do { - tick(); - } while (parse == 2); - if (byteCount > 0) { - byteCount = 0; + + // доступ к буферу + uint8_t buffer[TX_BUF]; + + private: + #ifdef G433_SLOW_MODE + uint32_t tmr = 0; + #endif + }; + + // ============ ПРИЁМНИК ============ + template + class Gyver433_RX { + public: + Gyver433_RX() { + pinMode(RX_PIN, INPUT); + } + + // неблокирующий приём, вернёт кол-во успешно принятых байт + uint8_t tick() { + uint32_t thisPulse = micros() - tmr; // время импульса + if (parse == 2 && thisPulse >= FRAME_TIME * 2) { // фрейм не закрыт + parse = size = 0; // приём окончен + if (byteCount > 1) { // если что то приняли + if (CRC_MODE == G433_CRC8) { // CRC8 + if (!G433_crc8(buffer, byteCount)) { + size = byteCount - 2; + dataReady = 1; + } + } else if (CRC_MODE == G433_XOR) { // CRC XOR + if (!G433_crc_xor(buffer, byteCount)) { + size = byteCount - 2; + dataReady = 1; + } + } else { // без CRC + size = byteCount - 1; + dataReady = 1; + } + } + return size; + } + bool bit = fastRead(RX_PIN); // читаем пин + if (bit != prevBit) { // ловим изменение сигнала + if (parse == 1) { // в прошлый раз поймали фронт + tmr += thisPulse; // сброс таймера + if (thisPulse > START_MIN && thisPulse < START_MAX) { // старт бит? + parse = 2; // ключ на старт + byteCount = bitCount = size = 0; // сброс + dataReady = 0; + for (uint8_t i = 0; i < RX_BUF; i++) buffer[i] = 0; // чистим буфер + } else parse = 0; // не старт бит + } else if (parse == 2) { // идёт парсинг + if (thisPulse > FRAME_MIN && thisPulse < FRAME_MAX) { // фронт внутри таймфрейма + tmr += thisPulse; // синхронизируем тайминги + buffer[byteCount] >>= 1; // двигаем байт + if (bit && !prevBit) buffer[byteCount] |= _BV(7); // пишем единичку + bitCount++; // счётчик битов + } + if (bitCount == 8) { // собрали байт + bitCount = 0; // сброс + if (++byteCount >= RX_BUF) parse = 0; // буфер переполнен + } + } + + if (bit && !prevBit && parse == 0) { // ловим фронт + parse = 1; // флаг на старт + tmr += thisPulse; // сброс таймера + } + prevBit = bit; + } + return 0; + } + + // блокирующий приём, вернёт кол-во успешно принятых байт + uint8_t tickWait() { + do { + if (tick()) return size; + } while (parse == 2); + return 0; + } + + // прочитает буфер в любой тип данных + template + bool readData(T &data) { + if (sizeof(T) > RX_BUF) return false; + uint8_t *ptr = (uint8_t*) &data; + for (uint16_t i = 0; i < sizeof(T); i++) *ptr++ = buffer[i]; + return true; + } + + // вернёт true при получении корректных данных + bool gotData() { + tick(); + if (dataReady) { + dataReady = 0; + return 1; + } return 0; + } + + // получить размер принятых данных + int getSize() { return size; - } else return 0; - } - - // прочитает буфер в любой тип данных - template - bool readData(T &data) { - if (sizeof(T) > G433_BUFSIZE) return false; - uint8_t *ptr = (uint8_t*) &data; - for (uint16_t i = 0; i < sizeof(T); i++) *ptr++ = buffer[i]; - return true; - } - - // получить размер принятых данных - int getSize() { - return size; - } - - int size = 0; - -private: - bool fastDR() { -#if defined(__AVR__) - return bool(*_pin_reg & _bit_mask); -#else - return digitalRead(_pin); -#endif - } - uint8_t buffer[G433_BUFSIZE]; - bool prevState; - uint8_t parse = 0; - uint32_t tmr = 0; - uint8_t bitCount = 0, byteCount = 0; -#if defined(__AVR__) - volatile uint8_t *_pin_reg; - volatile uint8_t _bit_mask; -#else - uint8_t _pin; -#endif -}; + } + + // размер принятых данных + int size = 0; + + // доступ к буферу + uint8_t buffer[RX_BUF]; + + private: + bool prevBit, dataReady = 0; + uint8_t parse = 0; + uint32_t tmr = 0; + uint8_t bitCount = 0, byteCount = 0; + }; -void G433_crc_byte(uint8_t &crc, uint8_t data) { + // ===== CRC ===== + void G433_crc8_byte(uint8_t &crc, uint8_t data) { #if defined (__AVR__) - // резкий алгоритм для AVR - uint8_t counter; - uint8_t buffer; - asm volatile ( - "EOR %[crc_out], %[data_in] \n\t" - "LDI %[counter], 8 \n\t" - "LDI %[buffer], 0x8C \n\t" - "_loop_start_%=: \n\t" - "LSR %[crc_out] \n\t" - "BRCC _loop_end_%= \n\t" - "EOR %[crc_out], %[buffer] \n\t" - "_loop_end_%=: \n\t" - "DEC %[counter] \n\t" - "BRNE _loop_start_%=" - : [crc_out]"=r" (crc), [counter]"=d" (counter), [buffer]"=d" (buffer) - : [crc_in]"0" (crc), [data_in]"r" (data) - ); + // резкий алгоритм для AVR + uint8_t counter; + uint8_t buffer; + asm volatile ( + "EOR %[crc_out], %[data_in] \n\t" + "LDI %[counter], 8 \n\t" + "LDI %[buffer], 0x8C \n\t" + "_loop_start_%=: \n\t" + "LSR %[crc_out] \n\t" + "BRCC _loop_end_%= \n\t" + "EOR %[crc_out], %[buffer] \n\t" + "_loop_end_%=: \n\t" + "DEC %[counter] \n\t" + "BRNE _loop_start_%=" + : [crc_out]"=r" (crc), [counter]"=d" (counter), [buffer]"=d" (buffer) + : [crc_in]"0" (crc), [data_in]"r" (data) + ); #else - // обычный для всех остальных - uint8_t i = 8; - while (i--) { - crc = ((crc ^ data) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1); - data >>= 1; - } + // обычный для всех остальных + uint8_t i = 8; + while (i--) { + crc = ((crc ^ data) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1); + data >>= 1; + } #endif -} + } -uint8_t G433_crc(uint8_t *buffer, uint8_t size) { - uint8_t crc = 0; - for (uint8_t i = 0; i < size; i++) G433_crc_byte(crc, buffer[i]); - return crc; -} + uint8_t G433_crc8(uint8_t *buffer, uint8_t size) { + uint8_t crc = 0; + for (uint8_t i = 0; i < size; i++) G433_crc8_byte(crc, buffer[i]); + return crc; + } + uint8_t G433_crc_xor(uint8_t *buffer, uint8_t size) { + uint8_t crc = 0; + for (uint8_t i = 0; i < size; i++) crc ^= buffer[i]; + return crc; + } #endif \ No newline at end of file