Difference between revisions of "NRF52840 AD7124 Code"

From HiveTool
Jump to: navigation, search
(Created page with " Inital nRF52840 code that talks to the AD7124-8")
 
Line 1: Line 1:
  
 
Inital nRF52840 code that talks to the AD7124-8
 
Inital nRF52840 code that talks to the AD7124-8
 +
 +
 +
/**
 +
* Copyright (c) 2015 - 2017, Nordic Semiconductor ASA
 +
*
 +
* All rights reserved.
 +
*
 +
* Redistribution and use in source and binary forms, with or without modification,
 +
* are permitted provided that the following conditions are met:
 +
*
 +
* 1. Redistributions of source code must retain the above copyright notice, this
 +
*    list of conditions and the following disclaimer.
 +
*
 +
* 2. Redistributions in binary form, except as embedded into a Nordic
 +
*    Semiconductor ASA integrated circuit in a product or a software update for
 +
*    such product, must reproduce the above copyright notice, this list of
 +
*    conditions and the following disclaimer in the documentation and/or other
 +
*    materials provided with the distribution.
 +
*
 +
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 +
*    contributors may be used to endorse or promote products derived from this
 +
*    software without specific prior written permission.
 +
*
 +
* 4. This software, with or without modification, must only be used with a
 +
*    Nordic Semiconductor ASA integrated circuit.
 +
*
 +
* 5. Any software provided in binary form under this license must not be reverse
 +
*    engineered, decompiled, modified and/or disassembled.
 +
*
 +
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 +
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 +
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 +
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 +
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 +
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 +
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 +
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 +
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 +
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 +
*
 +
*/
 +
#include "nrf_drv_spi.h"
 +
#include "app_util_platform.h"
 +
#include "nrf_gpio.h"
 +
#include "nrf_delay.h"
 +
#include "boards.h"
 +
#include "app_error.h"
 +
#include <string.h>
 +
#include "nrf_log.h"
 +
#include "nrf_log_ctrl.h"
 +
#include "nrf_log_default_backends.h"
 +
#include "SEGGER_RTT.h"
 +
 +
 +
 +
#define SPI_INSTANCE  0 /**< SPI instance index. */
 +
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
 +
static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */
 +
 +
 +
/* AD7124 parameters */
 +
 +
char FS[2] = { 0x01, 0x40 };                        /*  Filter Select (FS) default: 320 */
 +
char PGA  = 0x07;                                  /*  PGA default: 128                */
 +
char AINM  = 0x06;                                  /*  Negative analog input: 6 */
 +
char AINP  = 0x07;                                  /*  Positive analog input: 7 */
 +
enum { LOW_POWER=0x00, MEDIUM_POWER=0x40, HIGH_POWER=0x80 } power_mode = HIGH_POWER;
 +
int data_register=0;
 +
int return_code = 0;
 +
 +
char wrBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 +
char rdBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 +
int number_active_channels=1;
 +
int number_of_filtered_samples = 0;
 +
 +
int data_array[64];
 +
int number_of_samples = 16;                        /*  Number of data reads to average */
 +
 +
int sum = 0;
 +
int sample_counter = 0;
 +
 +
 +
 +
/* *****************************************************************************************
 +
*
 +
*                            reset_AD7124
 +
*
 +
*  sends 64 ones (eight 0xFF) to the AD7124 to reset it
 +
*
 +
* **************************************************************************************** */
 +
 +
 +
