ADXL345 Arduino -opas: kytkentä, I2C-koodi ja kallistuskulma
ADXL345 Arduino -yhdistelmä on pysynyt harrastajien suosikkina jo yli vuosikymmenen — ja hyvästä syystä. ADXL345 on Analog Devicesin 3-akselinen digitaalinen MEMS-kiihtyvyysanturi, joka ei ole vain halvempi vaihtoehto MPU-6050:lle: siinä on ominaisuuksia, joita gyroskooppimoduulista ei löydy: laitteistotasolla toteutettu napautuksen ja kaksoisnapautuksen tunnistus, vapaan pudotuksen tunnistus, liike- ja lepotilan valvonta automaattisella unitilalla sekä noin 40 µA virrankulutus mittaustilassa. Akkukäyttöisessä projektissa nämä ovat ratkaisevia.
Tässä oppaassa käydään läpi adxl345 arduino -yhdistelmä kokonaan: fyysinen kytkentä, I2C-osoitteen todellinen logiikka, neljä toimivaa koodiesimerkkiä sekä kallistuskulman (roll ja pitch) laskenta oikeilla kaavoilla. Kaikki esimerkit on kirjoitettu niin, että ne toimivat myös 32-bittisillä ohjaimilla, kuten ESP32:lla — tästä lisää myöhemmin, koska monissa netin esimerkeissä on juuri tähän liittyvä hiljainen bugi.
Sisällysluettelo
- Anturi ja mitä paketissa on
- I2C-osoite ja väylän valinta
- ADXL345 Arduino -kytkentä ja 3,3 V
- Esimerkki 1: I2C-skanneri
- Esimerkki 2: raakaluvut ja g-arvot
- Esimerkki 3: kirjastot ja Adafruit-versio
- Kallistuskulman laskenta
- Esimerkki 4: napautus ja pudotus
- Vianetsintä
- ADXL345 vai MPU-6050?
- Tarvittavat osat
- Yhteenveto
Anturi ja mitä paketissa on
Käytämme tässä oppaassa Fermion ADXL345 3-akselista kiihtyvyysanturia (SKU SEN-ADXL345-FERMION). Kortti on DFRobotin Fermion-sarjan riisuttu breakout, ja sen keskeiset tiedot ovat:

