Notifications
Clear all

ADXL345 SPI output 000 on the serial monitor

17 Posts
4 Users
2 Likes
1,455 Views
(@abdul)
Member
Joined: 1 year ago
Posts: 14
Topic starter  
#include <SPI.h>

// Define ADXL345 register addresses
#define ADXL345_DEVID       0x00
#define ADXL345_DATAX0      0x32
#define ADXL345_DATAX1      0x33
#define ADXL345_DATAY0      0x34
#define ADXL345_DATAY1      0x35
#define ADXL345_DATAZ0      0x36
#define ADXL345_DATAZ1      0x37

// Initialize SPI pins
#define ADXL345_CS          5
#define ADXL345_SCK         18
#define ADXL345_MOSI        23
#define ADXL345_MISO        19

void setup() {
  // Initialize serial communication
  Serial.begin(2000000);
  
  // Initialize SPI communication
  SPI.begin(ADXL345_SCK, ADXL345_MISO, ADXL345_MOSI, ADXL345_CS);
  SPI.setClockDivider(SPI_CLOCK_DIV2);  // Set SPI clock speed to 20 MHz

  // Configure ADXL345
  writeRegister(ADXL345_DEVID, 0x00);    // Read device ID
  writeRegister(0x2C, 0x0A);             // Set data rate to 3200Hz
  writeRegister(0x31, 0x00);             // Set range to +/-2g
  writeRegister(0x2D, 0x08);             // Enable measurement mode
}

void loop() {
  // Read accelerometer data
  int16_t x = readRegister16(ADXL345_DATAX0);
  int16_t y = readRegister16(ADXL345_DATAY0);
  int16_t z = readRegister16(ADXL345_DATAZ0);
  
  // Print data to serial monitor
  Serial.print("X: ");
  Serial.print(x);
  Serial.print(", Y: ");
  Serial.print(y);
  Serial.print(", Z: ");
  Serial.println(z);
  
  // Wait for 1/3200 seconds (sampling period)
  delayMicroseconds(313);
}

// Write a value to an ADXL345 register
void writeRegister(uint8_t reg, uint8_t value) {
  digitalWrite(ADXL345_CS, LOW);
  SPI.transfer(reg);
  SPI.transfer(value);
  digitalWrite(ADXL345_CS, HIGH);
}

// Read a 16-bit value from two consecutive ADXL345 registers
int16_t readRegister16(uint8_t reg) {
  int16_t value;
  digitalWrite(ADXL345_CS, LOW);
  SPI.transfer(reg | 0x80);    // Set read bit
  value = SPI.transfer(0);
  value |= (int16_t)SPI.transfer(0) << 8;
  digitalWrite(ADXL345_CS, HIGH);
  return value;
}

I wondered where the error occurred. It compiled and uploaded successfully, but on the serial monitor,  values of X ,Y and Z were 0, 0, 0


   
Quote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6971
 

Is that baud rate legit? I never seen that value used before, 115,200 is common but 200,000 is a first.

Your code is very different from this https://learn.sparkfun.com/tutorials/adxl345-hookup-guide/all

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1683
 

Hi @harri_son,

   I had to use Google to find out what an ADXL345 did, so obviously I don't have any direct experience.

I tend to agree Ron @zander's implied approach, namely try to start with a hardware configuration and program that has a very high probability of being tested and will work ... and something like the Sparkfun article that Ron referred to, would fit that category.

Presently, your problems could be hardware or software, or both. It is moderately complex chip, so that unless you can check it with an oscilloscope or logic analyser, there is no easy way of establishing whether it is trying to do SPI communications or not.

The SPI data rate of 2 Mbits/sec is well within the chips range of up to 5Mbits/sec, although personally, with an 'unknown new system',  I would start at lower data rate, maybe 100 kbits/sec, as it will be more 'forgiving' of the transmission line limitations usually encountered whilst prototyping. However, I suspect this is not the problem.

(Of course SPI is a clocked protocol, so the 'legacy' data rates (e.g. 115200) of asynchronous data transmission are not relevant.)