void reset_AD7124 ( void )
 +
  {
 +
    SEGGER_RTT_WriteString(0, "reset_AD7124.\n");
 +
  /* reset AD7124 by writing 64 ones (eight 0xFF) */
 +
   
 +
    uint8_t wrBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 +
    uint8_t rdBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 +
    // memset(m_rx_buf, 0, m_length);
 +
    spi_xfer_done = false;
 +
 +
    nrf_delay_ms(10);
 +
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 8, rdBuf, 8));
 +
   
 +
    /* check reset by reading device ID code */
 +
    wrBuf[0]= 0x45;
 +
    nrf_delay_ms(10);
 +
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 2, rdBuf, 2));
 +
   
 +
    SEGGER_RTT_printf(0, "Device ID Code: 0x%02x\n", rdBuf[1]);
 +
    if ( rdBuf[1] != 0x14 )
 +
      {
 +
      bsp_board_led_invert(BSP_BOARD_LED_3);
 +
      SEGGER_RTT_printf(0, "Reset failed: 0x%02x 0x%02x\n", rdBuf[0], rdBuf[1]);
 +
      }
 +
    }
 +
 +
/* ******************************************************************************************
 +
*
 +
*                            initialize_AD7124
 +
*
 +
*  turns of default number_active_channel
 +
*  turns on
 +
*  sets the filter
 +
*
 +
* **************************************************************************************** */
 +
 +
void initialize_AD7124 ( void )
 +
{
 +
uint8_t wrBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 +
uint8_t rdBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 +
 +
SEGGER_RTT_WriteString(0, "Starting initialize_AD7124.\n");
 +
 +
wrBuf[0]= 0x09; wrBuf[1]= 0x00; wrBuf[2]= 0x01;                        /* Turn off channel_0 (on by default) */
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));
 +
 +
SEGGER_RTT_WriteString(0, "Turned off channel 0.\n");
 +
 +
//wrBuf[1] = ( AIMP & 0x18 ) >> 3 ;
 +
wrBuf[0]= 0x0a;
 +
wrBuf[1] = (( AINP >> 3) & 0x07 ) | 0x80;
 +
wrBuf[2] = (( AINP & 0x07 ) << 5) | ( AINM & 0x1F );
 +
 +
SEGGER_RTT_printf(0, "Write buffers: 0x%02x  0x%02x  0x%02x\n", wrBuf[0], wrBuf[1], wrBuf[2]);
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));
 +
 +
SEGGER_RTT_WriteString(0, "Set up AINP.\n");
 +
 +
wrBuf[0]= 0x19; wrBuf[1]= 0x08; wrBuf[2]= 0x7F;
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));
 +
 +
wrBuf[0]= 0x21; wrBuf[1]= 0x06; wrBuf[2]= FS[0]; wrBuf[3]= FS[1];      /* Set the Filter Select bits            */
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));
 +
 +
SEGGER_RTT_WriteString(0, "Set up FS bits.\n");
 +
 +
wrBuf[0]= 0x07; wrBuf[1]= 0x03; wrBuf[2]= 0xF0; wrBuf[3]= 0x38;
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));
 +
 +
}
 +
 +
 +
/* ******************************************************************************************
 +
*
 +
*                            wait_for_AD7124_ready
 +
*
 +
*  waits for AD7124 to finish conversion by testing the busy busy bit
 +
*  if it times out, it return busy (1)
 +
*  waiting set the how long it will wait for the conversion before it times out
 +
*
 +
* **************************************************************************************** */
 +
 +
 +
uint8_t wait_for_AD7124_ready(void)
 +
  {
 +
  uint8_t wrBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 +
  uint8_t rdBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 +
  unsigned int waiting=10000;
 +
  uint8_t busy = 1;
 +
 +
  while( busy && waiting )
 +
    {
 +
    /* Read the Status Register */
 +
    wrBuf[0]= 0x40;
 +
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 2, rdBuf, 2));
 +
    busy = rdBuf[1] & 0x80 ;                      /* Test the Not Ready bit */
 +
 
 +
    nrf_delay_ms(1);                                /* wait 0.1 msec */
 +
    waiting--;
 +
    }
 +
//  SEGGER_RTT_printf(0, "waiting: %d\n", waiting);
 +
  return busy;
 +
  }
 +
 +
/* ******************************************************************************************
 +
*
 +
*                                read_AD7124
 +
*
 +
*  turn on load cell excitation
 +
*  start continuous data conversion
 +
*  Read the data register
 +
*  Read the error register
 +
*  Loop for the number of samples or untill have tried 128 times
 +
*  turn off load cell excitation
 +
*
 +
* **************************************************************************************** */
 +
 +
 +
