SX1261 and sx1262 common library

Dependents:   SX126xDevKit SX1262PingPong SX126X_TXonly SX126X_PingPong_Demo ... more

Fork of SX126xLib by Gregory Cristian

sx126x-hal.cpp

Committer:
GregCr
Date:
2016-09-06
Revision:
0:deaafdfde3bb
Child:
1:35d34672a089

File content as of revision 0:deaafdfde3bb:

/*
 / _____)             _              | |
( (____  _____ ____ _| |_ _____  ____| |__
 \____ \| ___ |    (_   _) ___ |/ ___)  _ \
 _____) ) ____| | | || |_| ____( (___| | | |
(______/|_____)_|_|_| \__)_____)\____)_| |_|
    (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 "sx1261-hal.h"

#define V1A_WORKAROUNDS

/*!
 * \brief Used to block execution waiting for low state on radio busy pin.
 *        Essentially used in SPI communications
 */
#define WaitBusy( )          while( BUSY == 1 ){ }



/*!
 * \brief Blocking routine for waiting the UART to be writeable
 *
 */
#define WaitUartWritable( )  while( RadioUart->writeable( ) == false ){ }

/*!
 * \brief Blocking routine for waiting the UART to be readable
 *
 */
#define WaitUartReadable( )  while( RadioUart->readable( ) == false ){ }

SX1261Hal::SX1261Hal( PinName mosi, PinName miso, PinName sclk, PinName nss, PinName busy, PinName dio1, PinName dio2, PinName dio3, PinName rst,
                            void ( *txDone )( ), void ( *rxDone )( ), void ( *rxPblSyncWordHeader )( IrqPblSyncHeaderCode_t val ),
                            void ( *rxTxTimeout )( ), void ( *rxError )( IrqErrorCode_t errorCode ), void ( *rangingDone )( IrqRangingCode_t val ),
                            void ( *cadDone )( bool channelActivityDetected ),
                            void ( *onDioIrq )( ) )
        :   SX1261( txDone, rxDone, rxPblSyncWordHeader, rxTxTimeout,  rxError, rangingDone, cadDone, onDioIrq ),
            RadioNss( nss ),
            RadioReset( rst ),
            BUSY( busy ),
            DIO1( dio1 ),
            DIO2( dio2 ),
            DIO3( dio3 )
{
    RadioSpi = new SPI( mosi, miso, sclk );
    RadioUart = NULL;

    RadioNss = 1;
    RadioReset = 1;
}

SX1261Hal::SX1261Hal( PinName tx, PinName rx, PinName busy, PinName dio1, PinName dio2, PinName dio3, PinName rst,
                            void ( *txDone )( ), void ( *rxDone )( ), void ( *rxPblSyncWordHeader )( IrqPblSyncHeaderCode_t val ),
                            void ( *rxTxTimeout )( ), void ( *rxError )( IrqErrorCode_t errorCode ), void ( *rangingDone )( IrqRangingCode_t val ),
                            void ( *cadDone )( bool channelActivityDetected ),
                            void ( *onDioIrq )( ) )
        :   SX1261( txDone, rxDone, rxPblSyncWordHeader, rxTxTimeout, rxError, rangingDone, cadDone, onDioIrq ),
            RadioNss( NC ),
            RadioReset( rst ),
            BUSY( busy ),
            DIO1( dio1 ),
            DIO2( dio2 ),
            DIO3( dio3 )
{
    RadioSpi = NULL;
    RadioUart = new Serial( tx, rx );
    RadioReset = 1;
}

void SX1261Hal::SpiInit( void )
{
    RadioNss = 1;
    RadioSpi->format( 8, 0 );
    RadioSpi->frequency( SX1261_SPI_FREQ_DEFAULT );

    wait( 0.1 );
}

