Issues with sensor registers through spi interface

Hey all, i'm working on designing/building an Arduino setup that uses a A1335 sensor which when in proximity to a permanent magnet can tell you how much that magnet has rotated.

In the picture below you can see the major components. The MCP4725 is controlled through I2C and the A1335 is controlled through SPI.

In my application, I will have an input voltage and based on the rotation on the magnet (i.e. what angle is it at) I want to output a specific voltage.... Also, the output voltage will need to be a function of the input voltage, (i.e. at 90° magnet location output voltage = inputvoltage * 2).

My problem is I don't seem to be reading the correct values from the sensor... I'm just doing temperature and magnetic field strength for now because I know approx. what those values should be (in addition i can easily adjust the temperature of the room and the magnetic strength by moving the magnet further or closer). I'm getting values for both temp and magnetic strength, they just aren't correct nor are they responding to changes i'm making (i.e. moving the magnet further away)

I've been doing the development in stages, first making sure the A1335 sensor is working properly (using the vendors development kit) and then integrating my Arduino Pro Mini and MCP4725 DAC.

I'm at the point now where I have verified the A1335 sensor is working correctly separate from the Arduino (using sensor vendor's software) and then began coding the Arduino. I started off easy, forgetting about the A1335 and making sure I can control the DAC first. I got it working, instead of using magnet strength in the matrix i just use input voltage.

Use the "secret Arduino voltmeter" code to determine what the actual Vin (in this case the ~+5V from the USB cable)

Setup a matrix which allows me to say for a given input voltage this is the output voltage. (note: once I integrate the A1335, this will really be magnet rotation vs output voltage).

A set of statements that looks at the input voltage, if it is higher or lower than the matrix values it clamps the output voltage at the max or low end respectively.
If the input voltage is within the matrix, I have a linear interpolation setup to determine the correct output voltage.

I added some code to read the registers, got the code from here (https://www.arduino.cc/en/Tutorial/BarometricPressureSensor).

I believe I know some of the issues, I just don't know how to solve them.

"const byte READ = 0b11111100" came straight from the example code... I can't find on the A1335 documentation what this sensor's correct value is.

  1. The A1335 sensor seems to have two registers addresses for each register whereas the example sensor doesn't... Not sure how to incorporate that into the code.

  2. In the example code it says they shift the register over 2 bytes, not sure why and i'm not sure if that is correct for my sensor.

A1335 Datasheet: https://www.allegromicro.com/~/media/Files/Datasheets/A1335-Datasheet.ashx

A1335 Programming manual: https://download.mikroe.com/documents/datasheets/A1335-Programming-Manual.pdf

Here is the code, look towards the end for code specific to the SPI/A1335 interface:

include <Wire.h> //standard library
#include <Adafruit_MCP4725.h> //library for the DAC
#include <SPI.h> //standard library
#include <avr/wdt.h>
#include "Definitions.h" //library developed for the A1335 sensor

float sensorVoltage = 0; //setting sensorVoltage variable which will represent Vin
float outputVoltage = 0; //setting outputVoltage variable which will represent Vout
const int TEMPERATURE = 0x28; //from A1335 Datasheet
const byte READ = 0b11111100;

Adafruit_MCP4725 dac;

//Code below is to read Vin, which will be used later as the "reference voltage" for the DAC

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  //#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
     ADMUX = _BV(MUX5) | _BV(MUX0) ;
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  
 
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring
 
  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both
 
  long result = (high<<8) | low;
 
  result = 1105300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

//End code to read Vin

void setup(void) {

  Serial.begin(9600);

 
  SPI.begin();

  pinMode (SS,OUTPUT);//Sets select slave pin as an output, I think this may redundant since I already call out SPI.h



//Initiate MCP4725 DAC
  dac.begin(0x62);  // For Adafruit MCP4725A1 the address is 0x62 (default
    TWBR = ((F_CPU /400000l) - 16) / 2; // Change the i2c clock to 400KHz

}


void loop(void) {


//Block is to set sensorVoltage equal to the readVcc() and display on the serial monitor    
  Serial.println("The Sensor Voltage Input is:  ");
  float sensorVoltage = readVcc();
  Serial.print(sensorVoltage);
  Serial.println(" ");


//Using this array to allow me to make adjustments between what the input voltage is and what the DAC outputs as Vout
float array[42] = {
// Voltage From Sensor on left // Corresponding Desired Output Voltage on right
0, 0,
250, 250,
500, 500,
750, 750, 
1000, 1000,
1250, 1250,
1500, 1500,
1750, 1750,
2000, 2000,
2250, 2250,
2500, 2500,
2750, 2750,
3000, 3000,
3250, 3250,
3500, 3500,
3750, 3750,
4000, 4000,
4250, 4250,
4500, 4500,
4750, 300,
5500, 400,
};

//Displays the top of the array for the Input Voltage (left side of array), used for debugging
Serial.println("The top of the array is: ");
Serial.println(array[40]);

//if and else if is if the input voltage value is outside the table I want to clamp it to the lowest or highest values
if (sensorVoltage<= array[0])
{
  outputVoltage = dac.setNearestActualVoltage(array[1], sensorVoltage, false);
  Serial.println("Your below the bottom end of the table");
}
else if (sensorVoltage >= array[40])
{
  outputVoltage = dac.setNearestActualVoltage(sensorVoltage, sensorVoltage, false);
  Serial.println("Your above the top end of the table");
}

//block is performing a linear interpolation for when the input voltage is between values on the table
int x;
  for (x= 0; x<42; x=x+2){
  if (sensorVoltage >=array[x]
&& sensorVoltage <=array[x+2])

        outputVoltage = dac.setNearestActualVoltage((array[x+1] + ( (array[x+3] - array[x+1]) * ( (sensorVoltage - array[x]
) / (array[x+2] - array[x]
) ) )),sensorVoltage, false);
Serial.println("The Output Voltage is:  ");
Serial.println(outputVoltage);
}


//code below is attempting to read from the A1335

SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE3)); // start the SPI library for A1335, settings out of datasheet
delay(100); //give A1335 sensor time to setup