void read_AD7124(void)
 +
{
 +
uint8_t wrBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 +
uint8_t rdBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 +
int i = 0;
 +
int read_counter = 0;
 +
 +
SEGGER_RTT_WriteString(0, "read_AD7124.\n");
 +
NRF_LOG_FLUSH();
 +
       
 +
/* turn on load cell excitation using PDSW Power Down Switch  */
 +
wrBuf[0]= 0x03; wrBuf[1]= 0x00; wrBuf[2]= 0x80; wrBuf[3]= 0x00;
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));
 +
SEGGER_RTT_printf(0, "Load Cell turned ON.\n");
 +
 +
 
 +
/* start data conversion, Continuous conversion mode 00 0084  ADC Control  High power, Mode 0*/
 +
wrBuf[0]= 0x01; wrBuf[1]= 0x06; wrBuf[2]= power_mode;   
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));
 +
SEGGER_RTT_printf(0, "Data conversion started.\n");
 +
 
 +
wrBuf[0]= 0x41; wrBuf[1]= 0x00; wrBuf[2]= 0x00; 
 +
nrf_delay_ms(100);
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));
 +
SEGGER_RTT_printf(0, "read from controll register.\n");
 +
SEGGER_RTT_printf(0, "Control register: 0x%02x  0x%02x  0x%02x\n", rdBuf[0], rdBuf[1], rdBuf[2]);
 +
 +
sum=0;
 +
sample_counter=0;
 +
read_counter = 0;
 +
 +
  while ( read_counter < 128 && sample_counter < number_of_samples )
 +
  { 
 +
  for ( i=0; i< number_active_channels; i++ )
 +
    {   
 +
    if ( !wait_for_AD7124_ready() )
 +
        {
 +
        /* Read the data register  */
 +
        wrBuf[0]= 0x42;
 +
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 5, rdBuf, 5));
 +
   
 +
        data_register = (rdBuf[1] << 16) | (rdBuf[2] << 8) | (rdBuf[3]);
 +
        SEGGER_RTT_printf(0, "Data register: 0x%02x  0x%02x  0x%02x\n", rdBuf[1], rdBuf[2], rdBuf[3]);
 +
 +
        /* Read the error register */
 +
        wrBuf[0]= 0x46;
 +
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));
 +
 +
      /* Check for errors  */
 +
//    SEGGER_RTT_printf(0, "Error register: 0x%02x  0x%02x  0x%02x\n", rdBuf[1], rdBuf[2], rdBuf[3]);
 +
      if ( (rdBuf[1] | rdBuf[2] | rdBuf[3]) == 0 )            /* if no errors,  use data to calculate average  */
 +
          {
 +
  data_array[sample_counter] = data_register;
 +
          sum += data_register;
 +
          sample_counter++;
 +
          }
 +
        }
 +
    } 
 +
  read_counter++;
 +
  }
 +
 +
/* turn off load cell excitation using PDSW Power Down Switch  */
 +
wrBuf[0]= 0x03; wrBuf[1]= 0x00; wrBuf[2]= 0x00; wrBuf[3]= 0x00;
 +
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));
 +
    SEGGER_RTT_printf(0, "Load Cell turned OFF.\n");
 +
 +
//if (sample_counter == 0)  return 5;
 +
number_of_filtered_samples = sample_counter;
 +
 +
}
 +
 
 +
 +
 +
/************************* median_select  *********************************
 +
*
 +
*      from http://ndevilla.free.fr/median/median/
 +
*  This Quickselect routine is based on the algorithm described in
 +
*  "Numerical recipes in C", Second Edition,
 +
*  Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5
 +
*  This code by Nicolas Devillard - 1998. Public domain.
 +
*
 +
***************************************************************************/
 +
 +