void SX1261Hal::UartInit( void )
{
    RadioUart->format( 9, SerialBase::Even, 1 ); // 8 data bits + 1 even parity bit + 1 stop bit
    RadioUart->baud( 115200 );

    // By default the SX1261 UART is setup to handle bytes MSB first.
    // In order to setup the radio to use the UART standard way we first send
    // the equivalent of a WriteRegister with reversed bit order in order to
    // change the endianness.
    //@todo 
    /*uint8_t regVal = 0;
    RadioUart->putc( 0x98 );            // Reversed opcode for read register (0x19)
    RadioUart->putc( 0x10 );            // Reversed MSB register address (0x08)
    RadioUart->putc( 0x18 );            // Reversed LSB register address (0x18)
    RadioUart->putc( 0x80 );            // Reversed value for reading only 1 byte (0x01)
    regVal = RadioUart->getc( )& 0xF3;  // Read reversed value and mask it

    RadioUart->putc( 0x18 );            // Reversed opcode for read register (0x18)
    RadioUart->putc( 0x10 );            // Reversed MSB register address (0x08)
    RadioUart->putc( 0x18 );            // Reversed LSB register address (0x18)
    RadioUart->putc( 0x80 );            // Reversed value for writing only 1 byte (0x01)
    RadioUart->putc( regVal );          // The new value of the register*/

    // After this point, the UART is running standard mode: 8 data bit, 1 even
    // parity bit, 1 stop bit, 115200 baud, LSB first
    wait_us( 10 );
}

void SX1261Hal::IoIrqInit( DioIrqHandler irqHandler )
{
    assert_param( RadioSpi != 0 || RadioUart != 0 );
    if( RadioSpi != NULL )
    {
        SpiInit( );
    }
    if( RadioUart != NULL )
    {
        UartInit( );
    }

    BUSY.mode( PullDown );
    DIO1.mode( PullDown );
    DIO2.mode( PullDown );
    DIO3.mode( PullDown );

    DIO1.rise( this, static_cast <Trigger>( irqHandler ) );
    DIO2.rise( this, static_cast <Trigger>( irqHandler ) );
    DIO3.rise( this, static_cast <Trigger>( irqHandler ) );
}

void SX1261Hal::Reset( void )
{
    __disable_irq( );
    wait( 0.05 );
    RadioReset = 0;
    wait( 0.1 );
    RadioReset = 1;
    wait( 0.05 );
    __enable_irq( );
}

void SX1261Hal::ClearInstructionRam( void )
{
    // Clearing the instruction RAM is writing 0x00s on every bytes of the
    // instruction RAM
    WaitBusy( );

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( RADIO_WRITE_REGISTER );                 // Send write register opcode
        RadioSpi->write( ( IRAM_START_ADDRESS >> 8 ) & 0x00FF ); // Send MSB of the first byte address
        RadioSpi->write( IRAM_START_ADDRESS & 0x00FF );          // Send LSB of the first byte address

        for( uint16_t address = IRAM_START_ADDRESS; address < ( IRAM_START_ADDRESS + IRAM_SIZE ); address++ )
        {
            RadioSpi->write( 0x00 );
        }
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        // We can't erase the whole instruction RAM in one shot with UART
        // because we need to send the length of register to erase
        // and this length is coded on 1 byte.
        for( uint16_t address = IRAM_START_ADDRESS; address < ( IRAM_START_ADDRESS + IRAM_SIZE ); address++ )
        {
            WriteRegister( address, 0 );
        }
    }

    WaitBusy( );
}

