stm32f1/lib/sensor_as3935.c

559 lines
20 KiB
C

/** library to communication with ams AS3935 Franklin lightning sensor IC using SPI
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @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 <stdint.h> // standard integer types
#include <stdlib.h> // general utilities
#include <stdbool.h> // boolean utilities
#include <math.h> // math utilities
#include <strings.h> // fff utilities
/* STM32 (including CM3) libraries */
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/spi.h> // SPI library
#include <libopencm3/stm32/exti.h> // external interrupt defines
#include <libopencm3/cm3/nvic.h> // 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
}