#define ELEM_SWAP(a,b) { register int t=(a);(a)=(b);(b)=t; }
 +
 +
int median_select(int arr[], int n)
 +
    {
 +
    int low, high;
 +
    int median;
 +
    int middle, ll, hh;
 +
 +
    low = 0 ; high = n-1 ; median = (low + high) / 2;
 +
   
 +
    for (;;) {
 +
      if (high <= low) /* One element only */
 +
            return arr[median];
 +
 +
      if (high == low + 1) {  /* Two elements only */
 +
            if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]);
 +
            return arr[median];
 +
            }
 +
 +
      /* Find median of low, middle and high items; swap into position low */
 +
      middle = (low + high) / 2;
 +
      if (arr[middle] > arr[high])    ELEM_SWAP(arr[middle], arr[high]);
 +
      if (arr[low] > arr[high])      ELEM_SWAP(arr[low], arr[high]);
 +
      if (arr[middle] > arr[low])    ELEM_SWAP(arr[middle], arr[low]);
 +
 +
      /* Swap low item (now in position middle) into position (low+1) */
 +
      ELEM_SWAP(arr[middle], arr[low+1]);
 +
 +
      /* Nibble from each end towards middle, swapping items when stuck */
 +
      ll = low + 1;
 +
      hh = high;
 +
      for (;;) {
 +
          do ll++; while (arr[low] > arr[ll]);
 +
          do hh--; while (arr[hh]  > arr[low]);
 +
 
 +
          if (hh < ll)
 +
          break;
 +
 
 +
          ELEM_SWAP(arr[ll], arr[hh]);
 +
          }
 +
 +
      /* Swap middle item (in position low) back into correct position */
 +
      ELEM_SWAP(arr[low], arr[hh]);
 +
 +
      /* Re-set active partition */
 +
      if (hh <= median) low = ll;
 +
      if (hh >= median) high = hh - 1;
 +
      }
 +
    }
 +
 +
#undef ELEM_SWAP
 +
 +
 +
int main(void)
 +
    {
 +
    char buffer[50];
 +
    int j=0;
 +
    bsp_board_leds_init();
 +
 +
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
 +
    NRF_LOG_DEFAULT_BACKENDS_INIT();
 +
 +
    SEGGER_RTT_printf(0, "\n\nStarted main routine ...\n");   
 +
    NRF_LOG_INFO("SPI example.");
 +
    nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
 +
   
 +
    spi_config.frequency = NRF_SPI_FREQ_4M;
 +
    spi_config.mode      = NRF_DRV_SPI_MODE_3;
 +
    spi_config.ss_pin  = 29;  // SPI_SS_PIN;                // 29
 +
    spi_config.miso_pin = 28;  // SPI_MISO_PIN;              // 28
 +
    spi_config.mosi_pin = 4;    // SPI_MOSI_PIN;              // 4
 +
    spi_config.sck_pin  = 3;    // SPI_SCK_PIN;              // 3
 +
//    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
 +
    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, NULL, NULL));
 +
    SEGGER_RTT_printf(0, "SPI Initialized.\n");
 +