void SX1261Hal::Wakeup( void )
{
    __disable_irq( );

    //Don't wait for DIO0 here

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( RADIO_GET_STATUS );
        RadioSpi->write( 0 );
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        RadioUart->putc( RADIO_GET_STATUS );
        WaitUartReadable( );
        RadioUart->getc( );
    }

    // Wait for chip to be ready.
    WaitBusy( );
    
    #ifdef V1A_WORKAROUNDS
    //V1a workaround: rc64k not enabled after warm_start, rtc_wake_up=0
    WriteRegister(0x91e, ReadRegister(0x91e) | 0x40);
    //rc13m enable bug
    uint8_t txFallbackFunc[2];
    //set to  ModeTx2Rc addr = 0fce, so rc is not enabled before ramp down
    txFallbackFunc[0] = 0x0f;
    txFallbackFunc[1] = 0xce;
    WriteRegister(0x00CC, txFallbackFunc, 2);
    #endif

    __enable_irq( );
}

void SX1261Hal::WriteCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size )
{
    WaitBusy( );

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( ( uint8_t )command );
        for( uint16_t i = 0; i < size; i++ )
        {
            RadioSpi->write( buffer[i] );
        }
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        RadioUart->putc( command );
        if( size > 0 )
        {
            RadioUart->putc( size );
            for( uint16_t i = 0; i < size; i++ )
            {
                RadioUart->putc( buffer[i] );
            }
        }
    }

    if( command != RADIO_SET_SLEEP )
    {
        WaitBusy( );
    }
}

void SX1261Hal::ReadCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size )
{
    WaitBusy( );

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( ( uint8_t )command );
        RadioSpi->write( 0 );
        for( uint16_t i = 0; i < size; i++ )
        {
             buffer[i] = RadioSpi->write( 0 );
        }
        RadioNss = 1;
    }
    else if( RadioUart != NULL )
    {
        RadioUart->putc( command );

        // Behavior on the UART is different depending of the opcode command
        if( ( command == RADIO_GET_PACKETTYPE ) ||
            ( command == RADIO_GET_RXBUFFERSTATUS ) ||
            ( command == RADIO_GET_RSSIINST ) ||
            ( command == RADIO_GET_PACKETSTATUS ) ||
            ( command == RADIO_GET_IRQSTATUS ) )
        {
            RadioUart->putc( size );
        }

        WaitUartReadable( );
        for( uint16_t i = 0; i < size; i++ )
        {
             buffer[i] = RadioUart->getc( );
        }
    }
    else
    {
        buffer[0] = 0xFF;
    }

    WaitBusy( );
}

void SX1261Hal::WriteRegister( uint16_t address, uint8_t *buffer, uint16_t size )
{
    WaitBusy( );

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( RADIO_WRITE_REGISTER );
        RadioSpi->write( ( address & 0xFF00 ) >> 8 );
        RadioSpi->write( address & 0x00FF );
        for( uint16_t i = 0; i < size; i++ )
        {
            RadioSpi->write( buffer[i] );
        }
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        RadioUart->putc( RADIO_WRITE_REGISTER );
        RadioUart->putc( ( address & 0xFF00 ) >> 8 );
        RadioUart->putc( address & 0x00FF );
        RadioUart->putc( size );
        for( uint16_t i = 0; i < size; i++ )
        {
            RadioUart->putc( buffer[i] );
        }
    }

    WaitBusy( );
}

void SX1261Hal::WriteRegisterNoBusy( uint16_t address, uint8_t *buffer, uint16_t size )
{
    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( RADIO_WRITE_REGISTER );
        RadioSpi->write( ( address & 0xFF00 ) >> 8 );
        RadioSpi->write( address & 0x00FF );
        for( uint16_t i = 0; i < size; i++ )
        {
            RadioSpi->write( buffer[i] );
        }
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        RadioUart->putc( RADIO_WRITE_REGISTER );
        RadioUart->putc( ( address & 0xFF00 ) >> 8 );
        RadioUart->putc( address & 0x00FF );
        RadioUart->putc( size );
        for( uint16_t i = 0; i < size; i++ )
        {
            RadioUart->putc( buffer[i] );
        }
    }
}

void SX1261Hal::WriteRegister( uint16_t address, uint8_t value )
{
    WriteRegister( address, &value, 1 );
}

