SX1261 and sx1262 common library

Dependents:   SX126xDevKit SX1262PingPong SX126X_TXonly SX126X_PingPong_Demo ... more

Fork of SX126xLib by Gregory Cristian

sx126x.cpp

Committer:
GregCr
Date:
2016-10-12
Revision:
3:7e3595a9ebe0
Parent:
2:4ff11ea92fbe
Child:
4:c6ef863d0b07

File content as of revision 3:7e3595a9ebe0:

/*
 / _____)             _              | |
( (____  _____ ____ _| |_ _____  ____| |__
 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 _____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
    (C)2016 Semtech

Description: Handling of the node configuration protocol

License: Revised BSD License, see LICENSE.TXT file include in the project

Maintainer: Miguel Luis, Gregory Cristian and Matthieu Verdy
*/
#include "mbed.h"
#include "sx126x.h"
#include "sx126x-hal.h"

#include "pram_c005.h"

/*!
 * Radio registers definition
 *
 */
typedef struct
{
    uint16_t      Addr;                             //!< The address of the register
    uint8_t       Value;                            //!< The value of the register
}RadioRegisters_t;

// [TODO] Is this also applicable for the V2 version of the chip
/*!
 * \brief Radio hardware registers initialization definition
 */
// { Address, RegValue }

#define RADIO_INIT_REGISTERS_VALUE \
{                                  \
    { 0x0722, 0x53 },              \
    { 0x0889, 0x01 },              \
}

/*!
 * \brief Radio hardware registers initialization
 */
const RadioRegisters_t RadioRegsInit[] = RADIO_INIT_REGISTERS_VALUE;

void SX126x::Init( void )
{
    Reset( );
    IoIrqInit( dioIrq );
    Wakeup( );
/****************************************************/
    SetStandby( STDBY_RC );
    WriteRegister( 0x610, 0x10 );
    for( uint16_t i = 0; i < PRAM_COUNT; i++ )
    {
        uint32_t val = pram[i];
        WriteRegister( 0x8000 + 4 * i, 0 );
        WriteRegister( 0x8001 + 4 * i, ( val >> 16 ) & 0xff );
        WriteRegister( 0x8002 + 4 * i, ( val >> 8 ) & 0xff  );
        WriteRegister( 0x8003 + 4 * i, val & 0xff );
    }
    WriteRegister( 0x610, 0x00 );
    WriteCommand( RADIO_SET_PRAMSWAPCMD, ( uint8_t[] ) { 2 } , 1 );  //  RADIO_SET_PRAMSWAPCMD      = 0x8D,
/****************************************************/
    SetRegistersDefault( );
}

void SX126x::SetRegistersDefault( void )
{
    for( uint8_t i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters_t ); i++ )
    {
        WriteRegister( RadioRegsInit[i].Addr, RadioRegsInit[i].Value );
    }
}

uint16_t SX126x::GetFirmwareVersion( void )
{
    return( ( ( ReadRegister( 0xA8 ) ) << 8 ) | ( ReadRegister( 0xA9 ) ) );
}

RadioStatus_t SX126x::GetStatus( void )
{
    uint8_t stat = 0;
    RadioStatus_t status;

    ReadCommand( RADIO_GET_STATUS, ( uint8_t * )&stat, 1 );
    status.Value = stat;
    
    
    return( status );
}

void SX126x::SetSleep( SleepParams_t sleepConfig )
{

    WriteCommand( RADIO_SET_SLEEP, &sleepConfig.Value, 1 );
    OperatingMode = MODE_SLEEP;
}

void SX126x::SetStandby( RadioStandbyModes_t standbyConfig )
{
    WriteCommand( RADIO_SET_STANDBY, ( uint8_t* )&standbyConfig, 1 );
    if( standbyConfig == STDBY_RC )
    {
        OperatingMode = MODE_STDBY_RC;
    }
    else
    {
        OperatingMode = MODE_STDBY_XOSC;
    }
}

void SX126x::SetFs( void )
{
    WriteCommand( RADIO_SET_FS, 0, 0 );
    OperatingMode = MODE_FS;
}

void SX126x::SetTx( uint32_t timeout )
{
    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( RADIO_SET_TX, buf, 3 );
    OperatingMode = MODE_TX;
}

void SX126x::SetRx( uint32_t timeout )
{
    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( RADIO_SET_RX, buf, 3 );
    OperatingMode = MODE_RX;
}