//    SEGGER_RTT_WriteString(0, "SPI Initialized.\n");
 +
 +
    reset_AD7124();
 +
    initialize_AD7124();
 +
 +
    while (1)
 +
      {
 +
      bsp_board_led_invert(BSP_BOARD_LED_0);
 +
//        reset_AD7124();
 +
//        initialize_AD7124();
 +
      read_AD7124();
 +
      bsp_board_led_invert(BSP_BOARD_LED_0);
 +
 +
      int median = median_select( data_array, sample_counter );
 +
      sum = 0;
 +
      number_of_filtered_samples = 0;
 +
 +
      /* set limits for outliers  */
 +
      int high_limit = median * 1.00002;
 +
      int low_limit = median * .99998;
 +
 
 +
      /* Test for outliers and throw them away */
 +
 +
      for ( j=0; j<sample_counter; j++ )
 +
          {
 +
          if ( data_array[j] < high_limit && data_array[j] > low_limit )
 +
            {
 +
            number_of_filtered_samples++;
 +
            sum += data_array[j];
 +
        //    printf ("data_array: %d less median: %d\n", data_array[j], data_array[j] - median);
 +
            }
 +
  //  else
 +
  //    printf ("OUTLIER    %d less median: %d\n", data_array[j], data_array[j] - median);
 +
          }
 +
 +
      if (number_of_filtered_samples == 0)  return 6;
 +
 +
      /* calculate raw average */
 +
      int filtered_average = sum/number_of_filtered_samples;
 +
      SEGGER_RTT_printf(0, "Filtered average: %d\n", filtered_average);
 +
 +
//    float weight = (float)(filtered_average - 8413535) * .0003375;
 +
//    float weight =  (float) (filtered_average * 0.0003438388) - 2892.912; 
 +
//    SEGGER_RTT_printf(0, "Weight: %7.3f\n",  (0.0003438388 *  (sum/number_of_filtered_samples))  - 2892.912  );
 +
//    float weight =  (filtered_average * 0.3438388) - 2892912; 
 +
      float weight =  (filtered_average * 0.0003438388) - 2892.912; 
 +
   
 +
      sprintf(buffer,  "Samples: %d        Weight: %7.3f lb\n",number_of_filtered_samples, weight);
 +
      SEGGER_RTT_printf(0, buffer);
 +
//    SEGGER_RTT_WriteString(0, buffer);
 +
//    SEGGER_RTT_printf(0, "weight: %d\n", weight);   
 +
        nrf_delay_ms(1000);
 +
      }
 +
}

Revision as of 08:28, 31 January 2018

Inital nRF52840 code that talks to the AD7124-8


/**
* Copyright (c) 2015 - 2017, Nordic Semiconductor ASA
* 
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 
* 1. Redistributions of source code must retain the above copyright notice, this
*    list of conditions and the following disclaimer.
* 
* 2. Redistributions in binary form, except as embedded into a Nordic
*    Semiconductor ASA integrated circuit in a product or a software update for
*    such product, must reproduce the above copyright notice, this list of
*    conditions and the following disclaimer in the documentation and/or other
*    materials provided with the distribution.
* 
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
*    contributors may be used to endorse or promote products derived from this
*    software without specific prior written permission.
* 
* 4. This software, with or without modification, must only be used with a
*    Nordic Semiconductor ASA integrated circuit.
* 
* 5. Any software provided in binary form under this license must not be reverse
*    engineered, decompiled, modified and/or disassembled.
* 
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* 
*/
#include "nrf_drv_spi.h"
#include "app_util_platform.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "boards.h"
#include "app_error.h"
#include <string.h>
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "SEGGER_RTT.h"



#define SPI_INSTANCE  0 /**< SPI instance index. */
static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);  /**< SPI instance. */
static volatile bool spi_xfer_done;  /**< Flag used to indicate that SPI instance completed the transfer. */


/* AD7124 parameters */

char FS[2] = { 0x01, 0x40 };                        /*  Filter Select (FS) default: 320 */
char PGA   = 0x07;                                  /*  PGA default: 128                */
char AINM  = 0x06;                                  /*  Negative analog input: 6 */
char AINP  = 0x07;                                  /*  Positive analog input: 7 */
enum { LOW_POWER=0x00, MEDIUM_POWER=0x40, HIGH_POWER=0x80 } power_mode = HIGH_POWER;
int data_register=0;
int return_code = 0;

char wrBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
char rdBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
int number_active_channels=1;
int number_of_filtered_samples = 0;

int data_array[64];
int number_of_samples = 16;                        /*  Number of data reads to average */

int sum = 0;
int sample_counter = 0;



/* *****************************************************************************************
* 
*                            reset_AD7124
* 
*  sends 64 ones (eight 0xFF) to the AD7124 to reset it
* 
* **************************************************************************************** */


