/* Библиотека для радиомодулей 433 МГц и Arduino Документация: GitHub: https://github.com/GyverLibs/Gyver433 Возможности: - Не использует прерывания и таймеры (кроме нулевого, читает micros()) - Встроенный CRC контроль целостности - Ускоренный алгоритм IO для AVR Arduino - Работает с хорошими и плохими 433 МГц модулями AlexGyver, alex@alexgyver.ru https://alexgyver.ru/ MIT License Версии: v1.0 - релиз v1.1 - оптимизация, новый интерфейс, поддержка дешёвых синих модулей, работа в прерывании */ #ifndef Gyver433_h #define Gyver433_h #include #include "FastIO.h" // настройки из скетча: // #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 3000 // скорость по умолчанию #endif // тайминги интерфейса #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 // количество импульсов в зависимости от 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() { pinMode(TX_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++; 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)); } } // отправка сырого набора байтов 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++) { fastWrite(TX_PIN, !(data & 1)); G433_DELAY(HALF_FRAME); fastWrite(TX_PIN, (data & 1)); G433_DELAY(HALF_FRAME); data >>= 1; } } fastWrite(TX_PIN, 0); // конец передачи #ifdef G433_SLOW_MODE tmr = millis(); #endif } // доступ к буферу 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); } // неблокирующий приём, вернёт кол-во успешно принятых байт uint16_t tick() { checkState(); return checkEnd(); } // tick для вызова в прерывании void tickISR() { checkState(); } // блокирующий приём, вернёт кол-во успешно принятых байт uint16_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() { return checkEnd(); } // получить размер принятых данных uint16_t getSize() { return size; } // размер принятых данных uint16_t size = 0; // доступ к буферу uint8_t buffer[RX_BUF]; private: void checkState() { bool bit = fastRead(RX_PIN); // читаем пин if (bit != prevBit) { // ловим изменение сигнала uint32_t thisPulse = micros() - tmr; // время импульса 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; } } uint16_t checkEnd() { if (parse == 2 && micros() - tmr >= FRAME_TIME * 2) { // фрейм не закрыт parse = size = 0; // приём окончен if (byteCount > 1) { // если что то приняли if (CRC_MODE == G433_CRC8) { // CRC8 if (!G433_crc8(buffer, byteCount)) size = byteCount - 2; } else if (CRC_MODE == G433_XOR) { // CRC XOR if (!G433_crc_xor(buffer, byteCount)) size = byteCount - 2; } else size = byteCount - 1; // без CRC } return size; } return 0; } bool prevBit, dataReady = 0; uint8_t parse = 0; uint32_t tmr = 0; uint8_t bitCount = 0, byteCount = 0; }; // ===== 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) ); #else // обычный для всех остальных uint8_t i = 8; while (i--) { crc = ((crc ^ data) & 1) ? (crc >> 1) ^ 0x8C : (crc >> 1); data >>= 1; } #endif } 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