From a107c60e787cc497c451ddb4cfc654f80956b905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Mon, 7 Dec 2020 19:11:07 +0100 Subject: [PATCH] sensor_max1247: add library to read ADC values from MAX1247 --- lib/sensor_max1247.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ lib/sensor_max1247.h | 20 +++++++++++ 2 files changed, 102 insertions(+) create mode 100644 lib/sensor_max1247.c create mode 100644 lib/sensor_max1247.h diff --git a/lib/sensor_max1247.c b/lib/sensor_max1247.c new file mode 100644 index 0000000..b7b674d --- /dev/null +++ b/lib/sensor_max1247.c @@ -0,0 +1,82 @@ +/** library to communication with Maxim MAX1247 12-bit 4-channel ADC using SPI + * @file + * @author King Kévin + * @copyright SPDX-License-Identifier: GPL-3.0-or-later + * @date 2020 + * @note peripherals used: SPI @ref sensor_max1247_spi + */ +/* standard libraries */ +#include // standard integer types +#include // general utilities +#include // boolean utilities + +/* STM32 (including CM3) libraries */ +#include // Cortex M3 utilities +#include // real-time control clock library +#include // general purpose input output library +#include // SPI library + +/* own libraries */ +#include "global.h" // common methods +#include "sensor_max1247.h" // own definitions + +/** @defgroup sensor_max1247_spi SPI peripheral used to communicate with the AS3935 + * @{ + */ +#define SENSOR_MAX1247_SPI 2 /**< SPI peripheral */ +/** @} */ + +void sensor_max1247_setup(void) +{ + // setup SPI + rcc_periph_clock_enable(RCC_SPI_SCK_PORT(SENSOR_MAX1247_SPI)); // enable clock for GPIO peripheral for clock signal + gpio_set_mode(SPI_SCK_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(SENSOR_MAX1247_SPI)); // set SCK as output (clock speed will be negotiated later) + rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(SENSOR_MAX1247_SPI)); // enable clock for GPIO peripheral for MOSI signal + gpio_set_mode(SPI_MOSI_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(SENSOR_MAX1247_SPI)); // set MOSI as output + rcc_periph_clock_enable(RCC_SPI_MISO_PORT(SENSOR_MAX1247_SPI)); // enable clock for GPIO peripheral for MISO signal + gpio_set_mode(SPI_MISO_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_MISO_PIN(SENSOR_MAX1247_SPI)); // set MISO as input + rcc_periph_clock_enable(RCC_SPI_NSS_PORT(SENSOR_MAX1247_SPI)); // enable clock for GPIO peripheral for NSS (CS) signal + gpio_set_mode(SPI_NSS_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, SPI_NSS_PIN(SENSOR_MAX1247_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_MAX1247_SPI)); // enable clock for SPI peripheral + spi_reset(SPI(SENSOR_MAX1247_SPI)); // clear SPI values to default + spi_init_master(SPI(SENSOR_MAX1247_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_64, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); // initialise SPI as master, divide clock by 64 (72E6/64=1125 kHz, max MAX1247 SCK is 2 MHz, maximum SPI PCLK clock is 72 MHz, depending on which SPI is used), set clock polarity to idle low, set clock phase to do bit change on falling edge (polarity depends on clock phase), use 8 bits frames (the control is 8-bit long, and the conversion response 16-bit), use MSb first + spi_set_full_duplex_mode(SPI(SENSOR_MAX1247_SPI)); // ensure we are in full duplex mode + spi_enable_software_slave_management(SPI(SENSOR_MAX1247_SPI)); // control NSS (CS) manually + spi_set_nss_high(SPI(SENSOR_MAX1247_SPI)); // set NSS high (internally) so we can output + spi_disable_ss_output(SPI(SENSOR_MAX1247_SPI)); // disable NSS output since we control CS manually + gpio_set(SPI_NSS_PORT(SENSOR_MAX1247_SPI), SPI_NSS_PIN(SENSOR_MAX1247_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_MAX1247_SPI)); // enable SPI +} + +void sensor_max1247_release(void) +{ + spi_reset(SPI(SENSOR_MAX1247_SPI)); + spi_disable(SPI(SENSOR_MAX1247_SPI)); + rcc_periph_clock_disable(RCC_SPI(SENSOR_MAX1247_SPI)); + gpio_set_mode(SPI_NSS_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_NSS_PIN(SENSOR_MAX1247_SPI)); + gpio_set_mode(SPI_MISO_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MISO_PIN(SENSOR_MAX1247_SPI)); + gpio_set_mode(SPI_MOSI_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MOSI_PIN(SENSOR_MAX1247_SPI)); + gpio_set_mode(SPI_SCK_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_SCK_PIN(SENSOR_MAX1247_SPI)); +} + +uint16_t sensor_max1247_read(uint8_t channel) +{ + if (channel > 3) { // ensure we read only from one of the 4 available channels + return UINT16_MAX; + } + + gpio_clear(SPI_NSS_PORT(SENSOR_MAX1247_SPI), SPI_NSS_PIN(SENSOR_MAX1247_SPI)); // set CS low to select device + const uint8_t channels[4] = { 1, 5, 2, 6 }; // SEL bits corresponding to channel (in single ended mode) + uint8_t spi_in = spi_xfer(SPI(SENSOR_MAX1247_SPI), 0x8e | (channels[channel] << 4)); // send conversion control (START bit set, unipolar, single ended, internal clock mode) + sleep_us(8); // wait for conversion to finish (max. 7.5 µs using internal clock) + spi_in = spi_xfer(SPI(SENSOR_MAX1247_SPI), 0); // read first conversion bytes + const uint16_t value = (spi_in << 8) + spi_xfer(SPI(SENSOR_MAX1247_SPI), 0); // read second conversion byte + gpio_set(SPI_NSS_PORT(SENSOR_MAX1247_SPI), SPI_NSS_PIN(SENSOR_MAX1247_SPI)); // set CS high to select device + if ((value & 0x8000) || (value & 0x0007)) { // ensure it has one leading and 3 trailing zeros + return UINT16_MAX; + } + return value >> 3; +} + diff --git a/lib/sensor_max1247.h b/lib/sensor_max1247.h new file mode 100644 index 0000000..b155cca --- /dev/null +++ b/lib/sensor_max1247.h @@ -0,0 +1,20 @@ +/** library to communication with Maxim MAX1247 12-bit 4-channel ADC using SPI + * @file + * @author King Kévin + * @copyright SPDX-License-Identifier: GPL-3.0-or-later + * @date 2020 + * @note peripherals used: SPI @ref sensor_max1247_spi + */ +#pragma once + +/** setup peripherals to communicate with sensor + * @note the sensor configuration will be set to default and powered down + */ +void sensor_max1247_setup(void); +/** release peripherals used to communicate with sensor */ +void sensor_max1247_release(void); +/** read conversion from channel + * @param[in] channel which of the 4 channels to convert + * @return 12-bit conversion value (0xffff if error) + */ +uint16_t sensor_max1247_read(uint8_t channel);