void reset_AD7124 ( void )
  {
   SEGGER_RTT_WriteString(0, "reset_AD7124.\n");
  /* reset AD7124 by writing 64 ones (eight 0xFF) */
   
   uint8_t wrBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
   uint8_t rdBuf[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
   // memset(m_rx_buf, 0, m_length);
   spi_xfer_done = false;

   nrf_delay_ms(10);
   APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 8, rdBuf, 8)); 
    
   /* check reset by reading device ID code */
   wrBuf[0]= 0x45;
   nrf_delay_ms(10);
   APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 2, rdBuf, 2));
    
   SEGGER_RTT_printf(0, "Device ID Code: 0x%02x\n", rdBuf[1]);
   if ( rdBuf[1] != 0x14 ) 
      { 
      bsp_board_led_invert(BSP_BOARD_LED_3);
      SEGGER_RTT_printf(0, "Reset failed: 0x%02x 0x%02x\n", rdBuf[0], rdBuf[1]);
      }
   }

/* ******************************************************************************************
* 
*                            initialize_AD7124
* 
*  turns of default number_active_channel
*  turns on 
*  sets the filter
* 
* **************************************************************************************** */

void initialize_AD7124 ( void )
{
uint8_t wrBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t rdBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 

SEGGER_RTT_WriteString(0, "Starting initialize_AD7124.\n");

wrBuf[0]= 0x09; wrBuf[1]= 0x00; wrBuf[2]= 0x01;                        /* Turn off channel_0 (on by default) */
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));

SEGGER_RTT_WriteString(0, "Turned off channel 0.\n");

//wrBuf[1] = ( AIMP & 0x18 ) >> 3 ;
wrBuf[0]= 0x0a;
wrBuf[1] = (( AINP >> 3) & 0x07 ) | 0x80;
wrBuf[2] = (( AINP & 0x07 ) << 5) | ( AINM & 0x1F );

SEGGER_RTT_printf(0, "Write buffers: 0x%02x  0x%02x  0x%02x\n", wrBuf[0], wrBuf[1], wrBuf[2]);
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));

SEGGER_RTT_WriteString(0, "Set up AINP.\n");

wrBuf[0]= 0x19; wrBuf[1]= 0x08; wrBuf[2]= 0x7F;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));

wrBuf[0]= 0x21; wrBuf[1]= 0x06; wrBuf[2]= FS[0]; wrBuf[3]= FS[1];       /* Set the Filter Select bits             */
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));

SEGGER_RTT_WriteString(0, "Set up FS bits.\n");

wrBuf[0]= 0x07; wrBuf[1]= 0x03; wrBuf[2]= 0xF0; wrBuf[3]= 0x38;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));

}


/* ******************************************************************************************
* 
*                            wait_for_AD7124_ready
* 
*  waits for AD7124 to finish conversion by testing the busy busy bit
*  if it times out, it return busy (1) 
*  waiting set the how long it will wait for the conversion before it times out
* 
* **************************************************************************************** */


uint8_t wait_for_AD7124_ready(void)
  {
  uint8_t wrBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  uint8_t rdBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  unsigned int waiting=10000;
  uint8_t busy = 1;

  while( busy && waiting )
    {
    /* Read the Status Register */
    wrBuf[0]= 0x40; 
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 2, rdBuf, 2));
    busy = rdBuf[1] & 0x80 ;                      /* Test the Not Ready bit */
 
    nrf_delay_ms(1);                                /* wait 0.1 msec */
    waiting--;
    }
//  SEGGER_RTT_printf(0, "waiting: %d\n", waiting);
  return busy;
  }

/* ******************************************************************************************
* 
*                                 read_AD7124
* 
*  turn on load cell excitation
*  start continuous data conversion
*  Read the data register
*  Read the error register
*  Loop for the number of samples or untill have tried 128 times
*  turn off load cell excitation
* 
* **************************************************************************************** */


