neptunium-firmware/Core/Src/radio.c
Peter aa3b50eed2 Initial commit. Logging using tinyusb on stm32f302c8t6. WORKING:
WORKING: lps22hb, lsm6dsox, neo-m9n
TODO: sx1262, SD card, freertos
2024-07-06 04:25:33 +08:00

291 lines
11 KiB
C

#include "radio.h"
void lora_direction()
{
}
void check_status(volatile uint8_t status)
{
if (SX126X_STATUS_OK != status)
HALT_BKPT();
}
struct sx126x_ctx
{
} ctx;
void lora_rx_data(void);
uint8_t rx_buf[256]; // MAXIMUM RECEIVE SIZE OF 256 FOR LORA
void lora_setup(uint8_t enable_reg)
{
if (!enable_reg)
return;
// 1. If not in STDBY_RC mode, then go to this mode with the command SetStandby(...)
sx126x_chip_status_t radio_status;
if (sx126x_get_status(&ctx, &radio_status) == SX126X_STATUS_OK)
{
check_status(sx126x_set_standby(&ctx, SX126X_STANDBY_CFG_RC));
}
else
HALT_BKPT();
volatile uint32_t number = 0;
sx126x_get_random_numbers(&ctx, &number, 1);
// 2. Define the protocol (LoRa® or FSK) with the command SetPacketType(...)
check_status(sx126x_set_pkt_type(&ctx, SX126X_PKT_TYPE_LORA));
// 3. Define the RF frequency with the command SetRfFrequency(...)
check_status(sx126x_set_rf_freq(&ctx, FREQUENCY));
// ENABLE DC-DC CONVERTER
check_status(sx126x_set_reg_mode(&ctx, SX126X_REG_MODE_DCDC));
if (enable_reg & EN_TX)
{
// 4. Define the Power Amplifier configuration with the command SetPaConfig(...)
// FROM DATASHEET:
// Table 13-21: PA Operating Modes with Optimal Settings
// Mode Output Power paDutyCycle hpMax deviceSel paLut Value in SetTxParams1
// SX1262 +22 dBm 0x04 0x07 0x00 0x01 +22 dBm
// Note:
// These changes make the use of nominal power either sub-optimal or unachievable.
// Caution!
// The following restrictions must be observed to avoid voltage overstress on the PA, exceeding the maximum ratings
// may cause irreversible damage to the device:
// • For SX1261 at synthesis frequency above 400 MHz, paDutyCycle should not be higher than 0x07.
// • For SX1261 at synthesis frequency below 400 MHz, paDutyCycle should not be higher than 0x04.
// • For SX1262, paDutyCycle should not be higher than 0x04.
sx126x_pa_cfg_params_t pa_params = {
.pa_duty_cycle = 0x04,
.hp_max = 0x07,
.device_sel = 0x00,
.pa_lut = 0x01};
check_status(sx126x_set_pa_cfg(&ctx, &pa_params));
// 5. Define output power and ramping time with the command SetTxParams(...)
sx126x_set_tx_params(&ctx, TX_PWR, RAMP_TIME);
}
if (enable_reg & EN_RX)
{
// ENABLE RX BOOSTED MODE
check_status(sx126x_cfg_rx_boosted(&ctx, true));
}
// 6. Define where the data payload will be stored with the command SetBufferBaseAddress(...)
check_status(sx126x_set_buffer_base_address(&ctx, 0x00, 0x00));
// ~~7. Send the payload to the data buffer with the command WriteBuffer(...)~~
// 8. Define the modulation parameter according to the chosen protocol with the command SetModulationParams(...)1
sx126x_mod_params_lora_t lora_mod_params = {
.sf = SX126X_LORA_SF7,
.bw = SX126X_LORA_BW_125,
.cr = SX126X_LORA_CR_4_5,
.ldro = 0x00};
check_status(sx126x_set_lora_mod_params(&ctx, &lora_mod_params));
// 9. Define the frame format to be used with the command SetPacketParams(...)2
// USED FOR RECEIVING - CALLED AGAIN WITH PROPER LENGTH DURING TX
sx126x_pkt_params_lora_t lora_pkt_params = {
.preamble_len_in_symb = 8,
.header_type = SX126X_LORA_PKT_EXPLICIT,
.pld_len_in_bytes = 0xFF,
.crc_is_on = true,
.invert_iq_is_on = false};
check_status(sx126x_set_lora_pkt_params(&ctx, &lora_pkt_params));
// 10. Configure DIO and IRQ: use the command SetDioIrqParams(...) to select TxDone IRQ and map this IRQ to a DIO (DIO1, DIO2 or DIO3)
// 7. Configure DIO and irq: use the command SetDioIrqParams(...) to select the IRQ RxDone and map this IRQ to a DIO (DIO1 or DIO2 or DIO3), set IRQ Timeout as well.
// ENABLE TCXO SOURCE ON DIO3
// TODO: CHECK IF THIS REQUIRES EXTERNAL 3.3V OR IF THE XTAL IS BUILT INTO THE MODULE).
// TIMEOUT = DELAY * 15.625 μs = 1.5625 ms
check_status(sx126x_set_dio3_as_tcxo_ctrl(&ctx, SX126X_TCXO_CTRL_3_3V, 100));
// ENABLE RF SWITCH ON DIO2
check_status(sx126x_set_dio2_as_rf_sw_ctrl(&ctx, true));
check_status(sx126x_set_tx(&ctx, 1000));
check_status(sx126x_set_rx(&ctx, 1000));
check_status(sx126x_set_tx(&ctx, 1000));
check_status(sx126x_set_rx(&ctx, 1000));
// ENABLE IRQ ON DIO1
uint16_t irq_mask = SX126X_IRQ_TIMEOUT | SX126X_IRQ_CRC_ERROR;
irq_mask |= (enable_reg & EN_RX ? SX126X_IRQ_RX_DONE : 0);
irq_mask |= (enable_reg & EN_TX ? SX126X_IRQ_TX_DONE : 0);
check_status(sx126x_set_dio_irq_params(&ctx, irq_mask,
irq_mask, SX126X_IRQ_NONE, SX126X_IRQ_NONE));
// 11. Define Sync Word value: use the command WriteReg(...) to write the value of the register via direct register access
check_status(sx126x_set_lora_sync_word(&ctx, 0x12));
sx126x_errors_mask_t errors = 0;
// IMAGE CALIBRATION
// When the 32 MHz clock is coming from a TCXO, the calibration will fail
// and the user should request a complete calibration after calling the
// function SetDIO3AsTcxoCtrl(...).
// Table 9-2: Image Calibration Over the ISM Bands
// Frequency Band [MHz] Freq1 Freq2
// 902 - 928 0xE1 (default) 0xE9 (default)
do
{
check_status(sx126x_cal_img(&ctx, 0xE1, 0xE9));
check_status(sx126x_cal(&ctx, SX126X_CAL_ALL));
sx126x_get_device_errors(&ctx, &errors);
} while (errors);
// 12. Set the circuit in transmitter mode to start transmission with the command SetTx(). Use the parameter to enable Timeout
// 9. Set the circuit in reception mode: use the command SetRx(). Set the parameter to enable timeout or continuous mode
// 13. Wait for the IRQ TxDone or Timeout: once the packet has been sent the chip goes automatically to STDBY_RC mode
// 10. Wait for IRQ RxDone2 or Timeout: the chip will stay in Rx and look for a new packet if the continuous mode is selected otherwise it will goes to STDBY_RC mode.
// 14. Clear the IRQ TxDone flag
// 11. In case of the IRQ RxDone, check the status to ensure CRC is correct: use the command GetIrqStatus()
// CHECK FOR ERRORS FIXME: it fails
}
void lora_handle_irq(void)
{
sx126x_irq_mask_t irq_mask;
if (sx126x_get_irq_status(&ctx, &irq_mask) == SX126X_STATUS_OK)
{
sx126x_clear_irq_status(&ctx, irq_mask);
if (irq_mask & SX126X_IRQ_TX_DONE)
__NOP();
if (irq_mask & SX126X_IRQ_RX_DONE)
lora_rx_data();
if (irq_mask & SX126X_IRQ_TIMEOUT)
__NOP();
if (irq_mask & SX126X_IRQ_CRC_ERROR)
__NOP();
}
}
void lora_rx_data(void)
{
// TODO: Should the chip go into standby mode here?
sx126x_set_standby(&ctx, SX126X_STANDBY_CFG_RC);
sx126x_pkt_status_lora_t pkt_status;
sx126x_get_lora_pkt_status(&ctx, &pkt_status); // TODO: Engineering logs
sx126x_rx_buffer_status_t buf_status;
sx126x_get_rx_buffer_status(&ctx, &buf_status);
if (buf_status.pld_len_in_bytes > 0)
{
sx126x_read_buffer(&ctx, buf_status.buffer_start_pointer, rx_buf, buf_status.pld_len_in_bytes);
tud_cdc_write(rx_buf, buf_status.pld_len_in_bytes);
}
}
void lora_tx_data(int8_t power, char *data, uint8_t length)
{
// TODO: Should the chip go into standby mode here?
sx126x_set_standby(&ctx, SX126X_STANDBY_CFG_RC);
check_status(sx126x_set_buffer_base_address(&ctx, 0x00, 0x00));
sx126x_pkt_params_lora_t lora_pkt_params = {
.preamble_len_in_symb = 8,
.header_type = SX126X_LORA_PKT_EXPLICIT,
.pld_len_in_bytes = length,
.crc_is_on = true,
.invert_iq_is_on = false};
check_status(sx126x_set_lora_pkt_params(&ctx, &lora_pkt_params));
check_status(sx126x_write_buffer(&ctx, 0, (uint8_t *)data, length));
check_status(sx126x_set_tx(&ctx, SX126X_MAX_TIMEOUT_IN_MS));
}
void lora_rx_mode(void)
{
check_status(sx126x_set_rx(&ctx, RADIO_RX_TIMEOUT));
// TODO: check and handle errors
}
#define SPI_TIMEOUT 1000
/**
* Radio data transfer - write
*
* @remark Shall be implemented by the user
*
* @param [in] context Radio implementation parameters
* @param [in] command Pointer to the buffer to be transmitted
* @param [in] command_length Buffer size to be transmitted
* @param [in] data Pointer to the buffer to be transmitted
* @param [in] data_length Buffer size to be transmitted
*
* @returns Operation status
*/
sx126x_hal_status_t sx126x_hal_write(const void *context, const uint8_t *command, const uint16_t command_length,
const uint8_t *data, const uint16_t data_length)
{
HAL_StatusTypeDef ok = HAL_OK;
HAL_GPIO_WritePin(LORA_CS_GPIO_Port, LORA_CS_Pin, GPIO_PIN_RESET);
ok |= HAL_SPI_Transmit(&hspi3, (uint8_t *)command, command_length, SPI_TIMEOUT);
if (data != NULL)
ok |= HAL_SPI_Transmit(&hspi3, (uint8_t *)data, data_length, SPI_TIMEOUT);
HAL_GPIO_WritePin(LORA_CS_GPIO_Port, LORA_CS_Pin, GPIO_PIN_SET);
return ok;
}
/**
* Radio data transfer - read
*
* @remark Shall be implemented by the user
*
* @param [in] context Radio implementation parameters
* @param [in] command Pointer to the buffer to be transmitted
* @param [in] command_length Buffer size to be transmitted
* @param [in] data Pointer to the buffer to be received
* @param [in] data_length Buffer size to be received
*
* @returns Operation status
*/
sx126x_hal_status_t sx126x_hal_read(const void *context, const uint8_t *command, const uint16_t command_length,
uint8_t *data, const uint16_t data_length)
{
HAL_StatusTypeDef ok = HAL_OK;
HAL_GPIO_WritePin(LORA_CS_GPIO_Port, LORA_CS_Pin, GPIO_PIN_RESET);
ok |= HAL_SPI_Transmit(&hspi3, (uint8_t *)command, command_length, SPI_TIMEOUT);
ok |= HAL_SPI_Receive(&hspi3, data, data_length, SPI_TIMEOUT);
HAL_GPIO_WritePin(LORA_CS_GPIO_Port, LORA_CS_Pin, GPIO_PIN_SET);
return ok;
}
/**
* Reset the radio
*
* @remark Shall be implemented by the user
*
* @param [in] context Radio implementation parameters
*
* @returns Operation status
*
* @warning NOT IMPLEMENTED
*/
sx126x_hal_status_t sx126x_hal_reset(const void *context)
{
// NOT IMPLEMENTED
}
/**
* Wake the radio up.
*
* @remark Shall be implemented by the user
*
* @param [in] context Radio implementation parameters
*
* @returns Operation status
*
* @warning NOT IMPLEMENTED
*/
sx126x_hal_status_t sx126x_hal_wakeup(const void *context)
{
// NOT IMPLEMENTED
}