digitalWrite (SS, LOW); //sets select slave low to allow for communication

int tempData = readRegister (0x28, 2);

float realTemp = ((float)tempData / 8);
Serial.print ("Temp[C]=");
Serial.print(realTemp);

int MagStrength = readRegister (0x2A, 2);

Serial.print ("Magnetic Strength");
Serial.print(MagStrength);

delay(1000);

}

//Read from register from the A1335:
unsigned int readRegister(byte thisRegister, int bytesToRead) {
  byte inByte = 0;           // incoming byte from the SPI
  unsigned int result = 0;   // result to return
  Serial.print(thisRegister, BIN);
  Serial.print("\t");
  // A1335 expects the register name in the upper 6 bits
  // of the byte. So shift the bits left by two bits:
  thisRegister = thisRegister << 2;
  // now combine the address and the command into one byte
  byte dataToSend = thisRegister & READ;
  Serial.println(thisRegister, BIN);
  // take the chip select low to select the device:
  digitalWrite(SS, LOW);
  // send the device the register you want to read:
  SPI.transfer(dataToSend);
  // send a value of 0 to read the first byte returned:
  result = SPI.transfer(0x00);
  // decrement the number of bytes left to read:
  bytesToRead--;
  // if you still have another byte to read:
  if (bytesToRead > 0) {
    // shift the first byte left, then get the second byte:
    result = result << 8;
    inByte = SPI.transfer(0x00);
    // combine the byte you just got with the previous one:
    result = result | inByte;
    // decrement the number of bytes left to read:
    bytesToRead--;
  }
  // take the chip select high to de-select:
  digitalWrite(SS, HIGH);
  // return the result:
  return (result);
}

Have you run the I2C scanner sketch, to confirm that the A1335 is connected correctly, and has the address you think it does?