- Mittausalue ±2 g / ±4 g / ±8 g / ±16 g, valittavissa rekisteristä
- 13-bittinen tarkkuus ±16 g:llä, kiinteä 4 mg/LSB resoluutio; ulostulo 16-bittinen kahden komplementti
- Liitännät: I2C ja SPI (3- ja 4-johtiminen)
- Käyttöjännite 2,0–3,6 V, 3,3 V logiikka — kortilla ei ole regulaattoria eikä tasonsiirtoa
- Virrankulutus ~40 µA mittaustilassa, 0,1 µA valmiustilassa
- 32-tasoinen FIFO ja kaksi ohjelmoitavaa keskeytyslähtöä
- Nastajärjestys kortilla: GND, VCC (3V3), /CS, INT1, INT2, SDO, SDA, SCL
- Koko noin 20 × 15 mm, kiinnitysreikä ja X/Y/Z-akselimerkinnät
Huomaa, että Fermion-sarjan kortit toimitetaan ilman juotettuja nastarimoja. Paketissa on suora nastarima, mutta se pitää juottaa itse ennen kuin kortti asettuu koekytkentäalustaan.
Mihin akselit osoittavat
Kortin silkkipainatuksessa on X-, Y- ja Z-nuolet. Oikeakätisessä koordinaatistossa Z osoittaa komponenttipuolelta ulos, eli kun kortti makaa pöydällä komponentit ylöspäin, Z-akseli näyttää +1 g. Sirun sisäinen nollapiste ei ole täydellinen: datalehden tyypillinen nollapoikkeama on ±35 mg X- ja Y-akselilla ja ±40 mg Z-akselilla, joten muutaman milli-g:n virhe vaakatasossa on normaalia eikä merkki viasta. Se korjataan OFSX/OFSY/OFSZ-rekistereillä, joihin palataan kallistuskulmaosiossa.
I2C-osoite ja väylän valinta
Tämä on se osa, jossa aloittelija useimmiten kaatuu. Netissä toistetaan, että ”ADXL345:n osoite on 0x53”, mutta se on totta vain tietyllä kytkennällä. Kaksi nastaa määrää käytännössä kaiken.
SDO valitsee osoitteen
Analog Devicesin datalehti ja AN-1077-sovellusohje sanovat asian suoraan: laitteen 7-bittinen I2C-osoite on 0x53, mutta vaihtoehtoinen osoite valitaan kytkemällä SDO/ALT ADDRESS -nasta käyttöjännitteeseen. Silloin osoite on 0x1D. DFRobotin oma kirjasto määrittelee saman:
// ADXL345-osoite kun SDO on ylhaalla
#define ADXL345_ADDR_ALT_HIGH 0x1D
// ADXL345-osoite kun SDO on alhaalla
#define ADXL345_ADDR_ALT_LOW 0x53
Fermion-kortilla SDO on tuotu nastarimalle eikä sitä ole vedetty alas kiinteästi. Jos jätät sen kellumaan, osoite ei ole taattu. Kytke SDO siis eksplisiittisesti GND:hen, jolloin osoite on varmasti 0x53 — sama kuin DFRobotin omassa esimerkkikoodissa ja Protocachen tuotesivulla. Osoite lukitaan nastan tilasta käynnistyksessä, joten jos vaihdat sitä, katkaise virta hetkeksi.
/CS valitsee väylän
ADXL345 käynnistyy I2C-tilaan vain jos /CS on ylhäällä. Datalehti: kun /CS on kytketty VDD I/O:hon, anturi on I2C-tilassa ja tarvitsee vain kaksijohtimisen kytkennän. Jos /CS on alhaalla, siru odottaa SPI-liikennettä eikä vastaa I2C-väylällä lainkaan. Tämä on ylivoimaisesti yleisin syy siihen, että adxl345 i2c -skanneri ei löydä mitään. Kytke /CS suoraan VCC:hen (3,3 V).
DEVID on ilmainen tarkistus
Rekisteri DEVID (0x00) on vain luettava ja palauttaa aina 0xE5. AN-1077 muotoilee sen näin: jos DEVID:stä luettu arvo ei ole 0xE5, joko fyysinen kytkentä tai komentojärjestys on väärä. Lisää tämä tarkistus jokaiseen sketchiin — se säästää tuntikausia. Arvo 0xFF tarkoittaa yleensä NACK:ia (mitään ei ole väylällä kyseisessä osoitteessa) ja 0x00 johdotusvirhettä tai SDA-linjan jumia.
ADXL345 Arduino -kytkentä ja 3,3 V logiikka
Alla oleva adxl345 kytkentä pätee Arduino UNO:lle ja Nanolle (ATmega328P):
- VCC → 3.3V (ei koskaan 5 V, sirun absoluuttinen maksimi on 3,6 V)
- GND → GND
- SDA → A4
- SCL → A5
- /CS → VCC (3,3 V) — pakottaa I2C-tilaan
- SDO → GND — osoite 0x53
- INT1 → D2 (keskeytyskykyinen nasta)
- INT2 → ei kytketty (tai D3)
Muilla korteilla I2C-nastat ovat eri paikoissa: Arduino Megassa SDA = 20 ja SCL = 21, Leonardossa SDA = 2 ja SCL = 3, ESP32:lla oletuksena SDA = 21 ja SCL = 22 (ESP32-C6-kehitysalustalla kannattaa antaa nastat itse: Wire.begin(sda, scl)). Raspberry Pi Picolla I2C0 on GP4 (SDA) ja GP5 (SCL).
Jännitetasot — älä ohita tätä
ADXL345:n absoluuttiset maksimiarvot ovat VS ja VDD I/O −0,3…+3,6 V, ja digitaalinastojen raja on VDD I/O + 0,3 V tai 3,6 V, kumpi näistä on pienempi. ATmega328P:n Wire-kirjasto kytkee sisäiset ylösvetovastukset A4/A5-nastoille 5 voltin käyttöjännitteeseen. Käytännössä moni pääsee tästä läpi, koska I2C on avokollektorinen ja AVR:n ylösvedot ovat heikkoja (20–50 kΩ), mutta se on datalehden ulkopuolella.
Siistit ratkaisut järjestyksessä: (a) käytä 3,3 V ohjainta, kuten ESP32-C6 DevKitC-1:tä, jolloin mitään ei tarvitse siirtää; (b) käytä kahdensuuntaista tasonsiirrinta; (c) vähintään lisää ulkoiset 4,7 kΩ ylösvetovastukset 3,3 volttiin SDA- ja SCL-linjoille. Jos käytät 5 V ATmega328P Nano -ohjainta, tee ainakin kohta (c). Huomaa myös, että SPI ei ole samalla tavalla armollinen: SPI-lähdöt ovat push-pull-tyyppisiä, joten SPI-tilassa 5 V kortilla tarvitset aidosti tasonsiirtimen. Sekin on hyvä syy pysyä I2C:ssä.

