driver for SX1232 transceiver

Dependents:   chat_sx1232

summary

The SX1232 is a fully integrated ISM band transceiver optimized for use in the (EN 300 220-1) 868 MHz band in Europe and the (FCC Part 15) 915 MHz band in the US with a minimum of external components. It offers a combination of high link budget and low current consumption in all operating modes.

features

This part offers similar functionality to XBee PRO 900 modules, except RF performance would be improved, especially at slower data-rates. This is due to available narrower receiver bandwidths, and higher TX power, among other things. Additionally, its probably redundant to have a microcontroller on your wireless module, since the mbed cpu can drive radio directly.

This version of driver library supports packet size up to FIFO size (64bytes) to keep it simple and small. For larger packet sizes, a different driver could support the FIFO flow control pins.

wiring connections

Minimum required connections are SPI: mosi, miso, sclk, cs, one interrupt pin, and optional hardware reset pin.

example code

For example usage see Demo Chat Application.

specification of chip

Information on device. /media/uploads/dudmuck/sm1232.png

sx1232.cpp

Committer:
dudmuck
Date:
2013-05-02
Revision:
2:8717baf6e00a
Parent:
1:67a841d57890

File content as of revision 2:8717baf6e00a:

#include "sx1232.h"

/* SX1232 driver
 * Copyright (c) 2013 Semtech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

SX1232::SX1232(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst, PinName dio_0) : m_spi(mosi, miso, sclk), m_cs(cs), reset_pin(rst), dio0(dio_0)
{
    dio0.rise(this, &SX1232::dio0_callback);
    reset_pin.input();
    m_cs = 1;
    m_spi.format(8, 0);
    m_spi.frequency(1000000);
    
    init();
    service_action = SERVICE_NONE;
}

SX1232::~SX1232()
{
    set_opmode(RF_OPMODE_SLEEP);
}

void SX1232::init()
{
    RegOpMode.octet = read_reg(REG_OPMODE);
    RegPktConfig1.octet = read_reg(REG_PACKETCONFIG1);
    RegPktConfig2.octet = read_reg(REG_PACKETCONFIG2);
    RegDioMapping1.octet = read_reg(REG_DIOMAPPING1);
    RegDioMapping2.octet = read_reg(REG_DIOMAPPING2);
    RegPayloadLength = read_reg(REG_PAYLOADLENGTH);
    RegPaConfig.octet = read_reg(REG_PACONFIG);
    RegOokPeak.octet = read_reg(REG_OOKPEAK);
    RegRxConfig.octet = read_reg(REG_RXCONFIG);
    RegLna.octet = read_reg(REG_LNA);
    RegTimerResol.octet = read_reg(REG_TIMERRESOL);
    RegSeqConfig1.octet = read_reg(REG_SEQCONFIG1);
    RegSeqConfig2.octet = read_reg(REG_SEQCONFIG2);
    RegAfcFei.octet = read_reg(REG_AFCFEI);
    
    RegPreambleDetect.octet = read_reg(REG_PREAMBLEDETECT);
    if (RegPreambleDetect.octet != 0xaa) {
        RegPreambleDetect.octet = 0xaa;
        write_reg(REG_PREAMBLEDETECT, RegPreambleDetect.octet);
    }
    
    RegSyncConfig.octet = read_reg(REG_SYNCCONFIG);
    RegSyncConfig.bits.SyncSize = 2;    // actual size is this+1
    RegSyncConfig.bits.AutoRestartRxMode = 1;   // restart Rx after fifo emptied.  2 is for frequency hopping
    write_reg(REG_SYNCCONFIG, RegSyncConfig.octet);
    write_reg(REG_SYNCVALUE1, 0xaa);
    write_reg(REG_SYNCVALUE1, 0x90); // 802.15.4g examples: 0x7bc9, 0x904e, 0xc9c2, 0x7a0e
    write_reg(REG_SYNCVALUE2, 0x4e);
    
    RegFifoThreshold.octet = read_reg(REG_FIFOTHRESH);
    if (!RegFifoThreshold.bits.TxStartCondition) { // is start condition at fifoThreshold?
        RegFifoThreshold.bits.TxStartCondition = 1; // make it start tx on FifoNotEmpty
        write_reg(REG_FIFOTHRESH, RegFifoThreshold.octet);
    }
}

void SX1232::hw_reset()
{
    /* only a french-swiss design would have hi-Z deassert */
    reset_pin.output();
    reset_pin.write(1);
    wait(0.05);
    reset_pin.input();
    wait(0.05);
}

