|
| 1 | +--- |
| 2 | +title: "Reading acceleration values" |
| 3 | +description: "Read the X, Y, and Z values from the accelerometer." |
| 4 | +weight: 3 |
| 5 | +--- |
| 6 | + |
| 7 | +Now that we have the accelerometer configured, we can start reading the values it reads. |
| 8 | + |
| 9 | +Most of the code is the same as before, but the reading part is new: |
| 10 | + |
| 11 | +```go |
| 12 | + // Read the acceleration data. |
| 13 | + w := []byte{0x28|0x80} |
| 14 | + r := make([]byte, 6) |
| 15 | + err := i2c.Tx(0x18, w, r) |
| 16 | +``` |
| 17 | + |
| 18 | +Here we read 6 bytes starting with register 0x28. We'll get back to why we need to set the highest bit later (through `|0x80`). First let's take a look at the [datasheet](https://cdn-shop.adafruit.com/datasheets/LIS3DH.pdf#page=33) again: |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +So here we have 6 registers that contain the acceleration data in the 3 directions (X, Y, Z) where each direction takes up two registers. Since each register is 8 bits, and the data itself is 10 bits, two registers are needed for each direction. |
| 23 | + |
| 24 | +Decoding it is a little bit tricky though, if you're not used to bitwise operations: |
| 25 | + |
| 26 | +```go |
| 27 | + x := int16(r[0]) | int16(r[1])<<8 |
| 28 | + y := int16(r[2]) | int16(r[3])<<8 |
| 29 | + z := int16(r[4]) | int16(r[5])<<8 |
| 30 | + println("x, y, z:", x, y, z) |
| 31 | +```` |
| 32 | + |
| 33 | +This combines the two 8 bit values into a single 16-bit signed integer. If we take the X axis for example, the datasheet implies the first register (`OUT_X_L`) is low, since it has "L" in the name. (Yes, it's not the most helpful datasheet in this regard). The next register (`OUT_X_H`) contains the upper bits. So the way to convert them to a single 16-bit value is to convert them both individually to a 16-bit value (which keeps the lower bits but fills the missing bits with zero), then _shift_ the bits of `OUT_X_H` to the high position, and OR them together. This gives us a single 16-bit value, which is the signed X axis. |
| 34 | +
|
| 35 | +On the right you can see the values we read from the accelerometer. On a desktop computer you will see simulated values, as if the board is lying flat on a surface. However, if you are on a supported mobile device (phone, table), you will actually be able to see the real acceleration values of your device! |
| 36 | +
|
| 37 | +Some things to note: |
| 38 | +
|
| 39 | + * **Gravity** is also measured. This is just how physics works: when the sensor is lying still, it shows values as if it was accelerating at *g* speed away from earth. (Unless you're doing this in a zero-gravity environment, of course). There are various algorithms to filter out the gravity component, which will usually be needed for practical applications. |
| 40 | + * There is **noise** in the output. Every sensor has noise, this one included. The simulator includes the noise to be more realistic, and if you use such a sensor in a project you need to be aware that such noise exists. |
| 41 | + * The sensor can be put on a board in various ways, for example it can either be on top or at the bottom. This will of course impact the measurements. |
| 42 | + |
| 43 | +## What's up with that upper bit? |
| 44 | +
|
| 45 | +While for I2C devices you can read multiple sequential registers by writing the register address and then reading multiple values back, the LIS3DH is a bit different. By default it will return the same register on every read. To get the behavior we want, we need to set the highest bit of the register address to one, which gets the sensor to return values incrementally. |
| 46 | +
|
| 47 | +<script type="module"> |
| 48 | +let code = ` |
| 49 | +package main |
| 50 | +
|
| 51 | +import ( |
| 52 | + "machine" |
| 53 | + "time" |
| 54 | +) |
| 55 | +
|
| 56 | +func main() { |
| 57 | + i2c := machine.I2C0 |
| 58 | + err := i2c.Configure(machine.I2CConfig{ |
| 59 | + SCL: machine.SCL_PIN, |
| 60 | + SDA: machine.SDA_PIN, |
| 61 | + }) |
| 62 | + if err != nil { |
| 63 | + println("could not configure I2C:", err.Error()) |
| 64 | + return |
| 65 | + } |
| 66 | +
|
| 67 | + err = i2c.Tx(0x18, []byte{0x20, 0b0100_0111}, nil) |
| 68 | + if err != nil { |
| 69 | + println("got error while configuring the LIS3DH:", err.Error()) |
| 70 | + return |
| 71 | + } |
| 72 | +
|
| 73 | + for { |
| 74 | + // Read the acceleration data. |
| 75 | + w := []byte{0x28|0x80} |
| 76 | + r := make([]byte, 6) |
| 77 | + err := i2c.Tx(0x18, w, r) |
| 78 | + if err != nil { |
| 79 | + println("got error while reading values:", err.Error()) |
| 80 | + } else { |
| 81 | + x := int16(r[0]) | int16(r[1])<<8 |
| 82 | + y := int16(r[2]) | int16(r[3])<<8 |
| 83 | + z := int16(r[4]) | int16(r[5])<<8 |
| 84 | + println("x, y, z:", x, y, z) |
| 85 | + } |
| 86 | +
|
| 87 | + time.Sleep(time.Second) |
| 88 | + } |
| 89 | +} |
| 90 | +`; |
| 91 | +import {setupLIS3DH} from "/tour-lis3dh.js"; |
| 92 | +setupLIS3DH(code); |
| 93 | +</script> |
0 commit comments