void SX126x::SetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime )
{
    uint8_t buf[6];

    buf[0] = ( uint8_t )( ( rxTime >> 16 ) & 0xFF );
    buf[1] = ( uint8_t )( ( rxTime >> 8 ) & 0xFF );
    buf[2] = ( uint8_t )( rxTime & 0xFF );
     buf[0] = ( uint8_t )( ( sleepTime >> 16 ) & 0xFF );
    buf[1] = ( uint8_t )( ( sleepTime >> 8 ) & 0xFF );
    buf[2] = ( uint8_t )( sleepTime & 0xFF );
    WriteCommand( RADIO_SET_RXDUTYCYCLE, buf, 6 );
    OperatingMode = MODE_RX;
}

void SX126x::SetCad( void )
{
    WriteCommand( RADIO_SET_CAD, 0, 0 );
    OperatingMode = MODE_CAD;
}

void SX126x::SetAutoTxRx( uint32_t time, uint8_t intMode, uint32_t timeout )
{
    uint32_t compensatedTime = time - ( uint16_t )AUTO_RX_TX_OFFSET;
    uint8_t buf[7];

    buf[0] = ( uint8_t )( ( compensatedTime >> 16 ) & 0xFF );
    buf[1] = ( uint8_t )( ( compensatedTime >> 8 ) & 0xFF );
    buf[2] = ( uint8_t )( compensatedTime & 0xFF );
    buf[3] = intMode;
    buf[4] = ( uint8_t )( ( timeout >> 16 ) & 0xFF );
    buf[5] = ( uint8_t )( ( timeout >> 8 ) & 0xFF );
    buf[6] = ( uint8_t )( timeout & 0xFF );
    WriteCommand( RADIO_SET_AUTOTXRX, buf, 7 );
}


void SX126x::SetTxContinuousWave( void )
{
    WriteCommand( RADIO_SET_TXCONTINUOUSWAVE, 0, 0 );
}

void SX126x::SetTxContinuousPreamble( void )
{
    WriteCommand( RADIO_SET_TXCONTINUOUSPREAMBLE, 0, 0 );
}

void SX126x::SetPacketType( RadioPacketType_t packetType )
{
    // Save packet type internally to avoid questioning the radio
    this->PacketType = packetType;

    WriteCommand( RADIO_SET_PACKETTYPE, ( uint8_t* )&packetType, 1 );
}

RadioPacketType_t SX126x::GetPacketType( void )
{
    return this->PacketType;
}

void SX126x::SetRfFrequency( uint32_t frequency )
{
    uint8_t buf[4];
    uint32_t freq = 0;

    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( RADIO_SET_RFFREQUENCY, buf, 3 );
}

void SX126x::SetTxParams( int8_t power, RadioRampTimes_t rampTime )
{
    uint8_t buf[2];
    
    buf[0] = power ;
    buf[1] = ( uint8_t )rampTime;
    WriteCommand( RADIO_SET_TXPARAMS, buf, 2 );
}

void SX126x::SetCadConfig( RadioLoRaCadSymbols_t cadSymbolNum , uint8_t cadExitMode, uint32_t cadRxTxTimeout)
{
    uint8_t buf[5];
    buf[0] = ( uint8_t )cadSymbolNum;
    buf[1] = cadExitMode;
    buf[2] = ( uint8_t )( ( cadRxTxTimeout >> 16 ) & 0xFF );
    buf[3] = ( uint8_t )( ( cadRxTxTimeout >> 8 ) & 0xFF );
    buf[4] = ( uint8_t )( cadRxTxTimeout & 0xFF );
    WriteCommand( RADIO_SET_CADPARAMS, buf, 5 );
    OperatingMode = MODE_CAD;
}

void SX126x::SetBufferBaseAddresses( uint8_t txBaseAddress, uint8_t rxBaseAddress )
{
    uint8_t buf[2];

    buf[0] = txBaseAddress;
    buf[1] = rxBaseAddress;
    WriteCommand( RADIO_SET_BUFFERBASEADDRESS, buf, 2 );
}