// variable-length pkt is only 255byte max
// fixed-length packet could be larger than 255 bytes
int SX1232::read_fifo()
{
    /* no blocking code or printf in an ISR */
    int i, len;
    
    if (RegPktConfig1.bits.PacketFormatVariable) {
        m_cs = 0;
        m_spi.write(REG_FIFO); // bit7 is low for reading from radio
        len = m_spi.write(0);
        m_cs = 1;
    } else
        len = get_PayloadLength();
     
    m_cs = 0;
    m_spi.write(REG_FIFO); // bit7 is low for reading from radio
    for (i = 0; i < len; i++) {
        // todo: flow control for pkt bigger than fifo (need ISR on FifoTreshold)
        rx_buf[i] = m_spi.write(0);
    }
    m_cs = 1;
    //_callback_rx.call();
    return len;
}
        
void SX1232::dio0_callback()
{
    /* no printf allowed here in ISR */
    switch (RegDioMapping1.bits.Dio0Mapping) {
        case 0:
            if (RegOpMode.bits.Mode == RF_OPMODE_RECEIVER) {
                service_action = SERVICE_READ_FIFO;
            } else if (RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER) {
                set_opmode(RF_OPMODE_STANDBY); // this should be quick enough for ISR
                service_action = SERVICE_TX_DONE;
            }
            break;
        case 1:
            service_action = SERVICE_READ_FIFO;
            break;
        default:
            service_action = SERVICE_ERROR;
            break;
    }
    
}

void SX1232::set_opmode(chip_mode_e mode)
{
    RegOpMode.bits.Mode = mode;
    write_reg(REG_OPMODE, RegOpMode.octet);
}

float SX1232::get_frf_MHz(void)
{
    uint32_t frf;
    uint8_t lsb, mid, msb;
    msb = read_reg(REG_FRFMSB);
    mid = read_reg(REG_FRFMID);
    lsb = read_reg(REG_FRFLSB);
    frf = msb;
    frf <<= 8;
    frf += mid;
    frf <<= 8;
    frf += lsb;
    return frf * FREQ_STEP_MHZ;
}

void SX1232::set_bitrate(uint32_t bps)
{
    uint8_t lsb, msb;
    uint16_t br = XTAL_FREQ / bps;
    msb = br >> 8;
    lsb = br & 0xff;
    write_reg(REG_BITRATEMSB, msb);
    write_reg(REG_BITRATELSB, lsb);
}

uint32_t SX1232::get_bitrate()
{
    uint16_t br;
    
    br = read_reg(REG_BITRATEMSB);
    br <<= 8;
    br += read_reg(REG_BITRATELSB);

    return XTAL_FREQ / br;
}

void SX1232::set_tx_fdev_hz(uint32_t hz)
{
    uint16_t fdev = hz / FREQ_STEP_HZ;
    uint8_t lsb, msb;
    msb = fdev >> 8;
    lsb = fdev & 0xff;
    write_reg(REG_FDEVMSB, msb);
    write_reg(REG_FDEVLSB, lsb);
}

uint32_t SX1232::get_tx_fdev_hz(void)
{
    uint16_t fdev;
    
    fdev = read_reg(REG_FDEVMSB);
    fdev <<= 8;
    fdev += read_reg(REG_FDEVLSB);

    return fdev * FREQ_STEP_HZ;
}

void SX1232::set_frf_MHz( float MHz )
{
    uint32_t frf;
    uint8_t lsb, mid, msb;
    
    frf = MHz / FREQ_STEP_MHZ;
    msb = frf >> 16;
    mid = (frf >> 8) & 0xff;
    lsb = frf & 0xff;
    write_reg(REG_FRFMSB, msb);
    write_reg(REG_FRFMID, mid);
    write_reg(REG_FRFLSB, lsb);
}