//Using this array to allow me to make adjustments between what the input voltage is and what the DAC outputs as Vout
float array[42] = {

Why is that array defined in loop()?

delay(100); //give A1335 sensor time to setup

Why do you think you need to do this on EVERY pass through loop()?

unsigned int readRegister(byte thisRegister, int bytesToRead) {

Do you ever REALLY plan to read more than 256 bytes?

  // take the chip select low to select the device:
  digitalWrite(SS, LOW);

Just in case it didn't work when you made the same call before calling this function?

Every call to beginTransaction() must have a corresponding endTransaction() call, which completes the transaction.

Paul, appreciate your response, see below.

PaulS:
Have you run the I2C scanner sketch, to confirm that the A1335 is connected correctly, and has the address you think it does?

I did not, would this work since the device is controlled through spi and not i2c. If not is there one for spi?

//Using this array to allow me to make adjustments between what the input voltage is and what the DAC outputs as Vout

float array[42] = {



Why is that array defined in loop()?

Good point, the answer is because I didn't know any better.



delay(100); //give A1335 sensor time to setup



Why do you think you need to do this on EVERY pass through loop()?

Ditto



unsigned int readRegister(byte thisRegister, int bytesToRead) {



Do you ever REALLY plan to read more than 256 bytes?

No, does this mean I should replace the "int"



// take the chip select low to select the device:
  digitalWrite(SS, LOW);



Just in case it didn't work when you made the same call before calling this function?

Ha, the spi.h covers this doesn't it? It was confusing why I kept seeing it in example code. Blind leading the blind I suppose.

Every call to beginTransaction() must have a corresponding endTransaction() call, which completes the transaction.

Good point, I should put this at the end. Do you think it will affect how I read my registers and the response I get?

When I first saw the data sheet for the device, and the Wire.h file included in your code, I assumed that you were using I2C to read the device. My question about the I2C scanner was based on that incorrect assumption.

I don't know that adding the required call to endTransaction() will solve your code problems. What I do know is that your code isn't working without it.

So i contacted the company and they pointed me to their software site which actually had arduino code for reading the device through a spi interface, " A1335SPIExample.ino" located here: GitHub - vettett15/A1335: A1335/Arduino. Wouldn't compile:

First issue was they had a line " SPI.setSCK(14);"... Looks like they were trying to change the SCK pin, which i don't need to do, so i deleted it.

Next was this... Not sure what the problem here is but this "SPI0_CTAR0" but in one of their PDFs they denote:

// On the Teensy, SPI0_CTAR0 is used to describe the SPI transaction for transfer (byte)

// while SPI0_CTAR1 is used to describe the SPI transaction for transfer16.

// To do a 20 bit transfer, change the length of the transaction to 4 bits for transfer

// and do a transfer16 followed by a transfer.

uint32_t oldSPI0_CTAR0 = SPI0_CTAR0;

SPI0_CTAR0 = (SPI0_CTAR0 & 0x87FFFFFF) | SPI_CTAR_FMSZ(3); // using SPI0_CTAR0 to send 4 bits

ERROR MESSAGE:

Arduino: 1.8.9 (Windows 10), Board: "Arduino Pro or Pro Mini, ATmega328P (5V, 16 MHz)"

C:\Users\Pete&Shell\Desktop\A1335SPIExample\A1335SPIExample.ino: In function 'void setup()':

A1335SPIExample:68:44: error: invalid initialization of non-const reference of type 'uint32_t& {aka long unsigned int&}' from an rvalue of type 'uint32_t {aka long unsigned int}'

ExtendedRead(ChipSelectPin, 0x22, flags);

^

C:\Users\Pete&Shell\Desktop\A1335SPIExample\A1335SPIExample.ino:319:10: note: initializing argument 3 of 'uint16_t ExtendedRead(uint16_t, uint16_t, uint32_t&)'

uint16_t ExtendedRead(uint16_t cs, uint16_t address, uint32_t& value)

^

C:\Users\Pete&Shell\Desktop\A1335SPIExample\A1335SPIExample.ino: In function 'uint16_t PrimaryRead(uint16_t, uint16_t, uint16_t&)':

A1335SPIExample:172:34: error: 'SPI0_CTAR0' was not declared in this scope

uint32_t oldSPI0_CTAR0 = SPI0_CTAR0;

^

A1335SPIExample:173:65: error: 'SPI_CTAR_FMSZ' was not declared in this scope

SPI0_CTAR0 = (SPI0_CTAR0 & 0x87FFFFFF) | SPI_CTAR_FMSZ(3); // using SPI0_CTAR0 to send 4 bits

^

C:\Users\Pete&Shell\Desktop\A1335SPIExample\A1335SPIExample.ino: In function 'uint16_t PrimaryWrite(uint16_t, uint16_t, uint16_t)':

A1335SPIExample:251:34: error: 'SPI0_CTAR0' was not declared in this scope

uint32_t oldSPI0_CTAR0 = SPI0_CTAR0;

^

A1335SPIExample:252:65: error: 'SPI_CTAR_FMSZ' was not declared in this scope

SPI0_CTAR0 = (SPI0_CTAR0 & 0x87FFFFFF) | SPI_CTAR_FMSZ(3); // using SPI0_CTAR0 to send 4 bits

^

exit status 1

invalid initialization of non-const reference of type 'uint32_t& {aka long unsigned int&}' from an rvalue of type 'uint32_t {aka long unsigned int}'

  1. I ended up deleting all instances of this SPI0_CTAR0 and i got it to run... I'm getting very sporadic output on the serial monitor. Without changing the code, running it one time will output angle, stopping it and uploading the same code will get me temp (seems to be wrong sometimes), sometimes magnetic field strength (seems to be right), etc.... I was playing with the baud rate and it didn't seem to change this behavior... Seems like timing is off somehow.... Thoughts?

here is the code i'm actually running (modified theirs): "sketch_jul26a.ino" located here: GitHub - vettett15/A1335: A1335/Arduino