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.
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
| Where | What |
|---|---|
| HEX5 | The axis: X, Y or Z |
| HEX4..HEX0 | That axis in milli-g (1000 = 1 g). SW9 up: cycles X, Y, Z each second; SW9 down: SW1..SW0 pick one |
| LEDR | A bubble level for X: tilt the board left and right and the light moves |
| Your PC | ID=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
Talking to the chip
| Step | SPI bytes | Meaning |
|---|---|---|
| 1 | 31 08 | DATA_FORMAT: full resolution, ±2 g (3.9 mg per count, 1 g ≈ 256) |
| 2 | 2C 0A | BW_RATE: 100 samples a second |
| 3 | 2D 08 | POWER_CTL: start measuring |
| 4 | 80 + 1 byte | Read register 0x00, DEVID: must be E5 |
| every 10 ms | F2 + 6 bytes | Read 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:
- 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.
- 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
| File | What |
|---|---|
src/accel_top.sv | The example |
lib/rtl/adxl345.sv | The accelerometer driver (4-wire, 3-wire option) |
lib/rtl/spi_master.sv | Reusable SPI master, mode 3 |
lib/rtl/bin2bcd_seq.sv | Binary to decimal, one bit per clock (fast and small) |