/****************************************************************************** * \attention * *

© COPYRIGHT 2016 STMicroelectronics

* * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * www.st.com/myliberty * * 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, * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /* * PROJECT: ST25R3911 firmware * Revision: * LANGUAGE: ISO C99 */ /*! \file * * \author Ulrich Herrmann * * \brief ST25R3911 high level interface * */ /* ****************************************************************************** * INCLUDES ****************************************************************************** */ #include "st25r3911.h" #include "st25r3911_com.h" #include "st25r3911_interrupt.h" #include "utils.h" /* ****************************************************************************** * LOCAL DEFINES ****************************************************************************** */ #define ST25R3911_OSC_STABLE_TIMEOUT 10U /*!< Timeout for Oscillator to get stable, datasheet: 700us, take 5 ms */ #define ST25R3911_CA_TIMEOUT 10U /*!< Timeout for Collision Avoidance command */ #define ST25R3911_TOUT_CALIBRATE_CAP_SENSOR 4U /*!< Max duration Calibrate Capacitive Sensor command Datasheet: 3ms */ /* ****************************************************************************** * LOCAL CONSTANTS ****************************************************************************** */ /* ****************************************************************************** * LOCAL VARIABLES ****************************************************************************** */ static uint32_t st25r3911NoResponseTime_64fcs; /* ****************************************************************************** * LOCAL FUNCTION PROTOTYPES ****************************************************************************** */ static ReturnCode st25r3911ExecuteCommandAndGetResult(uint8_t cmd, uint8_t resreg, uint8_t sleeptime, uint8_t* result); /* ****************************************************************************** * GLOBAL FUNCTIONS ****************************************************************************** */ void st25r3911TxRxOn( void ) { st25r3911SetRegisterBits(ST25R3911_REG_OP_CONTROL, (ST25R3911_REG_OP_CONTROL_rx_en | ST25R3911_REG_OP_CONTROL_tx_en) ); } void st25r3911TxRxOff( void ) { st25r3911ClrRegisterBits(ST25R3911_REG_OP_CONTROL, (ST25R3911_REG_OP_CONTROL_rx_en | ST25R3911_REG_OP_CONTROL_tx_en) ); } void st25r3911OscOn( void ) { /* Check if oscillator is already turned on and stable */ /* Use ST25R3911_REG_OP_CONTROL_en instead of ST25R3911_REG_AUX_DISPLAY_osc_ok to be on the safe side */ if( !st25r3911CheckReg( ST25R3911_REG_OP_CONTROL, ST25R3911_REG_OP_CONTROL_en, ST25R3911_REG_OP_CONTROL_en ) ) { /* Clear any eventual previous oscillator IRQ */ st25r3911GetInterrupt( ST25R3911_IRQ_MASK_OSC ); /* enable oscillator frequency stable interrupt */ st25r3911EnableInterrupts(ST25R3911_IRQ_MASK_OSC); /* enable oscillator and regulator output */ st25r3911ModifyRegister(ST25R3911_REG_OP_CONTROL, 0x00, ST25R3911_REG_OP_CONTROL_en); /* wait for the oscillator interrupt */ st25r3911WaitForInterruptsTimed(ST25R3911_IRQ_MASK_OSC, ST25R3911_OSC_STABLE_TIMEOUT); st25r3911DisableInterrupts(ST25R3911_IRQ_MASK_OSC); } } void st25r3911Initialize(void) { uint16_t vdd_mV; /* first, reset the st25r3911 */ st25r3911ExecuteCommand(ST25R3911_CMD_SET_DEFAULT); /* Set Operation Control Register to default value */ st25r3911WriteRegister(ST25R3911_REG_OP_CONTROL, 0x00); /* enable pull downs on miso line */ st25r3911ModifyRegister(ST25R3911_REG_IO_CONF2, 0, ST25R3911_REG_IO_CONF2_miso_pd1 | ST25R3911_REG_IO_CONF2_miso_pd2); /* after reset all interrupts are enabled. so disable them at first */ st25r3911DisableInterrupts(ST25R3911_IRQ_MASK_ALL); /* and clear them, just to be sure... */ st25r3911ClearInterrupts(); st25r3911OscOn(); /* Measure vdd and set sup3V bit accordingly */ vdd_mV = st25r3911MeasureVoltage(ST25R3911_REG_REGULATOR_CONTROL_mpsv_vdd); st25r3911ModifyRegister(ST25R3911_REG_IO_CONF2, ST25R3911_REG_IO_CONF2_sup3V, (uint8_t)((vdd_mV < 3600U)?ST25R3911_REG_IO_CONF2_sup3V:0U)); /* Make sure Transmitter and Receiver are disabled */ st25r3911TxRxOff(); return; } void st25r3911Deinitialize(void) { st25r3911DisableInterrupts(ST25R3911_IRQ_MASK_ALL); /* Disabe Tx and Rx, Keep OSC */ st25r3911TxRxOff(); return; } ReturnCode st25r3911AdjustRegulators(uint16_t* result_mV) { uint8_t result; uint8_t io_conf2; ReturnCode err = ERR_NONE; /* Reset logic and set regulated voltages to be defined by result of Adjust Regulators command */ st25r3911SetRegisterBits( ST25R3911_REG_REGULATOR_CONTROL, ST25R3911_REG_REGULATOR_CONTROL_reg_s ); st25r3911ClrRegisterBits( ST25R3911_REG_REGULATOR_CONTROL, ST25R3911_REG_REGULATOR_CONTROL_reg_s ); st25r3911ExecuteCommandAndGetResult(ST25R3911_CMD_ADJUST_REGULATORS, ST25R3911_REG_REGULATOR_RESULT, 5, &result); st25r3911ReadRegister(ST25R3911_REG_IO_CONF2, &io_conf2); result >>= ST25R3911_REG_REGULATOR_RESULT_shift_reg; result -= 5U; if (result_mV != NULL) { if((io_conf2 & ST25R3911_REG_IO_CONF2_sup3V) != 0U) { *result_mV = 2400; *result_mV += (uint16_t)result * 100U; } else { *result_mV = 3900; *result_mV += (uint16_t)result * 120U; } } return err; } void st25r3911MeasureAmplitude(uint8_t* result) { st25r3911ExecuteCommandAndGetResult(ST25R3911_CMD_MEASURE_AMPLITUDE, ST25R3911_REG_AD_RESULT, 10, result); } void st25r3911MeasurePhase(uint8_t* result) { st25r3911ExecuteCommandAndGetResult(ST25R3911_CMD_MEASURE_PHASE, ST25R3911_REG_AD_RESULT, 10, result); } void st25r3911MeasureCapacitance(uint8_t* result) { st25r3911ExecuteCommandAndGetResult(ST25R3911_CMD_MEASURE_CAPACITANCE, ST25R3911_REG_AD_RESULT, 10, result); } void st25r3911CalibrateAntenna(uint8_t* result) { st25r3911ExecuteCommandAndGetResult(ST25R3911_CMD_CALIBRATE_ANTENNA, ST25R3911_REG_ANT_CAL_RESULT, 10, result); } void st25r3911CalibrateModulationDepth(uint8_t* result) { st25r3911ExecuteCommandAndGetResult(ST25R3911_CMD_CALIBRATE_MODULATION, ST25R3911_REG_AM_MOD_DEPTH_RESULT, 10, result); } ReturnCode st25r3911CalibrateCapacitiveSensor(uint8_t* result) { ReturnCode ret; uint8_t res; /* Clear Manual calibration values to enable automatic calibration mode */ st25r3911ClrRegisterBits( ST25R3911_REG_CAP_SENSOR_CONTROL, ST25R3916_REG_CAP_SENSOR_CONTROL_mask_cs_mcal ); /* Execute automatic calibration */ ret = st25r3911ExecuteCommandAndGetResult( ST25R3911_CMD_CALIBRATE_C_SENSOR, ST25R3911_REG_CAP_SENSOR_RESULT, ST25R3911_TOUT_CALIBRATE_CAP_SENSOR, &res ); /* Check wether the calibration was successull */ if( ((res & ST25R3911_REG_CAP_SENSOR_RESULT_cs_cal_end) != ST25R3911_REG_CAP_SENSOR_RESULT_cs_cal_end) || ((res & ST25R3911_REG_CAP_SENSOR_RESULT_cs_cal_err) == ST25R3911_REG_CAP_SENSOR_RESULT_cs_cal_err) || (ret != ERR_NONE) ) { return ERR_IO; } if( result != NULL ) { (*result) = (uint8_t)(res >> ST25R3911_REG_CAP_SENSOR_CONTROL_shift_cs_mcal); } return ERR_NONE; } ReturnCode st25r3911SetBitrate(uint8_t txRate, uint8_t rxRate) { uint8_t reg; st25r3911ReadRegister(ST25R3911_REG_BIT_RATE, ®); if (rxRate != ST25R3911_BR_DO_NOT_SET) { if(rxRate > ST25R3911_BR_3390) { return ERR_PARAM; } else { reg = (uint8_t)(reg & ~ST25R3911_REG_BIT_RATE_mask_rxrate); /* MISRA 10.3 */ reg |= rxRate << ST25R3911_REG_BIT_RATE_shift_rxrate; } } if (txRate != ST25R3911_BR_DO_NOT_SET) { if(txRate > ST25R3911_BR_6780) { return ERR_PARAM; } else { reg = (uint8_t)(reg & ~ST25R3911_REG_BIT_RATE_mask_txrate); /* MISRA 10.3 */ reg |= txRate<> ST25R3911_REG_FIFO_RX_STATUS2_shift_fifo_lb); } uint32_t st25r3911GetNoResponseTime_64fcs(void) { return st25r3911NoResponseTime_64fcs; } void st25r3911StartGPTimer_8fcs(uint16_t gpt_8fcs, uint8_t trigger_source) { st25r3911SetGPTime_8fcs(gpt_8fcs); st25r3911ModifyRegister(ST25R3911_REG_GPT_CONTROL, ST25R3911_REG_GPT_CONTROL_gptc_mask, trigger_source); if (trigger_source == 0U) { st25r3911ExecuteCommand(ST25R3911_CMD_START_GP_TIMER); } return; } void st25r3911SetGPTime_8fcs(uint16_t gpt_8fcs) { st25r3911WriteRegister(ST25R3911_REG_GPT1, (uint8_t)(gpt_8fcs >> 8)); st25r3911WriteRegister(ST25R3911_REG_GPT2, (uint8_t)(gpt_8fcs & 0xffU)); return; } bool st25r3911CheckReg( uint8_t reg, uint8_t mask, uint8_t value ) { uint8_t regVal; regVal = 0; st25r3911ReadRegister( reg, ®Val ); return ((regVal & mask) == value ); } bool st25r3911CheckChipID( uint8_t *rev ) { uint8_t ID; ID = 0; st25r3911ReadRegister( ST25R3911_REG_IC_IDENTITY, &ID ); /* Check if IC Identity Register contains ST25R3911's IC type code */ if( (ID & ST25R3911_REG_IC_IDENTITY_mask_ic_type) != ST25R3911_REG_IC_IDENTITY_ic_type ) { return false; } if(rev != NULL) { *rev = (ID & ST25R3911_REG_IC_IDENTITY_mask_ic_rev); } return true; } ReturnCode st25r3911SetNoResponseTime_64fcs(uint32_t nrt_64fcs) { ReturnCode err = ERR_NONE; uint8_t nrt_step = 0; uint32_t noResponseTime_64fcs = nrt_64fcs; /* MISRA 17.8: Use intermediate variable */ st25r3911NoResponseTime_64fcs = noResponseTime_64fcs; if (noResponseTime_64fcs > (uint32_t)0xFFFFU) { nrt_step = ST25R3911_REG_GPT_CONTROL_nrt_step; noResponseTime_64fcs = (noResponseTime_64fcs + 63U) / 64U; if (noResponseTime_64fcs > (uint32_t)0xFFFFU) { noResponseTime_64fcs = 0xFFFFU; err = ERR_PARAM; } st25r3911NoResponseTime_64fcs = 64U * noResponseTime_64fcs; } st25r3911ModifyRegister(ST25R3911_REG_GPT_CONTROL, ST25R3911_REG_GPT_CONTROL_nrt_step, nrt_step); st25r3911WriteRegister(ST25R3911_REG_NO_RESPONSE_TIMER1, (uint8_t)(noResponseTime_64fcs >> 8)); st25r3911WriteRegister(ST25R3911_REG_NO_RESPONSE_TIMER2, (uint8_t)(noResponseTime_64fcs & 0xffU)); return err; } ReturnCode st25r3911SetStartNoResponseTime_64fcs(uint32_t nrt_64fcs) { ReturnCode err; err = st25r3911SetNoResponseTime_64fcs( nrt_64fcs ); if(err == ERR_NONE) { st25r3911ExecuteCommand(ST25R3911_CMD_START_NO_RESPONSE_TIMER); } return err; } ReturnCode st25r3911PerformCollisionAvoidance( uint8_t FieldONCmd, uint8_t pdThreshold, uint8_t caThreshold, uint8_t nTRFW ) { uint8_t treMask; uint32_t irqs; if( (FieldONCmd != ST25R3911_CMD_INITIAL_RF_COLLISION) && (FieldONCmd != ST25R3911_CMD_RESPONSE_RF_COLLISION_0) && (FieldONCmd != ST25R3911_CMD_RESPONSE_RF_COLLISION_N) ) { return ERR_PARAM; } /* Check if new thresholds are to be applied */ if( (pdThreshold != ST25R3911_THRESHOLD_DO_NOT_SET) || (caThreshold != ST25R3911_THRESHOLD_DO_NOT_SET) ) { treMask = 0; if(pdThreshold != ST25R3911_THRESHOLD_DO_NOT_SET) { treMask |= ST25R3911_REG_FIELD_THRESHOLD_mask_trg; } if(caThreshold != ST25R3911_THRESHOLD_DO_NOT_SET) { treMask |= ST25R3911_REG_FIELD_THRESHOLD_mask_rfe; } /* Set Detection Threshold and|or Collision Avoidance Threshold */ st25r3911ChangeRegisterBits( ST25R3911_REG_FIELD_THRESHOLD, treMask, (pdThreshold & ST25R3911_REG_FIELD_THRESHOLD_mask_trg) | (caThreshold & ST25R3911_REG_FIELD_THRESHOLD_mask_rfe ) ); } /* Set n x TRFW */ st25r3911ModifyRegister(ST25R3911_REG_AUX, ST25R3911_REG_AUX_mask_nfc_n, (nTRFW & ST25R3911_REG_AUX_mask_nfc_n) ); /* Enable and clear CA specific interrupts and execute command */ st25r3911EnableInterrupts( (ST25R3911_IRQ_MASK_CAC | ST25R3911_IRQ_MASK_CAT) ); st25r3911GetInterrupt( (ST25R3911_IRQ_MASK_CAC | ST25R3911_IRQ_MASK_CAT) ); st25r3911ExecuteCommand(FieldONCmd); irqs = st25r3911WaitForInterruptsTimed(ST25R3911_IRQ_MASK_CAC | ST25R3911_IRQ_MASK_CAT, ST25R3911_CA_TIMEOUT ); /* Clear any previous External Field events and disable CA specific interrupts */ st25r3911GetInterrupt( (ST25R3911_IRQ_MASK_EOF | ST25R3911_IRQ_MASK_EON) ); st25r3911DisableInterrupts(ST25R3911_IRQ_MASK_CAC | ST25R3911_IRQ_MASK_CAT); if((ST25R3911_IRQ_MASK_CAC & irqs) != 0U) /* Collision occurred */ { return ERR_RF_COLLISION; } if((ST25R3911_IRQ_MASK_CAT & irqs) != 0U) /* No Collision detected, Field On */ { return ERR_NONE; } /* No interrupt detected */ return ERR_INTERNAL; } ReturnCode st25r3911GetRegsDump(uint8_t* resRegDump, uint8_t* sizeRegDump) { uint8_t regIt; uint8_t regDump[ST25R3911_REG_IC_IDENTITY+1U]; if(!sizeRegDump || !resRegDump) { return ERR_PARAM; } for( regIt = ST25R3911_REG_IO_CONF1; regIt < SIZEOF_ARRAY(regDump); regIt++ ) { st25r3911ReadRegister(regIt, ®Dump[regIt] ); } *sizeRegDump = MIN(*sizeRegDump, regIt); if( *sizeRegDump > 0U ) /* MISRA 21.18 */ { ST_MEMCPY(resRegDump, regDump, *sizeRegDump ); } return ERR_NONE; } void st25r3911SetNumTxBits( uint32_t nBits ) { st25r3911WriteRegister(ST25R3911_REG_NUM_TX_BYTES2, (uint8_t)((nBits >> 0) & 0xffU)); st25r3911WriteRegister(ST25R3911_REG_NUM_TX_BYTES1, (uint8_t)((nBits >> 8) & 0xffU)); } bool st25r3911IsCmdValid( uint8_t cmd ) { if( !((cmd >= ST25R3911_CMD_SET_DEFAULT) && (cmd <= ST25R3911_CMD_ANALOG_PRESET)) && !((cmd >= ST25R3911_CMD_MASK_RECEIVE_DATA) && (cmd <= ST25R3911_CMD_CLEAR_RSSI)) && !((cmd >= ST25R3911_CMD_TRANSPARENT_MODE) && (cmd <= ST25R3911_CMD_START_NO_RESPONSE_TIMER)) && !((cmd >= ST25R3911_CMD_TEST_CLEARA) && (cmd <= ST25R3911_CMD_FUSE_PPROM)) ) { return false; } return true; } ReturnCode st25r3911StreamConfigure(const struct st25r3911StreamConfig *config) { uint8_t smd = 0; uint8_t mode; if (config->useBPSK != 0U) { mode = ST25R3911_REG_MODE_om_bpsk_stream; if ((config->din<2U) || (config->din>4U)) /* not in fc/4 .. fc/16 */ { return ERR_PARAM; } smd |= (4U - config->din) << ST25R3911_REG_STREAM_MODE_shift_scf; } else { mode = ST25R3911_REG_MODE_om_subcarrier_stream; if ((config->din<3U) || (config->din>6U)) /* not in fc/8 .. fc/64 */ { return ERR_PARAM; } smd |= (6U - config->din) << ST25R3911_REG_STREAM_MODE_shift_scf; if (config->report_period_length == 0U) { return ERR_PARAM; } } if ((config->dout<1U) || (config->dout>7U)) /* not in fc/2 .. fc/128 */ { return ERR_PARAM; } smd |= (7U - config->dout) << ST25R3911_REG_STREAM_MODE_shift_stx; if (config->report_period_length > 3U) { return ERR_PARAM; } smd |= config->report_period_length << ST25R3911_REG_STREAM_MODE_shift_scp; st25r3911WriteRegister(ST25R3911_REG_STREAM_MODE, smd); st25r3911ChangeRegisterBits(ST25R3911_REG_MODE, ST25R3911_REG_MODE_mask_om, mode); return ERR_NONE; } /*******************************************************************************/ ReturnCode st25r3911GetRSSI( uint16_t *amRssi, uint16_t *pmRssi ) { /*******************************************************************************/ /* MISRA 8.9 An object should be defined at block scope if its identifier only appears in a single function */ /*< ST25R3911 RSSI Display Reg values:0 1 2 3 4 5 6 7 8 9 a b c d e f */ const uint16_t st25r3911Rssi2mV[] = { 0 ,20 ,27 ,37 ,52 ,72 ,99 ,136 ,190 ,262 ,357 ,500 ,686 ,950, 0, 0 }; /* ST25R3911 2/3 stage gain reduction [dB] 0 0 0 0 0 3 6 9 12 15 18 na na na na na */ const uint16_t st25r3911Gain2Percent[] = { 100, 100, 100, 100, 100, 141, 200, 281, 398, 562, 794, 1, 1, 1, 1, 1 }; /*******************************************************************************/ uint8_t rssi; uint8_t gainRed; st25r3911ReadRegister( ST25R3911_REG_RSSI_RESULT, &rssi ); st25r3911ReadRegister( ST25R3911_REG_GAIN_RED_STATE, &gainRed ); if( amRssi != NULL ) { *amRssi = (uint16_t) ( ( (uint32_t)st25r3911Rssi2mV[ (rssi >> ST25R3911_REG_RSSI_RESULT_shift_rssi_am) ] * (uint32_t)st25r3911Gain2Percent[ (gainRed >> ST25R3911_REG_GAIN_RED_STATE_shift_gs_am) ] ) / 100U ); } if( pmRssi != NULL ) { *pmRssi = (uint16_t) ( ( (uint32_t)st25r3911Rssi2mV[ (rssi & ST25R3911_REG_RSSI_RESULT_mask_rssi_pm) ] * (uint32_t)st25r3911Gain2Percent[ (gainRed & ST25R3911_REG_GAIN_RED_STATE_mask_gs_pm) ] ) / 100U ); } return ERR_NONE; } /* ****************************************************************************** * LOCAL FUNCTIONS ****************************************************************************** */ /*! ***************************************************************************** * \brief Executes a direct command and returns the result * * This function executes the direct command given by \a cmd waits for * \a sleeptime for I_dct and returns the result read from register \a resreg. * No checking of the validity of the cmd is performed. * * \param[in] cmd: direct command to execute. * \param[in] resreg: Address of the register containing the result. * \param[in] sleeptime: time in milliseconds to wait before reading the result. * \param[out] result: 8 bit long result * ***************************************************************************** */ static ReturnCode st25r3911ExecuteCommandAndGetResult(uint8_t cmd, uint8_t resreg, uint8_t sleeptime, uint8_t* result) { st25r3911EnableInterrupts(ST25R3911_IRQ_MASK_DCT); st25r3911GetInterrupt(ST25R3911_IRQ_MASK_DCT); st25r3911ExecuteCommand(cmd); st25r3911WaitForInterruptsTimed(ST25R3911_IRQ_MASK_DCT, sleeptime); st25r3911DisableInterrupts(ST25R3911_IRQ_MASK_DCT); /* read out the result if the pointer is not NULL */ if (result != NULL) { st25r3911ReadRegister(resreg, result); } return ERR_NONE; }