Init
This commit is contained in:
863
User/sx1262.c
Normal file
863
User/sx1262.c
Normal file
@@ -0,0 +1,863 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include "sx1262.h"
|
||||
#include "util/log.h"
|
||||
#define TAG "SX1262"
|
||||
|
||||
// Global Stuff
|
||||
static uint8_t PacketParams[6];
|
||||
static char txActive;
|
||||
static int txLost = 0;
|
||||
static char debugPrint;
|
||||
|
||||
// Arduino compatible macros
|
||||
#define delayMicroseconds(us) esp_rom_delay_us (us)
|
||||
#define delay(ms) esp_rom_delay_us (ms * 1000)
|
||||
|
||||
void LoRaError (int error) {
|
||||
if (debugPrint) {
|
||||
ESP_LOGE (TAG, "LoRaErrorDefault=%d", error);
|
||||
}
|
||||
while (1) {
|
||||
vTaskDelay (1);
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaInit (void) {
|
||||
|
||||
txActive = 0;
|
||||
debugPrint = 0;
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
||||
SPI_InitTypeDef SPI_InitStructure = {0};
|
||||
|
||||
|
||||
// RCC_AHBPeriphClockCmd (RCC_AHBPeriph_SDIO, ENABLE);
|
||||
RCC_APB1PeriphClockCmd (RCC_APB1Periph_USART2 | RCC_APB1Periph_I2C2, ENABLE);
|
||||
|
||||
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOE | RCC_APB2Periph_SPI1, ENABLE);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
|
||||
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_Init (GPIOA, &GPIO_InitStructure);
|
||||
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init (GPIOE, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init (GPIOE, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||||
GPIO_Init (GPIOE, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
|
||||
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init (GPIOE, &GPIO_InitStructure);
|
||||
|
||||
|
||||
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
|
||||
|
||||
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
|
||||
|
||||
|
||||
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
|
||||
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
|
||||
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
|
||||
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
|
||||
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
|
||||
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
|
||||
SPI_InitStructure.SPI_CRCPolynomial = 7;
|
||||
SPI_Init (SPI1, &SPI_InitStructure);
|
||||
|
||||
GPIO_WriteBit (GPIOE, GPIO_Pin_3, 1);
|
||||
|
||||
SPI_Cmd (SPI1, ENABLE);
|
||||
}
|
||||
|
||||
void spi_write_byte (uint8_t *DataOut, size_t DataLength) {
|
||||
spi_read_byte (NULL, DataOut, DataLength);
|
||||
}
|
||||
|
||||
// Full-duplex transfer: TX and RX buffers
|
||||
void spi_read_byte (uint8_t *rx, const uint8_t *tx, size_t len) {
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 0);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t out = tx ? tx[i] : 0xFF;
|
||||
|
||||
// Wait TX ready
|
||||
while (SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_TXE) == RESET);
|
||||
if (debugPrint) {
|
||||
printf ("Sent %02X over spi.\n", out);
|
||||
}
|
||||
SPI_I2S_SendData (SPI1, out);
|
||||
// Wait RX ready
|
||||
|
||||
while (SPI_I2S_GetFlagStatus (SPI1, SPI_I2S_FLAG_RXNE) == RESET);
|
||||
|
||||
uint8_t in = (uint8_t)SPI_I2S_ReceiveData (SPI1);
|
||||
if (debugPrint) {
|
||||
printf ("Got %02X over spi.\n", in);
|
||||
}
|
||||
if (rx)
|
||||
rx[i] = in;
|
||||
}
|
||||
GPIO_WriteBit (GPIOA, GPIO_Pin_4, 1);
|
||||
}
|
||||
|
||||
int16_t LoRaBegin (uint32_t frequencyInHz, int8_t txPowerInDbm, float tcxoVoltage, char useRegulatorLDO) {
|
||||
if (txPowerInDbm > 22) {
|
||||
txPowerInDbm = 22;
|
||||
}
|
||||
if (txPowerInDbm < -3) {
|
||||
txPowerInDbm = -3;
|
||||
}
|
||||
|
||||
ResetLora();
|
||||
|
||||
uint8_t wk[2];
|
||||
ReadRegister (SX126X_REG_LORA_SYNC_WORD_MSB, wk, 2); // 0x0740
|
||||
uint16_t syncWord = (wk[0] << 8) + wk[1];
|
||||
ESP_LOGI (TAG, "syncWord=0x%x", syncWord);
|
||||
if (syncWord != SX126X_SYNC_WORD_PUBLIC && syncWord != SX126X_SYNC_WORD_PRIVATE) {
|
||||
ESP_LOGE (TAG, "SX126x error, maybe no SPI connection");
|
||||
return ERR_INVALID_MODE;
|
||||
}
|
||||
|
||||
ESP_LOGI (TAG, "SX126x installed");
|
||||
SetStandby (SX126X_STANDBY_RC);
|
||||
|
||||
SetDio2AsRfSwitchCtrl (1);
|
||||
ESP_LOGI (TAG, "tcxoVoltage=%f", tcxoVoltage);
|
||||
// set TCXO control, if requested
|
||||
if (tcxoVoltage > 0.0) {
|
||||
SetDio3AsTcxoCtrl (tcxoVoltage, RADIO_TCXO_SETUP_TIME); // Configure the radio to use a TCXO controlled by DIO3
|
||||
}
|
||||
|
||||
Calibrate (SX126X_CALIBRATE_IMAGE_ON | SX126X_CALIBRATE_ADC_BULK_P_ON | SX126X_CALIBRATE_ADC_BULK_N_ON | SX126X_CALIBRATE_ADC_PULSE_ON | SX126X_CALIBRATE_PLL_ON | SX126X_CALIBRATE_RC13M_ON | SX126X_CALIBRATE_RC64K_ON);
|
||||
|
||||
ESP_LOGI (TAG, "useRegulatorLDO=%d", useRegulatorLDO);
|
||||
if (useRegulatorLDO) {
|
||||
SetRegulatorMode (SX126X_REGULATOR_LDO); // set regulator mode: LDO
|
||||
} else {
|
||||
SetRegulatorMode (SX126X_REGULATOR_DC_DC); // set regulator mode: DC-DC
|
||||
}
|
||||
|
||||
SetBufferBaseAddress (0, 0);
|
||||
SetPaConfig (0x04, 0x07, 0x00, 0x01); // PA Optimal Settings +22 dBm
|
||||
SetOvercurrentProtection (60.0); // current max 60mA for the whole device
|
||||
SetPowerConfig (txPowerInDbm, SX126X_PA_RAMP_200U); // 0 fuer Empfaenger
|
||||
SetRfFrequency (frequencyInHz);
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
void FixInvertedIQ (uint8_t iqConfig) {
|
||||
// fixes IQ configuration for inverted IQ
|
||||
// see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.4 for details
|
||||
// When exchanging LoRa packets with inverted IQ polarity, some packet losses may be observed for longer packets.
|
||||
// Workaround: Bit 2 at address 0x0736 must be set to:
|
||||
// “0” when using inverted IQ polarity (see the SetPacketParam(...) command)
|
||||
// “1” when using standard IQ polarity
|
||||
|
||||
// read current IQ configuration
|
||||
uint8_t iqConfigCurrent = 0;
|
||||
ReadRegister (SX126X_REG_IQ_POLARITY_SETUP, &iqConfigCurrent, 1); // 0x0736
|
||||
|
||||
// set correct IQ configuration
|
||||
// if(iqConfig == SX126X_LORA_IQ_STANDARD) {
|
||||
if (iqConfig == SX126X_LORA_IQ_INVERTED) {
|
||||
iqConfigCurrent &= 0xFB; // using inverted IQ polarity
|
||||
} else {
|
||||
iqConfigCurrent |= 0x04; // using standard IQ polarity
|
||||
}
|
||||
|
||||
// update with the new value
|
||||
WriteRegister (SX126X_REG_IQ_POLARITY_SETUP, &iqConfigCurrent, 1); // 0x0736
|
||||
}
|
||||
|
||||
void LoRaConfig (uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint16_t preambleLength, uint8_t payloadLen, char crcOn, char invertIrq) {
|
||||
SetStopRxTimerOnPreambleDetect (0);
|
||||
SetLoRaSymbNumTimeout (0);
|
||||
SetPacketType (SX126X_PACKET_TYPE_LORA); // SX126x.ModulationParams.PacketType : MODEM_LORA
|
||||
uint8_t ldro = 0; // LowDataRateOptimize OFF
|
||||
SetModulationParams (spreadingFactor, bandwidth, codingRate, ldro);
|
||||
|
||||
PacketParams[0] = (preambleLength >> 8) & 0xFF;
|
||||
PacketParams[1] = preambleLength;
|
||||
if (payloadLen) {
|
||||
PacketParams[2] = 0x01; // Fixed length packet (implicit header)
|
||||
PacketParams[3] = payloadLen;
|
||||
} else {
|
||||
PacketParams[2] = 0x00; // Variable length packet (explicit header)
|
||||
PacketParams[3] = 0xFF;
|
||||
}
|
||||
|
||||
if (crcOn)
|
||||
PacketParams[4] = SX126X_LORA_CRC_ON;
|
||||
else
|
||||
PacketParams[4] = SX126X_LORA_CRC_OFF;
|
||||
|
||||
if (invertIrq)
|
||||
PacketParams[5] = 0x01; // Inverted LoRa I and Q signals setup
|
||||
else
|
||||
PacketParams[5] = 0x00; // Standard LoRa I and Q signals setup
|
||||
|
||||
// fixes IQ configuration for inverted IQ
|
||||
FixInvertedIQ (PacketParams[5]);
|
||||
|
||||
WriteCommand (SX126X_CMD_SET_PACKET_PARAMS, PacketParams, 6); // 0x8C
|
||||
|
||||
// Do not use DIO interruptst
|
||||
SetDioIrqParams (SX126X_IRQ_ALL, // all interrupts enabled
|
||||
SX126X_IRQ_NONE, // interrupts on DIO1
|
||||
SX126X_IRQ_NONE, // interrupts on DIO2
|
||||
SX126X_IRQ_NONE // interrupts on DIO3
|
||||
);
|
||||
|
||||
|
||||
ESP_LOGI (TAG, "Almost done setting LoRa");
|
||||
// Receive state no receive timeoout
|
||||
SetRx (0xFFFFFF);
|
||||
}
|
||||
|
||||
void LoRaDebugPrint (char enable) {
|
||||
debugPrint = enable;
|
||||
}
|
||||
|
||||
uint8_t LoRaReceive (uint8_t *pData, int16_t len) {
|
||||
uint8_t rxLen = 0;
|
||||
uint16_t irqRegs = GetIrqStatus();
|
||||
// uint8_t status = GetStatus();
|
||||
|
||||
if (irqRegs & SX126X_IRQ_RX_DONE) {
|
||||
// ClearIrqStatus(SX126X_IRQ_RX_DONE);
|
||||
ClearIrqStatus (SX126X_IRQ_ALL);
|
||||
rxLen = ReadBuffer (pData, len);
|
||||
}
|
||||
|
||||
return rxLen;
|
||||
}
|
||||
|
||||
char LoRaSend (uint8_t *pData, int16_t len, uint8_t mode) {
|
||||
uint16_t irqStatus;
|
||||
char rv = 0;
|
||||
|
||||
if (txActive == 0) {
|
||||
txActive = 1;
|
||||
if (PacketParams[2] == 0x00) { // Variable length packet (explicit header)
|
||||
PacketParams[3] = len;
|
||||
}
|
||||
WriteCommand (SX126X_CMD_SET_PACKET_PARAMS, PacketParams, 6); // 0x8C
|
||||
|
||||
// ClearIrqStatus(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT);
|
||||
ClearIrqStatus (SX126X_IRQ_ALL);
|
||||
|
||||
WriteBuffer (pData, len);
|
||||
SetTx (5000);
|
||||
|
||||
if (mode & SX126x_TXMODE_SYNC) {
|
||||
irqStatus = GetIrqStatus();
|
||||
while ((!(irqStatus & SX126X_IRQ_TX_DONE)) && (!(irqStatus & SX126X_IRQ_TIMEOUT))) {
|
||||
vTaskDelay (1);
|
||||
irqStatus = GetIrqStatus();
|
||||
}
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "irqStatus=0x%x", irqStatus);
|
||||
if (irqStatus & SX126X_IRQ_TX_DONE) {
|
||||
ESP_LOGI (TAG, "SX126X_IRQ_TX_DONE");
|
||||
}
|
||||
if (irqStatus & SX126X_IRQ_TIMEOUT) {
|
||||
ESP_LOGI (TAG, "SX126X_IRQ_TIMEOUT");
|
||||
}
|
||||
}
|
||||
txActive = 0;
|
||||
|
||||
SetRx (0xFFFFFF);
|
||||
|
||||
if (irqStatus & SX126X_IRQ_TX_DONE) {
|
||||
rv = 1;
|
||||
}
|
||||
} else {
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "Send rv=0x%x", rv);
|
||||
}
|
||||
if (rv == 0)
|
||||
txLost++;
|
||||
return rv;
|
||||
}
|
||||
|
||||
char ReceiveMode (void) {
|
||||
uint16_t irq;
|
||||
char rv = 0;
|
||||
|
||||
if (txActive == 0) {
|
||||
rv = 1;
|
||||
} else {
|
||||
irq = GetIrqStatus();
|
||||
if (irq & (SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT)) {
|
||||
SetRx (0xFFFFFF);
|
||||
txActive = 0;
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void GetPacketStatus (int8_t *rssiPacket, int8_t *snrPacket) {
|
||||
uint8_t buf[4];
|
||||
ReadCommand (SX126X_CMD_GET_PACKET_STATUS, buf, 4); // 0x14
|
||||
*rssiPacket = (buf[3] >> 1) * -1;
|
||||
(buf[2] < 128) ? (*snrPacket = buf[2] >> 2) : (*snrPacket = ((buf[2] - 256) >> 2));
|
||||
}
|
||||
|
||||
void SetTxPower (int8_t txPowerInDbm) {
|
||||
SetPowerConfig (txPowerInDbm, SX126X_PA_RAMP_200U);
|
||||
}
|
||||
|
||||
void ResetLora (void) {
|
||||
vTaskDelay (pdMS_TO_TICKS (10));
|
||||
GPIO_WriteBit (GPIOE, GPIO_Pin_3, 0);
|
||||
vTaskDelay (pdMS_TO_TICKS (20));
|
||||
GPIO_WriteBit (GPIOE, GPIO_Pin_3, 1);
|
||||
ESP_LOGW (TAG, "Waiting for idle");
|
||||
vTaskDelay (pdMS_TO_TICKS (10));
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "Reset", 1);
|
||||
}
|
||||
|
||||
void Wakeup (void) {
|
||||
GetStatus();
|
||||
}
|
||||
|
||||
void SetStandby (uint8_t mode) {
|
||||
uint8_t data = mode;
|
||||
WriteCommand (SX126X_CMD_SET_STANDBY, &data, 1); // 0x80
|
||||
}
|
||||
|
||||
uint8_t GetStatus (void) {
|
||||
uint8_t rv;
|
||||
ReadCommand (SX126X_CMD_GET_STATUS, &rv, 1); // 0xC0
|
||||
return rv;
|
||||
}
|
||||
|
||||
void SetDio3AsTcxoCtrl (float voltage, uint32_t delay) {
|
||||
uint8_t buf[4];
|
||||
|
||||
// buf[0] = tcxoVoltage & 0x07;
|
||||
if (fabs (voltage - 1.6) <= 0.001) {
|
||||
buf[0] = SX126X_DIO3_OUTPUT_1_6;
|
||||
} else if (fabs (voltage - 1.7) <= 0.001) {
|
||||
buf[0] = SX126X_DIO3_OUTPUT_1_7;
|
||||
} else if (fabs (voltage - 1.8) <= 0.001) {
|
||||
buf[0] = SX126X_DIO3_OUTPUT_1_8;
|
||||
} else if (fabs (voltage - 2.2) <= 0.001) {
|
||||
buf[0] = SX126X_DIO3_OUTPUT_2_2;
|
||||
} else if (fabs (voltage - 2.4) <= 0.001) {
|
||||
buf[0] = SX126X_DIO3_OUTPUT_2_4;
|
||||
} else if (fabs (voltage - 2.7) <= 0.001) {
|
||||
buf[0] = SX126X_DIO3_OUTPUT_2_7;
|
||||
} else if (fabs (voltage - 3.0) <= 0.001) {
|
||||
buf[0] = SX126X_DIO3_OUTPUT_3_0;
|
||||
} else {
|
||||
buf[0] = SX126X_DIO3_OUTPUT_3_3;
|
||||
}
|
||||
|
||||
uint32_t delayValue = (float)delay / 15.625;
|
||||
buf[1] = (uint8_t)((delayValue >> 16) & 0xFF);
|
||||
buf[2] = (uint8_t)((delayValue >> 8) & 0xFF);
|
||||
buf[3] = (uint8_t)(delayValue & 0xFF);
|
||||
|
||||
WriteCommand (SX126X_CMD_SET_DIO3_AS_TCXO_CTRL, buf, 4); // 0x97
|
||||
}
|
||||
|
||||
void Calibrate (uint8_t calibParam) {
|
||||
uint8_t data = calibParam;
|
||||
WriteCommand (SX126X_CMD_CALIBRATE, &data, 1); // 0x89
|
||||
}
|
||||
|
||||
void SetDio2AsRfSwitchCtrl (uint8_t enable) {
|
||||
uint8_t data = enable;
|
||||
WriteCommand (SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL, &data, 1); // 0x9D
|
||||
}
|
||||
|
||||
void SetRfFrequency (uint32_t frequency) {
|
||||
uint8_t buf[4];
|
||||
uint32_t freq = 0;
|
||||
|
||||
CalibrateImage (frequency);
|
||||
|
||||
freq = (uint32_t)((double)frequency / (double)FREQ_STEP);
|
||||
buf[0] = (uint8_t)((freq >> 24) & 0xFF);
|
||||
buf[1] = (uint8_t)((freq >> 16) & 0xFF);
|
||||
buf[2] = (uint8_t)((freq >> 8) & 0xFF);
|
||||
buf[3] = (uint8_t)(freq & 0xFF);
|
||||
WriteCommand (SX126X_CMD_SET_RF_FREQUENCY, buf, 4); // 0x86
|
||||
}
|
||||
|
||||
void CalibrateImage (uint32_t frequency) {
|
||||
uint8_t calFreq[2];
|
||||
|
||||
if (frequency > 900000000) {
|
||||
calFreq[0] = 0xE1;
|
||||
calFreq[1] = 0xE9;
|
||||
} else if (frequency > 850000000) {
|
||||
calFreq[0] = 0xD7;
|
||||
calFreq[1] = 0xDB;
|
||||
} else if (frequency > 770000000) {
|
||||
calFreq[0] = 0xC1;
|
||||
calFreq[1] = 0xC5;
|
||||
} else if (frequency > 460000000) {
|
||||
calFreq[0] = 0x75;
|
||||
calFreq[1] = 0x81;
|
||||
} else if (frequency > 425000000) {
|
||||
calFreq[0] = 0x6B;
|
||||
calFreq[1] = 0x6F;
|
||||
}
|
||||
WriteCommand (SX126X_CMD_CALIBRATE_IMAGE, calFreq, 2); // 0x98
|
||||
}
|
||||
|
||||
void SetRegulatorMode (uint8_t mode) {
|
||||
uint8_t data = mode;
|
||||
WriteCommand (SX126X_CMD_SET_REGULATOR_MODE, &data, 1); // 0x96
|
||||
}
|
||||
|
||||
void SetBufferBaseAddress (uint8_t txBaseAddress, uint8_t rxBaseAddress) {
|
||||
uint8_t buf[2];
|
||||
|
||||
buf[0] = txBaseAddress;
|
||||
buf[1] = rxBaseAddress;
|
||||
WriteCommand (SX126X_CMD_SET_BUFFER_BASE_ADDRESS, buf, 2); // 0x8F
|
||||
}
|
||||
|
||||
void SetPowerConfig (int8_t power, uint8_t rampTime) {
|
||||
uint8_t buf[2];
|
||||
|
||||
if (power > 22) {
|
||||
power = 22;
|
||||
} else if (power < -3) {
|
||||
power = -3;
|
||||
}
|
||||
|
||||
buf[0] = power;
|
||||
buf[1] = (uint8_t)rampTime;
|
||||
WriteCommand (SX126X_CMD_SET_TX_PARAMS, buf, 2); // 0x8E
|
||||
}
|
||||
|
||||
void SetPaConfig (uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut) {
|
||||
uint8_t buf[4];
|
||||
|
||||
buf[0] = paDutyCycle;
|
||||
buf[1] = hpMax;
|
||||
buf[2] = deviceSel;
|
||||
buf[3] = paLut;
|
||||
WriteCommand (SX126X_CMD_SET_PA_CONFIG, buf, 4); // 0x95
|
||||
}
|
||||
|
||||
void SetOvercurrentProtection (float currentLimit) {
|
||||
if ((currentLimit >= 0.0) && (currentLimit <= 140.0)) {
|
||||
uint8_t buf[1];
|
||||
buf[0] = (uint8_t)(currentLimit / 2.5);
|
||||
WriteRegister (SX126X_REG_OCP_CONFIGURATION, buf, 1); // 0x08E7
|
||||
}
|
||||
}
|
||||
|
||||
void SetSyncWord (int16_t sync) {
|
||||
uint8_t buf[2];
|
||||
|
||||
buf[0] = (uint8_t)((sync >> 8) & 0x00FF);
|
||||
buf[1] = (uint8_t)(sync & 0x00FF);
|
||||
WriteRegister (SX126X_REG_LORA_SYNC_WORD_MSB, buf, 2); // 0x0740
|
||||
}
|
||||
|
||||
void SetDioIrqParams (uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask) {
|
||||
uint8_t buf[8];
|
||||
|
||||
buf[0] = (uint8_t)((irqMask >> 8) & 0x00FF);
|
||||
buf[1] = (uint8_t)(irqMask & 0x00FF);
|
||||
buf[2] = (uint8_t)((dio1Mask >> 8) & 0x00FF);
|
||||
buf[3] = (uint8_t)(dio1Mask & 0x00FF);
|
||||
buf[4] = (uint8_t)((dio2Mask >> 8) & 0x00FF);
|
||||
buf[5] = (uint8_t)(dio2Mask & 0x00FF);
|
||||
buf[6] = (uint8_t)((dio3Mask >> 8) & 0x00FF);
|
||||
buf[7] = (uint8_t)(dio3Mask & 0x00FF);
|
||||
WriteCommand (SX126X_CMD_SET_DIO_IRQ_PARAMS, buf, 8); // 0x08
|
||||
}
|
||||
|
||||
void SetStopRxTimerOnPreambleDetect (char enable) {
|
||||
ESP_LOGI (TAG, "SetStopRxTimerOnPreambleDetect enable=%d", enable);
|
||||
// uint8_t data = (uint8_t)enable;
|
||||
uint8_t data = 0;
|
||||
if (enable)
|
||||
data = 1;
|
||||
WriteCommand (SX126X_CMD_STOP_TIMER_ON_PREAMBLE, &data, 1); // 0x9F
|
||||
}
|
||||
|
||||
void SetLoRaSymbNumTimeout (uint8_t SymbNum) {
|
||||
uint8_t data = SymbNum;
|
||||
WriteCommand (SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT, &data, 1); // 0xA0
|
||||
}
|
||||
|
||||
void SetPacketType (uint8_t packetType) {
|
||||
uint8_t data = packetType;
|
||||
WriteCommand (SX126X_CMD_SET_PACKET_TYPE, &data, 1); // 0x01
|
||||
}
|
||||
|
||||
void SetModulationParams (uint8_t spreadingFactor, uint8_t bandwidth, uint8_t codingRate, uint8_t lowDataRateOptimize) {
|
||||
uint8_t data[4];
|
||||
// currently only LoRa supported
|
||||
data[0] = spreadingFactor;
|
||||
data[1] = bandwidth;
|
||||
data[2] = codingRate;
|
||||
data[3] = lowDataRateOptimize;
|
||||
WriteCommand (SX126X_CMD_SET_MODULATION_PARAMS, data, 4); // 0x8B
|
||||
}
|
||||
|
||||
void SetCadParams (uint8_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, uint8_t cadExitMode, uint32_t cadTimeout) {
|
||||
uint8_t data[7];
|
||||
data[0] = cadSymbolNum;
|
||||
data[1] = cadDetPeak;
|
||||
data[2] = cadDetMin;
|
||||
data[3] = cadExitMode;
|
||||
data[4] = (uint8_t)((cadTimeout >> 16) & 0xFF);
|
||||
data[5] = (uint8_t)((cadTimeout >> 8) & 0xFF);
|
||||
data[6] = (uint8_t)(cadTimeout & 0xFF);
|
||||
WriteCommand (SX126X_CMD_SET_CAD_PARAMS, data, 7); // 0x88
|
||||
}
|
||||
|
||||
void SetCad() {
|
||||
uint8_t data = 0;
|
||||
WriteCommand (SX126X_CMD_SET_CAD, &data, 0); // 0xC5
|
||||
}
|
||||
|
||||
uint16_t GetIrqStatus (void) {
|
||||
uint8_t data[3];
|
||||
ReadCommand (SX126X_CMD_GET_IRQ_STATUS, data, 3); // 0x12
|
||||
return (data[1] << 8) | data[2];
|
||||
}
|
||||
|
||||
void ClearIrqStatus (uint16_t irq) {
|
||||
uint8_t buf[2];
|
||||
|
||||
buf[0] = (uint8_t)(((uint16_t)irq >> 8) & 0x00FF);
|
||||
buf[1] = (uint8_t)((uint16_t)irq & 0x00FF);
|
||||
WriteCommand (SX126X_CMD_CLEAR_IRQ_STATUS, buf, 2); // 0x02
|
||||
}
|
||||
|
||||
void SetRx (uint32_t timeout) {
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "----- SetRx timeout=%d", timeout);
|
||||
}
|
||||
SetStandby (SX126X_STANDBY_RC);
|
||||
uint8_t buf[3];
|
||||
buf[0] = (uint8_t)((timeout >> 16) & 0xFF);
|
||||
buf[1] = (uint8_t)((timeout >> 8) & 0xFF);
|
||||
buf[2] = (uint8_t)(timeout & 0xFF);
|
||||
WriteCommand (SX126X_CMD_SET_RX, buf, 3); // 0x82
|
||||
|
||||
for (int retry = 0; retry < 10; retry++) {
|
||||
if ((GetStatus() & 0x70) == 0x50)
|
||||
break;
|
||||
vTaskDelay (pdMS_TO_TICKS (1));
|
||||
}
|
||||
if ((GetStatus() & 0x70) != 0x50) {
|
||||
ESP_LOGE (TAG, "SetRx Illegal Status");
|
||||
LoRaError (ERR_INVALID_SETRX_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
void SetTx (uint32_t timeoutInMs) {
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "----- SetTx timeoutInMs=%d", timeoutInMs);
|
||||
}
|
||||
SetStandby (SX126X_STANDBY_RC);
|
||||
uint8_t buf[3];
|
||||
uint32_t tout = timeoutInMs;
|
||||
if (timeoutInMs != 0) {
|
||||
uint32_t timeoutInUs = timeoutInMs * 1000;
|
||||
tout = (uint32_t)(timeoutInUs / 0.015625);
|
||||
}
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "SetTx timeoutInMs=%d" " tout=%d", timeoutInMs, tout);
|
||||
}
|
||||
buf[0] = (uint8_t)((tout >> 16) & 0xFF);
|
||||
buf[1] = (uint8_t)((tout >> 8) & 0xFF);
|
||||
buf[2] = (uint8_t)(tout & 0xFF);
|
||||
WriteCommand (SX126X_CMD_SET_TX, buf, 3); // 0x83
|
||||
|
||||
for (int retry = 0; retry < 10; retry++) {
|
||||
if ((GetStatus() & 0x70) == 0x60)
|
||||
break;
|
||||
vTaskDelay (1);
|
||||
}
|
||||
if ((GetStatus() & 0x70) != 0x60) {
|
||||
ESP_LOGE (TAG, "SetTx Illegal Status");
|
||||
LoRaError (ERR_INVALID_SETTX_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
int GetPacketLost() {
|
||||
return txLost;
|
||||
}
|
||||
|
||||
uint8_t GetRssiInst() {
|
||||
uint8_t buf[2];
|
||||
ReadCommand (SX126X_CMD_GET_RSSI_INST, buf, 2); // 0x15
|
||||
return buf[1];
|
||||
}
|
||||
|
||||
void GetRxBufferStatus (uint8_t *payloadLength, uint8_t *rxStartBufferPointer) {
|
||||
uint8_t buf[3];
|
||||
ReadCommand (SX126X_CMD_GET_RX_BUFFER_STATUS, buf, 3); // 0x13
|
||||
*payloadLength = buf[1];
|
||||
*rxStartBufferPointer = buf[2];
|
||||
}
|
||||
|
||||
void WaitForIdleBegin (unsigned long timeout, char *text) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
char stop = 0;
|
||||
for (int retry = 0; retry < 10; retry++) {
|
||||
if (retry == 9)
|
||||
stop = 1;
|
||||
char ret = WaitForIdle (BUSY_WAIT, text, stop);
|
||||
if (ret == 1)
|
||||
break;
|
||||
ESP_LOGW (TAG, "WaitForIdle fail retry=%d", retry);
|
||||
vTaskDelay (pdMS_TO_TICKS (10));
|
||||
}
|
||||
}
|
||||
|
||||
char WaitForIdle (unsigned long timeout, char *text, char stop) {
|
||||
char ret = 1;
|
||||
TickType_t start = xTaskGetTickCount();
|
||||
vTaskDelay (pdMS_TO_TICKS (1));
|
||||
while (xTaskGetTickCount() - start < (timeout / portTICK_PERIOD_MS)) {
|
||||
if (GPIO_ReadInputDataBit (GPIOE, GPIO_Pin_1) == 0)
|
||||
break;
|
||||
vTaskDelay (pdMS_TO_TICKS (1));
|
||||
}
|
||||
|
||||
if (GPIO_ReadInputDataBit (GPIOE, GPIO_Pin_1)) {
|
||||
if (stop) {
|
||||
ESP_LOGE (TAG, "WaitForIdle Timeout text=%s timeout=%lu start=%d", text, timeout, start);
|
||||
LoRaError (ERR_IDLE_TIMEOUT);
|
||||
} else {
|
||||
ESP_LOGW (TAG, "WaitForIdle Timeout text=%s timeout=%lu start=%d", text, timeout, start);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t ReadBuffer (uint8_t *rxData, int16_t rxDataLen) {
|
||||
uint8_t offset = 0;
|
||||
uint8_t payloadLength = 0;
|
||||
GetRxBufferStatus (&payloadLength, &offset);
|
||||
if (payloadLength > rxDataLen) {
|
||||
ESP_LOGW (TAG, "ReadBuffer rxDataLen too small. payloadLength=%d rxDataLen=%d", payloadLength, rxDataLen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start ReadBuffer", 1);
|
||||
|
||||
// start transfer
|
||||
uint8_t *buf;
|
||||
buf = malloc (payloadLength + 3);
|
||||
if (buf != NULL) {
|
||||
buf[0] = SX126X_CMD_READ_BUFFER; // 0x1E
|
||||
buf[1] = offset; // offset in rx fifo
|
||||
buf[2] = SX126X_CMD_NOP;
|
||||
memset (&buf[3], SX126X_CMD_NOP, payloadLength);
|
||||
spi_read_byte (buf, buf, payloadLength + 3);
|
||||
memcpy (rxData, &buf[3], payloadLength);
|
||||
free (buf);
|
||||
} else {
|
||||
ESP_LOGE (TAG, "ReadBuffer malloc fail");
|
||||
payloadLength = 0;
|
||||
}
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end ReadBuffer", 0);
|
||||
|
||||
return payloadLength;
|
||||
}
|
||||
|
||||
void WriteBuffer (uint8_t *txData, int16_t txDataLen) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start WriteBuffer", 1);
|
||||
|
||||
// start transfer
|
||||
uint8_t *buf;
|
||||
buf = malloc (txDataLen + 2);
|
||||
if (buf != NULL) {
|
||||
buf[0] = SX126X_CMD_WRITE_BUFFER; // 0x0E
|
||||
buf[1] = 0; // offset in tx fifo
|
||||
memcpy (&buf[2], txData, txDataLen);
|
||||
spi_write_byte (buf, txDataLen + 2);
|
||||
free (buf);
|
||||
} else {
|
||||
ESP_LOGE (TAG, "WriteBuffer malloc fail");
|
||||
}
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end WriteBuffer", 0);
|
||||
}
|
||||
|
||||
void WriteRegister (uint16_t reg, uint8_t *data, uint8_t numBytes) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start WriteRegister", 1);
|
||||
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "WriteRegister: REG=0x%02x", reg);
|
||||
for (uint8_t n = 0; n < numBytes; n++) {
|
||||
ESP_LOGI (TAG, "DataOut:%02x ", data[n]);
|
||||
}
|
||||
}
|
||||
|
||||
// start transfer
|
||||
uint8_t buf[16];
|
||||
buf[0] = SX126X_CMD_WRITE_REGISTER;
|
||||
buf[1] = (reg & 0xFF00) >> 8;
|
||||
buf[2] = reg & 0xff;
|
||||
memcpy (&buf[3], data, numBytes);
|
||||
spi_write_byte (buf, 3 + numBytes);
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end WriteRegister", 0);
|
||||
}
|
||||
|
||||
void ReadRegister (uint16_t reg, uint8_t *data, uint8_t numBytes) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start ReadRegister", 1);
|
||||
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "ReadRegister: REG=0x%02x", reg);
|
||||
}
|
||||
|
||||
// start transfer
|
||||
uint8_t buf[16];
|
||||
uint8_t buf2[16];
|
||||
memset (buf, SX126X_CMD_NOP, sizeof (buf));
|
||||
memset (buf2, SX126X_CMD_NOP, sizeof (buf2));
|
||||
buf[0] = SX126X_CMD_READ_REGISTER;
|
||||
buf[1] = (reg & 0xFF00) >> 8;
|
||||
buf[2] = reg & 0xff;
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "Reading bytes");
|
||||
}
|
||||
|
||||
spi_read_byte (buf2, buf, 4 + numBytes);
|
||||
ESP_LOGI (TAG, "read a byte");
|
||||
memcpy (data, &buf2[4], numBytes);
|
||||
if (debugPrint) {
|
||||
for (uint8_t n = 0; n < numBytes; n++) {
|
||||
ESP_LOGI (TAG, "DataIn:%02x ", data[n]);
|
||||
}
|
||||
}
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end ReadRegister", 0);
|
||||
}
|
||||
|
||||
// WriteCommand with retry
|
||||
void WriteCommand (uint8_t cmd, uint8_t *data, uint8_t numBytes) {
|
||||
uint8_t status;
|
||||
for (int retry = 1; retry < 10; retry++) {
|
||||
status = WriteCommand2 (cmd, data, numBytes);
|
||||
if (debugPrint) {
|
||||
ESP_LOGD (TAG, "status=%02x", status);
|
||||
}
|
||||
if (status == 0)
|
||||
break;
|
||||
ESP_LOGW (TAG, "WriteCommand2 status=%02x retry=%d", status, retry);
|
||||
}
|
||||
if (status != 0) {
|
||||
ESP_LOGE (TAG, "SPI Transaction error:0x%02x", status);
|
||||
LoRaError (ERR_SPI_TRANSACTION);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WriteCommand2 (uint8_t cmd, uint8_t *data, uint8_t numBytes) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdle (BUSY_WAIT, "start WriteCommand2", 1);
|
||||
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "WriteCommand: CMD=0x%02x", cmd);
|
||||
}
|
||||
|
||||
// start transfer
|
||||
uint8_t buf[16];
|
||||
buf[0] = cmd;
|
||||
memcpy (&buf[1], data, numBytes);
|
||||
spi_read_byte (buf, buf, numBytes + 1);
|
||||
|
||||
uint8_t status = 0;
|
||||
uint8_t cmd_status = buf[1] & 0xe;
|
||||
|
||||
switch (cmd_status) {
|
||||
case SX126X_STATUS_CMD_TIMEOUT:
|
||||
case SX126X_STATUS_CMD_INVALID:
|
||||
case SX126X_STATUS_CMD_FAILED:
|
||||
status = cmd_status;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
case 7:
|
||||
status = SX126X_STATUS_SPI_FAILED;
|
||||
break;
|
||||
// default: break; // success
|
||||
}
|
||||
|
||||
// wait for BUSY to go low
|
||||
WaitForIdle (BUSY_WAIT, "end WriteCommand2", 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
void ReadCommand (uint8_t cmd, uint8_t *data, uint8_t numBytes) {
|
||||
// ensure BUSY is low (state meachine ready)
|
||||
WaitForIdleBegin (BUSY_WAIT, "start ReadCommand");
|
||||
|
||||
if (debugPrint) {
|
||||
ESP_LOGI (TAG, "ReadCommand: CMD=0x%02x", cmd);
|
||||
}
|
||||
|
||||
// start transfer
|
||||
uint8_t buf[16];
|
||||
memset (buf, SX126X_CMD_NOP, sizeof (buf));
|
||||
buf[0] = cmd;
|
||||
spi_read_byte (buf, buf, 1 + numBytes);
|
||||
if (data != NULL && numBytes)
|
||||
memcpy (data, &buf[1], numBytes);
|
||||
|
||||
// wait for BUSY to go low
|
||||
vTaskDelay (1);
|
||||
WaitForIdle (BUSY_WAIT, "end ReadCommand", 0);
|
||||
}
|
Reference in New Issue
Block a user