void SX126x::SetModulationParams( ModulationParams_t *modulationParams )
{
    uint8_t n;
    uint32_t tempVal = 0;
    uint8_t buf[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    
    // Check if required configuration corresponds to the stored packet type
    // If not, silently update radio packet type
    if( this->PacketType != modulationParams->PacketType )
    {
        this->SetPacketType( modulationParams->PacketType );
    }

    switch( modulationParams->PacketType )
    {
        case PACKET_TYPE_GFSK:
            tempVal = ( uint32_t )( ( double )XTAL_FREQ / ( double )modulationParams->Params.Gfsk.BitRate );
            tempVal = tempVal * 32;
            buf[0] = ( tempVal >> 16 ) & 0xFF;
            buf[1] = ( tempVal >> 8 ) & 0xFF;
            buf[2] = tempVal & 0xFF;
            
            buf[3] = modulationParams->Params.Gfsk.PulseShape;
            buf[4] = modulationParams->Params.Gfsk.BW;
        
            tempVal = ( uint32_t )( ( double )FREQ_STEP * ( double )modulationParams->Params.Gfsk.Fdev );
            buf[5] = ( tempVal >> 16 ) & 0xFF;
            buf[6] = ( tempVal >> 8 ) & 0xFF;
            buf[7] = ( tempVal& 0xFF );
            n = 8;
            break;
        
        case PACKET_TYPE_LORA:
            n = 3;
            buf[0] = modulationParams->Params.LoRa.SpreadingFactor;
            buf[1] = modulationParams->Params.LoRa.Bandwidth;
            buf[2] = modulationParams->Params.LoRa.CodingRate;       
            break;
        
        case PACKET_TYPE_RESERVED:
            return;
    }
    WriteCommand( RADIO_SET_MODULATIONPARAMS, buf, n );
}

void SX126x::SetPacketParams( PacketParams_t *packetParams )
{
    uint8_t n;
    
    // Check if required configuration corresponds to the stored packet type
    // If not, silently update radio packet type
    if( this->PacketType != packetParams->PacketType )
    {
        this->SetPacketType( packetParams->PacketType );
    }

    switch( packetParams->PacketType )
    {
        case PACKET_TYPE_GFSK:
            n = 8;
            break; 
        case PACKET_TYPE_LORA:
            n = 5;
            break;
        case PACKET_TYPE_RESERVED:
           return;
    }
    WriteCommand( RADIO_SET_PACKETPARAMS, packetParams->Params.Buffer, n );
}

void SX126x::GetRxBufferStatus( uint8_t *payloadLength, uint8_t *rxStartBufferPointer )
{
    uint8_t status[2];

    ReadCommand( RADIO_GET_RXBUFFERSTATUS, status, 2 );

    *payloadLength = status[0];
    *rxStartBufferPointer = status[1];
}

void SX126x::GetPacketStatus( PacketStatus_t *pktStatus )
{
    uint8_t status[3];


    ReadCommand( RADIO_GET_PACKETSTATUS, status, 3 );

    pktStatus->packetType = this -> GetPacketType( );
    switch( pktStatus->packetType )
    {
        case PACKET_TYPE_GFSK:
            pktStatus->Gfsk.RxStatus = status[0];
            pktStatus->Gfsk.RssiSync = -status[1] / 2;
            pktStatus->Gfsk.RssiAvg = -status[2] / 2;
            break;

        case PACKET_TYPE_LORA:
            pktStatus->LoRa.RssiPkt = -status[0] / 2;
            ( status[1] < 128 ) ? ( pktStatus->LoRa.SnrPkt = status[1] / 4 ) : ( pktStatus->LoRa.SnrPkt = ( ( status[1] - 256 ) /4 ) );
            pktStatus->LoRa.SignalRssiPkt = -status[2] / 2;
            break;

        case PACKET_TYPE_RESERVED:
            // In that specific case, we set everything in the pktStatus to zeros
            // and reset the packet type accordingly
            memset( pktStatus, 0, sizeof( PacketStatus_t ) );
            pktStatus->packetType = PACKET_TYPE_RESERVED;
            break;
    }
}

int8_t SX126x::GetRssiInst( void )
{
    int8_t rssi;

    ReadCommand( RADIO_GET_RSSIINST, ( uint8_t* )&rssi, 1 );
    return -rssi / 2;
}

void SX126x::GetStats( RxCounter_t *rxCounter )
{
    uint8_t status[6];


    rxCounter->packetType = this -> GetPacketType( );
    ReadCommand( RADIO_GET_STATS, status, 6 );
    rxCounter->NbPktReceived = ( status[0] << 8 ) | status[1];
    rxCounter->NbPktCrcOk = ( status[2] << 8 ) | status[3];
    rxCounter->NbPktLengthError = ( status[4] << 8 ) | status[4];
}

void SX126x::ResetStats(  )
{
    uint8_t status[6];

    memset( status, 0, sizeof( status ) );
    WriteCommand( RADIO_RESET_STATS, status, 6 );
}

uint8_t SX126x::GetError()
{
    uint8_t error;

    ReadCommand( RADIO_GET_ERROR, &error, 1 );
    return error; 
}


void SX126x::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( RADIO_CFG_DIOIRQ, buf, 8 );
}

