From 53f39b99d541754030728d3ce99dc112797dfa38 Mon Sep 17 00:00:00 2001
From: Anton Sarukhanov <code@ant.sr>
Date: Fri, 2 Nov 2018 00:17:03 -0400
Subject: [PATCH] Define ADC register constants, implement register access &
 sending commands.

---
 ads8698.c         | 65 ++++++++++++++++++++++++++++++++---------------
 include/ads8698.h | 49 ++++++++++++++++++++++++++++++++---
 include/spi.h     |  2 +-
 measure.c         |  3 ++-
 spi.c             |  6 ++---
 5 files changed, 96 insertions(+), 29 deletions(-)

diff --git a/ads8698.c b/ads8698.c
index 0591f2c..ffcc403 100644
--- a/ads8698.c
+++ b/ads8698.c
@@ -1,5 +1,5 @@
 /**
- * This file includes functions for interacting with an ADC
+ * This file includes functions for interacting with an ADC.
  *
  * Written for: TI ADS8698 8-channel ADC
  *
@@ -34,42 +34,67 @@ void ADS8698_init(void) {
 
     // Reset the ADC.
     ADS8698_reset();
-
-    // Start measurements
-    ADS8698_send_command(ADS8698_RESET);
-    SPI_transmit('H');
-    SPI_transmit('e');
-    SPI_transmit('l');
-    SPI_transmit('l');
-    SPI_transmit('o');
-    SPI_transmit('!');
 }
 
 /* Set the input range on the ADC. */
 void ADS8698_set_range(ADS8698_range range) {
-    // TODO
-    return;
+    uint8_t address;
+    for (uint8_t channel = 0; channel < 8; channel++) {
+        address = 0x05 + channel;
+        ADS8698_write_register(address, range);
+        ADS8698_read_register(address);
+    }
 }
 