void read_AD7124(void)
{
uint8_t wrBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t rdBuf[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int i = 0; 
int read_counter = 0;

SEGGER_RTT_WriteString(0, "read_AD7124.\n");
NRF_LOG_FLUSH();
        
/* turn on load cell excitation using PDSW Power Down Switch  */
wrBuf[0]= 0x03; wrBuf[1]= 0x00; wrBuf[2]= 0x80; wrBuf[3]= 0x00;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));
SEGGER_RTT_printf(0, "Load Cell turned ON.\n");

 
/* start data conversion, Continuous conversion mode 00 0084  ADC Control  High power, Mode 0*/
wrBuf[0]= 0x01; wrBuf[1]= 0x06; wrBuf[2]= power_mode;    
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));
SEGGER_RTT_printf(0, "Data conversion started.\n");
 
wrBuf[0]= 0x41; wrBuf[1]= 0x00; wrBuf[2]= 0x00;  
nrf_delay_ms(100);
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 3, rdBuf, 3));
SEGGER_RTT_printf(0, "read from controll register.\n");
SEGGER_RTT_printf(0, "Control register: 0x%02x  0x%02x  0x%02x\n", rdBuf[0], rdBuf[1], rdBuf[2]);

sum=0;
sample_counter=0;
read_counter = 0;

 while ( read_counter < 128 && sample_counter < number_of_samples )
  {  
  for ( i=0; i< number_active_channels; i++ ) 
    {     
    if ( !wait_for_AD7124_ready() )
       {
       /* Read the data register  */
       wrBuf[0]= 0x42; 
       APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 5, rdBuf, 5));
    
       data_register = (rdBuf[1] << 16) | (rdBuf[2] << 8) | (rdBuf[3]); 
       SEGGER_RTT_printf(0, "Data register: 0x%02x  0x%02x  0x%02x\n", rdBuf[1], rdBuf[2], rdBuf[3]);

       /* Read the error register */
       wrBuf[0]= 0x46;
       APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));

      /* Check for errors  */
//    SEGGER_RTT_printf(0, "Error register: 0x%02x  0x%02x  0x%02x\n", rdBuf[1], rdBuf[2], rdBuf[3]);
      if ( (rdBuf[1] | rdBuf[2] | rdBuf[3]) == 0 )             /* if no errors,  use data to calculate average  */
          {
	   data_array[sample_counter] = data_register;
          sum += data_register;
          sample_counter++;
          }
       }
   }   
 read_counter++;
 }

/* turn off load cell excitation using PDSW Power Down Switch  */
wrBuf[0]= 0x03; wrBuf[1]= 0x00; wrBuf[2]= 0x00; wrBuf[3]= 0x00;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, wrBuf, 4, rdBuf, 4));
    SEGGER_RTT_printf(0, "Load Cell turned OFF.\n");

//if (sample_counter == 0)  return 5;
number_of_filtered_samples = sample_counter; 

}
 


/************************* median_select  *********************************
*
*      from http://ndevilla.free.fr/median/median/
*  This Quickselect routine is based on the algorithm described in
*  "Numerical recipes in C", Second Edition,
*  Cambridge University Press, 1992, Section 8.5, ISBN 0-521-43108-5
*  This code by Nicolas Devillard - 1998. Public domain.
*
***************************************************************************/

#define ELEM_SWAP(a,b) { register int t=(a);(a)=(b);(b)=t; }