uint16_t SX126x::GetIrqStatus( void )
{
    uint8_t irqStatus[2];
    ReadCommand( RADIO_GET_IRQSTATUS, irqStatus, 2 );
    return ( irqStatus[0] << 8 ) | irqStatus[1];
}

void SX126x::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( RADIO_CLR_IRQSTATUS, buf, 2 );
}

void SX126x::Calibrate( CalibrationParams_t calibParam )
{
    WriteCommand( RADIO_CALIBRATE, &calibParam.Value, 1 );
}

void SX126x::SetRegulatorMode( uint8_t mode )
{
    WriteCommand( RADIO_SET_REGULATORMODE, &mode, 1 );
}


void SX126x::SetLongPreamble( uint8_t enable )
{
    WriteCommand( RADIO_SET_LONGPREAMBLE, &enable, 1 );
}

void SX126x::SetPayload( uint8_t *buffer, uint8_t size )
{
    WriteBuffer( 0x00, buffer, size );
}

uint8_t SX126x::GetPayload( uint8_t *buffer, uint8_t *size,  uint8_t maxSize )
{
    uint8_t offset;

    GetRxBufferStatus( size, &offset );
    if(*size > maxSize)
    {
        return 1;
    }
    ReadBuffer( offset, buffer, *size );
    return 0;
}

void SX126x::SendPayload( uint8_t *payload, uint8_t size, uint32_t timeout )
{
    SetPayload( payload, size );
    SetTx( timeout );
}

uint8_t SX126x::SetSyncWord( uint8_t *syncWord )
{
    WriteRegister( REG_LR_SYNCWORDBASEADDRESS, syncWord, 8 );
    return 0;
}

void SX126x::SetCrcSeed( uint16_t seed )
{
    uint8_t buf[2];
    buf[0] = ( uint8_t )( ( seed >> 8 ) & 0xFF );
    buf[1] = ( uint8_t )( seed & 0xFF );
    
    switch( GetPacketType( ) )
    {
        case PACKET_TYPE_GFSK:
            WriteRegister( REG_LR_CRCSEEDBASEADDR, buf, 2 );
            break;
        default:
            break;
    }
}

void SX126x::SetCrcPolynomial( uint16_t polynomial )
{
    uint8_t buf[2];
    buf[0] = ( uint8_t )( ( polynomial >> 8 ) & 0xFF );
    buf[1] = ( uint8_t )( polynomial & 0xFF );
    
    switch( GetPacketType( ) )
    {
        case PACKET_TYPE_GFSK:
            WriteRegister( REG_LR_CRCPOLYBASEADDR, buf, 2 );
            break;
        default:
            break;
    }
}

void SX126x::SetWhiteningSeed( uint8_t seed )
{
    switch( GetPacketType( ) )
    {
        case PACKET_TYPE_GFSK:
            WriteRegister( REG_LR_WHITSEEDBASEADDR, seed );
            break;
        default:
            break;
    }
}

int8_t SX126x::ParseHexFileLine( char* line )
{
    uint16_t addr;
    uint16_t n;
    uint8_t code;
    uint8_t bytes[256];

    if( GetHexFileLineFields( line, bytes, &addr, &n, &code ) != 0 )
    {
        if( code == 0 )
        {
            WriteRegister( addr, bytes, n );
        }
        if( code == 1 )
        { // end of file
            //return 2;
        }
        if( code == 2 )
        { // begin of file
            //return 3;
        }
    }
    else
    {
        return 0;
    }
    return 1;
}

int8_t SX126x::GetHexFileLineFields( char* line, uint8_t *bytes, uint16_t *addr, uint16_t *num, uint8_t *code )
{
    uint16_t sum, len, cksum;
    char *ptr;

    *num = 0;
    if( line[0] != ':' )
    {
        return 0;
    }
    if( strlen( line ) < 11 )
    {
        return 0;
    }
    ptr = line + 1;
    if( !sscanf( ptr, "%02hx", &len ) )
    {
        return 0;
    }
    ptr += 2;
    if( strlen( line ) < ( 11 + ( len * 2 ) ) )
    {
        return 0;
    }
    if( !sscanf( ptr, "%04hx", addr ) )
    {
        return 0;
    }
    ptr += 4;
    if( !sscanf( ptr, "%02hhx", code ) )
    {
        return 0;
    }
    ptr += 2;
    sum = ( len & 255 ) + ( ( *addr >> 8 ) & 255 ) + ( *addr & 255 ) + ( ( *code >> 8 ) & 255 ) + ( *code & 255 );
    while( *num != len )
    {
        if( !sscanf( ptr, "%02hhx", &bytes[*num] ) )
        {
            return 0;
        }
        ptr += 2;
        sum += bytes[*num] & 255;
        ( *num )++;
        if( *num >= 256 )
        {
            return 0;
        }
    }
    if( !sscanf( ptr, "%02hx", &cksum ) )
    {
        return 0;
    }
    if( ( ( sum & 255 ) + ( cksum & 255 ) ) & 255 )
    {
        return 0; // checksum error
    }

    return 1;
}

