2. ADXL345 via SPI

WiiMote
De 2e sensor die vandaag op het menu staat is ook een Accelerometer: ADXL345, gebouwd door Analog Devices uit de USA. Dit is een digitale 3-assige accelerometer datasheet, PDF met een resolutie van 13 bit. Een accelerometer is een apparaat wat versnelling kan meten. Bijgevolg kan je dit principe gebruiken om de inclinatie van de sensor te bepalen ten opzichte van de zwaartekracht. In alle smartphones kom je dergelijke sensoren tegen, deze zorgen voor de automatische rotatie tussen landscape en portait modus. Het kleiner broertje van de ADXL345, de ADXL330, is onder andere terug te vinden in controller van de Nintendo Wii: de WiiMote.
De SPI-BUS
Deze sensor kan gebruik maken van zowel I²C als SPI, maar in dit Labo hebben we er voor gekozen om de sensoren aan te sturen met SPI. De SPI bus is minimaal een 3-wire bus, maar in de meeste applicaties wordt de 4-wire variant gebruikt. Je hebt dus naast de 3 of 4 communicatiedraden ook telkens een spanningsreferentie (GND) nodig, wat het totaal op 5 draden brengt. In tegenstelling tot de I²C bus is de SPI bus een Full-duplex bus, waardoor de communicatie veel sneller kan verlopen dan bij I²C.
De terminologie van pinnen voor SPI ligt veel minder vast dan bij I²C, waardoor je een veelvoud aan pin-namen hebt voor dezelfde functie, bijvoorbeeld: SDI/SDO (Serial Data In/Out) DIN/DOUT (Data In/Out) MOSI/MISO (Master Out Slave In/Master In Slave Out)
Daarnaast zijn er 4 verschillende modi waarop de communicatie kan plaatsvinden, deze worden bepaald door de Clock polariteit en clock fase (Veelgebruikt): Mode 0: Data is sampled op de Riging Edge van de Clock, Clock is laag wanneer niet in gebruik Mode 1: Data is sampled op de Falling Edge van de Clock, Clock is laag wanneer niet in gebruik Mode 2: Data is sampled op de Faling Edge van de Clock, Clock is hoog wanneer niet in gebruik Mode 3: Data is sampled op de Riging Edge van de Clock, Clock is hoog wanneer niet in gebruik Ook de bitvolgorde (MSB first of LSB first) is niet gestandaardiseerd, maar meest gebruikt is MSB first.
De apparaten op de SPI bus hebben geen adres, maar worden geadresseerd door de CS lijn (Chip Select), dit houdt dus in dat je voor elk apparaat op de bus een extra output-pin nodig hebt, om dat specifieke apparaat te adresseren. De andere datalijnen worden gedeeld door de aangesloten apparaten. De CS lijn is “altijd” actief laag, dus 0V op deze lijn laat het aangesloten apparaat luisteren. Er bestaat geen voorgeprogrammeerde limiet op het aantal mogelijke slaves op een SPI bus.

Timing diagram voor Mode 0 SPI communicatie
Eigenschappen van de ADXL345
- 3-assige accelerometer
- Ultra low power (23 μA)
- Voedingsspanning: 2.0 V tot 3.6 V
- Hoge resolutie (13-bit, 4mg/LSB -> min. meetbare rotatie: 0.25°)
- Maximale acceleratie ±16 g
- Digitale interface: SPI (3- of 4- draads) of I²C
- Meet zowel statische als dynamisce acceleratie

Schema ADXL345
Pin beschijvingen
Aansluiten
- Het aansluiten van SPI sensoren is weerom eenvoudig:
- Sluit de GND van de ESP aan op de GND van de sensor
- Sluit GPIO25 van de ESP aan op de SCL (Clock) van de sensor
- Sluit GPIO26 van de ESP aan op de SDA (MOSI) van de sensor
- Sluit GPIO27 van de ESP aan op de SDO (MISO) van de sensor
- Sluit GPIO33 van de ESP aan op de CS (Chip Select) van de sensor
- Sluit tenslotte de 3.3V van de ESP aan op de VCC van de sensor. De andere pinnen op de sensor (INT1, INT2) hoeven niet verbonden te worden.

