diff --git a/CMakeLists.txt b/CMakeLists.txt index 617774e..923073c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ add_compile_definitions( DEBUG TARGETS=stm32/f1 ) -add_executable(lora-car src/main.c) +add_executable(lora-car src/main.c src/sx1276.c src/util.c) set_target_properties(lora-car PROPERTIES SUFFIX .elf) @@ -34,5 +34,5 @@ add_subdirectory(RTT) target_link_libraries(lora-car PUBLIC RTT) target_include_directories(lora-car PUBLIC - RTT ${LIBOPENCM3_DIR}/include include + RTT ${LIBOPENCM3_DIR}/include src ) diff --git a/README.md b/README.md index 1a679d9..9f281b1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,32 @@ # RC Car using LoRa -### Print libopencm3 variables +## Build + +First time: + +0. `git submodule update --init` +1. `make -C libopencm3` +2. `cmake -S . --preset=default` + +or older cmake: + +2. `cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=toolchain-STM32F1.cmake` + +And then to build an `.elf` + +0. `cmake --build build --verbose` + + +## RTT debug console + +run this while `openocd` is running + + while true; do if nc -z localhost 9090; then telnet localhost 9090; else sleep 1; fi; done + +## Print libopencm3 variables make -f print_vars.mk print_genlink_vars -### Build linker file +## Build linker file make -f print_vars.mk generated.STM32F103C8T6.ld diff --git a/datasheets/DS_SX1276-7-8-9_W_APP_V7.pdf b/datasheets/DS_SX1276-7-8-9_W_APP_V7.pdf new file mode 100644 index 0000000..6d04c2c Binary files /dev/null and b/datasheets/DS_SX1276-7-8-9_W_APP_V7.pdf differ diff --git a/datasheets/maplemini.pdf b/datasheets/maplemini.pdf new file mode 100644 index 0000000..95952f0 Binary files /dev/null and b/datasheets/maplemini.pdf differ diff --git a/src/delay.h b/src/delay.h new file mode 100644 index 0000000..e259fad --- /dev/null +++ b/src/delay.h @@ -0,0 +1,143 @@ +/* + * This file is part of the HAL project, inline library above libopencm3. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +/** @defgroup DELAY_module DELAY module + * + * @brief Spin-waiting blocking API + * + * @ingroup modules + * + * LGPL License Terms @ref lgpl_license + */ +#ifndef HAL_DELAY_H_INCLUDED +#define HAL_DELAY_H_INCLUDED + +#include +#include + +/**@{*/ + +/*****************************************************************************/ +/* API definitions */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* API Functions */ +/*****************************************************************************/ + +BEGIN_DECLS + +/*---------------------------------------------------------------------------*/ +/** @brief Spin-wait delay, spinning specified amount of processor cycles + * + * @note this function can be used for delays of max 2500000 cycles. + * For larger delays, please consider using timers or other waiting techniques. + * + * @param[in] cycles Cycles count need to spent in spin-wait + */ +static void delay_cycles(const int64_t cycles); + +/*---------------------------------------------------------------------------*/ +/** @brief Spin-wait delay, spinning specified amount of microseconds + * + * @note this function can be used for delays max 25 sec @ 168MHz CPU clock, or + * max 525 sec @ 8MHz CPU clock! For larger delays, please consider using + * timers or other waiting techniques. + * + * @param[in] us Microseconds needed to spin wait. + * @param[in] cpufreq Current CPU frequency in Hz + */ +static void delay_us(uint32_t us); + +/*---------------------------------------------------------------------------*/ +/** @brief Spin-wait delay, spinning specified amount of microseconds + * + * @note this function can be used for delays max 25 sec @ 168MHz CPU clock, or + * max 525 sec @ 8MHz CPU clock! For larger delays, please consider using + * timers or other waiting techniques. + * + * @param[in] ms Milliseconds needed to spin wait. + * @param[in] cpufreq Current CPU frequency in Hz + */ +static void delay_ms(uint32_t ms); + +END_DECLS + +/**@}*/ + +/*****************************************************************************/ +/* Architecture dependent implementations */ +/*****************************************************************************/ + +static void _delay_3t(uint32_t cycles) __attribute__((naked)); + +/* 3 Tcyc per tick, 4Tcyc call/ret, 1Tcyc hidden reg assignment */ +static void _delay_3t(uint32_t cycles) +{ + asm __volatile__ ( + ".syntax unified\n" + "1: \n" + " subs %[cyc],#1 \n" /* 1Tck */ + " bne 1b \n" /* 2Tck */ + " bx lr \n" + ".syntax divided\n" + : /* No output */ + : [cyc] "r" (cycles) + : /* No memory */ + ); +} + + +static inline __attribute__((always_inline)) void delay_cycles(const int64_t cycles) +{ + if (cycles <= 0) + return; + + switch (cycles % 3) { + default: + case 0: break; + case 1: asm __volatile__ ("nop"); break; + case 2: asm __volatile__ ("nop\nnop"); break; + } + + if (cycles > 3) + _delay_3t((uint32_t)(cycles / 3)); + else /* same delay as the function call */ + asm __volatile__ ("nop\nnop\nnop\nnop\nnop\nnop\n"); +} + + +/* max 25 sec @ 168MHz! */ +/* max 525 sec @ 8MHz! */ +static inline __attribute__((always_inline)) void delay_us(uint32_t us) +{ + if (us == 0) + return; + + delay_cycles(us * rcc_ahb_frequency / 1000000 - 6); +} + +/* max 25 sec @ 168MHz! */ +/* max 525 sec @ 8MHz! */ +static inline __attribute__((always_inline)) void delay_ms(uint32_t ms) +{ + if (ms == 0) + return; + + delay_cycles(ms * rcc_ahb_frequency / 1000 - 6); +} + +#endif /* HAL_DELAY_H_INCLUDED */ diff --git a/src/main.c b/src/main.c index 33c14ce..67afe35 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,325 @@ -int main() { +#include + +#include +#include +#include + +#include +#include + +#include "delay.h" +#include "util.h" + +#define JOY_PORT GPIOB +#define JOY_UP GPIO7 +#define JOY_DOWN GPIO6 +#define JOY_LEFT GPIO5 +#define JOY_RIGHT GPIO4 + + +#define AIN1_PORT GPIOB +#define AIN1_PIN GPIO14 +#define AIN2_PORT GPIOB +#define AIN2_PIN GPIO15 + +#define BIN1_PORT GPIOB +#define BIN1_PIN GPIO12 +#define BIN2_PORT GPIOB +#define BIN2_PIN GPIO13 + + +uint8_t FIFO[FIFO_SIZE] = {0}; + +static void clock_setup(void) +{ + rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]); + + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + + rcc_periph_clock_enable(RCC_SPI1); +} + +static void gpio_setup(void) +{ + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, + GPIO0 | GPIO1 | AIN1_PIN | AIN2_PIN | BIN1_PIN | BIN2_PIN); + + gpio_set_mode(JOY_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, + JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT); + gpio_set(JOY_PORT, JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT); +} + +static void spi_setup(void) +{ + /* Configure GPIOs: SS=PA4, SCK=PA5, MISO=PA6 and MOSI=PA7 */ + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, + GPIO5 | GPIO7 ); + + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO4); + gpio_set(GPIOA, GPIO4); + + gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, + GPIO6); + + /* Reset SPI, SPI_CR1 register cleared, SPI is disabled */ + rcc_periph_reset_pulse(RST_SPI1); + + + /* Set up SPI in Master mode with: + * Clock baud rate: 1/64 of peripheral clock frequency + * Clock polarity: Idle Low + * Clock phase: Data valid on 1st clock pulse + * Data frame format: 8-bit + * Frame format: MSB First + */ + spi_init_master(SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_64, + SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, + SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); + + /* + * Set NSS management to software. + * + * Note: + * Setting nss high is very important, even if we are controlling the GPIO + * ourselves this bit needs to be at least set to 1, otherwise the spi + * peripheral will not send any data out. + */ + spi_enable_software_slave_management(SPI1); + spi_set_nss_high(SPI1); + + /* Enable SPI1 periph. */ + spi_enable(SPI1); +} + +static void print_status(void) { + int dio = gpio_get(GPIOA, GPIO2 | GPIO3); + SEGGER_RTT_printf(0, "DIO5: %d DIO0: %d ", (dio&GPIO3)?1:0, (dio&GPIO2)?1:0); + int ret = sx1276_read(SX1276_REG_OP_MODE); + SEGGER_RTT_printf(0, "MOD: 0x%02x ", ret); + ret = sx1276_read(SX1276_REG_MODEM_STATUS); + SEGGER_RTT_printf(0, "STAT: 0x%02x ", ret); + ret = sx1276_read(SX1276_REG_IRQ_FLAGS); + SEGGER_RTT_printf(0, "IRQ: 0x%02x ", ret); + ret = sx1276_read(SX1276_REG_FIFO_RX_CURRENT_ADDR); + SEGGER_RTT_printf(0, "FIFO_RX_START: 0x%02x ", ret); + ret = sx1276_read(SX1276_REG_FIFO_RX_BYTE_PTR); + SEGGER_RTT_printf(0, "FIFO_RX_END: 0x%02x\n", ret); +} + + +static uint8_t handle_irq_flags(void) { + uint8_t irq_flags = sx1276_read(SX1276_REG_IRQ_FLAGS); + + if (irq_flags & SX1267_LORA_IRQ_RX_TIMEOUT) { + sx1276_write(SX1276_REG_IRQ_FLAGS, SX1267_LORA_IRQ_RX_TIMEOUT); + SEGGER_RTT_printf(0," RX_TIMEOUT "); + } + if (irq_flags & SX1267_LORA_IRQ_RX_DONE) { + sx1276_write(SX1276_REG_IRQ_FLAGS, SX1267_LORA_IRQ_RX_DONE); + //SEGGER_RTT_printf(0, " RX_DONE "); + } + if (irq_flags & SX1267_LORA_IRQ_PAYLOAD_CRC_ERR) { + sx1276_write(SX1276_REG_IRQ_FLAGS, SX1267_LORA_IRQ_PAYLOAD_CRC_ERR); + SEGGER_RTT_printf(0, " RX_CRC_ERR "); + } + if (irq_flags & SX1267_LORA_IRQ_VALID_HEADER) { + sx1276_write(SX1276_REG_IRQ_FLAGS, SX1267_LORA_IRQ_VALID_HEADER); + //SEGGER_RTT_printf(0, " RX_VALID_HEADER "); + } + if (irq_flags & SX1267_LORA_IRQ_TX_DONE) { + sx1276_write(SX1276_REG_IRQ_FLAGS, SX1267_LORA_IRQ_TX_DONE); + //SEGGER_RTT_printf(0, " TX_DONE "); + } + if (irq_flags) { + //SEGGER_RTT_printf(0, "\n"); + } + return irq_flags; +} + +static void send(struct sx1276_state_st *state, uint8_t *msg, size_t msg_len) { + gpio_set(GPIOB, GPIO1); + + memcpy(state->fifo, msg, msg_len); + sx1276_fifo_load(state); + sx1276_fifo_dump(state); + //hexdump((char *)state->fifo, msg_len, 8); + + sx1276_write(SX1276_REG_PAYLOAD_LENGTH, msg_len); + + sx1276_write(SX1276_REG_DIO_MAPPING_1, 0b01000000); // configure DIO0 as TX done flag + + print_status(); + sx1276_write(SX1276_REG_OP_MODE, SX1276_MODE_LONG_RANGE_MODE | SX1276_MODE_TX); + + for (int i = 0; i < 10; ++i) { + print_status(); + if (handle_irq_flags() & SX1267_LORA_IRQ_TX_DONE) { // tx done flag check + break; + } + delay_ms(1); + } + + print_status(); + sx1276_write(SX1276_REG_DIO_MAPPING_1, 0b00000000); // configure DIO0 as RX done flag + //SEGGER_RTT_printf(0, "Done\n"); + + gpio_clear(GPIOB, GPIO1); + +} + + +typedef enum { + MOTOR_BRAKE, + MOTOR_CCW, + MOTOR_CW, + MOTOR_STOP, +} motor_state_t; + + +static void drive_b(motor_state_t state) { + switch (state) { + case MOTOR_BRAKE: + gpio_set(BIN1_PORT, BIN1_PIN); + gpio_set(BIN2_PORT, BIN2_PIN); + break; + case MOTOR_CCW: + gpio_clear(BIN1_PORT, BIN1_PIN); + gpio_set(BIN2_PORT, BIN2_PIN); + break; + case MOTOR_CW: + gpio_set(BIN1_PORT, BIN1_PIN); + gpio_clear(BIN2_PORT, BIN2_PIN); + break; + case MOTOR_STOP: + gpio_clear(BIN1_PORT, BIN1_PIN); + gpio_clear(BIN2_PORT, BIN2_PIN); + break; + } +} + +static void drive_a(motor_state_t state) { + switch (state) { + case MOTOR_BRAKE: + gpio_set(AIN1_PORT, AIN1_PIN); + gpio_set(AIN2_PORT, AIN2_PIN); + break; + case MOTOR_CCW: + gpio_clear(AIN1_PORT, AIN1_PIN); + gpio_set(AIN2_PORT, AIN2_PIN); + break; + case MOTOR_CW: + gpio_set(AIN1_PORT, AIN1_PIN); + gpio_clear(AIN2_PORT, AIN2_PIN); + break; + case MOTOR_STOP: + gpio_clear(AIN1_PORT, AIN1_PIN); + gpio_clear(AIN2_PORT, AIN2_PIN); + break; + } +} + +static void receive(struct sx1276_state_st *state) { + gpio_set(GPIOB, GPIO1); + + //print_status(); + sx1276_write(SX1276_REG_PAYLOAD_LENGTH, 5); + sx1276_write(SX1276_REG_OP_MODE, SX1276_MODE_LONG_RANGE_MODE | SX1276_MODE_RX_CONTINUOUS); + //for (int i = 0; i < 10; ++i) { + while(1){ + //print_status(); + if (handle_irq_flags() & ( + SX1267_LORA_IRQ_RX_TIMEOUT | + SX1267_LORA_IRQ_RX_DONE | + SX1267_LORA_IRQ_PAYLOAD_CRC_ERR + )) + { + sx1276_fifo_dump(state); + //hexdump((char *)state->fifo, 4, 16); + if (state->fifo[0] == 0xde && state->fifo[1] == 0xad && state->fifo[2] == 0xbe) { + SEGGER_RTT_printf(0, "MOTOR: %x, %x\n", state->fifo[3], state->fifo[4]); + drive_b(state->fifo[3]); + drive_a(state->fifo[4]); + } else { + //hexdump((char *)state->fifo, 5, 16); + } + break; + } + delay_ms(10); + } + + //SEGGER_RTT_printf(0, "Done\n"); + + sx1276_write(SX1276_REG_OP_MODE, SX1276_MODE_LONG_RANGE_MODE | SX1276_MODE_STDBY); + + + gpio_clear(GPIOB, GPIO1); +} + + +static uint16_t joy_state(void) { + uint32_t joy_state = ~gpio_get(JOY_PORT, JOY_UP|JOY_DOWN|JOY_LEFT|JOY_RIGHT); + uint8_t ret[2] = {MOTOR_BRAKE, MOTOR_BRAKE}; + if (joy_state & JOY_UP) { + //SEGGER_RTT_printf(0, "UP\n"); + ret[0] = MOTOR_CCW; + } + else if (joy_state & JOY_DOWN) { + //SEGGER_RTT_printf(0, "DOWN\n"); + ret[0] = MOTOR_CW; + } else { + //SEGGER_RTT_printf(0, "BREAK\n"); + ret[0] = MOTOR_BRAKE; + } + + if (joy_state & JOY_LEFT) { + SEGGER_RTT_printf(0, "LEFT\n"); + ret[1] = MOTOR_CCW; + } else if (joy_state & JOY_RIGHT) { + SEGGER_RTT_printf(0, "RIGHT\n"); + ret[1] = MOTOR_CW; + } else { + SEGGER_RTT_printf(0, "CENTER\n"); + ret[1] = MOTOR_BRAKE; + } + + return (uint16_t)(ret[0] | (ret[1] << 8)); +} + + +int main(void) +{ + clock_setup(); + spi_setup(); + gpio_setup(); + + SEGGER_RTT_WriteString(0, "SX1276 LoRa test\n"); + SEGGER_RTT_printf(0, "CPU clk: %dHz\n", rcc_ahb_frequency); + + struct sx1276_state_st *sx1276_state = sx1276_init(865200000); + + //send(sx1276_state); + + /// Transmitter code +// while(1) { +// uint16_t motorState = joy_state(); +// SEGGER_RTT_printf(0, "Motor: %x\n", motorState); +// uint8_t msg[5] = {0xde, 0xad, 0xbe, (uint8_t) (motorState & 0xff), (uint8_t)(motorState >> 8)}; +// send(sx1276_state, msg, 5); +// //for (int i = 0; i < 10; i++) { +// delay_ms(5000); +// //} +// } + + /// Receiver code + while (1) { +// char r = SEGGER_RTT_WaitKey(); +// if (r == '\n') { + receive(sx1276_state); +// } + //delay_ms(50); + } + return 0; } diff --git a/src/sx1276.c b/src/sx1276.c new file mode 100644 index 0000000..2a7bdfc --- /dev/null +++ b/src/sx1276.c @@ -0,0 +1,123 @@ +#include +#include + +#include +#include "delay.h" +#include "util.h" +#include "sx1276.h" + + +static uint8_t sx1276_spi_transaction(uint8_t reg, uint8_t val) { + uint8_t ret; + gpio_clear(SX1276_SS_PORT, SX1276_SS_PIN); + spi_send(SX1276_SPI, reg); + spi_read(SX1276_SPI); + spi_send(SX1276_SPI, val); + ret = spi_read(SX1276_SPI); + gpio_set(SX1276_SS_PORT, SX1276_SS_PIN); + return ret; +} + +uint8_t sx1276_read(uint8_t reg) { + reg &= 0x7f; // clear upper bit to make it read request + return sx1276_spi_transaction(reg, 0x00); +} + +uint8_t sx1276_write(uint8_t reg, uint8_t val) { + reg |= 0x80; // set upper bit to make it write request + return sx1276_spi_transaction(reg, val); +} + +void sx1276_fifo_dump(struct sx1276_state_st *state) { + sx1276_write(SX1276_REG_FIFO_ADDR_PTR, 0x00); + gpio_clear(SX1276_SS_PORT, SX1276_SS_PIN); + spi_send(SX1276_SPI, SX1276_REG_FIFO & 0x7f); // read + spi_read(SX1276_SPI); + for (int i=0; i < FIFO_SIZE; i++){ + spi_send(SX1276_SPI, 0x00); + int val = spi_read(SX1276_SPI); + state->fifo[i] = val; + } + gpio_set(GPIOA, GPIO4); +} + +void sx1276_fifo_load(struct sx1276_state_st *state) { + sx1276_write(SX1276_REG_FIFO_ADDR_PTR, 0x00); + gpio_clear(SX1276_SS_PORT, SX1276_SS_PIN); + spi_send(SX1276_SPI, SX1276_REG_FIFO | 0x80); // write + spi_read(SX1276_SPI); + for (int i=0; i < FIFO_SIZE; i++){ + spi_send(SX1276_SPI, state->fifo[i]); + spi_read(SX1276_SPI); + } + gpio_set(GPIOA, GPIO4); +} + +void sx1276_fifo_write(uint8_t addr, uint8_t val) { + sx1276_write(SX1276_REG_FIFO_ADDR_PTR, addr); + sx1276_write(SX1276_REG_FIFO, val); +} + +void sx1276_set_frequency(uint64_t frequency) { + uint64_t frf = ((uint64_t)frequency << 19) / 32000000; + + sx1276_write(SX1276_REG_FRF_MSB, (uint8_t)(frf >> 16)); + sx1276_write(SX1276_REG_FRF_MID, (uint8_t)(frf >> 8)); + sx1276_write(SX1276_REG_FRF_LSB, (uint8_t)(frf >> 0)); +} + +struct sx1276_state_st *sx1276_init(uint64_t frequency) { + struct sx1276_state_st *sx1276_state = (struct sx1276_state_st*) malloc(sizeof(struct sx1276_state_st)); + if (sx1276_state == NULL) { + return NULL; + } + + SEGGER_RTT_printf(0, "SX1276: Reset\n"); + gpio_clear(GPIOB, GPIO0); + delay_us(100); + gpio_set(GPIOB, GPIO0); + delay_ms(5); + + uint8_t ret = sx1276_read(SX1276_REG_VERSION); +// if (ret != 0x12) { +// SEGGER_RTT_printf(0, "Invalid Chip Version, got: 0x%02x, expected: 0x12\n", ret); +// //free(sx1276_state); +// //return NULL; +// } + SEGGER_RTT_printf(0, "Chip Version: 0x%02X\n", ret); + + uint8_t set = SX1276_MODE_LONG_RANGE_MODE | SX1276_MODE_SLEEP; + ret = sx1276_write(SX1276_REG_OP_MODE, set); + SEGGER_RTT_printf(0, "Going to LoRa SLEEP from: 0x%02X, to: 0x%02X\n", + ret, set); + + sx1276_set_frequency(frequency); + + sx1276_write(SX1276_REG_FIFO_TX_BASE_ADDR, 0); + sx1276_write(SX1276_REG_FIFO_RX_BASE_ADDR, 0); + +// ret = sx1276_read(SX1276_REG_LNA); +// set = ret | 0x03; +// sx1276_write(SX1276_REG_LNA, set); +// SEGGER_RTT_printf(0, "LNA from: 0x%02x, to: 0x%02x\n", ret, set); + +// sx1276_write(SX1276_REG_MODEM_CONFIG_3, 0x04); // LNA gain set by the internal AGC loop + //sx1276_write(SX1276_REG_PA_CONFIG, 1<<7 | 15); // select PA_BOOST pin as output + + sx1276_write(SX1276_REG_LNA, 0b11000000); // minimum strenght + sx1276_write(SX1276_REG_PA_CONFIG, 1<<7); // minimum 2dBm + + // Bw 7 125khz; CodingRate 1 4/5; implicit header mode + sx1276_write(SX1276_REG_MODEM_CONFIG_1, (7 << 4) | (1 << 1) | 1); + // SpreadingFactor 7 128 chips + sx1276_write(SX1276_REG_MODEM_CONFIG_2, (7 << 4)); + + set = SX1276_MODE_LONG_RANGE_MODE | SX1276_MODE_STDBY; + ret = sx1276_write(SX1276_REG_OP_MODE, set); + SEGGER_RTT_printf(0, "Going to LoRa STDBY from: 0x%02X, to: 0x%02x\n", + ret, set); + + // We need to wait ~250us for TS_OSC to startup before we can access FIFO and some other things + delay_us(250); + return sx1276_state; +}; diff --git a/src/sx1276.h b/src/sx1276.h new file mode 100644 index 0000000..5b40b68 --- /dev/null +++ b/src/sx1276.h @@ -0,0 +1,91 @@ +#include +#include + +#define SX1276_RESET_PORT GPIOB +#define SX1276_RESET_PIN GPIO0 +#define SX1276_SPI SPI1 +#define SX1276_SS_PORT GPIOA +#define SX1276_SS_PIN GPIO4 + +enum sx1276_reg { + SX1276_REG_FIFO = 0x00, // FIFO read/write access + SX1276_REG_OP_MODE = 0x01, // Operating mode & LoRa (1) / FSK (0) selection + SX1276_REG_FRF_MSB = 0x06, // RF Carrier Frequency, Most Significant Bits + SX1276_REG_FRF_MID = 0x07, // RF Carrier Frequency, Intermediate Bits + SX1276_REG_FRF_LSB = 0x08, // RF Carrier Frequency, Least Significant Bits + SX1276_REG_PA_CONFIG = 0x09, // PA selection and Output Power control + SX1276_REG_PA_RAMP = 0x0a, // Control of PA ramp time, low phase noise PLL + SX1276_REG_OCP = 0x0b, // Over Current Protection control + SX1276_REG_LNA = 0x0c, // LNA settings + SX1276_REG_FIFO_ADDR_PTR = 0x0d, // FIFO SPI pointer + SX1276_REG_FIFO_TX_BASE_ADDR = 0x0e, // Start Tx data + SX1276_REG_FIFO_RX_BASE_ADDR = 0x0f, // Start Rx data + SX1276_REG_FIFO_RX_CURRENT_ADDR = 0x10, // Start address of last packet received + SX1276_REG_IRQ_FLAGS = 0x12, // IRQ flags + SX1276_REG_RX_NB_BYTES = 0x13, // Number of received bytes + SX1276_REG_MODEM_STATUS = 0x18, // Live LoRa modem status + SX1276_REG_PKT_SNR_VALUE = 0x19, // Espimation of last packet SNR + SX1276_REG_PKT_RSSI_VALUE = 0x1a, // RSSI of last packet + SX1276_REG_MODEM_CONFIG_1 = 0x1d, // Modem PHY config 1 + SX1276_REG_MODEM_CONFIG_2 = 0x1e, // Modem PHY config 2 + SX1276_REG_PREAMBLE_MSB = 0x20, // Size of preamble + SX1276_REG_PREAMBLE_LSB = 0x21, // Size of preamble + SX1276_REG_PAYLOAD_LENGTH = 0x22, // LoRa payload length + SX1276_REG_FIFO_RX_BYTE_PTR = 0x25, // Current value of RX databuffer pointer (written by Lora receiver) + SX1276_REG_MODEM_CONFIG_3 = 0x26, // Modem PHY config 3 + SX1276_REG_RSSI_WIDEBAND = 0x2c, // Wideband RSSI measurement + SX1276_REG_DETECTION_OPTIMIZE = 0x31, // LoRa detection Optimize for SF6 + SX1276_REG_DETECTION_THRESHOLD = 0x37, // LoRa detection threshold for SF6 + SX1276_REG_SYNC_WORD = 0x39, // LoRa Sync Word + SX1276_REG_IRQ_FLAGS1 = 0x3e, // (FSK) Status register: PLL Lock state, Timeout, RSSI + SX1276_REG_DIO_MAPPING_1 = 0x40, // Mapping of pins DIO0 to DIO3 + SX1276_REG_DIO_MAPPING_2 = 0x41, // Mapping of pins DIO4 and DIO5, ClkOut frequency + SX1276_REG_VERSION = 0x42 // Semtech ID relating the silicon revision (0x12) +}; + +enum sx1276_mode { // SX1276_REG_OP_MODE values + SX1276_MODE_LONG_RANGE_MODE = 1<<7, // Set to enable LoRa mode + SX1276_MODE_ACCESS_SHARED_REG = 1<<6, // Set to access FSK registers in 0x0D:0x3F while in LoRa mode + SX1276_MODE_LOW_FREQUENCY_MODE = 1<<3, // Set to access Low Frequency Mode registers + // 3 bits of LoRa Modes + SX1276_MODE_SLEEP = 0x00, // FSK and LoRa modes can only be swiched in SLEEP + SX1276_MODE_STDBY = 0x01, + SX1276_MODE_FSTX = 0x02, // Frequency synthesis TX + SX1276_MODE_TX = 0x03, + SX1276_MODE_FSRX = 0x04, // Frequency synthesis RX + SX1276_MODE_RX_CONTINUOUS = 0x05, + SX1276_MODE_RX_SINGLE = 0x06, + SX1276_MODE_CAD = 0x07 // Channel activity detection +}; + +// PA config +#define PA_BOOST 1<<7 + +// LORA IRQ masks +enum sx1276_lora_irq { + SX1267_LORA_IRQ_RX_TIMEOUT = 1<<7, + SX1267_LORA_IRQ_RX_DONE = 1<<6, + SX1267_LORA_IRQ_PAYLOAD_CRC_ERR = 1<<5, + SX1267_LORA_IRQ_VALID_HEADER = 1<<4, + SX1267_LORA_IRQ_TX_DONE = 1<<3, + SX1267_LORA_IRQ_CAD_DONE = 1<<2, + SX1267_LORA_IRQ_FHSS_CHANGE_CH = 1<<1, + SX1267_LORA_IRQ_CAD_DETECTED = 1<<0, +}; + +// FSK IRQ FLAGS +#define IRQ_FLAGS1_MODE_READY 1<<7 // Set when the operation mode requested in Mode, is ready + +#define FIFO_SIZE 0xff + +struct sx1276_state_st { + uint8_t fifo[FIFO_SIZE]; +}; + +uint8_t sx1276_read(uint8_t reg); +uint8_t sx1276_write(uint8_t reg, uint8_t val); +void sx1276_fifo_dump(struct sx1276_state_st *state); +void sx1276_fifo_load(struct sx1276_state_st *state); +void sx1276_fifo_write(uint8_t addr, uint8_t val); +void sx1276_set_frequency(uint64_t frequency); +struct sx1276_state_st *sx1276_init(uint64_t frequency); diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..5104b97 --- /dev/null +++ b/src/util.c @@ -0,0 +1,17 @@ +#include +#include "util.h" + +void hexdump(char *buf, size_t size, uint32_t width) { + if (width == 0) { + width = 16; + } + SEGGER_RTT_SetFlagsUpBuffer(0, SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); + for (unsigned int i = 0; i < size; i++) { + SEGGER_RTT_printf(0, "%.2x ", buf[i]); + if ((i+1) % width == 0) { + SEGGER_RTT_printf(0,"\n"); + } + } + SEGGER_RTT_printf(0,"\n"); + SEGGER_RTT_SetFlagsUpBuffer(0, SEGGER_RTT_MODE_DEFAULT); +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..ee83190 --- /dev/null +++ b/src/util.h @@ -0,0 +1,8 @@ +#ifndef DHT_TEST_UTIL_H +#define DHT_TEST_UTIL_H + +#include + +void hexdump(char *buf, size_t size, uint32_t width); + +#endif //DHT_TEST_UTIL_H