83 lines
5.3 KiB
C
83 lines
5.3 KiB
C
/** library to communication with Maxim MAX1247 12-bit 4-channel ADC using SPI
|
|
* @file
|
|
* @author King Kévin <kingkevin@cuvoodoo.info>
|
|
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
|
* @date 2020
|
|
* @note peripherals used: SPI @ref sensor_max1247_spi
|
|
*/
|
|
/* standard libraries */
|
|
#include <stdint.h> // standard integer types
|
|
#include <stdlib.h> // general utilities
|
|
#include <stdbool.h> // boolean 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
|
|
|
|
/* 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;
|
|
}
|
|
|