Die-shot van het MEMS device in de ADXL345!
Experimenteren met de ADXL345
Code demonstratie
Om de ADXL345 uit te lezen kan je de registers rechtstreeks aanspreken, of gebruik maken van een bibliotheek. Wanneer we op ons development board (TTGO T-Display) gebruik willen maken van de SPI bus, stoten we op een moeilijkheid: deze is al in gebruik door het display, of beter gezegd: door de display driver. Bij gevolg moeten we gebruik maken van de 2e hardware SPI poort: HSPI. In het totaal zijn er 4 hardware SPI poorten beschikbaar, maar enkel VSPI en HSPI zijn in praktijk bruikbaar voor onze toepassingen. In de Arduino-omgeving wordt VSPI als “standaard” aangeduid, dus we moet aan de code kunnen meegeven dat we gebruik wensen te maken van HSPI. Indien je een bibliotheek gebruikt, dan moet deze het ook ondersteunen om een poort te “injecteren”.
Test het uitlezen van de ADXL345 zonder gebruik te maken van een bibliotheek aan de hand van de code hieronder.
#define DISPLAYTEXT "ELSY - LAB2 - ADXL345"
#include <SPI.h>
#include <TFT_eSPI.h>
SPIClass SPIADXL(HSPI); // Make use of the HSPI spi bus for the ADXL sensor
TFT_eSPI tft = TFT_eSPI(); // Constructor for the TFT library
//ADXL345 setting parameters
#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
//TTGO T-display pins : GPIOxx--> ADXLBreakoput
#define MISO_ADXL 27 //GPIO27 --> SDO
#define MOSI_ADXL 26 //GPIO26 --> SDA
#define SCK_ADXL 25 //GIPO25 --> SCL
#define CS_ADXL 33 //GPIO33 --> CS
//Global variables
char values[10] = {'\0'};
int16_t x, y, z;
float xg, yg, zg;
//Function prototypes
void printTitel();
void readRegister(char, int16_t, char *);
void writeRegister(char, char);
void setup() {
//Enable PIN_CS as an output and make HIGH to disable sensor
pinMode(CS_ADXL, OUTPUT);
digitalWrite(CS_ADXL, HIGH);
//Setup SPI-bus
SPIADXL.begin(SCK_ADXL, MISO_ADXL, MOSI_ADXL, CS_ADXL);
SPIADXL.setDataMode(SPI_MODE3);
SPIADXL.setBitOrder(MSBFIRST);
SPIADXL.setFrequency(1000000);
//Start Serial communication
Serial.begin(115200);
// set up the ADXL345
writeRegister(DATA_FORMAT, 0x03); // ±16g 10bit
writeRegister(POWER_CTL, 0x08);
writeRegister(BW_RATE, 0x0F);
//Start Display
printTitel();
Serial.println("\n\nSetup Ready!\n\n");
}
void loop() {
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.03125 = (16*2)/(2^10)
xg = x * 0.03125;
yg = y * 0.03125;
zg = (z * 0.03125) - 1;
Serial.print("X:"); Serial.print(xg);
Serial.print(", Y:"); Serial.print(yg);
Serial.print(", Z:"); Serial.println(zg);
delay(10);
}
//Writes data over the SPI port to the sensor
void writeRegister(char registerAddress, char value) {
digitalWrite(CS_ADXL, LOW); //Sensor starts listening when CS is low
SPIADXL.transfer(registerAddress); //Transfer desired register
SPIADXL.transfer(value); //Transfer desired value
digitalWrite(CS_ADXL, HIGH);
}
//Reads data over the SPI port from the sensor
void readRegister(char registerAddress, int16_t numBytes, char * values) {
//Prepare data before transmission
char address = 0x80 | registerAddress; //Set MSB to 1, leave rest intact
if (numBytes > 1) {
address = address | 0x40; //Set x1xx xxxx to announce multiple bytes
}
digitalWrite(CS_ADXL, LOW); //Make sensor active
SPIADXL.transfer(address); //Activate data transfer on the sensor
for (int16_t i = 0; i < numBytes; i++) { //Write 0x00 for as many times you want to collect data.
values[i] = SPIADXL.transfer(0x00);
}
digitalWrite(CS_ADXL, HIGH); //Disable sensor communication
}
void printTitel(){
tft.init();
tft.setRotation(3); //setRotation: 1: Screen in landscape(USB to the right)
tft.fillScreen(TFT_BLACK); //Fill screen with random colour
tft.setCursor(0, 0, 4); //(cursor at 0,0; font 4
tft.setTextColor(TFT_BLACK, TFT_YELLOW); // Textcolor, Backgr.Color; independent of fillscreen
tft.println(DISPLAYTEXT); //Print on cursorpos 0,0
}
Indien er geen bibliotheek beschikbaar is, of je kan de correcte SPI poort niet injecteren, dan ben je verplicht om de library zelf te schrijven (of een andere sensor te kiezen). De gegevens die je nodig zou hebben om zelf een lib te maken of de sensor rechtstreeks aan te spreken kan je terugvinden in de datasheet. vb: Add 0x32 wordt gebruikt om de Acc data te lezen, de beschrijving hiervan kan je terugvinden onderaan pagina 27 van de datasheet (Sectie: Register 0x31—DATA_FORMAT).
Bibliotheek installeren
We stellen voor om het ons niet te moeilijk te maken, en gebruik te maken van een goede bibliotheek: “ADXL345_WE” geschreven door een enthousiasteling: Wolfgang Ewald, en ondersteunt het gebruik van een alternatieve SPI poort. Installeren verloopt net zoals je gedaan hebt voor de I²C sensor, en zou dus zonder stappenplan moeten lukken.
Voorbeeldcode
Copy-paste de code hieronder zelf, en kijk of je Accelometer data ontvangt. Je kan de functionaliteit eenvoudig testen, aangezien de sensor ook altijd de gravitatiekracht van aarde zal meten!
#include <SPI.h>
#include <ADXL345_WE.h> // ADXL345_WE by Wolfgang Ewald
// Define the HSPI pins
#define HSPI_SCK 25
#define HSPI_MOSI 26
#define HSPI_MISO 27
#define HSPI_CS 33
//Global variables
xyzFloat raw,g;
// 1. Create the HSPI object
SPIClass hspi(HSPI);
// 2. Init the lib with a pointer the hspi object, Parameters: (&SPI_object, CS_pin, isSPI)
ADXL345_WE accel = ADXL345_WE(&hspi, HSPI_CS, true);
void setup() {
Serial.begin(115200);
// 3. Start the HSPI bus with custom pins
hspi.begin(HSPI_SCK, HSPI_MISO, HSPI_MOSI, HSPI_CS);
// 4. Initialize the sensor
if (!accel.init()) {
Serial.println("ADXL345 not found! Check your wiring.");
while (1);
}
// Optional: Set range and data rate
accel.setDataRate(ADXL343_DATA_RATE_100); // 100 samples per second
accel.setRange(ADXL345_RANGE_2G); // Maximal range of the sensor
Serial.println("ADXL345 via HSPI is Initialized.");
}
void loop() {
accel.getRawValues(&raw);
accel.getGValues(&g);
Serial.printf("X: %.3f\tY: %.3f\tZ: %.3f\n", raw.x, raw.y, raw.z);
Serial.printf("X: %.3f\tY: %.3f\tZ: %.3f\n", g.x, g.y, g.z);
delay(100);
}
Indien de data wel verschijnt in de Serial Monitor, maar niet in de Serial Plotter, dan is deze niet correct geformatteerd voor de plotter. Formatteer het print-statement correct om deze wel te laten verschijnen. Gebruik de stategie uit 1. Digitale Sensors of beter nog: bekijk de Serial Plotter Tutorial op de Arduino Website!
Nieuw in deze code?
- Dit zijn variabelen die zichtbaar zijn vanuit de volledige code (setup, main, en eigen functies).
- Ze worden gedefinieerd boven de “setup()” functie.
- Enkel gebruikt wanneer “echt nodig” Verwijzing “by reference”:
- In voorgaande functies en methoden maakten we gebruik van een verwijzing “by value”: de waarde van een variabele werd gekopieerd naar de functie. In dit geval wordt het geheugen-adres van de functie doorgegeven, zodat de volledige niet de hele tijd heen-en-weer gekopieerd hoeft te worden.
Optionele aanpassing
Wanneer alles naar behoren werkt, pas dan de bovenstaande voorbeeldcode (met bibliotheek) zo aan, zodat je microcontroller een bericht stuurt naar de terminal wanneer de sensor een “double tap” event waarneemt. Kijk hiervoor naar de Examples op de Githubpagina van de bibliotheek. Let op, hiervoor die je nog een extra draad te gebruiken: van int1 naar een digitale pin op je ESP. De sensor laat via deze draad weten dat een double-tap heeft plaatsgevonden.
Roep de docent, demonstreer de functionaliteit van de schakeling.
ANS vraag 2a, 2b, 2c, 2d, 2e