Files
LoraIDF/main/sx1262.c
2025-04-14 07:06:29 +02:00

592 lines
15 KiB
C

#include "sx1262.h"
spi_device_handle_t spiSXko;
#define TAG_SPIDUMP "[SPI]"
void sx1262_init()
{
ESP_LOGI(TAG, "Initializing SX1262...");
gpio_set_direction(PIN_NSS, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_RESET, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_BUSY, GPIO_MODE_INPUT);
gpio_set_level(PIN_NSS, 1);
gpio_set_level(PIN_RESET, 1);
spi_bus_config_t buscfg;
memset(&buscfg, 0, sizeof(spi_bus_config_t));
buscfg.miso_io_num = PIN_MISO;
buscfg.mosi_io_num = PIN_MOSI;
buscfg.sclk_io_num = PIN_SCK;
buscfg.max_transfer_sz = 32;
buscfg.quadwp_io_num = -1;
buscfg.quadhd_io_num = -1;
spi_device_interface_config_t devcfg = {
.command_bits = 0,
.address_bits = 0,
.mode = 0,
.cs_ena_pretrans = 10,
.clock_speed_hz = 8 * 1000 * 1000,
.spics_io_num = -1,
.flags = 0,
.queue_size = 1};
ESP_LOGI(TAG, "Initializing SPI bus...");
esp_err_t ret = spi_bus_initialize(LORA_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "SPI bus init failed!");
return;
}
ESP_LOGI(TAG, "Adding SPI device...");
ret = spi_bus_add_device(LORA_SPI_HOST, &devcfg, &spiSXko);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "SPI device init failed!");
return;
}
ESP_LOGI(TAG, "SX1262 Initialized");
sx1262_reset();
}
void sx1262_reset()
{
ESP_LOGI(TAG, "Resetting SX1262...");
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(PIN_RESET, 0);
vTaskDelay(pdMS_TO_TICKS(20));
gpio_set_level(PIN_RESET, 1);
vTaskDelay(pdMS_TO_TICKS(10));
sx1262_wait_for_busy();
ESP_LOGI(TAG, "SX1262 Reset complete.");
}
void sx1262_wait_for_busy()
{
ESP_LOGV("WAIT", "Waiting for SX1262 to be ready...");
while (gpio_get_level(PIN_BUSY) == 1)
{
vTaskDelay(pdMS_TO_TICKS(10));
}
ESP_LOGV("WAIT", "SX1262 is ready.");
}
void sx1262_write_command(uint8_t cmd, uint8_t *data, uint8_t len)
{
ESP_LOGV(TAG, "Writing command 0x%02X with length %d", cmd, len);
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = (len + 1) * 8;
uint8_t *tx_data = malloc(len + 1);
memset(tx_data, 0, len + 1);
tx_data[0] = cmd;
if (len)
{
memcpy(tx_data + 1, data, len);
}
t.tx_buffer = tx_data;
sx1262_wait_for_busy();
ESP_LOGI(TAG_SPIDUMP, "SPI TX (cmd 0x%02X):", cmd);
ESP_LOG_BUFFER_HEXDUMP(TAG_SPIDUMP, tx_data, len + 1, ESP_LOG_INFO);
gpio_set_level(PIN_NSS, 0);
esp_err_t ret = spi_device_transmit(spiSXko, &t);
gpio_set_level(PIN_NSS, 1);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "SPI write failed!");
}
else
{
ESP_LOGV(TAG, "SPI write successful");
}
free(tx_data);
}
uint8_t SX1262_STATUS = 0;
void sx1262_read_command(uint8_t cmd, uint8_t *tx_payload_buffer, uint8_t tx_payload_len, uint8_t *rx_buffer, uint8_t len)
{
ESP_LOGV(TAG, "Reading command 0x%02X with expected length %d", cmd, len);
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = (len + 2) * 8;
uint8_t *tx_data = malloc(len + 2);
uint8_t *rx_data = malloc(len + 2);
memset(tx_data, 0, len + 2);
memset(rx_data, 0, len + 2);
memset(tx_data, 0, sizeof(tx_data));
memset(rx_data, 0, sizeof(rx_data));
if (tx_payload_buffer != NULL && tx_payload_len > 0)
{
if (tx_payload_len > len + 1)
{
tx_payload_len = len + 1;
}
memcpy(tx_data + 1, tx_payload_buffer, tx_payload_len);
}
tx_data[0] = cmd;
t.tx_buffer = tx_data;
t.rx_buffer = rx_data;
sx1262_wait_for_busy();
ESP_LOGI(TAG_SPIDUMP, "SPI TX (cmd 0x%02X):", cmd);
ESP_LOG_BUFFER_HEXDUMP(TAG_SPIDUMP, tx_data, len + 1, ESP_LOG_INFO);
gpio_set_level(PIN_NSS, 0);
esp_err_t ret = spi_device_transmit(spiSXko, &t);
gpio_set_level(PIN_NSS, 1);
if (ret == ESP_OK)
{
SX1262_STATUS = rx_data[1];
memcpy(rx_buffer, &rx_data[2], len);
ESP_LOGI(TAG_SPIDUMP, "SPI RX (cmd 0x%02X):", cmd);
ESP_LOG_BUFFER_HEXDUMP(TAG_SPIDUMP, rx_data, len + 2, ESP_LOG_INFO);
ESP_LOGV(TAG_SPIDUMP, "SPI read successful, status: 0x%02X", SX1262_STATUS);
}
else
{
ESP_LOGE(TAG, "SPI read failed!");
}
free(rx_data);
free(tx_data);
}
void resolve_status_byte(uint8_t status, uint16_t op_error)
{
const char *chip_modes[] = {
"Unused", "RFU", "STBY_RC", "STBY_XOSC",
"FS", "RX", "TX", "RFU"};
const char *command_statuses[] = {
"Reserved", "RFU", "Data is available to host", "Command timeout",
"Command processing error", "Failure to execute command", "Command TX done", "RFU"};
uint8_t chip_mode = (status >> 4) & 0x07; // Bits 6:4
uint8_t command_status = (status >> 1) & 0x07; // Bits 3:1
const char *chip_mode_str = chip_modes[chip_mode];
const char *command_status_str = command_statuses[command_status];
const char *op_errors[] = {
"RC64k calibration failed",
"RC13M calibration failed",
"PLL calibration failed",
"ADC calibration failed",
"IMG calibration failed",
"XOSC failed to start",
"PLL failed to lock",
"RFU",
"PA ramping failed"};
char errorStr[1024];
memset(errorStr, 0, sizeof(errorStr));
if (command_status == 0x05)
{
strcat(errorStr, "Command error: ");
}
for (int i = 0; i < 9; i++)
{
if (op_error & (1 << i))
{
strcat(errorStr, op_errors[i]);
strcat(errorStr, ", ");
ESP_LOGV(TAG, "OpError: %s", op_errors[i]);
}
}
ESP_LOGI(TAG, "Chip Mode: %s(0x%02X), Command Status: %s(0x%02X) %s", chip_mode_str, chip_mode, command_status_str, command_status, errorStr);
}
sx1262_status_t sx1262_get_status()
{
ESP_LOGV(TAG, "Getting SX1262 status...");
sx1262_status_t status;
uint8_t buffer[1] = {0};
sx1262_read_command(0xC0, NULL, 0, buffer, 1);
status.status = buffer[0];
status.error = sx1262_getDeviceErrors();
sx1262_clearDeviceErrors();
resolve_status_byte(status.status, status.error);
return status;
}
void sx1262_setSleep(uint8_t sleepCFG)
{
sx1262_write_command(0x84, &sleepCFG, 1);
}
void sx1262_setStandby(uint8_t standbyConf)
{
sx1262_write_command(0x80, &standbyConf, 1);
}
void sx1262_setFrequencySynthesis()
{
sx1262_write_command(0xC1, NULL, 0);
}
void sx1262_setMode(uint8_t mode, uint32_t timeout)
{
uint32_t timeoutUnits = 0;
if (timeout != 0)
{
uint64_t timeoutInUs = (uint64_t)timeout * 1000; // Convert ms → us
timeoutUnits = (uint32_t)((timeoutInUs * 64) / 1000); // Convert us → SX1262 units
}
uint8_t buffer[4] = {
(uint8_t)((timeoutUnits >> 24) & 0xFF),
(uint8_t)((timeoutUnits >> 16) & 0xFF),
(uint8_t)((timeoutUnits >> 8) & 0xFF),
(uint8_t)((timeoutUnits) & 0xFF)};
if (mode)
{
sx1262_write_command(0x83, buffer, 3);
}
else
{
sx1262_write_command(0x82, buffer, 3);
}
}
void sx1262_stopTimerOnPreamble(uint8_t enable)
{
sx1262_write_command(0x9F, &enable, 1);
}
void sx1262_setRxDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod)
{
uint8_t payload[6];
payload[0] = (rxPeriod >> 16) & 0xFF;
payload[1] = (rxPeriod >> 8) & 0xFF;
payload[2] = rxPeriod & 0xFF;
payload[3] = (sleepPeriod >> 16) & 0xFF;
payload[4] = (sleepPeriod >> 8) & 0xFF;
payload[5] = sleepPeriod & 0xFF;
sx1262_write_command(0x94, payload, 6);
}
void sx1262_setChannelActivityDetection(void)
{
sx1262_write_command(0xC5, NULL, 0);
}
void sx1262_setTxContinuousWave(void)
{
sx1262_write_command(0xD1, NULL, 0);
}
void sx1262_setTxInfinitePreamble(void)
{
sx1262_write_command(0xD2, NULL, 0);
}
void sx1262_setRegulatorMode(uint8_t mode)
{
sx1262_write_command(0x96, &mode, 1);
}
void sx1262_calibrate(uint8_t calibParam)
{
sx1262_write_command(0x89, &calibParam, 1);
}
void sx1262_calibrateImage(uint8_t freq1, uint8_t freq2)
{
uint8_t payload[2] = {freq1, freq2};
sx1262_write_command(0x98, payload, 2);
}
void sx1262_setRxTXFallbackMode(uint8_t fallbackMode)
{
sx1262_write_command(0x93, &fallbackMode, 1);
}
// Write to register function
void sx1262_writeRegister(uint16_t address, const uint8_t *data, size_t length)
{
uint8_t payload[length + 2];
payload[0] = (address >> 8) & 0xFF;
payload[1] = address & 0xFF;
for (size_t i = 0; i < length; i++)
{
payload[i + 2] = data[i];
}
sx1262_write_command(0x0D, payload, length + 2);
}
// Read from register function
void sx1262_readRegister(uint16_t address, uint8_t *data, size_t length)
{
uint8_t payload[2];
payload[0] = (address >> 8) & 0xFF;
payload[1] = address & 0xFF;
uint8_t rx_payload[2 + length];
sx1262_read_command(0x1D, payload, 2, rx_payload, 2 + length);
for (size_t i = 0; i < length; i++)
{
data[i] = rx_payload[i + 2];
}
}
// Write to buffer function
void sx1262_writeBuffer(uint8_t offset, const uint8_t *data, size_t length)
{
uint8_t payload[length + 1];
payload[0] = offset;
for (size_t i = 0; i < length; i++)
{
payload[i + 1] = data[i];
}
sx1262_write_command(0x0E, payload, length + 1);
}
// Read from buffer function
void sx1262_readBuffer(uint8_t offset, uint8_t *data, size_t length)
{
uint8_t tx_payload[1];
uint8_t payload[1 + length];
tx_payload[0] = offset;
sx1262_read_command(0x1E, tx_payload, 1, payload, 1 + length);
for (size_t i = 0; i < length; i++)
{
data[i] = payload[i + 1];
}
}
void sx1262_setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask)
{
uint8_t payload[8] = {
(uint8_t)((irqMask >> 8) & 0xFF), (uint8_t)((irqMask) & 0xFF),
(uint8_t)((dio1Mask >> 8) & 0xFF), (uint8_t)(dio1Mask & 0xFF),
(uint8_t)((dio2Mask >> 8) & 0xFF), (uint8_t)(dio2Mask & 0xFF),
(uint8_t)((dio3Mask >> 8) & 0xFF), (uint8_t)(dio3Mask & 0xFF)};
sx1262_write_command(0x08, payload, 8);
}
uint16_t sx1262_getIrqStatus(void)
{
uint8_t response[2] = {0};
sx1262_read_command(0x12, NULL, 0, response, 2);
return (response[0] << 8) | response[1];
}
void sx1262_clearIrqStatus(uint16_t clearIrqParam)
{
uint8_t payload[2] = {
(uint8_t)((clearIrqParam >> 8) & 0xFF),
(uint8_t)(clearIrqParam & 0xFF)};
sx1262_write_command(0x02, payload, 2);
}
void sx1262_setDIO2AsRfSwitchCtrl(uint8_t enable)
{
uint8_t buf[1];
buf[0] = enable;
sx1262_write_command(0x9D, buf, 1);
}
void sx1262_setDIO3AsTCXOCtrl(uint8_t tcxoVoltage, uint32_t delay)
{
uint8_t payload[4] = {
tcxoVoltage,
(uint8_t)((delay >> 16) & 0xFF),
(uint8_t)((delay >> 8) & 0xFF),
(uint8_t)(delay & 0xFF)};
sx1262_write_command(0x97, payload, 4);
}
void sx1262_setFrequency(uint32_t frequency)
{
uint32_t freq_reg = frequency * FREQ_STEP;
uint8_t buffer[4] = {
(uint8_t)((freq_reg >> 24) & 0xFF),
(uint8_t)((freq_reg >> 16) & 0xFF),
(uint8_t)((freq_reg >> 8) & 0xFF),
(uint8_t)((freq_reg) & 0xFF)};
sx1262_write_command(0x86, buffer, 4);
}
void sx1262_setPacketType(uint8_t packetType)
{
uint8_t data[1];
data[0] = packetType;
sx1262_write_command(0x8A, data, 1);
}
void sx1262_setLoRaModulationParams(const sx1262_LoRaModulationParams_t *params)
{
uint8_t payload[4] = {
params->spreadingFactor,
params->bandwidth,
params->codingRate,
params->lowDataRateOpt};
sx1262_write_command(0x8B, payload, sizeof(payload));
}
void sx1262_setGFSKModulationParams(const sx1262_GFSKModulationParams_t *params)
{
uint8_t payload[8] = {
(uint8_t)((params->bitRate >> 16) & 0xFF),
(uint8_t)((params->bitRate >> 8) & 0xFF),
(uint8_t)(params->bitRate & 0xFF),
params->pulseShape,
params->bandwidth,
(uint8_t)((params->frequencyDev >> 16) & 0xFF),
(uint8_t)((params->frequencyDev >> 8) & 0xFF),
(uint8_t)(params->frequencyDev & 0xFF)};
sx1262_write_command(0x8B, payload, sizeof(payload));
}
uint8_t sx1262_getPacketType()
{
uint8_t response;
sx1262_read_command(0x11, NULL, 0, &response, 1);
return response;
}
void sx1262_configure_tx_power(uint8_t paDutyCycle, uint8_t hpMax, uint8_t paLut, int8_t power, uint8_t rampTime)
{
if (paDutyCycle > 0x04)
paDutyCycle = 0x04;
if (rampTime > 0x07)
rampTime = 0x07;
if (hpMax > 0x07)
hpMax = 0x07;
uint8_t deviceSel;
if (power >= -9)
{
deviceSel = 0x00; // High Power PA
if (power > 22)
power = 22;
if (power < -9)
power = -9;
}
else
{
deviceSel = 0x01; // Low Power PA
if (power > 14)
power = 14;
if (power < -17)
power = -17;
}
uint8_t paConfig[4] = {paDutyCycle, hpMax, deviceSel, paLut};
sx1262_write_command(0x95, paConfig, 4);
// Direct cast keeps correct 2's complement representation
uint8_t txPower[2] = {(uint8_t)power, rampTime};
sx1262_write_command(0x8E, txPower, 2);
}
void sx1262_setLoRaPacketParams(sx1262_LoRaPacketParams_t *params)
{
uint8_t payload[6] = {
(uint8_t)((params->preambleLength >> 8) & 0xFF),
(uint8_t)(params->preambleLength & 0xFF),
params->headerType,
params->payloadLength,
params->crcType,
params->invertIQ};
sx1262_write_command(0x8C, payload, sizeof(payload));
}
void sx1262_setCadParams(uint8_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, uint8_t cadExitMode, uint32_t cadTimeout)
{
uint8_t payload[7] = {
cadSymbolNum,
cadDetPeak,
cadDetMin,
cadExitMode,
(uint8_t)((cadTimeout >> 16) & 0xFF),
(uint8_t)((cadTimeout >> 8) & 0xFF),
(uint8_t)(cadTimeout & 0xFF)};
sx1262_write_command(0x88, payload, sizeof(payload));
}
void sx1262_setBufferBaseAddress(uint8_t txBaseAddr, uint8_t rxBaseAddr)
{
uint8_t payload[2] = {txBaseAddr, rxBaseAddr};
sx1262_write_command(0x8F, payload, sizeof(payload));
}
void sx1262_setLoRaSymbNumTimeout(uint8_t symbNum)
{
sx1262_write_command(0xA0, &symbNum, 1);
}
void sx1262_getStatus(uint8_t *status)
{
sx1262_read_command(0xC0, NULL, 0, status, 0);
}
void sx1262_getRxBufferStatus(uint8_t *payloadLengthRx, uint8_t *rxStartBufferPointer)
{
uint8_t response[2];
sx1262_read_command(0x13, NULL, 0, response, sizeof(response));
*payloadLengthRx = response[0];
*rxStartBufferPointer = response[1];
}
void sx1262_getPacketStatus(uint8_t *rssi, uint8_t *snr, uint8_t *signalRssi)
{
uint8_t response[3];
sx1262_read_command(0x14, NULL, 0, response, sizeof(response));
*rssi = response[0];
*snr = response[1];
*signalRssi = response[2];
}
uint8_t sx1262_getRssiInst(uint8_t *rssiInst)
{
uint8_t response;
sx1262_read_command(0x15, NULL, 0, &response, 1);
return response;
}
void sx1262_getStats(uint16_t *pktReceived, uint16_t *pktCrcError, uint16_t *pktHeaderErr)
{
uint8_t response[6];
sx1262_read_command(0x10, NULL, 0, response, sizeof(response));
*pktReceived = (response[0] << 8) | response[1];
*pktCrcError = (response[2] << 8) | response[3];
*pktHeaderErr = (response[4] << 8) | response[5];
}
void sx1262_resetStats(void)
{
uint8_t payload[6] = {0};
sx1262_write_command(0x00, payload, sizeof(payload));
}
uint16_t sx1262_getDeviceErrors()
{
uint8_t response[2];
sx1262_read_command(0x17, NULL, 0, response, sizeof(response));
return (((uint16_t)response[0]) << 8) | (uint16_t)response[1];
}
void sx1262_clearDeviceErrors(void)
{
uint8_t payload[2] = {0x00, 0x00};
sx1262_write_command(0x07, payload, sizeof(payload));
}