From ee7bd8701168e5a3fe1d3298f5f4a123eb28e55f Mon Sep 17 00:00:00 2001 From: UncleRus Date: Fri, 7 Jul 2017 03:31:11 +0500 Subject: [PATCH] Driver for AD7705/AD7706 SPI ADC --- examples/ad770x/Makefile | 4 + examples/ad770x/main.c | 50 ++++++++++ extras/ad770x/ad770x.c | 182 +++++++++++++++++++++++++++++++++++++ extras/ad770x/ad770x.h | 118 ++++++++++++++++++++++++ extras/ad770x/component.mk | 9 ++ 5 files changed, 363 insertions(+) create mode 100644 examples/ad770x/Makefile create mode 100644 examples/ad770x/main.c create mode 100644 extras/ad770x/ad770x.c create mode 100644 extras/ad770x/ad770x.h create mode 100644 extras/ad770x/component.mk diff --git a/examples/ad770x/Makefile b/examples/ad770x/Makefile new file mode 100644 index 0000000..f102848 --- /dev/null +++ b/examples/ad770x/Makefile @@ -0,0 +1,4 @@ +PROGRAM = ad770x +EXTRA_COMPONENTS = extras/ad770x +#ESPBAUD = 460800 +include ../../common.mk diff --git a/examples/ad770x/main.c b/examples/ad770x/main.c new file mode 100644 index 0000000..6b13afc --- /dev/null +++ b/examples/ad770x/main.c @@ -0,0 +1,50 @@ +/* + * Example of using AD7705/AD7706 driver + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include +#include +#include +#include + +#define CS_PIN 2 +#define AIN_CHANNEL 0 // AIN1+,AIN1- for AD7705 + +static const ad770x_params_t dev = { + .cs_pin = CS_PIN, + .master_clock = AD770X_MCLK_4_9152MHz, // 4.9152 MHz + .bipolar = false, // Unipolar mode + .gain = AD770X_GAIN_1, // No gain + .update_rate = AD770X_RATE_50 // 50 Hz output update rate +}; + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + while (ad770x_init(&dev, AIN_CHANNEL) != 0) + { + printf("Cannot initialize AD7705\n"); + vTaskDelay(500 / portTICK_PERIOD_MS); + } + + while (true) + { + // wait for data + while (!ad770x_data_ready(&dev, AIN_CHANNEL)) {} + + // Read result + uint16_t raw = ad770x_raw_adc_value(&dev, AIN_CHANNEL); + + printf("Raw ADC value: %d\n", raw); + + vTaskDelay(500 / portTICK_PERIOD_MS); + } +} diff --git a/extras/ad770x/ad770x.c b/extras/ad770x/ad770x.c new file mode 100644 index 0000000..9bd8af5 --- /dev/null +++ b/extras/ad770x/ad770x.c @@ -0,0 +1,182 @@ +/** + * Driver for AD7705/AD7706 SPI ADC + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include "ad770x.h" + +#include +#include +#include +#include + +#define AD770X_DEBUG + +#define BUS 1 +#define INIT_TIMEOUT 500000 // 500ms + +#ifdef AD770X_DEBUG +#include +#define debug(fmt, ...) printf("%s" fmt "\n", "AD770x: ", ## __VA_ARGS__) +#else +#define debug(fmt, ...) +#endif + +#define timeout_expired(start, len) ((uint32_t)(sdk_system_get_time() - (start)) >= (len)) + +#define _BV(x) (1 << (x)) + +#define REG_COMM 0x00 // 8 bits +#define REG_SETUP 0x01 // 8 bits +#define REG_CLOCK 0x02 // 8 bits +#define REG_DATA 0x03 // 16 bits +#define REG_TEST 0x04 // 8 bits +#define REG_OFFS 0x06 // 24 bits +#define REG_GAIN 0x07 // 24 bits + +#define BIT_COMM_CH0 0 +#define BIT_COMM_CH1 1 +#define BIT_COMM_STBY 2 +#define BIT_COMM_RW 3 +#define BIT_COMM_RS0 4 +#define BIT_COMM_RS1 5 +#define BIT_COMM_RS2 6 +#define BIT_COMM_DRDY 7 + +#define MASK_COMM_CH 0x03 +#define MASK_COMM_RS 0x70 + +#define BIT_CLOCK_FS0 0 +#define BIT_CLOCK_FS1 1 +#define BIT_CLOCK_CLK 2 +#define BIT_CLOCK_CLKDIV 3 +#define BIT_CLOCK_CLKDIS 4 + +#define MASK_CLOCK_FS 0x03 +#define MASK_CLOCK_CLK 0x0c + +#define BIT_SETUP_FSYNC 0 +#define BIT_SETUP_BUF 1 +#define BIT_SETUP_BU 2 +#define BIT_SETUP_G0 3 +#define BIT_SETUP_G1 4 +#define BIT_SETUP_G2 5 +#define BIT_SETUP_MD0 6 +#define BIT_SETUP_MD1 7 + +#define MASK_SETUP_GAIN 0x38 +#define MASK_SETUP_MODE 0xc0 + +static const spi_settings_t config = { + .endianness = SPI_BIG_ENDIAN, + .msb = true, + .minimal_pins = true, + .mode = SPI_MODE3, + .freq_divider = SPI_FREQ_DIV_500K +}; + +static uint8_t write(uint8_t cs_pin, uint8_t value) +{ + spi_settings_t old; + spi_get_settings(BUS, &old); + spi_set_settings(BUS, &config); + + gpio_write(cs_pin, false); + uint8_t res = spi_transfer_8(BUS, value); + //debug("byte wr: 0x%02x", value); + gpio_write(cs_pin, true); + + spi_set_settings(BUS, &old); + return res; +} + +inline static uint8_t read_byte(uint8_t cs_pin) +{ + return write(cs_pin, 0); +} + +static uint16_t read_word(uint8_t cs_pin) +{ + spi_settings_t old; + spi_get_settings(BUS, &old); + spi_set_settings(BUS, &config); + + gpio_write(cs_pin, false); + uint16_t res = spi_transfer_16(BUS, 0); + gpio_write(cs_pin, true); + + spi_set_settings(BUS, &old); + return res; +} + +static void prepare(uint8_t channel, uint8_t reg, bool read, uint8_t cs_pin, bool standby) +{ + write(cs_pin, + (channel & MASK_COMM_CH) | + (read ? _BV(BIT_COMM_RW) : 0) | + ((reg << BIT_COMM_RS0) & MASK_COMM_RS) | + (standby ? _BV(BIT_COMM_STBY) : 0) + ); +} + +int ad770x_init(const ad770x_params_t *params, uint8_t channel) +{ + if (!spi_set_settings(BUS, &config)) + { + debug("Cannot init SPI"); + return -EIO; + } + + if ((params->master_clock >= AD770X_MCLK_2_4576MHz && params->update_rate < AD770X_RATE_50) + || (params->master_clock < AD770X_MCLK_2_4576MHz && params->update_rate > AD770X_RATE_200)) + { + debug("Invalid update rate / master clock combination"); + return -EINVAL; + } + + gpio_enable(params->cs_pin, GPIO_OUTPUT); + gpio_write(params->cs_pin, true); + + prepare(channel, REG_CLOCK, false, params->cs_pin, false); + write(params->cs_pin, + ((params->master_clock << BIT_CLOCK_CLK) & MASK_CLOCK_CLK) | + (params->update_rate & MASK_CLOCK_FS) + ); + + ad770x_set_mode(params, channel, AD770X_MODE_CALIBRATION); + + uint32_t start = sdk_system_get_time(); + while (!ad770x_data_ready(params, channel)) + if (timeout_expired(start, INIT_TIMEOUT)) + { + debug("Timeout while calibration"); + return -EIO; + } + + return 0; +} + +void ad770x_set_mode(const ad770x_params_t *params, uint8_t channel, ad770x_mode_t mode) +{ + prepare(channel, REG_SETUP, false, params->cs_pin, false); + write(params->cs_pin, + ((params->gain << BIT_SETUP_G0) & MASK_SETUP_GAIN) | + (params->bipolar ? 0 : _BV(BIT_SETUP_BU)) | + ((mode << BIT_SETUP_MD0) & MASK_SETUP_MODE) + ); +} + +bool ad770x_data_ready(const ad770x_params_t *params, uint8_t channel) +{ + prepare(channel, REG_COMM, true, params->cs_pin, false); + return !(read_byte(params->cs_pin) & _BV(BIT_COMM_DRDY)); +} + +uint16_t ad770x_raw_adc_value(const ad770x_params_t *params, uint8_t channel) +{ + prepare(channel, REG_DATA, true, params->cs_pin, false); + return read_word(params->cs_pin); +} + diff --git a/extras/ad770x/ad770x.h b/extras/ad770x/ad770x.h new file mode 100644 index 0000000..bfa2a98 --- /dev/null +++ b/extras/ad770x/ad770x.h @@ -0,0 +1,118 @@ +/** + * Driver for AD7705/AD7706 SPI ADC + * + * Part of esp-open-rtos + * Copyright (C) 2017 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#ifndef _EXTRAS_AD770X_H_ +#define _EXTRAS_AD770X_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Input gain + */ +typedef enum +{ + AD770X_GAIN_1 = 0, + AD770X_GAIN_2, + AD770X_GAIN_4, + AD770X_GAIN_8, + AD770X_GAIN_16, + AD770X_GAIN_32, + AD770X_GAIN_64, + AD770X_GAIN_128 +} ad770x_gain_t; + +/** + * Master clock frequency + */ +typedef enum +{ + AD770X_MCLK_1MHz = 0, //!< 1 MHz + AD770X_MCLK_2MHz, //!< 2 MHz + AD770X_MCLK_2_4576MHz, //!< 2.4576 MHz + AD770X_MCLK_4_9152MHz, //!< 4.9152 MHz +} ad770x_master_clock_t; + +/** + * Output update rate + */ +typedef enum +{ + AD770X_RATE_20 = 0, //!< Output update rate is 20 Hz, -3 dB filter cutoff = 5.24 Hz, works with 1 or 2 MHz master clock + AD770X_RATE_25, //!< Output update rate is 25 Hz, -3 dB filter cutoff = 6.55 Hz, works with 1 or 2 MHz master clock + AD770X_RATE_100, //!< Output update rate is 100 Hz, -3 dB filter cutoff = 26.2 Hz, works with 1 or 2 MHz master clock + AD770X_RATE_200, //!< Output update rate is 200 Hz, -3 dB filter cutoff = 52.4 Hz, works with 1 or 2 MHz master clock + AD770X_RATE_50, //!< Output update rate is 50 Hz, -3 dB filter cutoff = 13.1 Hz, works with 2.4576 or 4.9152 MHz master clock + AD770X_RATE_60, //!< Output update rate is 60 Hz, -3 dB filter cutoff = 15.7 Hz, works with 2.4576 or 4.9152 MHz master clock + AD770X_RATE_250, //!< Output update rate is 250 Hz, -3 dB filter cutoff = 65.5 Hz, works with 2.4576 or 4.9152 MHz master clock + AD770X_RATE_500, //!< Output update rate is 500 Hz, -3 dB filter cutoff = 131 Hz, works with 2.4576 or 4.9152 MHz master clock +} ad770x_update_rate_t; + +/** + * Device mode + */ +typedef enum +{ + AD770X_MODE_NORMAL = 0, + AD770X_MODE_CALIBRATION, + AD770X_MODE_ZERO_CALIBRATION, + AD770X_MODE_FULL_CALIBRATION +} ad770x_mode_t; + +/** + * Device descriptor + */ +typedef struct +{ + uint8_t cs_pin; //!< GPIO pin for chip select + ad770x_master_clock_t master_clock; //!< Master clock frequency + bool bipolar; //!< Bipolar/Unipolar mode + ad770x_gain_t gain; //!< Input gain + ad770x_update_rate_t update_rate; //!< Output update rate +} ad770x_params_t; + +/** + * Init device and setup channel params + * @param params Device descriptor pointer + * @param channel Input channel + * @return Non-zero when error occured + */ +int ad770x_init(const ad770x_params_t *params, uint8_t channel); + +/** + * Set device mode (see datasheet) + * @param params Device descriptor pointer + * @param channel Input channel + * @param mode Device mode + */ +void ad770x_set_mode(const ad770x_params_t *params, uint8_t channel, ad770x_mode_t mode); + +/** + * Get conversion status + * @param params Device descriptor pointer + * @param channel Input channel + * @return true when data is ready + */ +bool ad770x_data_ready(const ad770x_params_t *params, uint8_t channel); + +/** + * Get converted ADC value + * @param params Device descriptor pointer + * @param channel Input channel + * @return Raw ADC value + */ +uint16_t ad770x_raw_adc_value(const ad770x_params_t *params, uint8_t channel); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXTRAS_AD770X_H_ */ diff --git a/extras/ad770x/component.mk b/extras/ad770x/component.mk new file mode 100644 index 0000000..a4d47f5 --- /dev/null +++ b/extras/ad770x/component.mk @@ -0,0 +1,9 @@ +# Component makefile for extras/ad770x (AD7705/AD7706 driver) + +# expected anyone using ADC driver includes it as 'ad770x/ad770x.h' +INC_DIRS += $(ad770x_ROOT).. + +# args for passing into compile rule generation +ad770x_SRC_DIR = $(ad770x_ROOT) + +$(eval $(call component_compile_rules,ad770x))