AS3935: add library to communicate with ams AS3935 lightning sensor over SPI
This commit is contained in:
parent
67baf340fe
commit
9d7903ab79
|
@ -0,0 +1,571 @@
|
|||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communication with ams AS3935 Franklin lightning sensor IC using SPI
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @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;
|
||||
uint8_t mask;
|
||||
bool writable;
|
||||
};
|
||||
|
||||
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_PIN(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)
|
||||
{
|
||||
exti_reset_request(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ)); // reset interrupt
|
||||
if (sensor_as3935_calibrating) {
|
||||
sensor_as3935_interrupt_count++; // increment pulse count to calculate to frequency
|
||||
} else {
|
||||
sensor_as3935_interrupt = true; // notify user
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communication with ams AS3935 Franklin lightning sensor IC using SPI
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2019
|
||||
* @note peripherals used: SPI @ref sensor_as3935_spi, GPIO @ref sensor_as3935_gpio
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** a interrupt has been received */
|
||||
extern volatile bool sensor_as3935_interrupt;
|
||||
|
||||
/** SPI operation mode */
|
||||
enum sensor_as3935_operation_t {
|
||||
SENSOR_AS3935_OPERATION_WRITE = 0, /**< write to register */
|
||||
SENSOR_AS3935_OPERATION_DIRECT_COMMAND = 0, /**< send direct command (equivalent to write to register) */
|
||||
SENSOR_AS3935_OPERATION_READ = 1, /**< read from register */
|
||||
};
|
||||
|
||||
/** register names */
|
||||
enum sensor_as3935_register_t {
|
||||
SENSOR_AS3935_REGISTER_AFE_GB, /**< AFE Gain Boost */
|
||||
SENSOR_AS3935_REGISTER_PWD, /**< Power-down */
|
||||
SENSOR_AS3935_REGISTER_NF_LEV, /**< Noise Floor Level */
|
||||
SENSOR_AS3935_REGISTER_WDTH, /**< Watchdog threshold */
|
||||
SENSOR_AS3935_REGISTER_CL_STAT, /**< Clear statistics */
|
||||
SENSOR_AS3935_REGISTER_MIN_NUM_LIGH, /**< Minimum number of lightning */
|
||||
SENSOR_AS3935_REGISTER_SREJ, /**< Spike rejection */
|
||||
SENSOR_AS3935_REGISTER_LCO_FDIV, /**< Frequency division ration for antenna tuning */
|
||||
SENSOR_AS3935_REGISTER_MASK_DIST, /**< Mask Disturber */
|
||||
SENSOR_AS3935_REGISTER_INT, /**< Interrupt */
|
||||
SENSOR_AS3935_REGISTER_S_LIG_L, /**< Energy of the Single Lightning LSBYTE */
|
||||
SENSOR_AS3935_REGISTER_S_LIG_M, /**< Energy of the Single Lightning MSBYTE */
|
||||
SENSOR_AS3935_REGISTER_S_LIG_MM, /**< Energy of the Single Lightning MMSBYTE */
|
||||
SENSOR_AS3935_REGISTER_DISTANCE, /**< Distance estimation */
|
||||
SENSOR_AS3935_REGISTER_DISP_LCO, /**< Display LCO on IRQ pin */
|
||||
SENSOR_AS3935_REGISTER_DISP_SRCO, /**< Display SRCO on IRQ pin */
|
||||
SENSOR_AS3935_REGISTER_DISP_TRCO, /**< Display TRCO on IRQ pin */
|
||||
SENSOR_AS3935_REGISTER_TUN_CAP, /**< Internal Tuning Capacitors */
|
||||
SENSOR_AS3935_REGISTER_LDLUT1, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT2, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT3, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT4, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT5, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT6, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT7, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT8, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT9, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT10, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT11, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT12, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT13, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT14, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT15, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT16, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT17, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT18, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT19, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT20, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT21, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT22, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT23, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT24, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT25, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT26, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT27, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT28, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT29, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT30, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT31, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT32, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT33, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT34, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT35, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT36, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT37, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT38, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT39, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT40, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT41, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_LDLUT42, /**< Lightning Detection Look-up table */
|
||||
SENSOR_AS3935_REGISTER_TRCO_CALIB_DONE, /**< Calibration of TRCO done (1=successful) */
|
||||
SENSOR_AS3935_REGISTER_TRCO_CALIB_NOK, /**< Calibration of TRCO unsuccessful (1=not successful) */
|
||||
SENSOR_AS3935_REGISTER_SRCO_CALIB_DONE, /**< Calibration of SRCO done (1=successful) */
|
||||
SENSOR_AS3935_REGISTER_SRCO_CALIB_NOK, /**< Calibration of SRCO unsuccessful (1=not successful) */
|
||||
};
|
||||
|
||||
/** direct command to sets all registers in default mode */
|
||||
#define SENSOR_AS3935_DIRECT_COMMAND_PRESET_DEFAULT 0x3C
|
||||
/** direct command to calibrate automatically the internal RC oscillators */
|
||||
#define SENSOR_AS3935_DIRECT_COMMAND_CALIB_RCO 0x3D
|
||||
/** value to be used for direct commands */
|
||||
#define SENSOR_AS3935_DIRECT_COMMAND_VALUE 0x96
|
||||
|
||||
/** interrupt meaning
|
||||
* @note use as bit mask
|
||||
* @note if IRQ is high but no bit is set, a new front calculation is available
|
||||
*/
|
||||
enum sensor_as3935_interrupt_t {
|
||||
SENSOR_AS3935_INT_NH = 0x01, /**< noise level too high */
|
||||
SENSOR_AS3935_INT_D = 0x04, /**< disturber detected */
|
||||
SENSOR_AS3935_INT_L = 0x08, /**< lightning detected */
|
||||
};
|
||||
|
||||
/** register value for indoor AFE setting */
|
||||
#define SENSOR_AS3935_REGISTER_AFE_INDOOR 0x12
|
||||
/** register value for outdoor AFE setting */
|
||||
#define SENSOR_AS3935_REGISTER_AFE_OUTDOOR 0x0e
|
||||
|
||||
/** setup peripherals to communicate with sensor
|
||||
* @return false if communication with sensor failed
|
||||
* @note the sensor configuration will be set to default and powered down
|
||||
*/
|
||||
bool sensor_as3935_setup(void);
|
||||
/** release peripherals used to communicate with sensor */
|
||||
void sensor_as3935_release(void);
|
||||
/** send command
|
||||
* @param[in] operation operation mode
|
||||
* @param[in] address_command register address or direct command
|
||||
* @param[in] data register data
|
||||
* @return register value
|
||||
*/
|
||||
uint8_t sensor_as3935_command(enum sensor_as3935_operation_t operation, uint8_t address_command, uint8_t data);
|
||||
/** read register
|
||||
* @param[in] register register name
|
||||
* @return register value
|
||||
*/
|
||||
uint8_t sensor_as3935_read_register(enum sensor_as3935_register_t name);
|
||||
/** read register
|
||||
* @param[in] register register name
|
||||
* @param[in] register value
|
||||
* @return if write is allowed and succeeded
|
||||
*/
|
||||
bool sensor_as3935_write_register(enum sensor_as3935_register_t name, uint8_t value);
|
||||
/* power down sensor
|
||||
* @note after a power down the sensor needs to be calibrated again
|
||||
*/
|
||||
void sensor_as3935_power_down(void);
|
||||
/* power up sensor and calibrate RCO
|
||||
* @return 0 if calibration succeeded, < 0 else
|
||||
* @warning the antenna should be tuned before
|
||||
*/
|
||||
int8_t sensor_as3935_power_up(void);
|
||||
/* tune antenna
|
||||
* @return if calibration succeeded (false if no capacitor value leading to a frequency error < 3.5% has been found)
|
||||
* @note powers the sensor (but does not calibrates it)
|
||||
* @warning should be run after setup and before power up
|
||||
*/
|
||||
bool sensor_as3935_tune(void);
|
Loading…
Reference in New Issue