void SX1232::ComputeRxBwMantExp( uint32_t rxBwValue, uint8_t* mantisse, uint8_t* exponent )
{
    uint8_t tmpExp, tmpMant;
    double tmpRxBw;
    double rxBwMin = 10e6;

    for( tmpExp = 0; tmpExp < 8; tmpExp++ ) {
        for( tmpMant = 16; tmpMant <= 24; tmpMant += 4 ) {
            tmpRxBw = ComputeRxBw(tmpMant, tmpExp);         
            if( fabs( tmpRxBw - rxBwValue ) < rxBwMin ) {
                rxBwMin = fabs( tmpRxBw - rxBwValue );
                *mantisse = tmpMant;
                *exponent = tmpExp;
            }
        }
    }
}

uint32_t SX1232::ComputeRxBw( uint8_t mantisse, uint8_t exponent ) {
    // rxBw
    if (RegOpMode.bits.ModulationType == 0)
        return XTAL_FREQ / (mantisse * (1 << exponent+2));
    else
        return XTAL_FREQ / (mantisse * (1 << exponent+3));
}

void SX1232::set_rx_dcc_bw_hz(uint32_t dccValue, uint32_t bw_hz )
{
    uint8_t mantisse = 0;
    uint8_t exponent = 0;
    uint8_t reg;
    
    reg = ( uint8_t )dccValue & 0x60;
    ComputeRxBwMantExp( bw_hz, &mantisse, &exponent );
    switch( mantisse ) {
        case 16:
            reg |= ( uint8_t )( 0x00 | ( exponent & 0x07 ) );
            break;
        case 20:
            reg |= ( uint8_t )( 0x08 | ( exponent & 0x07 ) );
            break;
        case 24:
            reg |= ( uint8_t )( 0x10 | ( exponent & 0x07 ) );
            break;
        default:
            // Something went terribely wrong
            printf("maintisse:%d\r\n", mantisse);
            break;
    }
    
    write_reg(REG_RXBW, reg);
}

uint32_t SX1232::get_rx_bw_hz(uint8_t addr)
{
    uint8_t mantisse = 0;
    uint8_t reg = read_reg(REG_RXBW);
    switch( ( reg & 0x18 ) >> 3 )
    {
        case 0:
            mantisse = 16;
            break;
        case 1:
            mantisse = 20;
            break;
        case 2:
            mantisse = 24;
            break;
        default:
            break;
    }
    return ComputeRxBw( mantisse, ( uint8_t )reg & 0x07 );
}

uint16_t SX1232::get_PayloadLength()
{
    uint16_t ret;

    RegPktConfig2.octet = read_reg(REG_PACKETCONFIG2);
    RegPayloadLength = read_reg(REG_PAYLOADLENGTH);

    ret = RegPktConfig2.bits.PayloadLengthHi;
    ret <<= 8;
    ret += RegPayloadLength;

    return ret;
}

void SX1232::set_RegPayloadLength(uint16_t len)
{
    RegPktConfig2.bits.PayloadLengthHi = len >> 8;
    write_reg(REG_PACKETCONFIG2, RegPktConfig2.octet);
    RegPayloadLength = len & 0xff;
    write_reg(REG_PAYLOADLENGTH, RegPayloadLength);
}

void SX1232::enable_afc(char en)
{
    if (en) {
        RegRxConfig.bits.AfcAutoOn = 1;
        RegRxConfig.bits.RxTrigger = 6; // 6: trigger on preamble detect
        
        if (RegSyncConfig.bits.AutoRestartRxMode != 1) {
            // 2 is for frequency hopping application
            // if 0 then manual RestartRx in RxConfig required
            RegSyncConfig.bits.AutoRestartRxMode = 1;
        }
        // preamble detector triggers AFC
        if (!RegPreambleDetect.bits.PreambleDetectorOn) {
            RegPreambleDetect.bits.PreambleDetectorOn = 1;
            RegPreambleDetect.bits.PreambleDetectorTol = 10; // chips
            RegPreambleDetect.bits.PreambleDetectorSize = 1; // bytes
            write_reg(REG_PREAMBLEDETECT, RegPreambleDetect.octet);
        }
        // set DIO4 output.  not required, only for monitoring
        if ((RegDioMapping2.bits.Dio4Mapping != 3) || !RegDioMapping2.bits.MapPreambleDetect) {
            RegDioMapping2.bits.Dio4Mapping = 3;
            RegDioMapping2.bits.MapPreambleDetect = 1;
            write_reg(REG_DIOMAPPING2, RegDioMapping2.octet);
        }
    } else {
        RegRxConfig.bits.AfcAutoOn = 0;
        RegRxConfig.bits.RxTrigger = 0;
    }
    write_reg(REG_RXCONFIG, RegRxConfig.octet);
}

