/** * Author Wojciech Domski * www: www.Domski.pl * * work based on DORJI.COM sample code and * https://github.com/realspinner/SX1276_LoRa */ #include "SX1276.h" #include ////////////////////////////////// // logic ////////////////////////////////// __weak void SX1276_hw_init(SX1276_hw_t * hw) { SX1276_hw_SetNSS(hw, 1); HAL_GPIO_WritePin(hw->reset.port, hw->reset.pin, GPIO_PIN_SET); } __weak void SX1276_hw_SetNSS(SX1276_hw_t * hw, int value) { HAL_GPIO_WritePin(hw->nss.port, hw->nss.pin, (value == 1) ? GPIO_PIN_SET : GPIO_PIN_RESET); } __weak void SX1276_hw_Reset(SX1276_hw_t * hw) { SX1276_hw_SetNSS(hw, 1); HAL_GPIO_WritePin(hw->reset.port, hw->reset.pin, GPIO_PIN_RESET); SX1276_hw_DelayMs(1); HAL_GPIO_WritePin(hw->reset.port, hw->reset.pin, GPIO_PIN_SET); SX1276_hw_DelayMs(100); } #if 0 // PYJ.2019.04.01_BEGIN -- __weak void SX1276_hw_SPICommand(SX1276_hw_t * hw, uint8_t cmd) { SX1276_hw_SetNSS(hw, 0); HAL_SPI_Transmit(hw->spi, &cmd, 1, 1000); while (HAL_SPI_GetState(hw->spi) != HAL_SPI_STATE_READY) ; } #endif // PYJ.2019.04.01_END -- void SX1276_hw_SPICommand(SX1276_hw_t * hw, uint8_t cmd) { SX1276_hw_SetNSS(hw, 0); BLUECELL_SPI_Transmit(cmd); } #if 0 // PYJ.2019.04.01_BEGIN -- __weak uint8_t SX1276_hw_SPIReadByte(SX1276_hw_t * hw) { uint8_t txByte = 0x00; uint8_t rxByte = 0x00; SX1276_hw_SetNSS(hw, 0); HAL_SPI_TransmitReceive(hw->spi, &txByte, &rxByte, 1, 1000); return rxByte; } #endif // PYJ.2019.04.01_END -- uint8_t SX1276_hw_SPIReadByte(SX1276_hw_t * hw) { //uint8_t txByte = 0x00; uint8_t rxByte = 0x00; SX1276_hw_SetNSS(hw, 0); // HAL_SPI_TransmitReceive(hw->spi, &txByte, &rxByte, 1, 1000); rxByte = SpiRead(); return rxByte; } __weak void SX1276_hw_DelayMs(uint32_t msec) { HAL_Delay(msec); } __weak int SX1276_hw_GetDIO0(SX1276_hw_t * hw) { return (HAL_GPIO_ReadPin(hw->dio0.port, hw->dio0.pin) == GPIO_PIN_SET); } ////////////////////////////////// // logic ////////////////////////////////// uint8_t SX1276_SPIRead(SX1276_t * module, uint8_t addr) { uint8_t tmp; SX1276_hw_SPICommand(module->hw, addr); tmp = SX1276_hw_SPIReadByte(module->hw); SX1276_hw_SetNSS(module->hw, 1); return tmp; } uint8_t SX1276_SPIIDRead(SX1276_t * module, uint8_t addr) { uint8_t tmp; SX1276_hw_SPICommand(module->hw, addr); tmp = SX1276_hw_SPIReadByte(module->hw); SX1276_hw_SetNSS(module->hw, 1); return tmp; } void SX1276_SPIWrite(SX1276_t * module, uint8_t addr, uint8_t cmd) { SX1276_hw_SetNSS(module->hw, 0); SX1276_hw_SPICommand(module->hw, addr | 0x80); SX1276_hw_SPICommand(module->hw, cmd); // SPIGPIOTxRx(addr | 0x80,cmd); SX1276_hw_SetNSS(module->hw, 1); } void SX1276_SPIBurstRead(SX1276_t * module, uint8_t addr, uint8_t* rxBuf, uint8_t length) { uint8_t i; if (length <= 1) { return; } else { SX1276_hw_SetNSS(module->hw, 0); SX1276_hw_SPICommand(module->hw, addr); //printf("Test Data:"); for (i = 0; i < length; i++) { rxBuf[i] = SX1276_hw_SPIReadByte(module->hw); //printf("%02x ",rxBuf[i]); } //printf("\n"); SX1276_hw_SetNSS(module->hw, 1); } } void SX1276_SPIBurstWrite(SX1276_t * module, uint8_t addr, uint8_t* txBuf, uint8_t length) { uint8_t i; if (length <= 1) { return; } else { SX1276_hw_SetNSS(module->hw, 0); SX1276_hw_SPICommand(module->hw, addr | 0x80); // printf("Test Data:"); for (i = 0; i < length; i++) { SX1276_hw_SPICommand(module->hw, txBuf[i]); // printf("%02x ",txBuf[i]); } // printf("\n"); SX1276_hw_SetNSS(module->hw, 1); } } /* RegPaConfig - 0x09 PaSelect Mode Power Range Pout Formula 0 PA_HF or PA_LF on RFO_HF or RFO_LF -4 to +15dBm Pout=Pmax-(15-OutputPower), Pmax=10.8+0.6*MaxPower [dBm] 1 PA_HP on PA_BOOST, any frequency +2 to +17dBm Pout=17-(15-OutputPower) [dBm] PA_LF option doesn't seem to transmit anything. Not connected on these boards, perhaps? Use only PA_BOOST mode. PA_LF Pmax Pout 0 1 2 3 4 5 6 7 0 -4.2 -3.6 -3 -2.4 -1.8 -1.2 -0.6 0 1 -3.2 -2.6 -2 -1.4 -0.8 -0.2 0.4 1 2 -2.2 -1.6 -1 -0.4 0.2 0.8 1.4 2 3 -1.2 -0.6 0 0.6 1.2 1.8 2.4 3 4 -0.2 0.4 1 1.6 2.2 2.8 3.4 4 5 0.8 1.4 2 2.6 3.2 3.8 4.4 5 6 1.8 2.4 3 3.6 4.2 4.8 5.4 6 7 2.8 3.4 4 4.6 5.2 5.8 6.4 7 8 3.8 4.4 5 5.6 6.2 6.8 7.4 8 9 4.8 5.4 6 6.6 7.2 7.8 8.4 9 10 5.8 6.4 7 7.6 8.2 8.8 9.4 10 11 6.8 7.4 8 8.6 9.2 9.8 10.4 11 12 7.8 8.4 9 9.6 10.2 10.8 11.4 12 13 8.8 9.4 10 10.6 11.2 11.8 12.4 13 14 9.8 10.4 11 11.6 12.2 12.8 13.4 14 15 10.8 11.4 12 12.6 13.2 13.8 14.4 15 [dBm] PA_BOOST Pout OutputPower 0 2 dBm 1 3 dBm 2 4 dBm 3 5 dBm 4 6 dBm 5 7 dBm 6 8 dBm 7 9 dBm 8 10 dBm 9 11 dBm 10 12 dBm 11 13 dBm 12 14 dBm 13 15 dBm 14 16 dBm 15 17 dBm PaSelect 0 RFO pin. Maximum power of +14 dBm 1 PA_BOOST pin. Maximum power of +20 dBm MaxPower Select max output power: Pmax=10.8+0.6*MaxPower [dBm] OutputPower Pout=Pmax-(15-OutputPower) if PaSelect = 0 (RFO pins) Pout=17-(15-OutputPower) if PaSelect = 1 (PA_BOOST pin) PA_HF and PA_LF are high efficiency amplifiers capable of yielding RF power programmable in 1 dB steps from -4 to +14dBm directly into a 50 ohm load with low current consumption. PA_LF covers the lower bands (up to 525 MHz), whilst PA_HF will cover the upper bands (from 860 MHz). PA_HP (High Power), connected to the PA_BOOST pin, covers all frequency bands that the chip addresses. It permits continuous operation at up to +17 dBm and duty cycled operation at up to +20dBm. RegOcp - 0x0B OcpTrim IMAX Imax Formula 0 to 15 45 to 120 mA 45 + 5*OcpTrim [mA] 16 to 27 130 to 240 mA -30 + 10*OcpTrim [mA] 27+ 240 mA 240 mA The power amplifiers of RFM95/96/97/98(W) are protected against current over supply in adverse RF load conditions by the over current protection block. The current limiter value is controlled by the OcpTrim bits in RegOcp. Imax sets a limit on the current drain of the Power Amplifier only, hence the maximum current drain of the RFM96/77/78 is equal to Imax + IFS. Default 100mA changed to 240mA. */ void SX1276_set_power(SX1276_t * module) { // SX1276_SPIWrite(LR_RegPaConfig, (PaSelect << 7) | ((MaxPower & 0x07) << 4) | (OutputPower & 0x0F)); SX1276_SPIWrite(module,LR_RegPaConfig, (module->LoRa_Pa_boost << 7) | ((0x07) << 4) | (module->power & 0x0F)); //RFO -> 0 ~ 15 / PA_BOOST -> 2 ~ 17 } void SX1276_defaultConfig(SX1276_t * module) { SX1276_config(module, module->frequency, module->power, module->LoRa_Rate, module->LoRa_BW,module->LoRa_Lna,module->LoRa_Pa_boost); } void SX1276_config(SX1276_t * module, uint8_t frequency, uint8_t power, uint8_t LoRa_Rate, uint8_t LoRa_BW,uint8_t LoRa_Lna,uint8_t LoRa_PaBoost) { SX1276_sleep(module); //Change modem mode Must in Sleep mode SX1276_hw_DelayMs(15); SX1276_entryLoRa(module); //SX1276_SPIWrite(module, 0x5904); //?? Change digital regulator form 1.6V to 1.47V: see errata note SX1276_SPIBurstWrite(module, LR_RegFrMsb, (uint8_t*) SX1276_Frequency[frequency], 3); //setting frequency parameter //setting base parameter SX1276_SPIWrite(module, LR_RegPaConfig, SX1276_Power[power]); //Setting output power parameter SX1276_SPIWrite(module, LR_RegOcp, 0x0B); //RegOcp,Close Ocp // SX1276_SPIWrite(module, LR_RegLna, 0x20); //RegLNA,High & LNA Enable SX1276_set_power(module); SX1276_SPIWrite(module, LR_RegLna, LoRa_Lna); //RegLNA,High & LNA Enable if (SX1276_SpreadFactor[LoRa_Rate] == 6) { //SFactor=6 uint8_t tmp; SX1276_SPIWrite(module, LR_RegModemConfig1, ((SX1276_LoRaBandwidth[LoRa_BW] << 4) + (SX1276_CR << 1) + 0x01)); //Implicit Enable CRC Enable(0x02) & Error Coding rate 4/5(0x01), 4/6(0x02), 4/7(0x03), 4/8(0x04) SX1276_SPIWrite(module,LR_RegModemConfig2,((SX1276_SpreadFactor[LoRa_Rate] << 4) + (SX1276_CRC << 2) + 0x03)); tmp = SX1276_SPIRead(module, 0x31); tmp &= 0xF8; tmp |= 0x05; SX1276_SPIWrite(module, 0x31, tmp); SX1276_SPIWrite(module, 0x37, 0x0C); } else { SX1276_SPIWrite(module, LR_RegModemConfig1, ((SX1276_LoRaBandwidth[LoRa_BW] << 4) + (SX1276_CR << 1) + 0x00)); //Explicit Enable CRC Enable(0x02) & Error Coding rate 4/5(0x01), 4/6(0x02), 4/7(0x03), 4/8(0x04) SX1276_SPIWrite(module, LR_RegModemConfig2, ((SX1276_SpreadFactor[LoRa_Rate] << 4) + (SX1276_CRC << 2) + 0x03)); //SFactor & LNA gain set by the internal AGC loop } SX1276_SPIWrite(module, LR_RegSymbTimeoutLsb, 0xFF); //RegSymbTimeoutLsb Timeout = 0x3FF(Max) SX1276_SPIWrite(module, LR_RegPreambleMsb, 0x00); //RegPreambleMsb SX1276_SPIWrite(module, LR_RegPreambleLsb, 12); //RegPreambleLsb 8+4=12byte Preamble SX1276_SPIWrite(module, REG_LR_DIOMAPPING2, 0x01); //RegDioMapping2 DIO5=00, DIO4=01 module->readBytes = 0; SX1276_standby(module); //Entry standby mode } void SX1276_standby(SX1276_t * module) { // SX1276_SPIWrite(module, LR_RegOpMode, 0x09); SX1276_SPIWrite(module, LR_RegOpMode, 0x01); module->status = STANDBY; } void SX1276_sleep(SX1276_t * module) { // SX1276_SPIWrite(module, LR_RegOpMode, 0x08); SX1276_SPIWrite(module, LR_RegOpMode, 0x80); module->status = SLEEP; } void SX1276_entryLoRa(SX1276_t * module) { // SX1276_SPIWrite(module, LR_RegOpMode, 0x88); SX1276_SPIWrite(module, LR_RegOpMode, 0x80); } void SX1276_clearLoRaIrq(SX1276_t * module) { SX1276_SPIWrite(module, LR_RegIrqFlags, 0xFF); } int SX1276_LoRaEntryRx(SX1276_t * module, uint8_t length, uint32_t timeout) { uint8_t addr; module->packetLength = length; SX1276_defaultConfig(module); //Setting base parameter SX1276_SPIWrite(module, REG_LR_PADAC, 0x84); //Normal and RX SX1276_SPIWrite(module, LR_RegHopPeriod, 0xFF); //No FHSS SX1276_SPIWrite(module, REG_LR_DIOMAPPING1, 0x01);//DIO=00,DIO1=00,DIO2=00, DIO3=01 SX1276_SPIWrite(module, LR_RegIrqFlagsMask, 0x3F);//Open RxDone interrupt & Timeout SX1276_clearLoRaIrq(module); SX1276_SPIWrite(module, LR_RegPayloadLength, length);//Payload Length 21byte(this register must difine when the data long of one byte in SF is 6) addr = SX1276_SPIRead(module, LR_RegFifoRxBaseAddr); //Read RxBaseAddr SX1276_SPIWrite(module, LR_RegFifoAddrPtr, addr); //RxBaseAddr->FiFoAddrPtr SX1276_SPIWrite(module, LR_RegOpMode, 0x85); //Mode//Low Frequency Mode //SX1276_SPIWrite(module, LR_RegOpMode,0x05); //Continuous Rx Mode //High Frequency Mode module->readBytes = 0; while (1) { if ((SX1276_SPIRead(module, LR_RegModemStat) & 0x04) == 0x04) { //Rx-on going RegModemStat module->status = RX; return 1; } if (--timeout == 0) { printf( "Function : %s Line : %d \n",__func__,__LINE__); NVIC_SystemReset(); SX1276_hw_Reset(module->hw); SX1276_defaultConfig(module); return 0; } SX1276_hw_DelayMs(1); } } uint8_t SX1276_LoRaRxPacket(SX1276_t * module) { unsigned char addr; unsigned char packet_size; if (SX1276_hw_GetDIO0(module->hw)) { memset(module->rxBuffer, 0x00, SX1276_MAX_PACKET); addr = SX1276_SPIRead(module, LR_RegFifoRxCurrentaddr); //last packet addr SX1276_SPIWrite(module, LR_RegFifoAddrPtr, addr); //RxBaseAddr -> FiFoAddrPtr if (module->LoRa_Rate == SX1276_LORA_SF_6) { //When SpreadFactor is six,will used Implicit Header mode(Excluding internal packet length) packet_size = module->packetLength; } else { packet_size = SX1276_SPIRead(module, LR_RegRxNbBytes); //Number for received bytes } SX1276_SPIBurstRead(module, 0x00, module->rxBuffer, packet_size); module->readBytes = packet_size; SX1276_clearLoRaIrq(module); } return module->readBytes; } int SX1276_LoRaEntryTx(SX1276_t * module, uint8_t length, uint32_t timeout) { uint8_t addr; uint8_t temp; module->packetLength = length; SX1276_defaultConfig(module); //setting base parameter SX1276_SPIWrite(module, REG_LR_PADAC, 0x87); //Tx for 20dBm SX1276_SPIWrite(module, LR_RegHopPeriod, 0x00); //RegHopPeriod NO FHSS SX1276_SPIWrite(module, REG_LR_DIOMAPPING1, 0x41); //DIO0=01, DIO1=00,DIO2=00, DIO3=01 SX1276_clearLoRaIrq(module); SX1276_SPIWrite(module, LR_RegIrqFlagsMask, 0xF7); //Open TxDone interrupt SX1276_SPIWrite(module, LR_RegPayloadLength, length); //RegPayloadLength 21byte addr = SX1276_SPIRead(module, LR_RegFifoTxBaseAddr); //RegFiFoTxBaseAddr SX1276_SPIWrite(module, LR_RegFifoAddrPtr, addr); //RegFifoAddrPtr while (1) { temp = SX1276_SPIRead(module, LR_RegPayloadLength); if (temp == length) { module->status = TX; return 1; } if (--timeout == 0) { printf( "Function : %s Line : %d \n",__func__,__LINE__); NVIC_SystemReset(); SX1276_hw_Reset(module->hw); SX1276_defaultConfig(module); return 0; } } } int SX1276_LoRaTxPacket(SX1276_t * module, uint8_t* txBuffer, uint8_t length, uint32_t timeout) { SX1276_SPIBurstWrite(module, 0x00, txBuffer, length); // SX1276_SPIWrite(module, LR_RegOpMode, 0x8b); //Tx Mode SX1276_SPIWrite(module, LR_RegOpMode, 0x83); //Tx Mode while (1) { if (SX1276_hw_GetDIO0(module->hw)) { //if(Get_NIRQ()) //Packet send over SX1276_SPIRead(module, LR_RegIrqFlags); SX1276_clearLoRaIrq(module); //Clear irq SX1276_standby(module); //Entry Standby mode return 1; } if (--timeout == 0) { printf( "Function : %s Line : %d \n",__func__,__LINE__); NVIC_SystemReset(); SX1276_hw_Reset(module->hw); SX1276_defaultConfig(module); return 0; } SX1276_hw_DelayMs(1); } } void SX1276_begin(SX1276_t * module, uint8_t frequency, uint8_t power, uint8_t LoRa_Rate, uint8_t LoRa_BW, uint8_t packetLength,uint8_t LoRa_Lna,uint8_t LoRa_PaBoost) { SX1276_hw_init(module->hw); module->frequency = frequency; module->power = power; module->LoRa_Rate = LoRa_Rate; module->LoRa_BW = LoRa_BW; module->packetLength = packetLength; module->LoRa_Lna = LoRa_Lna; module->LoRa_Pa_boost = LoRa_PaBoost; SX1276_defaultConfig(module); } int SX1276_transmit(SX1276_t * module, uint8_t* txBuf, uint8_t length, uint32_t timeout) { if (SX1276_LoRaEntryTx(module, length, timeout)) { return SX1276_LoRaTxPacket(module, txBuf, length, timeout); } return 0; } int SX1276_receive(SX1276_t * module, uint8_t length, uint32_t timeout) { return SX1276_LoRaEntryRx(module, length, timeout); } uint8_t SX1276_available(SX1276_t * module) { return SX1276_LoRaRxPacket(module); } uint8_t SX1276_read(SX1276_t * module, uint8_t* rxBuf, uint8_t length) { if (length != module->readBytes) length = module->readBytes; memcpy(rxBuf, module->rxBuffer, length); rxBuf[length] = '\0'; module->readBytes = 0; return length; } uint8_t SX1276_RSSI_LoRa(SX1276_t * module) { uint32_t temp = 10; temp = SX1276_SPIRead(module, LR_RegRssiValue); //Read RegRssiValue, Rssi value temp = temp + 127 - 137; //127:Max RSSI, 137:RSSI offset return (uint8_t) temp; } uint8_t SX1276_RSSI(SX1276_t * module) { uint8_t temp = 0xff; temp = SX1276_SPIRead(module, RegRssiValue); temp = 127 - (temp >> 1); //127:Max RSSI return temp; }