/* Библиотека для радиомодулей 433 МГц и Arduino Документация: GitHub: https://github.com/GyverLibs/Gyver433 Возможности: - Не использует прерывания и таймеры (кроме нулевого, читает micros()) - Встроенный CRC контроль целостности - Ускоренный алгоритм IO для AVR Arduino AlexGyver, alex@alexgyver.ru https://alexgyver.ru/ MIT License Версии: v1.0 - релиз */ #ifndef Gyver433_h #define Gyver433_h #include /* Передатчик: Gyver433_TX tx(пин) - создать объект sendData(data) - отправить, любой тип данных Приёмник: Gyver433_RX rx(пин) - создать объект tick() - вызывать постоянно для чтения. Асинхронный. Вернёт количество принятых байт tickWait() - тож самое, но блокирует выполнение, принимает более четко readData(data) - прочитать, любой тип данных size - количество принятых байтов */ #ifndef G433_SPEED #define G433_SPEED 2000 // скорость бит/сек (минимальная) #endif #ifndef G433_BUFSIZE #define G433_BUFSIZE 64 // размер буфера приёма и отправки #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) // crc uint8_t G433_crc(uint8_t *buffer, uint8_t size); void G433_crc_byte(uint8_t &crc, uint8_t data); // ============ ПЕРЕДАТЧИК ============ class Gyver433_TX { public: Gyver433_TX(uint8_t pin) : _pin(pin) { #if defined(__AVR__) _port_reg = portOutputRegister(digitalPinToPort(pin)); _bit_mask = digitalPinToBitMask(pin); #endif pinMode(pin, OUTPUT); } // отправка, блокирующая. Кушает любой тип данных template 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); } fastDW(1); // старт бит delayMicroseconds(START_PULSE); // старт бит for (int n = 0; n < sizeof(T) + 1; n++) { // буфер + CRC for (uint8_t b = 0; b < 8; b++) { fastDW(flag); flag = !flag; if (bitRead(buffer[n], b)) delayMicroseconds(HIGH_PULSE); else delayMicroseconds(LOW_PULSE); } } 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]; const uint8_t _pin; #if defined(__AVR__) volatile uint8_t *_port_reg; volatile uint8_t _bit_mask; #endif }; // ============ ПРИЁМНИК ============ class Gyver433_RX { public: Gyver433_RX(uint8_t pin){ #if defined(__AVR__) _pin_reg = portInputRegister(digitalPinToPort(pin)); _bit_mask = digitalPinToBitMask(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; } return 0; } // блокирующий приём, вернёт кол-во успешно принятых байт uint8_t tickWait() { do { tick(); } while (parse == 2); if (byteCount > 0) { byteCount = 0; 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; #endif }; void G433_crc_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) ); #else // обычный для всех остальных 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; } #endif