int16_t SX1232::read_reg_s16(uint8_t addr)
{
    int16_t ret;
    // Select the device by seting chip select low
    m_cs = 0;

    m_spi.write(addr); // bit7 is low for reading from radio
 
    // Send a dummy byte to receive the contents of the WHOAMI register
    ret = m_spi.write(0x00);
    ret <<= 8;
    ret += m_spi.write(0x00);
 
    // Deselect the device
    m_cs = 1;
    
    return ret;
}

uint8_t SX1232::read_reg(uint8_t addr)
{
    uint8_t ret;
    // Select the device by seting chip select low
    m_cs = 0;

    m_spi.write(addr); // bit7 is low for reading from radio
 
    // Send a dummy byte to receive the contents of the WHOAMI register
    ret = m_spi.write(0x00);
 
    // Deselect the device
    m_cs = 1;
    
    return ret;
}

void SX1232::write_reg(uint8_t addr, uint8_t data)
{
    m_cs = 0;   // Select the device by seting chip select low

    m_spi.write(addr | 0x80); // bit7 is high for writing to radio
 
    // Send a dummy byte to receive the contents of the WHOAMI register
    m_spi.write(data);
 
    m_cs = 1;   // Deselect the device
}

// variable-length pkt is only 255byte max
void SX1232::write_fifo__varlen(uint8_t len)
{
    int i;
    
    m_cs = 0;
    m_spi.write(REG_FIFO | 0x80); // bit7 is high for writing to radio
    m_spi.write(len);
    for (i = 0; i < len; i++) {
        // todo: flow control for pkt bigger than fifo
        m_spi.write(tx_buf[i]);
    }
    m_cs = 1;
}

// fixed-length packet could be larger than 255 bytes
void SX1232::write_fifo__fixedlen(void)
{
    int i, len = get_PayloadLength();
    m_cs = 0;
    m_spi.write(REG_FIFO | 0x80); // bit7 is high for writing to radio
    for (i = 0; i < len; i++) {
        // todo: flow control for pkt bigger than fifo
        m_spi.write(tx_buf[i]);
    }
    m_cs = 1;
}

// arg only for variable-length
void SX1232::start_tx(uint8_t len)
{
    if (RegOpMode.bits.Mode > RF_OPMODE_STANDBY) {
        RegIrqFlags1_t RegIrqFlags1;
        set_opmode(RF_OPMODE_STANDBY); // unwise fill fifo in RX
        wait(0.01);
        RegIrqFlags1.octet = read_reg(REG_IRQFLAGS1);
        while (!RegIrqFlags1.bits.ModeReady) {
            //printf("to stby:%02x\r\n", RegIrqFlags1.octet);
            wait(0.01);
            RegIrqFlags1.octet = read_reg(REG_IRQFLAGS1);
        }
    }
        
    // DIO0 to PacketSent
    if (RegDioMapping1.bits.Dio0Mapping != 0) {
        RegDioMapping1.bits.Dio0Mapping = 0;
        write_reg(REG_DIOMAPPING1, RegDioMapping1.octet);
    }
    
    if (RegPktConfig1.bits.PacketFormatVariable)
        write_fifo__varlen(len);
    else
        write_fifo__fixedlen();

    set_opmode(RF_OPMODE_TRANSMITTER);
}

void SX1232::start_rx()
{
    if (RegPktConfig1.bits.CrcOn) {    // DIO0 to CrcOk
        if (RegDioMapping1.bits.Dio0Mapping != 1) {
            RegDioMapping1.bits.Dio0Mapping = 1;
            write_reg(REG_DIOMAPPING1, RegDioMapping1.octet);
        }
    } else {    // DIO0 to PayloadReady
        if (RegDioMapping1.bits.Dio0Mapping != 0) {
            RegDioMapping1.bits.Dio0Mapping = 0;
            write_reg(REG_DIOMAPPING1, RegDioMapping1.octet);
        }    
    }
    
    set_opmode(RF_OPMODE_RECEIVER);
}