void SX1261Hal::WriteRegisterNoBusy( uint16_t address, uint8_t value )
{
    WriteRegisterNoBusy( address, &value, 1 );
}

void SX1261Hal::ReadRegister( uint16_t address, uint8_t *buffer, uint16_t size )
{
    WaitBusy( );

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( RADIO_READ_REGISTER );
        RadioSpi->write( ( address & 0xFF00 ) >> 8 );
        RadioSpi->write( address & 0x00FF );
        RadioSpi->write( 0 );
        for( uint16_t i = 0; i < size; i++ )
        {
            buffer[i] = RadioSpi->write( 0 );
        }
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        RadioUart->putc( RADIO_READ_REGISTER );
        RadioUart->putc( ( address & 0xFF00 ) >> 8 );
        RadioUart->putc( address & 0x00FF );
        RadioUart->putc( size );
        WaitUartReadable( );
        for( uint16_t i = 0; i < size; i++ )
        {
            buffer[i] = RadioUart->getc( );
        }
    }

    WaitBusy( );
}

void SX1261Hal::ReadRegisterNoBusy( uint16_t address, uint8_t *buffer, uint16_t size )
{

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( RADIO_READ_REGISTER );
        RadioSpi->write( ( address & 0xFF00 ) >> 8 );
        RadioSpi->write( address & 0x00FF );
        RadioSpi->write( 0 );
        for( uint16_t i = 0; i < size; i++ )
        {
            buffer[i] = RadioSpi->write( 0 );
        }
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        RadioUart->putc( RADIO_READ_REGISTER );
        RadioUart->putc( ( address & 0xFF00 ) >> 8 );
        RadioUart->putc( address & 0x00FF );
        RadioUart->putc( size );
        WaitUartReadable( );
        for( uint16_t i = 0; i < size; i++ )
        {
            buffer[i] = RadioUart->getc( );
        }
    }

}

uint8_t SX1261Hal::ReadRegister( uint16_t address )
{
    uint8_t data;

    ReadRegister( address, &data, 1 );
    return data;
}

uint8_t SX1261Hal::ReadRegisterNoBusy( uint16_t address )
{
    uint8_t data;

    ReadRegisterNoBusy( address, &data, 1 );
    return data;
}

void SX1261Hal::WriteBuffer( uint8_t offset, uint8_t *buffer, uint8_t size )
{
    WaitBusy( );

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( RADIO_WRITE_BUFFER );
        RadioSpi->write( offset );
        for( uint16_t i = 0; i < size; i++ )
        {
            RadioSpi->write( buffer[i] );
        }
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        RadioUart->putc( RADIO_WRITE_BUFFER );
        RadioUart->putc( offset );
        RadioUart->putc( size );
        for( uint16_t i = 0; i < size; i++ )
        {
            RadioUart->putc( buffer[i] );
        }
    }

    WaitBusy( );
}

void SX1261Hal::ReadBuffer( uint8_t offset, uint8_t *buffer, uint8_t size )
{
    WaitBusy( );

    if( RadioSpi != NULL )
    {
        RadioNss = 0;
        RadioSpi->write( RADIO_READ_BUFFER );
        RadioSpi->write( offset );
        RadioSpi->write( 0 );
        for( uint16_t i = 0; i < size; i++ )
        {
            buffer[i] = RadioSpi->write( 0 );
        }
        RadioNss = 1;
    }
    if( RadioUart != NULL )
    {
        RadioUart->putc( RADIO_READ_BUFFER );
        RadioUart->putc( offset );
        RadioUart->putc( size );
        WaitUartReadable( );
        for( uint16_t i = 0; i < size; i++ )
        {
            buffer[i] = RadioUart->getc( );
        }
    }

    WaitBusy( );
}

uint8_t SX1261Hal::GetDioStatus( void )
{
    return ( DIO3 << 3 ) | ( DIO2 << 2 ) | ( DIO1 << 1 ) | ( BUSY << 0 );
}