Riittääkö 3.3V-nastan virta?
ADXL345 vetää vain noin 40 µA, joten virransyötöstä ei ole huolta millään kortilla. Arduino UNO:n 3.3V-nasta antaa 50 mA (se tulee USB-sarjapiirin regulaattorista) ja Nano-yhteensopivilla korteilla tyypillisesti 100–150 mA. Yksi anturi mahtuu tähän tuhatkertaisesti — ainoa oikea riski on kytkeä VCC vahingossa 5V-nastaan. Kannattaa varata yksi hyppylankaväri vain 3,3 voltille.
Esimerkki 1: I2C-skanneri
Aja tämä aina ensin. Se kertoo sekunnissa, onko kytkentä kunnossa ja mikä osoite on käytössä.
#include <Wire.h>
void setup() {
Serial.begin(115200);
while (!Serial) { ; }
Wire.begin();
Serial.println("I2C-skannaus...");
for (uint8_t addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print("Loytyi laite osoitteesta 0x");
Serial.println(addr, HEX);
}
}
Serial.println("Valmis.");
}
void loop() {}
Odotettu tulos on 0x53 (SDO maassa) tai 0x1D (SDO käyttöjännitteessä). Jos mitään ei löydy, tarkista VCC = 3,3 V, maadoitus, että /CS on ylhäällä ja etteivät SDA ja SCL ole vaihtuneet keskenään.
Esimerkki 2: raakaluvut, g-arvot ja kulmat pelkällä Wire.h:lla
Ennen koodia yksi tärkeä asia: miten raakaluvut muunnetaan g-yksiköiksi. ADXL345:llä on kaksi toimintatapaa.
- FULL_RES = 1 (DATA_FORMAT-rekisterin bitti D3): resoluutio skaalautuu mittausalueen mukana, joten kertoja pysyy 4 mg/LSB kaikilla alueilla. Silloin 1 g = 250 yksikköä ja
g = raaka * 0.004. Tarkkuus kasvaa 13 bittiin ±16 g:llä. - FULL_RES = 0 (kiinteä 10 bittiä): kertoja muuttuu alueen mukana. ±2 g:llä se on 3,9 mg/LSB eli 1 g ≈ 256 yksikköä, ±16 g:llä 31,2 mg/LSB.
Analog Devicesin oma suositus on jättää anturi 13-bittiseen tilaan, koska data on silloin aina 4 mg/LSB mittausalueesta riippumatta — harvoin tiedät etukäteen, kuinka suuri suurin kiihtyvyys on. Siksi kirjoitamme DATA_FORMAT = 0x0B (FULL_RES + ±16 g) ja käytämme aina kertojaa 0,004. Tämä on paljon vähemmän virhealtista kuin aluekohtainen taulukko. Metreihin sekunnissa toiseen (m/s²) pääset kertomalla g-arvon luvulla 9,80665.
#include <Wire.h>
#define ADXL345_ADDR 0x53 // SDO -> GND. 0x1D jos SDO -> VCC.
#define REG_DEVID 0x00
#define REG_BW_RATE 0x2C
#define REG_POWER_CTL 0x2D
#define REG_DATA_FORMAT 0x31
#define REG_DATAX0 0x32
const float LSB_TO_G = 0.004f; // FULL_RES -> 4 mg/LSB kaikilla alueilla
void writeReg(uint8_t reg, uint8_t val) {
Wire.beginTransmission(ADXL345_ADDR);
Wire.write(reg);
Wire.write(val);
Wire.endTransmission();
}
uint8_t readReg(uint8_t reg) {
Wire.beginTransmission(ADXL345_ADDR);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom((uint8_t)ADXL345_ADDR, (uint8_t)1);
return Wire.available() ? Wire.read() : 0xFF;
}
bool readXYZ(int16_t &x, int16_t &y, int16_t &z) {
Wire.beginTransmission(ADXL345_ADDR);
Wire.write(REG_DATAX0);
Wire.endTransmission(false);
Wire.requestFrom((uint8_t)ADXL345_ADDR, (uint8_t)6);
if (Wire.available() < 6) return false;
uint8_t b[6];
for (uint8_t i = 0; i < 6; i++) b[i] = Wire.read();
x = (int16_t)(((uint16_t)b[1] << 8) | b[0]);
y = (int16_t)(((uint16_t)b[3] << 8) | b[2]);
z = (int16_t)(((uint16_t)b[5] << 8) | b[4]);
return true;
}
void setup() {
Serial.begin(115200);
while (!Serial) { ; }
Wire.begin();
uint8_t id = readReg(REG_DEVID);
Serial.print("DEVID = 0x");
Serial.println(id, HEX);
if (id != 0xE5) {
Serial.println("VIRHE: odotettiin 0xE5. Tarkista kytkenta, /CS ja osoite.");
while (1) { delay(1000); }
}
writeReg(REG_DATA_FORMAT, 0x0B); // FULL_RES = 1, alue +-16 g -> 4 mg/LSB
writeReg(REG_BW_RATE, 0x0A); // 100 Hz
writeReg(REG_POWER_CTL, 0x08); // Measure-bitti paalle
delay(20);
}
void loop() {
int16_t x, y, z;
if (readXYZ(x, y, z)) {
float gx = x * LSB_TO_G;
float gy = y * LSB_TO_G;
float gz = z * LSB_TO_G;
float roll = atan2(gy, gz) * 180.0 / PI;
float pitch = atan2(-gx, sqrt(gy * gy + gz * gz)) * 180.0 / PI;
Serial.print("raaka: "); Serial.print(x); Serial.print('\t');
Serial.print(y); Serial.print('\t'); Serial.print(z);
Serial.print("\tg: "); Serial.print(gx, 3); Serial.print('\t');
Serial.print(gy, 3); Serial.print('\t'); Serial.print(gz, 3);
Serial.print("\troll: "); Serial.print(roll, 1);
Serial.print("\tpitch: "); Serial.println(pitch, 1);
} else {
Serial.println("Luku epaonnistui");
}
delay(100);
}
Kun kortti makaa pöydällä tasossa, raakaluvut ovat noin 0 / 0 / 250, g-arvot 0,00 / 0,00 / 1,00 ja sekä roll että pitch lähellä nollaa. Huomaa, että painovoima näkyy +1 g:nä sillä akselilla, joka osoittaa ylöspäin. Nosta kortti kyljelleen, niin roll kääntyy noin ±90 asteeseen.
Kaksi bugia, jotka kannattaa tietää
DFRobotin virallinen esimerkki tälle kortille ei koske DATA_FORMAT-rekisteriin lainkaan, joten se pyörii oletusarvoisessa 10-bittisessä ±2 g -tilassa (~256 yksikköä/g, ei 250) ja tulostaa vain raakaluvut. Jos ihmettelet, miksi lukemasi eivät täsmää, tämä on syy.
Vakavampi ongelma on rivi x = (((int)buff[1]) << 8) | buff[0];. Se laajentaa etumerkin oikein vain siksi, että int on AVR:llä 16-bittinen. ESP32:lla, Picolla ja Duella int on 32-bittinen, ja rivi rikkoutuu hiljaisesti negatiivisilla kiihtyvyyksillä — saat valtavia positiivisia lukuja. Yllä käytetty muoto (int16_t)(((uint16_t)b[1] << 8) | b[0]) on siirrettävä ja toimii kaikilla alustoilla.
Esimerkki 3: kirjastot ja Adafruit-versio
Järkeviä vaihtoehtoja on kolme. Pelkkä Wire.h on paras opetteluun, koska rekisterikirjoitukset ovat näkyvissä. Adafruit ADXL345 on siisti yleiskäyttöön: asenna kirjastonhallinnasta hakusanalla ”Adafruit ADXL345” ja hyväksy myös riippuvuudet Adafruit Unified Sensor ja Adafruit BusIO — moni ohittaa ne ja saa käännösvirheen. SparkFun ADXL345 on ainoa, jossa napautus, kaksoisnapautus, vapaa pudotus ja liiketunnistus ovat valmiina funktioina, mutta se ei salli osoitteen vaihtoa (0x53 on kovakoodattu). Neljäs mainitsemisen arvoinen on Wolfgang Ewaldin ADXL345_WE, joka on parhaiten dokumentoitu ja sisältää kuuden asennon kalibrointisketchin.
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h>
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);
void setup() {
Serial.begin(115200);
while (!Serial) { ; }
if (!accel.begin(0x53)) { // 0x1D jos SDO -> VCC
Serial.println("ADXL345 ei vastaa!");
while (1) { delay(1000); }
}
accel.setRange(ADXL345_RANGE_16_G);
accel.setDataRate(ADXL345_DATARATE_100_HZ);
}
void loop() {
sensors_event_t event;
accel.getEvent(&event);
// event.acceleration on m/s^2 -> jaa 9.80665 saadaksesi g
float gx = event.acceleration.x / SENSORS_GRAVITY_STANDARD;
float gy = event.acceleration.y / SENSORS_GRAVITY_STANDARD;
float gz = event.acceleration.z / SENSORS_GRAVITY_STANDARD;
float roll = atan2(gy, gz) * 180.0 / PI;
float pitch = atan2(-gx, sqrt(gy * gy + gz * gz)) * 180.0 / PI;
Serial.print("g: "); Serial.print(gx, 3); Serial.print('\t');
Serial.print(gy, 3); Serial.print('\t'); Serial.print(gz, 3);
Serial.print("\troll: "); Serial.print(roll, 1);
Serial.print("\tpitch: "); Serial.println(pitch, 1);
delay(100);
}
Adafruitin kirjasto paljastaa myös writeRegister(), readRegister() ja getDeviceID(), joten voit konfiguroida napautus- ja pudotustunnistuksen käsin, vaikka luet dataa kirjaston kautta.
Kallistuskulman laskenta
Levossa oleva kiihtyvyysanturi antaa painovoiman suunnasta kaksi kolmesta Euler-kulmasta: roll ja pitch. Kaavat ovat:
roll = atan2(y, z) * 180.0 / PI;
pitch = atan2(-x, sqrt(y*y + z*z)) * 180.0 / PI;
Muutama tärkeä yksityiskohta:
- Käytä
atan2-funktiota, eiatan-funktiota, niin saat koko −180…+180° alueen oikeassa neljänneksessä. - Voit syöttää kaavoihin suoraan raakaluvut: skaalauskerroin supistuu pois suhteesta, joten g-muunnosta ei tarvita kulmaa varten.
- Muunnoskerroin 180/π on tarkasti 57,29578. Monet esimerkit käyttävät pyöristystä 57,3 — käytä mieluummin
RAD_TO_DEGtai180.0/PI. - Yaw eli suuntakulma ei ole laskettavissa pelkästä kiihtyvyysanturista. Siihen tarvitset magnetometrin tai gyroskoopin.
- 4 mg/LSB resoluutio riittää datalehden mukaan alle 1 asteen kallistusmuutosten mittaamiseen, mutta trigonometrinen herkkyys on paras vaakatasossa ja huonoin pystyssä: lähellä 90 astetta kulmakohina kasvaa rajusti.
Suurin sudenkuoppa: kallistuskulma on luotettava vain silloin, kun anturi ei kiihdy. Jarruttava robotti tai tärisevä tulostuspää tuottaa lineaarista kiihtyvyyttä, jota anturi ei osaa erottaa painovoimasta. Yksinkertainen eksponentiaalinen suodatus auttaa hiljaiseen kohinaan:
rollFilt = 0.9 * rollFilt + 0.1 * roll;
Nollapisteen kalibrointiin on rekisterit OFSX, OFSY ja OFSZ (0x1E–0x20). Ne ovat etumerkillisiä 8-bittisiä trimmirekistereitä, joiden askel on 15,6 mg ja jotka vaikuttavat laitteistossa ennen datarekistereitä. Helpoin tapa: aseta kortti tasolle, keskiarvota 100 näytettä ja kirjoita käänteiset poikkeamat rekistereihin tai vähennä ne ohjelmallisesti.
Esimerkki 4: napautus, kaksoisnapautus ja vapaa pudotus
Nämä ovat ADXL345:n oikea myyntivaltti. Tunnistus tapahtuu sirussa, joten mikrokontrolleri voi nukkua ja herätä INT-nastasta. Kynnysarvojen laskenta:
THRESH_TAP(0x1D): 62,5 mg/LSB. Älä koskaan jätä arvoon 0 — datalehti varoittaa arvaamattomasta käytöksestä.DUR(0x21): 625 µs/LSB. Enimmäisaika, jonka tapahtuma saa olla kynnyksen yli ja tulla silti lasketuksi napautukseksi.LATENT(0x22): 1,25 ms/LSB. Kuollut aika ensimmäisen napautuksen jälkeen.WINDOW(0x23): 1,25 ms/LSB. Ikkuna, jonka sisällä toisen napautuksen pitää saapua.THRESH_FF(0x28): 62,5 mg/LSB, verrataan kaikkien kolmen akselin neliösummajuureen. Suositeltu 5–9 eli 312–562 mg.TIME_FF(0x29): 5 ms/LSB. Suositeltu 20–70 eli 100–350 ms.
#include <Wire.h>
#define ADXL345_ADDR 0x53
#define REG_DEVID 0x00
#define REG_THRESH_TAP 0x1D
#define REG_DUR 0x21
#define REG_LATENT 0x22
#define REG_WINDOW 0x23
#define REG_THRESH_FF 0x28
#define REG_TIME_FF 0x29
#define REG_TAP_AXES 0x2A
#define REG_BW_RATE 0x2C
#define REG_POWER_CTL 0x2D
#define REG_INT_ENABLE 0x2E
#define REG_INT_MAP 0x2F
#define REG_INT_SOURCE 0x30
#define REG_DATA_FORMAT 0x31
#define INT_SINGLE_TAP 0x40
#define INT_DOUBLE_TAP 0x20
#define INT_FREE_FALL 0x04
const uint8_t INT1_PIN = 2;
volatile bool intFlag = false;
void isr() { intFlag = true; }
void writeReg(uint8_t reg, uint8_t val) {
Wire.beginTransmission(ADXL345_ADDR);
Wire.write(reg);
Wire.write(val);
Wire.endTransmission();
}
uint8_t readReg(uint8_t reg) {
Wire.beginTransmission(ADXL345_ADDR);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom((uint8_t)ADXL345_ADDR, (uint8_t)1);
return Wire.available() ? Wire.read() : 0xFF;
}
void setup() {
Serial.begin(115200);
while (!Serial) { ; }
Wire.begin();
pinMode(INT1_PIN, INPUT); // INT on aktiivi-korkea (INT_INVERT = 0)
if (readReg(REG_DEVID) != 0xE5) {
Serial.println("ADXL345 ei vastaa (DEVID != 0xE5)");
while (1) { delay(1000); }
}
writeReg(REG_POWER_CTL, 0x00); // standby konfiguroinnin ajaksi
writeReg(REG_DATA_FORMAT, 0x0B); // FULL_RES, +-16 g
writeReg(REG_BW_RATE, 0x0A); // 100 Hz
// --- Napautus ---
writeReg(REG_THRESH_TAP, 50); // 50 * 62.5 mg = 3.125 g
writeReg(REG_DUR, 15); // 15 * 625 us = 9.4 ms
writeReg(REG_LATENT, 80); // 80 * 1.25 ms = 100 ms
writeReg(REG_WINDOW, 200); // 200 * 1.25 ms = 250 ms
writeReg(REG_TAP_AXES, 0x07); // X, Y ja Z mukaan (0x01 = vain Z)
// --- Vapaa pudotus ---
writeReg(REG_THRESH_FF, 7); // 7 * 62.5 mg = 437 mg
writeReg(REG_TIME_FF, 30); // 30 * 5 ms = 150 ms
writeReg(REG_INT_MAP, 0x00); // kaikki keskeytykset INT1-nastaan
writeReg(REG_INT_ENABLE, INT_SINGLE_TAP | INT_DOUBLE_TAP | INT_FREE_FALL);
readReg(REG_INT_SOURCE); // nollaa vanhat liput
attachInterrupt(digitalPinToInterrupt(INT1_PIN), isr, RISING);
writeReg(REG_POWER_CTL, 0x08); // Measure paalle
Serial.println("Valmis: napauta korttia tai pudota se pehmealle alustalle.");
}
void loop() {
if (intFlag) {
intFlag = false;
uint8_t src = readReg(REG_INT_SOURCE); // luku nollaa liput
if (src & INT_DOUBLE_TAP) Serial.println("KAKSOISNAPAUTUS");
else if (src & INT_SINGLE_TAP) Serial.println("NAPAUTUS");
if (src & INT_FREE_FALL) Serial.println("VAPAA PUDOTUS");
}
}
Kaksi asiaa rikkovat tämän, jos ne ohittaa. Ensinnäkin INT_SOURCE (0x30) on luettava, muuten keskeytyslippu jää lukittuna ja saat täsmälleen yhden keskeytyksen koko ajokerran aikana. Toiseksi testaa DOUBLE_TAP ennen SINGLE_TAP: kaksoisnapautus asettaa molemmat bitit, joten väärässä järjestyksessä jokainen kaksoisnapautus näkyy yksittäisenä. Muista myös, että TAP_AXES-rekisterin bitti D3 (Suppress) parantaa kaksoisnapautuksen luotettavuutta selvästi.
Akkuprojektissa kannattaa yhdistää tähän POWER_CTL-rekisterin Link-bitti (D5) ja AUTO_SLEEP (D4). Silloin anturi pudottaa itsensä 8 Hz virransäästönäytteistykseen levossa ja herää liikkeestä — näin 40 µA:n kulutus putoaa keskimäärin alle mikroampeeriin.
Vianetsintä
- Skanneri ei löydä mitään: VCC ei ole 3,3 V, maa puuttuu, SDA ja SCL ovat ristissä tai /CS ei ole ylhäällä (siru käynnistyi SPI-tilaan).
- Skanneri löytää 0x1D: SDO on ylhäällä tai kelluu. Kytke se GND:hen ja katkaise virta hetkeksi.
- DEVID = 0xFF: NACK, väylällä ei ole mitään kyseisessä osoitteessa tai ylösvedot puuttuvat.
- DEVID = 0x00: SDA jumissa alhaalla tai oikosulku kytkennässä.
- Kaikki akselit lukevat nollaa: POWER_CTL-rekisterin Measure-bittiä ei ole asetettu — anturi käynnistyy valmiustilaan.
- Lukemat ovat ~256 yksikköä/g eikä 250: FULL_RES ei ole päällä, olet 10-bittisessä ±2 g -tilassa.
- Suuria negatiivisia hyppyjä ESP32:lla tai Picolla: etumerkin laajennus tehty
int-tyypilläint16_t:n sijaan. - Keskeytys laukeaa vain kerran: INT_SOURCE jää lukematta.
- Kulma heittelehtii liikkeessä: anturi ei erota painovoimaa lineaarisesta kiihtyvyydestä — suodata tai lisää gyro.
- Anturi kuumeni tai hajosi: syötettiin 5 V. Maksimi on 3,6 V, kortilla ei ole regulaattoria.
ADXL345 vai MPU-6050?
Tämä on yleisin kysymys, ja vastaus riippuu siitä, liikkuuko anturi mittaushetkellä. ADXL345:ssä on kolme akselia, 13-bittinen kiinteä 4 mg/LSB resoluutio, jopa 3200 Hz:n näytteenottotaajuus, napautuksen ja vapaan pudotuksen laitteistotunnistus sekä ~40 µA virrankulutus. MPU-6050-kiihtyvyysanturi- ja gyroskooppimoduulissa on kuusi akselia eli myös gyroskooppi, mutta ei kunnollista napautus- tai pudotustunnistusta, ja se kuluttaa noin 3,9 mA.