int median_select(int arr[], int n) 
   {
   int low, high;
   int median;
   int middle, ll, hh;

   low = 0 ; high = n-1 ; median = (low + high) / 2;
    
   for (;;) {
      if (high <= low) /* One element only */
           return arr[median];

      if (high == low + 1) {  /* Two elements only */
           if (arr[low] > arr[high]) ELEM_SWAP(arr[low], arr[high]);
           return arr[median];
           }

      /* Find median of low, middle and high items; swap into position low */
      middle = (low + high) / 2;
      if (arr[middle] > arr[high])    ELEM_SWAP(arr[middle], arr[high]);
      if (arr[low] > arr[high])       ELEM_SWAP(arr[low], arr[high]);
      if (arr[middle] > arr[low])     ELEM_SWAP(arr[middle], arr[low]);

      /* Swap low item (now in position middle) into position (low+1) */
      ELEM_SWAP(arr[middle], arr[low+1]);

      /* Nibble from each end towards middle, swapping items when stuck */
      ll = low + 1;
      hh = high;
      for (;;) {
         do ll++; while (arr[low] > arr[ll]);
         do hh--; while (arr[hh]  > arr[low]);
 
         if (hh < ll)
         break;
 
         ELEM_SWAP(arr[ll], arr[hh]);
         }

      /* Swap middle item (in position low) back into correct position */
      ELEM_SWAP(arr[low], arr[hh]);

      /* Re-set active partition */
      if (hh <= median) low = ll;
      if (hh >= median) high = hh - 1;
      }
   }

#undef ELEM_SWAP


int main(void)
   {
   char buffer[50];
   int j=0;
   bsp_board_leds_init();

   APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
   NRF_LOG_DEFAULT_BACKENDS_INIT();

   SEGGER_RTT_printf(0, "\n\nStarted main routine ...\n");    
   NRF_LOG_INFO("SPI example.");
   nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
   
   spi_config.frequency = NRF_SPI_FREQ_4M;
   spi_config.mode      = NRF_DRV_SPI_MODE_3;
   spi_config.ss_pin   = 29;   // SPI_SS_PIN;                // 29
   spi_config.miso_pin = 28;   // SPI_MISO_PIN;              // 28
   spi_config.mosi_pin = 4;    // SPI_MOSI_PIN;              // 4
   spi_config.sck_pin  = 3;    // SPI_SCK_PIN;               // 3
//    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
   APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, NULL, NULL));
   SEGGER_RTT_printf(0, "SPI Initialized.\n");
//    SEGGER_RTT_WriteString(0, "SPI Initialized.\n");

   reset_AD7124();
   initialize_AD7124();

    while (1)
      {
      bsp_board_led_invert(BSP_BOARD_LED_0);
//        reset_AD7124();
//        initialize_AD7124();
      read_AD7124();
      bsp_board_led_invert(BSP_BOARD_LED_0);

      int median = median_select( data_array, sample_counter );
      sum = 0;
      number_of_filtered_samples = 0;

      /* set limits for outliers  */
      int high_limit = median * 1.00002;
      int low_limit = median * .99998;
  
      /* Test for outliers and throw them away */

      for ( j=0; j<sample_counter; j++ )
         {
         if ( data_array[j] < high_limit && data_array[j] > low_limit ) 
            {
            number_of_filtered_samples++;
            sum += data_array[j]; 
        //    printf ("data_array: %d less median: %d\n", data_array[j], data_array[j] - median);
            }
 //  else 
 //    printf ("OUTLIER     %d less median: %d\n", data_array[j], data_array[j] - median);
         }

      if (number_of_filtered_samples == 0)  return 6;

      /* calculate raw average */ 
      int filtered_average = sum/number_of_filtered_samples;
      SEGGER_RTT_printf(0, "Filtered average: %d\n", filtered_average);

//    float weight = (float)(filtered_average - 8413535) * .0003375;
//    float weight =  (float) (filtered_average * 0.0003438388) - 2892.912;  
//    SEGGER_RTT_printf(0, "Weight: %7.3f\n",  (0.0003438388 *  (sum/number_of_filtered_samples))  - 2892.912  );
//    float weight =   (filtered_average * 0.3438388) - 2892912;  
      float weight =   (filtered_average * 0.0003438388) - 2892.912;  
    
      sprintf(buffer,  "Samples: %d         Weight: %7.3f lb\n",number_of_filtered_samples, weight);
      SEGGER_RTT_printf(0, buffer);
//    SEGGER_RTT_WriteString(0, buffer);
//    SEGGER_RTT_printf(0, "weight: %d\n", weight);    
       nrf_delay_ms(1000);
      }
}