07 · Accelerometer (ADXL345 over SPI)

The DE10-Lite has a 3-axis accelerometer on board. This example talks to it over SPI, proves the link (the chip's ID must read E5, and at rest the three axes must add up to 1 g), and shows the tilt.

Verified on the board 2026-10-24 Quartus Prime Lite 17.1 No shield needed Skill: read-accelerometer
Download accelerometer.qar (full project)

The repo keeps the full project (.qar, plus the sources here) but no prebuilt bitstream: one build (a few minutes) makes the .sof on your computer.

What you will see

WhereWhat
HEX5The axis: X, Y or Z
HEX4..HEX0That axis in milli-g (1000 = 1 g). SW9 up: cycles X, Y, Z each second; SW9 down: SW1..SW0 pick one
LEDRA bubble level for X: tilt the board left and right and the light moves
Your PCID=E5 X=+0010 Y=+0167 Z=+0196, ten lines a second (raw counts, 3.9 mg each)

Measured on the board

$ python scripts/de10.py log 2
ID=E5 X=+0012 Y=+0166 Z=+0196
ID=E5 X=+0010 Y=+0167 Z=+0196
ID=E5 X=+0011 Y=+0166 Z=+0195

ID E5 is the ADXL345's fixed device ID: the SPI link works. The board stands tilted on its holder, and √(10² + 167² + 196²) = 257 counts × 3.9 mg = 1.00 g: gravity, as it must be at rest.

Resources: 1,247 logic elements (3 %). Timing: no negative slack.

Block diagram

ADXL345on the boardCS SCLK SDI SDO spi_mastermode 3, 1 MHzbyte by byte adxl345init 3 registers, DEVID,X Y Z every 10 ms × 125 / 32counts → milli-g HEX5..0 LEDR bubble bin2bcd_seq+ line_printer USB → PC

Talking to the chip

StepSPI bytesMeaning
131 08DATA_FORMAT: full resolution, ±2 g (3.9 mg per count, 1 g ≈ 256)
22C 0ABW_RATE: 100 samples a second
32D 08POWER_CTL: start measuring
480 + 1 byteRead register 0x00, DEVID: must be E5
every 10 msF2 + 6 bytesRead 6 registers from 0x32: X, Y, Z, low byte first (0x80 = read, 0x40 = several bytes)

SPI mode 3: the clock idles high, the FPGA changes data on the falling edge, the chip samples on the rising edge. 4-wire: we send on GSENSOR_SDI, the chip answers on GSENSOR_SDO. The driver can also do 3-wire (data both ways on SDI, as Terasic's demo does), but that mode is not verified here.

How it was debugged (a real story)

The first version read ID=00 and zeros. Signal Tap on the four SPI pins (scripts/make_stp.py, trigger on CS falling) showed the bytes going out looked right, but zoomed to single samples it showed two bugs in the SPI master:

  1. Data changed on the same clock as SCLK rose, exactly when the chip samples it. In mode 3 data must change on the falling edge.
  2. CS went high on the same clock as the last rising edge, so the chip dropped the last bit of every byte (the datasheet's CS hold time).

A unit test with a model SPI device (lib/sim/tb_spi_master.sv) now fails on either bug and passes on the fixed master. Before finding them, switching to 3-wire mode looked like the cure; it was not. Measure before you conclude.

Two more lessons: the report line first failed timing by 15 ns (a "/ 1000" in logic), fixed with a sequential decimal converter (lib/rtl/bin2bcd_seq.sv); and the chip keeps its registers while the FPGA is reprogrammed, so after a bad experiment it may be in a mode you did not set: always write every register you depend on.

Run it

python scripts/de10.py build examples/07_accelerometer
python scripts/de10.py program examples/07_accelerometer
python scripts/de10.py log 3

Build it yourself

python scripts/new_project.py examples/07_accelerometer accel_top --lib accel print bcd bcdseq seg7
python scripts/de10.py build examples/07_accelerometer

Test without the board

python scripts/sim.py lib/sim/tb_spi_master.sv       → 0 errors   PASS
python scripts/sim.py lib/sim/tb_bin2bcd_seq.sv      → 65536 values, 0 errors   PASS

Files

FileWhat
src/accel_top.svThe example
lib/rtl/adxl345.svThe accelerometer driver (4-wire, 3-wire option)
lib/rtl/spi_master.svReusable SPI master, mode 3
lib/rtl/bin2bcd_seq.svBinary to decimal, one bit per clock (fast and small)