void SX126x::OnDioIrq( void )
{
    if( onCustomDioIrq != NULL )
    {
        onCustomDioIrq();
        return;
    }

    uint16_t irqRegs = GetIrqStatus( );
    LastIrqs = irqRegs;
//    printf("0x%04x\n\r", irqRegs );
    ClearIrqStatus( IRQ_RADIO_ALL );


#if( SX126x_DEBUG == 1 )
    DigitalOut TEST_PIN_1( D14 );
    DigitalOut TEST_PIN_2( D15 );
    for( int i = 0x8000; i != 0; i >>= 1 )
    {
        TEST_PIN_2 = 0;
        TEST_PIN_1 = ( ( irqRegs & i ) != 0 ) ? 1 : 0;
        TEST_PIN_2 = 1;
    }
    TEST_PIN_1 = 0;
    TEST_PIN_2 = 0;
#endif

    if( ( irqRegs & IRQ_TX_DONE ) == IRQ_TX_DONE )
    {
        if( txDone != NULL )
        {
            txDone( );
        }
    }

    if( ( irqRegs & IRQ_RX_DONE ) == IRQ_RX_DONE )
    {
        if( ( irqRegs & IRQ_CRC_ERROR ) == IRQ_CRC_ERROR )
        {
            if( rxError != NULL )
            {
                rxError( IRQ_CRC_ERROR_CODE );
            }
        }
        else
        {
            if( rxDone != NULL )
            {
                rxDone( );
            }
        }
    }

    if( ( irqRegs & IRQ_RX_TX_TIMEOUT ) == IRQ_RX_TX_TIMEOUT )
    {
        if( ( rxTxTimeout != NULL ) && ( OperatingMode == MODE_TX ) )
        {
            rxTxTimeout( IRQ_TX_TIMEOUT );
        }
        else if( ( rxTxTimeout != NULL ) && ( OperatingMode == MODE_RX ) )
        {
            rxTxTimeout( IRQ_RX_TIMEOUT );
        }
        else
        {
            rxTxTimeout( IRQ_XYZ );
        }
    }
    
/*
    //IRQ_PREAMBLE_DETECTED                   = 0x0004,
    if( irqRegs & IRQ_PREAMBLE_DETECTED )
    {
        if( rxPblSyncWordHeader != NULL )
        {
            rxPblSyncWordHeader( IRQ_PBL_DETECT_CODE);
            
        }
    }

    //IRQ_SYNCWORD_VALID                      = 0x0008,
    if( irqRegs & IRQ_SYNCWORD_VALID )
    {
        if( rxPblSyncWordHeader != NULL )
        {
            rxPblSyncWordHeader( IRQ_SYNCWORD_VALID_CODE  );
        }
    }

    //IRQ_HEADER_VALID                        = 0x0010,
    if ( irqRegs & IRQ_HEADER_VALID ) 
    {
        if( rxPblSyncWordHeader != NULL )
        {
            rxPblSyncWordHeader( IRQ_HEADER_VALID_CODE );
        }
    } 

    //IRQ_HEADER_ERROR                        = 0x0020,    
    if( irqRegs & IRQ_HEADER_ERROR )
    {
        if( rxError != NULL )
        {
            rxError( IRQ_HEADER_ERROR_CODE );
        }
    }  
    
    //IRQ_CAD_DONE                            = 0x0080,
    //IRQ_CAD_ACTIVITY_DETECTED               = 0x0100,
    if(  irqRegs & IRQ_CAD_DONE )
    {
        bool detected = ( ( irqRegs & IRQ_CAD_ACTIVITY_DETECTED ) == IRQ_CAD_ACTIVITY_DETECTED );

        if( cadDone != NULL )
        {
            cadDone( detected );
        }
                    
    }*/
}