-/* Write to a register on the ADC. */
-void ADS8698_write_register(uint8_t address, uint8_t data) {
-    // TODO
-    return;
+/* Start the ADC's auto-scan mode. */
+void ADS8698_auto_scan(void) {
+    return; // TODO
 }
 
-/* Send a command to the ADC. */
+/* Send a command to the ADC.
+ *
+ * See datasheet section 8.5.1.
+ */
 void ADS8698_send_command(ADS8698_command command) {
-    // TODO
-    return;
+    ADS8698_CS_LOW;
+    SPI_transmit(command);  // First byte
+    SPI_transmit(0x00);  // Second byte (always zero).
+    ADS8698_CS_HIGH;
+}
+
+/* Write to a register on the ADC.
+ *
+ * See datasheet section 8.5.2.
+ */
+void ADS8698_write_register(uint8_t address, uint8_t data) {
+    ADS8698_CS_LOW;
+    address |= 1UL << 7;  // 8th bit signifies "Read/Write Mode"; set 1.
+    SPI_transmit(address);
+    SPI_transmit(data);
+    ADS8698_CS_HIGH;
 }
 
+/* Read from a register on the ADC.
+ *
+ * See datasheet section 8.5.2.
+ */
+uint8_t ADS8698_read_register(uint8_t address) {
+    ADS8698_CS_LOW;
+    address |= 0UL << 7;  // 8th bit signifies "Read/Write Mode"; set 0.
+    SPI_transmit(address << 1);
+    uint8_t data = SPI_receive();
+    ADS8698_CS_HIGH;
+    return data;
+}
+
+
 /* Reset the ADC.
  *
- * Pull RST low for 70ns (instructions: 40-100ns).
  * See datasheet section 8.4.1.1.6.
  */
 void ADS8698_reset(void) {
     PORTC &= ~_BV(PORTC0);
+    // Pull RST low for 70ns (instructions: 40-100ns).
     _delay_us(0.47);
     PORTC |= _BV(PORTC0);
 }
diff --git a/include/ads8698.h b/include/ads8698.h
index 293a92d..b890efd 100644
--- a/include/ads8698.h
+++ b/include/ads8698.h
@@ -1,5 +1,5 @@
 /**
- * This file includes functions for interacting with an ADC
+ * This file includes functions for interacting with an ADC.
  *
  * Written for: TI ADS8698 8-channel ADC
  *
@@ -14,7 +14,13 @@
  */
 
 
-/* ADC Commands.
+// Macros to toggle control pins
+#define ADS8698_RST_LOW PORTC &= ~_BV(PORTC0)
+#define ADS8698_RST_HIGH PORTC |= _BV(PORTC0)
+#define ADS8698_CS_LOW PORTC &= ~_BV(PORTC1)
+#define ADS8698_CS_HIGH PORTC |= _BV(PORTC1)
+
+/* ADC commands
  *
  * See datasheet section 8.5.1.
  */
@@ -36,7 +42,36 @@ typedef enum {
 } ADS8698_command;
 
 
-/* ADC Input Ranges.
+/* ADC program registers
+ *
+ * See datasheet section 8.5.1.
+ */
+typedef enum {
+    // Auto-Scan control
+    ADS8698_AUTO_CH_SEL = 0x01,  // Channels to use in auto-scan mode
+    ADS8698_CH_POWER_DN = 0x02,  // Channels to shut down
+
+    // Feature selection
+    ADS8698_FEATURE_SEL = 0x03,  // Enable & configure device features
+
+    // Range selection
+    ADS8698_CH0_RANGE   = 0x05,  // Input range for channel 0
+    ADS8698_CH1_RANGE   = 0x06,  // Input range for channel 1
+    ADS8698_CH2_RANGE   = 0x07,  // Input range for channel 2
+    ADS8698_CH3_RANGE   = 0x08,  // Input range for channel 3
+    ADS8698_CH4_RANGE   = 0x09,  // Input range for channel 4
+    ADS8698_CH5_RANGE   = 0x0A,  // Input range for channel 5
+    ADS8698_CH6_RANGE   = 0x0B,  // Input range for channel 6
+    ADS8698_CH7_RANGE   = 0x0C,  // Input range for channel 7
+
+    // TODO: Implement alarm-related registers.
+
+    // Command read-back (read-only)
+    ADS8698_COMMAND     = 0x00,  // Current device command (mode of operation)
+} ADS8698_register;
+
+
+/* ADC input ranges
  *
  * See datasheet section 8.5.2.3.3.
  */
@@ -45,7 +80,7 @@ typedef enum {
     ADS8698_RANGE_BIPOLAR_1_25X  = 0x01,  // 0 +/- 1.25 x Vref
     ADS8698_RANGE_BIPOLAR_0_625X = 0x02,  // 0 +/- 0.625 x Vref
     ADS8698_RANGE_UNIPOLAR_2_5X  = 0x05,  // 0 + 2.5 x Vref
-    ADS8698_RANGE_UNIPOLAR_1_25X = 0x0    // 0 + 1.25 x Vref
+    ADS8698_RANGE_UNIPOLAR_1_25X = 0x06   // 0 + 1.25 x Vref
 } ADS8698_range;
 
 
@@ -58,5 +93,11 @@ void ADS8698_set_range(ADS8698_range range);
 /* Send a command to the ADC. */
 void ADS8698_send_command(ADS8698_command command);
 
+/* Write to a register on the ADC. */
+void ADS8698_write_register(uint8_t address, uint8_t data);
+
+/* Read from a register on the ADC. */
+uint8_t ADS8698_read_register(uint8_t address);
+
 /* Reset the ADC. */
 void ADS8698_reset(void);
diff --git a/include/spi.h b/include/spi.h
index 5bf901f..f0d0df8 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -9,4 +9,4 @@ void SPI_init(void);
 void SPI_transmit(char data);
 
 /* Receive a byte from the SPI interface. */
-char SPI_receive(void);
+uint8_t SPI_receive(void);
diff --git a/measure.c b/measure.c
index c14a95b..eb61fc1 100644
--- a/measure.c
+++ b/measure.c
@@ -9,7 +9,7 @@
 #include "include/ads8698.h"
 #include "include/measure.h"
 
-
+#define MEASURE_RANGE ADS8698_RANGE_UNIPOLAR_1_25X
 
 /* Acquire resistance measurements. */
 struct Measurements measure() {
@@ -22,6 +22,7 @@ struct Measurements measure() {
 /* Prepare for measuring resistance. */
 void Measure_init(void) {
     ADS8698_init();
+    ADS8698_set_range(MEASURE_RANGE);
     _delay_us(100);
     // int sample_count = 2048; // TODO: what is this
     // ADS8698_config(&sample_buffer, sample_count, ADS8698_CHANNEL_0, INPUT_RANGE_UNIPOLAR_1_25X_VREF);
diff --git a/spi.c b/spi.c
index e20d0c6..d11b552 100644
--- a/spi.c
+++ b/spi.c
@@ -11,8 +11,8 @@ void SPI_init(void) {
     // Set MOSI, SCK, and SS as outputs
     DDRB = (1<<DDB3) | (1<<DDB5) | (1<<DDB2);
 
-    // Enable SPI, become master, set clock rate to CPU clock / 16.
-    SPCR = (1<<SPE) | (1<<MSTR);
+    // Enable SPI, become master, set SPI clock rate to 1 MHz.
+    SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR0);
 }
 
 /* Send a byte over the SPI interface. */
@@ -26,7 +26,7 @@ void SPI_transmit(char data) {
 }
 
 /* Receive a byte from the SPI interface. */
-char SPI_receive(void) {
+uint8_t SPI_receive(void) {
     // Wait for reception to complete.
     while (!(SPSR & (1<<SPIF)))
         ;
-- 
GitLab