So, if you are in a position to recreate Sparkfun's 'recipe' exactly, I recommend you do this and maybe report back on your findings.

Best wishes,

Dave


   
Ron reacted
ReplyQuote
(@abdul)
Member
Joined: 1 year ago
Posts: 14
Topic starter  

@zander The highest baudrate in Arduino IDE 2.0.4 is 2000000.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6971
 

@harri_son Ok, good luck with that.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1683
 

Hi @harri_son,

  Apologies, I misread your code, but it does open a concern

Your code has a comment

Set SPI clock speed to 20 MHz

... If that is correct, then that is a lot quicker than the data sheet maximum for the ADXL345 device which includes

The maximum SPI clock speed is 5 MHz with 100 pF
maximum loading, and the timing scheme follows clock polarity
(CPOL) = 1 and clock phase (CPHA) = 1.

As the SPI speed is set by the microcontroller, the value

SPI_CLOCK_DIV2

is unclear when the processor maximum clock speed is not specified.

Of course, your comment might be inaccurate.

Best  wishes, Dave


   
ReplyQuote
Will
 Will
(@will)
Member
Joined: 3 years ago
Posts: 2531
 

@davee 

It appears that that command will set the speed to 8MHz (1/2 the Arduino's clock speed), if he's using an Arduino (which he doesn't say).

That speed would certainly overload the 5 MHz limit you listed.

 

Anything seems possible when you don't know what you're talking about.


   
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1683
 

Hi @will,

  Thanks ... 1/2 of 8MHz is 4MHz, which would be within the ADXL's spec, but it is high enough for the usual prototype wiring to struggle.

Howver,  modern processors often have multiple clocks, some higher than the 'visible' clock, and the processor wasn't stated, so from my viewpoint it was 'How long is (half) a piece of string?' situation.

I note this thread's forum address includes ESP8266 & ESP32 ... but occasionally the discussions go slightly off-topic, so I ignored that cue. 😉 

Best wishes, Dave


   
ReplyQuote
(@abdul)
Member
Joined: 1 year ago
Posts: 14
Topic starter  

@will I am using ESP32 mini D1, thanks.


   
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1683
 

Hi @harri_son & @will,

   Determining ESP32 SPI bus speed seems to be a research topic in its own right:

e.g.

https://www.esp32.com/viewtopic.php?t=7364 says (note this is only an unreferenced observation)

I would like to switch this two on demand but when I select with SPI.setClockDivider(SPI_CLOCK_DIV2) I get 8MHz

---------------------------

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html says:

Note

While in general, speeds up to 80MHz on the dedicated SPI pins and 40MHz on GPIO-matrix-routed pins are supported, full-duplex transfers routed over the GPIO matrix only support speeds up to 26MHz.

------------------

Confusion galore at https://github.com/nRF24/RF24/issues/591 which suggests Espressif may have made SPI clock "Arduino compatible"  by messing with the SPI software library code ... ie limiting choices to frequencies much lower than the ESP chips can achieve ...

-----------------

The only 'safe' way seems to be to measure it ... but obviously that requires a 'scope or similar.

The documents I found are horrendous as there are so many options, etc., and if the software libraries have been 'fudged' to make SPI 'easier' to deal with, almost anything is possible.

---------

Second choice, being religiously follow a 'proven' design, such as the one Ron suggested, and even then you need some good luck.

----------

What we can be sure is that the ADXL chip is only designed to go up to 5MHz, and that is with an 'ideal' physical implementation, not the usual breadboards, etc. whilst the ESP32 appears to be able to go up to 80MHz in some situations, and 26 Mhz and 40 MHz in others.

What it is in your rig is anybodies guess ... 20MHz is among the likely suspects.

Best wishes,

Dave 

 


   
ReplyQuote
Will
 Will
(@will)
Member
Joined: 3 years ago
Posts: 2531
 

@davee FYI @harri_son

Wouldn't the easiest test to be just switching to a divisor of SPI_CLOCK_DIV256 ?

If that works, it's a clock speed problem and can be solved by trying successively lower valued divisors until the sketch stops working and then reverting to the previous value.

Anything seems possible when you don't know what you're talking about.


   
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1683
 

Hi @will,

 I was tempted to suggest something similar to your Div256 proposal, and my first comment on the thread of picking (say) 100 kbits/set was aimed to do the same thing.

My concern from my '5 minute Google' is of reports that specifying something like that in ESP32 software land,  didn't always give the 'expected' result, and without test equipment, how do you know what is going on?

These reports were uncorroborated, and hence may have been false, but made it hard to recommend.

Still, I agree, if it works, go for it ... if it doesn't your level of confusion is maybe only slightly increased. 🙄 🤨 

I think the fundamental problem is that the SPI clock architecture inside the ESP32 includes so much complexity that it is difficult to predict what any individual system will do, short of spending many hours decomposing the whole thing, plus of course any R&D lab can quickly check with a scope... which is exactly what I would do ... but not all of our forum members are in such a fortunate position.

Hence, there software libraries have picked some 'workarounds' ... but maybe not explained them clearly ... and it is tricky to see a 'watertight' solution that everyone can follow.

Best wishes, Dave


   
ReplyQuote
(@abdul)
Member
Joined: 1 year ago
Posts: 14
Topic starter  

@davee Thanks for your contributions and analysis. I tried all the DIVs to avail of no. I resort to using another code attached.

#include <SPI.h>


//ADXL345
#define BW_RATE 0x2C      //Data rate and power mode control
#define POWER_CTL 0x2D    //Power Control Register
#define DATA_FORMAT 0x31  //Data format control
#define DATAX0 0x32       //X-Axis Data 0

//unsigned long wasMillis = millis(), elapsedMillis = 0;
//float loopCount = 0;


#define SS 5  //(IO15 -> IO5)

char values[10];
int16_t x, y, z;
float xg, yg, zg;

void setup() {
  SPI.begin();
  SPI.setDataMode(SPI_MODE3);
  SPI.setBitOrder(MSBFIRST);
  SPI.setFrequency(5000000);
  //SPI.setClockDivider(SPI_CLOCK_DIV16);


  Serial.begin(2000000);

  // SS Hight
  pinMode(SS, OUTPUT);
  digitalWrite(SS, HIGH);

  // ADXL345
  writeRegister(DATA_FORMAT, 0x00);  // ±2g 10bit
  writeRegister(POWER_CTL, 0x08);    //
  writeRegister(BW_RATE, 0x0F);      //
}

void loop() {
  // DATAX0
  readRegister(DATAX0, 6, values);

  // 2Byte
  x = ((int16_t)values[1] << 8) | (int16_t)values[0];
  y = ((int16_t)values[3] << 8) | (int16_t)values[2];
  z = ((int16_t)values[5] << 8) | (int16_t)values[4];

  // 0.03906 = (2*2)/(2^10)
  xg = x * 0.03906;
  yg = y * 0.03906;
  zg = (z * 0.03906);



  //
  //Serial.print(micros());
  //Serial.print(",");
  Serial.print(xg);
  Serial.print(",");
  Serial.print(yg);
  Serial.print(",");
  Serial.println(zg);


  // Wait for 1/3200 seconds (sampling period)
  delayMicroseconds(313);
  /*elapsedMillis = millis() - wasMillis;  // Calc elapsed time
  loopCount += 1;                        // Count loop passage
  if (elapsedMillis >= 1000) {           // If one second has elapsed
    Serial.print("Average time per sample = ");
    Serial.print(loopCount / elapsedMillis, 5);
    Serial.print("ms for ");
    Serial.print(loopCount);
    wasMillis = millis();  // Reset for next group
    loopCount = 0.0;
  }*/
}

void writeRegister(char registerAddress, char value) {
  // SPI
  digitalWrite(SS, LOW);
  //
  SPI.transfer(registerAddress);
  //
  SPI.transfer(value);
  // SPI
  digitalWrite(SS, HIGH);
}

void readRegister(char registerAddress, int16_t numBytes, char* values) {
  //
  char address = 0x80 | registerAddress;
  //
  if (numBytes > 1) address = address | 0x40;
  // SPI
  digitalWrite(SS, LOW);
  //
  SPI.transfer(address);
  //
  for (int16_t i = 0; i < numBytes; i++) {
    values[i] = SPI.transfer(0x00);
  }
  // SPI CS HIGH
  digitalWrite(SS, HIGH);
}

   
ReplyQuote
(@abdul)
Member
Joined: 1 year ago
Posts: 14
Topic starter  

The serial monitor output is attached. I want to do the FFT of the data, but I don't know the "t" to use. Whether from the timestamp (the laptop time) or I should use Serial.print(micros());

0.47,-0.31,9.61
0.47,-0.31,9.69
0.70,-0.23,9.69
0.47,-0.39,9.77
0.70,-0.47,9.61
0.47,-0.23,9.61
0.39,-0.23,9.53
0.70,-0.39,9.45

The timestamp is attached below.

09:50:36.276 -> 0.39,-0.23,9.45
09:50:36.276 -> 0.55,-0.39,9.37
09:50:36.276 -> 0.62,-0.31,9.53
09:50:36.276 -> 0.39,-0.39,9.53
09:50:36.276 -> 0.78,-0.47,9.45
09:50:36.276 -> 0.55,-0.16,9.30
09:50:36.276 -> 0.55,-0.31,9.45
09:50:36.276 -> 0.55,-0.23,9.30

The Serial.print(micros()) is attached below.

73119143,0.62,-0.31,9.37
73119859,0.55,0.00,9.69
73120559,0.47,-0.31,9.37
73121288,0.78,-0.39,9.37
73122010,0.39,-0.23,9.61
73122727,0.55,-0.31,9.30
73123452,0.55,-0.31,9.37
73124179,0.55,-0.39,9.61
73124895,0.55,-0.39,9.53
73125617,0.62,-0.23,9.61
73126342,0.47,-0.16,9.37
73127062,0.39,-0.16,9.22

   
ReplyQuote
(@davee)
Member
Joined: 3 years ago
Posts: 1683
 

Hi @harri_son,

   Sorry, I am not sure that I understand your question.

  The  'common' FFT .. Fast Fourier Transform .. that I am aware of does not explicitly use time values ... it is uses a list of measured values taken at equal spaced time intervals, assuming it is being used to analyse the change of something with respect to time.

FFT's are essentially computer algorithm optimisations of the Discrete Fourier Transform, where the optimisations aim to minimise the occasions of doing the same calculation twice.

From Wikipedia ... https://en.wikipedia.org/wiki/Fast_Fourier_transform

image

The formula only uses a series of N values of 'x', where the values of x were consecutive measurements, with an equal spacing of time between the values.

I haven't tried to analyse your code. but if your time between consecutive 'x' values period is 1/3200 of a second, as suggested by a comment in the code,  i.e. 312.5 microseconds, then considerable care will be needed in any coding to minimise the variation between samples. You might be able to get a 'reasonable' result using micros() if you remove alll of the unnecessary code, etc., but it will have considerable sources of error ... e.g. see discussion on creating a square wave..

https://forum.arduino.cc/t/micros-accuracy-pulse/458380

Note this avoids interrupts, print statements, etc... whilst performing the timed operations. However, it might be 'good enough'.

------------

To improve the situation,  consider using a dedicated timer .. which might be a timer peripheral inside a microcontroller.

I have also ignored any timing constraints or uncertainities your ADXL chip may impose .. that means digging into the data sheet.

-----------

My discussion so far has only considered the variation between consecutive samples ... microcontrollers often have 'inaccurate' oscillators, in that their frequency may be reasonably constant, provided power voltage, temperature, etc. are controlled, but that frequency can be considerably different from the 'nominal' 8MHz or whatever figure quoted in the data sheet. This depends on the design and manufacture of the specific product.

---------

The laptop time is usually synced on the Internet, so may be good for recording the time that the overall measurement took place, but I would not advise for the short time period between samples.

Best wishes, Dave


   
ReplyQuote
Page 1 / 2