/** library to communication with ams AS3935 Franklin lightning sensor IC using SPI * @file * @author King Kévin * @copyright SPDX-License-Identifier: GPL-3.0-or-later * @date 2019 * @note peripherals used: SPI @ref sensor_as3935_spi, GPIO @ref sensor_as3935_gpio */ /* standard libraries */ #include // standard integer types #include // general utilities #include // boolean utilities #include // math utilities #include // fff utilities /* STM32 (including CM3) libraries */ #include // Cortex M3 utilities #include // real-time control clock library #include // general purpose input output library #include // SPI library #include // external interrupt defines #include // interrupt handler /* own libraries */ #include "global.h" // common methods #include "sensor_as3935.h" // own definitions /** @defgroup sensor_as3935_spi SPI peripheral used to communicate with the AS3935 * @{ */ #define SENSOR_AS3935_SPI 1 /**< SPI peripheral */ /** @} */ /** @defgroup sensor_as3935_gpio GPIO used to control the AS3935 * @{ */ #define SENSOR_AS3935_GPIO_SI PA3 /**< AS3935 Select Interface pin (high = SPI, low = I2C) */ #define SENSOR_AS3935_GPIO_IRQ PA2 /**< AS3935 Interrupt pin (active high) */ #define SENSOR_AS3935_GPIO_EN_VREG PA0 /**< AS3935 Voltage Regulator Enable pin (active high) */ /** @} */ /** register details */ struct sensor_as3935_register_info_t { uint8_t address; /**< register address */ uint8_t mask; /**< mask of relevant register bits */ bool writable; /**< if register is writable */ }; /** AS3935 registers description */ static const struct sensor_as3935_register_info_t sensor_as3935_register_infos[] = { [SENSOR_AS3935_REGISTER_AFE_GB] = { .address = 0x00, .mask = 0x3e, .writable = true, }, [SENSOR_AS3935_REGISTER_PWD] = { .address = 0x00, .mask = 0x01, .writable = true, }, [SENSOR_AS3935_REGISTER_NF_LEV] = { .address = 0x01, .mask = 0x70, .writable = true, }, [SENSOR_AS3935_REGISTER_WDTH] = { .address = 0x01, .mask = 0x0f, .writable = true, }, [SENSOR_AS3935_REGISTER_CL_STAT] = { .address = 0x02, .mask = 0x40, .writable = true, }, [SENSOR_AS3935_REGISTER_MIN_NUM_LIGH] = { .address = 0x02, .mask = 0x30, .writable = true, }, [SENSOR_AS3935_REGISTER_SREJ] = { .address = 0x02, .mask = 0x0f, .writable = true, }, [SENSOR_AS3935_REGISTER_LCO_FDIV] = { .address = 0x03, .mask = 0xc0, .writable = true, }, [SENSOR_AS3935_REGISTER_MASK_DIST] = { .address = 0x03, .mask = 0x20, .writable = true, }, [SENSOR_AS3935_REGISTER_INT] = { .address = 0x03, .mask = 0x0f, .writable = false, }, [SENSOR_AS3935_REGISTER_S_LIG_L] = { .address = 0x04, .mask = 0xff, .writable = false, }, [SENSOR_AS3935_REGISTER_S_LIG_M] = { .address = 0x05, .mask = 0xff, .writable = false, }, [SENSOR_AS3935_REGISTER_S_LIG_MM] = { .address = 0x06, .mask = 0x1f, .writable = false, }, [SENSOR_AS3935_REGISTER_DISTANCE] = { .address = 0x07, .mask = 0x3f, .writable = false, }, [SENSOR_AS3935_REGISTER_DISP_LCO] = { .address = 0x08, .mask = 0x80, .writable = true, }, [SENSOR_AS3935_REGISTER_DISP_SRCO] = { .address = 0x08, .mask = 0x40, .writable = true, }, [SENSOR_AS3935_REGISTER_DISP_TRCO] = { .address = 0x08, .mask = 0x20, .writable = true, }, [SENSOR_AS3935_REGISTER_TUN_CAP] = { .address = 0x08, .mask = 0x0f, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT1] = { .address = 0x09, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT2] = { .address = 0x0a, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT3] = { .address = 0x0b, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT4] = { .address = 0x0c, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT5] = { .address = 0x0d, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT6] = { .address = 0x0e, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT7] = { .address = 0x0f, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT8] = { .address = 0x10, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT9] = { .address = 0x11, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT10] = { .address = 0x12, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT11] = { .address = 0x13, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT12] = { .address = 0x14, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT13] = { .address = 0x15, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT14] = { .address = 0x16, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT15] = { .address = 0x17, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT16] = { .address = 0x18, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT17] = { .address = 0x19, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT18] = { .address = 0x1a, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT19] = { .address = 0x1b, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT20] = { .address = 0x1c, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT21] = { .address = 0x1d, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT22] = { .address = 0x1e, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT23] = { .address = 0x1f, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT24] = { .address = 0x20, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT25] = { .address = 0x21, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT26] = { .address = 0x22, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT27] = { .address = 0x23, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT28] = { .address = 0x24, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT29] = { .address = 0x25, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT30] = { .address = 0x26, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT31] = { .address = 0x27, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT32] = { .address = 0x28, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT33] = { .address = 0x29, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT34] = { .address = 0x2a, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT35] = { .address = 0x2b, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT36] = { .address = 0x2c, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT37] = { .address = 0x2d, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT38] = { .address = 0x2e, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT39] = { .address = 0x2f, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT40] = { .address = 0x30, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT41] = { .address = 0x31, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_LDLUT42] = { .address = 0x32, .mask = 0xff, .writable = true, }, [SENSOR_AS3935_REGISTER_TRCO_CALIB_DONE] = { .address = 0x3a, .mask = 0x80, .writable = false, }, [SENSOR_AS3935_REGISTER_TRCO_CALIB_NOK] = { .address = 0x3a, .mask = 0x40, .writable = false, }, [SENSOR_AS3935_REGISTER_SRCO_CALIB_DONE] = { .address = 0x3b, .mask = 0x80, .writable = false, }, [SENSOR_AS3935_REGISTER_SRCO_CALIB_NOK] = { .address = 0x3b, .mask = 0x40, .writable = false, }, }; /** flag set if an interrupt has been received */ volatile bool sensor_as3935_interrupt = false; /** if we are currently calibrating the sensor */ static bool sensor_as3935_calibrating = false; /** number of interrupts received */ static volatile uint16_t sensor_as3935_interrupt_count = 0; bool sensor_as3935_setup(void) { // disable internal voltage regulator since we already provide 3.3V (plus VREG is tied to VCC using a 0R resistor) rcc_periph_clock_enable(GPIO_RCC(SENSOR_AS3935_GPIO_EN_VREG)); // enable clock for GPIO port gpio_clear(GPIO_PORT(SENSOR_AS3935_GPIO_EN_VREG), GPIO_PIN(SENSOR_AS3935_GPIO_EN_VREG)); // set low to disable voltage regulator gpio_set_mode(GPIO_PORT(SENSOR_AS3935_GPIO_EN_VREG), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(SENSOR_AS3935_GPIO_EN_VREG)); // set GPIO as output // select SPI interface rcc_periph_clock_enable(GPIO_RCC(SENSOR_AS3935_GPIO_SI)); // enable clock for GPIO port gpio_clear(GPIO_PORT(SENSOR_AS3935_GPIO_SI), GPIO_PIN(SENSOR_AS3935_GPIO_SI)); // pull low since it's active high gpio_set_mode(GPIO_PORT(SENSOR_AS3935_GPIO_SI), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(SENSOR_AS3935_GPIO_SI)); // set GPIO as output // configure interrupt rcc_periph_clock_enable(GPIO_RCC(SENSOR_AS3935_GPIO_IRQ)); // enable clock for GPIO port gpio_set_mode(GPIO_PORT(SENSOR_AS3935_GPIO_IRQ), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(SENSOR_AS3935_GPIO_IRQ)); // set interrupt as input // setup SPI rcc_periph_clock_enable(RCC_SPI_SCK_PORT(SENSOR_AS3935_SPI)); // enable clock for GPIO peripheral for clock signal gpio_set_mode(SPI_SCK_PORT(SENSOR_AS3935_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(SENSOR_AS3935_SPI)); // set SCK as output (clock speed will be negotiated later) rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(SENSOR_AS3935_SPI)); // enable clock for GPIO peripheral for MOSI signal gpio_set_mode(SPI_MOSI_PORT(SENSOR_AS3935_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(SENSOR_AS3935_SPI)); // set MOSI as output rcc_periph_clock_enable(RCC_SPI_MISO_PORT(SENSOR_AS3935_SPI)); // enable clock for GPIO peripheral for MISO signal gpio_set_mode(SPI_MISO_PORT(SENSOR_AS3935_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_MISO_PIN(SENSOR_AS3935_SPI)); // set MISO as input rcc_periph_clock_enable(RCC_SPI_NSS_PORT(SENSOR_AS3935_SPI)); // enable clock for GPIO peripheral for NSS (CS) signal gpio_set_mode(SPI_NSS_PORT(SENSOR_AS3935_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, SPI_NSS_PIN(SENSOR_AS3935_SPI)); // set NSS (CS) as output rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function rcc_periph_clock_enable(RCC_SPI(SENSOR_AS3935_SPI)); // enable clock for SPI peripheral spi_reset(SPI(SENSOR_AS3935_SPI)); // clear SPI values to default spi_init_master(SPI(SENSOR_AS3935_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_64, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_2, SPI_CR1_DFF_16BIT, SPI_CR1_MSBFIRST); // initialise SPI as master, divide clock by 64 (72E6/64=1125 kHz, max AS3935 SCK is 2 MHz, maximum SPI PCLK clock is 72 Mhz, depending on which SPI is used), set clock polarity to idle low (not that important), set clock phase to do bit change on falling edge (polarity depends on clock phase), use 16 bits frames , use MSb first spi_set_full_duplex_mode(SPI(SENSOR_AS3935_SPI)); // ensure we are in full duplex mode spi_enable_software_slave_management(SPI(SENSOR_AS3935_SPI)); // control NSS (CS) manually spi_set_nss_high(SPI(SENSOR_AS3935_SPI)); // set NSS high (internally) so we can output spi_disable_ss_output(SPI(SENSOR_AS3935_SPI)); // disable NSS output since we control CS manually gpio_set(SPI_NSS_PORT(SENSOR_AS3935_SPI), SPI_NSS_PIN(SENSOR_AS3935_SPI)); // set CS high to unselect device // sadly we can't use the interrupts as events to sleep (WFE) since sleep disables also communication (e.g. going to sleep until Rx buffer is not empty prevents transmission) spi_enable(SPI(SENSOR_AS3935_SPI)); // enable SPI sleep_ms(2); // be sure TCO has enough time to start // configure device sensor_as3935_command(SENSOR_AS3935_OPERATION_DIRECT_COMMAND, SENSOR_AS3935_DIRECT_COMMAND_PRESET_DEFAULT, SENSOR_AS3935_DIRECT_COMMAND_VALUE); // reset all values to default if (0x24 != sensor_as3935_command(SENSOR_AS3935_OPERATION_READ, 0, 0)) { // check if communication works by checking default register value return false; } sensor_as3935_power_down(); // power down to save power rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt // configure IRQ interrupt exti_select_source(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ), GPIO_PORT(SENSOR_AS3935_GPIO_IRQ)); // mask external interrupt of the IRQ pin only for this port exti_set_trigger(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ), EXTI_TRIGGER_RISING); // IRQ goes high in interrupt exti_enable_request(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ)); // enable external interrupt nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(SENSOR_AS3935_GPIO_IRQ)); // enable interrupt sensor_as3935_interrupt = (0 != gpio_get(GPIO_PORT(SENSOR_AS3935_GPIO_IRQ), GPIO_PIN(SENSOR_AS3935_GPIO_IRQ))); // update interrupt status return true; } void sensor_as3935_release(void) { nvic_disable_irq(GPIO_NVIC_EXTI_IRQ(SENSOR_AS3935_GPIO_IRQ)); exti_disable_request(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ)); spi_reset(SPI(SENSOR_AS3935_SPI)); spi_disable(SPI(SENSOR_AS3935_SPI)); rcc_periph_clock_disable(RCC_SPI(SENSOR_AS3935_SPI)); gpio_set_mode(SPI_NSS_PORT(SENSOR_AS3935_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_NSS_PIN(SENSOR_AS3935_SPI)); gpio_set_mode(SPI_MISO_PORT(SENSOR_AS3935_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MISO_PIN(SENSOR_AS3935_SPI)); gpio_set_mode(SPI_MOSI_PORT(SENSOR_AS3935_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MOSI_PIN(SENSOR_AS3935_SPI)); gpio_set_mode(SPI_SCK_PORT(SENSOR_AS3935_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_SCK_PIN(SENSOR_AS3935_SPI)); gpio_set_mode(GPIO_PORT(SENSOR_AS3935_GPIO_IRQ), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(SENSOR_AS3935_GPIO_IRQ)); gpio_set_mode(GPIO_PORT(SENSOR_AS3935_GPIO_SI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(SENSOR_AS3935_GPIO_SI)); gpio_set_mode(GPIO_PORT(SENSOR_AS3935_GPIO_EN_VREG), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(SENSOR_AS3935_GPIO_EN_VREG)); } uint8_t sensor_as3935_command(enum sensor_as3935_operation_t operation, uint8_t address_command, uint8_t data) { gpio_clear(SPI_NSS_PORT(SENSOR_AS3935_SPI), SPI_NSS_PIN(SENSOR_AS3935_SPI)); // set CS low to select device spi_send(SPI(SENSOR_AS3935_SPI), ((operation & 0x3) << 14) | ((address_command & 0x3f) << 8) | data); // send command (void)SPI_DR(SPI(SENSOR_AS3935_SPI)); // clear RXNE flag (by reading previously received data (not done by spi_read or spi_xref) while (!(SPI_SR(SPI(SENSOR_AS3935_SPI)) & SPI_SR_TXE)); // wait until Tx buffer is empty before clearing the (previous) RXNE flag while (!(SPI_SR(SPI(SENSOR_AS3935_SPI)) & SPI_SR_RXNE)); // wait for next data to be available gpio_set(SPI_NSS_PORT(SENSOR_AS3935_SPI), SPI_NSS_PIN(SENSOR_AS3935_SPI)); // set CS high to unselect device return SPI_DR(SPI(SENSOR_AS3935_SPI)); // return received data } uint8_t sensor_as3935_read_register(enum sensor_as3935_register_t name) { if (name >= LENGTH(sensor_as3935_register_infos)) { // check if register exists return 0; } if (SENSOR_AS3935_REGISTER_INT == name) { // you need to wait 2 ms after an interrupt before reading the register sleep_ms(2); } struct sensor_as3935_register_info_t register_info = sensor_as3935_register_infos[name]; // get register information uint8_t value = sensor_as3935_command(SENSOR_AS3935_OPERATION_READ, register_info.address, 0); // get register value value &= register_info.mask; // only keep relevant bits value >>= (ffs(register_info.mask) - 1); // shit value bits return value; } bool sensor_as3935_write_register(enum sensor_as3935_register_t name, uint8_t value) { if (name >= LENGTH(sensor_as3935_register_infos)) { // check if register exists return false; } struct sensor_as3935_register_info_t register_info = sensor_as3935_register_infos[name]; // get register information if (!register_info.writable) { return false; } uint8_t data = sensor_as3935_command(SENSOR_AS3935_OPERATION_READ, register_info.address, 0); // get register data value <<= (ffs(register_info.mask) - 1); // shit value bits value &= register_info.mask; // only keep relevant bits data &= ~register_info.mask; // clear relevant bit is final data data |= value; // put value in data sensor_as3935_command(SENSOR_AS3935_OPERATION_WRITE, register_info.address, data); // write register data return true; } void sensor_as3935_power_down(void) { sensor_as3935_write_register(SENSOR_AS3935_REGISTER_PWD, 1); } int8_t sensor_as3935_power_up(void) { nvic_disable_irq(GPIO_NVIC_EXTI_IRQ(SENSOR_AS3935_GPIO_IRQ)); // no need to count the next clock calibration sensor_as3935_write_register(SENSOR_AS3935_REGISTER_PWD, 0); // power up sensor_as3935_command(SENSOR_AS3935_OPERATION_DIRECT_COMMAND, SENSOR_AS3935_DIRECT_COMMAND_CALIB_RCO, SENSOR_AS3935_DIRECT_COMMAND_VALUE); // start RCO calibration sensor_as3935_write_register(SENSOR_AS3935_REGISTER_DISP_SRCO, 1); // output SRCO to start calibration sleep_ms(2); // as defined in the calibration procedure sensor_as3935_write_register(SENSOR_AS3935_REGISTER_DISP_SRCO, 0); // stop SRCO output nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(SENSOR_AS3935_GPIO_IRQ)); // re-enable interrupt sensor_as3935_interrupt = (0 != gpio_get(GPIO_PORT(SENSOR_AS3935_GPIO_IRQ), GPIO_PIN(SENSOR_AS3935_GPIO_IRQ))); // update interrupt status // check if calibration succeeded if (0 == sensor_as3935_read_register(SENSOR_AS3935_REGISTER_SRCO_CALIB_DONE)) { return -1; } if (1 == sensor_as3935_read_register(SENSOR_AS3935_REGISTER_SRCO_CALIB_NOK)) { return -2; } // the datasheet does not mention how to calibrate TRCO, but it is not equivalent to the SRCO mechanism and just happens if you try it often enough if (0 == sensor_as3935_read_register(SENSOR_AS3935_REGISTER_TRCO_CALIB_DONE)) { return -3; } if (1 == sensor_as3935_read_register(SENSOR_AS3935_REGISTER_TRCO_CALIB_NOK)) { return -4; } return 0; } bool sensor_as3935_tune(void) { sensor_as3935_write_register(SENSOR_AS3935_REGISTER_PWD, 0); // power up (without calibration) sensor_as3935_write_register(SENSOR_AS3935_REGISTER_LCO_FDIV, 0); // divide LCO by 16 (500 kHz LCO becomes 31250 Hz output) sensor_as3935_write_register(SENSOR_AS3935_REGISTER_DISP_LCO, 1); // output LCO on INT to measure frequency uint8_t best_tune = 0; // remember best internal tuning capacitor setting uint16_t best_offset = UINT16_MAX; // remember best LCO frequency offset sensor_as3935_calibrating = true; // count the pulses now for (uint8_t tune_cap = 0; tune_cap < 16; tune_cap++) { // test all internal tuning capacitor settings sensor_as3935_write_register(SENSOR_AS3935_REGISTER_TUN_CAP, tune_cap); // set internal capacitor sleep_ms(1); // wait a bit until the output stabilize sensor_as3935_interrupt_count = 0; // start counting sleep_ms(100); // wait to measure frequency precisely enough (3.5% corresponds to 109 pulses) uint16_t offset = abs(sensor_as3935_interrupt_count - 3125); // calculate offset to ideal frequency (500 kHz / 16 during 100 ms) if (offset < best_offset) { // check if the tune frequency is closed to the ideal best_offset = offset; // remember new best offset best_tune = tune_cap; // remember we found a new better tuning setting } } sensor_as3935_write_register(SENSOR_AS3935_REGISTER_DISP_LCO, 0); // switch back INT output for interrupts sensor_as3935_calibrating = false; // stop counting the pulses if (best_offset > 109 + 10) { // no setting has an error < 3.5% for 500 kHz (500 kHz / 16 * 3.5% during 100 ms, plus some measurement error margin) return false; } sensor_as3935_write_register(SENSOR_AS3935_REGISTER_TUN_CAP, best_tune); // set best capacitor tuning setting return true; } /** interrupt service routine called when IRQ does high to indicate interrupt (or clock outputs) */ void GPIO_EXTI_ISR(SENSOR_AS3935_GPIO_IRQ)(void) { if (sensor_as3935_calibrating) { sensor_as3935_interrupt_count++; // increment pulse count to calculate to frequency } else { sensor_as3935_interrupt = true; // notify user } exti_reset_request(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ)); // reset interrupt }