Valitse ADXL345 kun teet akkukäyttöistä liikeherätystä, napautusohjattua käyttöliittymää, pudotus- tai iskuvalvontaa, tärinämittausta, staattista kallistusmittaria tai 3D-tulostimen resonanssimittausta Klipperin input shaper -viritykseen — siinä ADXL345 on käytännössä vakio-osa. Valitse MPU-6050 kun laitteen pitää tietää asentonsa samalla kun se kääntyy: itsestään tasapainottuva robotti, gimbaali, lennokin vakautus. Jos haluat absoluuttisen asennon suuntakulmineen ja anturifuusion valmiina sirulla, katso Fermion BNO055 9-akselista IMU-anturia.
Tarvittavat osat
Tämän oppaan kaikki esimerkit toimivat alla olevilla osilla. Anturi on aina sama; ohjain valitaan sen mukaan, haluatko 3,3 V logiikan valmiina (ESP32-C6) vai klassisen 5 V AVR:n ulkoisilla ylösvedoilla (Nano). Lisäksi tarvitset koekytkentäalustan ja muutaman hyppylangan. Kun kytkentä on kunnossa ja DEVID palauttaa 0xE5, loput on pelkkää rekisterien säätöä — ja tämän oppaan koodit toimivat pohjana niin kallistusmittarille kuin tärinäloggerille.
Yhteenveto
ADXL345 on epätavallisen kiitollinen anturi, kun kolme asiaa on kunnossa: VCC on 3,3 V, /CS on ylhäällä ja SDO on maassa (osoite 0x53). Tarkista aina DEVID-rekisteri — jos se palauttaa 0xE5, ongelma on koodissa eikä johdoissa, ja se puolittaa vianetsintään kuluvan ajan. Aseta DATA_FORMAT arvoon 0x0B, niin kertoja on aina 0,004 g/LSB, ja käytä etumerkin laajennuksessa int16_t:tä, jotta sama koodi toimii sekä UNO:lla että ESP32:lla.
Tästä eteenpäin luonnollisia jatkoaskelia ovat FIFO-tilan käyttö tärinämittauksessa (32 näytettä puskuriin, ei yhtään hukattua näytettä 3200 Hz:llä), OFSX/OFSY/OFSZ-kalibrointi kuuden asennon menetelmällä sekä AUTO_SLEEP-tilan viritys akkuprojektiin. Jos tarvitset myös suuntakulman, lisää rinnalle gyroskooppi tai siirry suoraan anturifuusioon.
Tutustu valikoimaamme: Protocachen Fermion ADXL345 -kiihtyvyysanturi, MPU-6050 IMU-moduuli ja Fermion BNO055 lähtevät varastosta Suomesta — valitse projektiisi sopiva anturi ja aloita mittaaminen jo tällä viikolla.







