/* Copyright 2022, Savanni D'Gerinel This file is part of Savanni's AVR library. Lumeto is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Lumeto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Lumeto. If not, see . */ #include #define REG_FIFO 0x00 #define REG_OP_MODE 0x01 #define REG_DATA_MODULE 0x02 #define REG_VERSION 0x10 #define REG_RSSI_VALUE 0x24 #define REG_DIO_MAPPING1 0x25 #define REG_IRQ_FLAGS1 0x27 #define REG_IRQ_FLAGS2 0x28 #define REG_SYNC_CONFIG 0x2e #define REG_SYNC_VALUE1 0x2f #define REG_PACKET_CONFIG1 0x37 #define REG_FIFO_THRESH 0x3c #define REG_TEST_PA1 0x5a #define REG_TEST_PA2 0x5c #define PA1_LOW_POWER 0x55 #define PA2_LOW_POWER 0x70 void _rfm_write(rfm_t *rfm, uint8_t reg, uint8_t *data, size_t length) { spi_acquire(&rfm->spi); spi_transceive(&rfm->spi, _BV(7) | reg); for (size_t i = 0; i < length; i++) { spi_transceive(&rfm->spi, data[i]); } spi_release(&rfm->spi); } void _rfm_read(rfm_t *rfm, uint8_t reg, uint8_t *data, size_t length) { spi_acquire(&rfm->spi); spi_transceive(&rfm->spi, reg); for (size_t i = 0; i < length; i++) { data[i] = spi_transceive(&rfm->spi, 0); } spi_release(&rfm->spi); } void _rfm_set_low_power(rfm_t *rfm) { _rfm_write(rfm, REG_TEST_PA1, (uint8_t [1]){ PA1_LOW_POWER }, 1); _rfm_write(rfm, REG_TEST_PA2, (uint8_t [1]){ PA2_LOW_POWER }, 1); } void rfm_init(rfm_t *rfm, uint8_t *sync_word, size_t length, rfm_error_e *error) { if (!error) return; spi_initialize(&rfm->spi); set_line_direction(&rfm->irq, LINE_IN); set_line_direction(&rfm->reset, LINE_OUT); rfm_reset(rfm); rfm_sleep(rfm); uint8_t version; _rfm_read(rfm, REG_VERSION, &version, 1); if (version != 0x24) { *error = radio_not_found; return; } rfm_set_mode(rfm, (op_mode_t){ .listen_on = false, .mode = standby }); _rfm_write(rfm, REG_FIFO_THRESH, (uint8_t [1]){ 0x8f }, 1); _rfm_set_low_power(rfm); _rfm_write(rfm, REG_PACKET_CONFIG1, (uint8_t [1]){ _BV(7) /* variable length packets */ | _BV(6) /* DC whitening */ | _BV(4) /* CRC on */ | _BV(1) /* packet filtering requires nodeaddress */ }, 1); _rfm_write(rfm, REG_SYNC_CONFIG, (uint8_t [1]){ _BV(7) | (length << 3) }, 1); uint8_t word_base = REG_SYNC_VALUE1; for (int i = 0; i < length; i++) { _rfm_write(rfm, word_base + i, &sync_word[i], length); } } void rfm_reset(rfm_t *rfm) { set_line(&rfm->reset); _delay_us(100); clear_line(&rfm->reset); _delay_us(5); } void rfm_packet_format(rfm_t *rfm, packet_format_e format, size_t length) { switch (format) { case unlimited: _rfm_write(rfm, 0x37, (uint8_t [1]){ 0 }, 1); _rfm_write(rfm, 0x38, (uint8_t [1]){ 0 }, 1); break; case fixed: _rfm_write(rfm, 0x37, (uint8_t [1]){ 0 }, 1); _rfm_write(rfm, 0x38, (uint8_t [1]){ length }, 1); break; case variable: _rfm_write(rfm, 0x37, (uint8_t [1]){ _BV(7) }, 1); _rfm_write(rfm, 0x38, (uint8_t [1]){ 0 }, 1); break; } } void rfm_sleep(rfm_t *rfm) { rfm_set_mode(rfm, (op_mode_t){ .listen_on = false, .mode = sleep }); } void rfm_standby(rfm_t *rfm) { rfm_set_mode(rfm, (op_mode_t){ .listen_on = false, .mode = standby }); } void rfm_transmit(rfm_t *rfm, uint8_t *data, size_t length) { rfm_set_mode(rfm, (op_mode_t){ .listen_on = false, .mode = standby }); _rfm_write(rfm, REG_FIFO, data, length); _rfm_write(rfm, REG_DIO_MAPPING1, (uint8_t [1]){ 0x00 }, 1); rfm_set_mode(rfm, (op_mode_t){ .listen_on = false, .mode = tx }); } void rfm_receive_mode(rfm_t *rfm) { _rfm_write(rfm, REG_DIO_MAPPING1, (uint8_t [1]){ _BV(6) }, 1); rfm_set_mode(rfm, (op_mode_t){ .listen_on = false, .mode = rx }); } void rfm_receive(rfm_t *rfm, uint8_t *data, size_t length) { _rfm_write(rfm, REG_DIO_MAPPING1, (uint8_t [1]){ _BV(6) }, 1); rfm_set_mode(rfm, (op_mode_t){ .listen_on = false, .mode = rx }); _rfm_read(rfm, 0x00, data, length); } uint8_t rfm_temperature(rfm_t *rfm) { uint8_t ready = 0; uint8_t temp; _rfm_write(rfm, 0x4e, (uint8_t [1]){ _BV(3) }, 1); _delay_ms(1); uint8_t i = 0; while (!(ready & _BV(2)) && i < 10) { _rfm_read(rfm, 0x4e, &ready, 1); i++; _delay_us(100); } _rfm_read(rfm, 0x4f, &temp, 1); return temp; } op_mode_t rfm_mode(rfm_t *rfm) { op_mode_t op_mode = { .listen_on = false, .mode = sleep, }; uint8_t flags; _rfm_read(rfm, 0x01, &flags, 1); if (flags & _BV(6)) op_mode.listen_on = 1; op_mode.mode = (flags & 0b00011100) >> 2; return op_mode; } void rfm_set_mode(rfm_t *rfm, op_mode_t mode) { op_mode_t current = rfm_mode(rfm); uint8_t flags = mode.mode << 2; if (mode.listen_on) { flags |= _BV(6); } else if (current.listen_on) { flags |= _BV(5); _rfm_write(rfm, REG_OP_MODE, (uint8_t [1]){ flags }, 1); flags = flags & ~_BV(5); } _rfm_write(rfm, REG_OP_MODE, (uint8_t [1]){ flags }, 1); } uint8_t rfm_status(rfm_t *rfm) { uint8_t status_flag; _rfm_read(rfm, 0x28, &status_flag, 1); return status_flag; } uint32_t rfm_frequency(rfm_t *rfm) { uint8_t frequency_bytes[3]; uint32_t frequency; _rfm_read(rfm, 0x07, &frequency_bytes[2], 1); _rfm_read(rfm, 0x08, &frequency_bytes[1], 1); _rfm_read(rfm, 0x09, &frequency_bytes[0], 1); frequency = frequency_bytes[2]; frequency = frequency << 8; frequency += frequency_bytes[1]; frequency = frequency << 8; frequency += frequency_bytes[0]; return frequency * 61; } interrupt_flags_t rfm_interrupts(rfm_t *rfm) { uint8_t irq_1; uint8_t irq_2; interrupt_flags_t flags = { .mode_ready = false, .rx_ready = false, .tx_ready = false, .timeout = false, .auto_mode = false, .sync_addr_match = false, .fifo_full = false, .fifo_not_empty = false, .fifo_level = false, .fifo_overrun = false, .packet_sent = false, .payload_ready = false, .crc_ok = false, }; _rfm_read(rfm, REG_IRQ_FLAGS1, &irq_1, 1); _rfm_read(rfm, REG_IRQ_FLAGS2, &irq_2, 1); if (irq_1 & _BV(7)) flags.mode_ready = true; if (irq_1 & _BV(6)) flags.rx_ready = true; if (irq_1 & _BV(5)) flags.tx_ready = true; if (irq_1 & _BV(2)) flags.timeout = true; if (irq_1 & _BV(1)) flags.auto_mode = true; if (irq_1 & _BV(0)) flags.sync_addr_match = true; if (irq_2 & _BV(7)) flags.fifo_full = true; if (irq_2 & _BV(6)) flags.fifo_not_empty = true; if (irq_2 & _BV(5)) flags.fifo_level = true; if (irq_2 & _BV(4)) flags.fifo_overrun = true; if (irq_2 & _BV(3)) flags.packet_sent = true; if (irq_2 & _BV(2)) flags.payload_ready = true; if (irq_2 & _BV(1)) flags.crc_ok = true; return flags; }