|
|
|
|
|
|
|
It's time to try out the SPI driver that we wrote in the previous tutorial.
|
|
|
|
|
|
|
|
This example uses the [LIS3DSH](https://www.st.com/resource/en/datasheet/lis3dsh.pdf) accelerometer and temperature sensor that present on the STM32F407VG discovery board.
|
|
|
|
You can find an implementation for the accelerometer and the temperature sensor in `src/shared/sensors/LIS3DSH/LIS3DSH.h`.
|
|
|
|
Here we will only consider reading the temperature data.
|
|
|
|
|
|
|
|
> :warning: **As a suggestion keep the sensor's datasheet with you while following the tutorial and try to find in it all the things that here are mentioned.**
|
|
|
|
|
|
|
|
## Header file
|
|
|
|
We define the methods that we will be implemented in the `cpp` file:
|
|
|
|
- *init*: configure sensor driver. It returns a boolean indicating if the initialization was successful.
|
|
|
|
- *checkWhoAmI*: every sensor has a special only-read register that contains a number called `who_am_i`. This method checks that the `who_am_i` value is equal to the default one specified in the sensor's datasheet (which in this case is `63`). It returns a boolean indicating if the `who_am_i` value is correct.
|
|
|
|
- *getTemperature*: read the temperature from the sensor and return it. As described in the datasheet the temperature is an 8-bit 2-complement (`int8_t`) value with a resolution of 1 LSB/deg. It means that is can only output integer temperature values. Finally it is also told that reading zero from the sensor corresponds to 25 celsius degrees (so `TEMPERATURE_REF` is set to 25).
|
|
|
|
|
|
|
|
We also need two other memebers:
|
|
|
|
- *ODR*: it indicates the *output data rate* of the sensor.
|
|
|
|
- *BDU*: it indicates the *block data update* mode. This can be set to continuous mode (i.e. the sensor continuously produces samples) or we can configure the sensor so that it outputs a new sample only after the previous one has been read.
|
|
|
|
|
|
```cpp
|
|
```cpp
|
|
#pragma once
|
|
#pragma once
|
|
|
|
|
... | @@ -7,109 +26,178 @@ class TempSensor |
... | @@ -7,109 +26,178 @@ class TempSensor |
|
{
|
|
{
|
|
|
|
|
|
public:
|
|
public:
|
|
TempSensor(uint8_t odr = ODR_25_HZ,
|
|
TempSensor(uint8_t odr, uint8_t bdu);
|
|
uint8_t bdu = UPDATE_AFTER_READ_MODE)
|
|
|
|
: odr(odr), bdu(bdu)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool init()
|
|
bool init();
|
|
{
|
|
|
|
if (!checkWhoAmI())
|
|
|
|
{
|
|
|
|
TRACE("[TempSensor] Init failed \n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the output data rate and the BDU in CTRL_REG4
|
|
bool checkWhoAmI();
|
|
uint8_t ctrl_reg4_value = (odr << 4) | (bdu << 3);
|
|
|
|
spi_driver.write(CTRL_REG4, ctrl_reg4_value);
|
|
|
|
|
|
|
|
TRACE("[TempSensor] Init ok \n");
|
|
int8_t getTemperature();
|
|
return true; // correctly initialized
|
|
|
|
}
|
|
|
|
|
|
|
|
bool checkWhoAmI()
|
|
private:
|
|
{
|
|
|
|
uint8_t who_am_i = spi_driver.read(WHO_AM_I_REG);
|
|
|
|
|
|
|
|
TRACE("[TempSensor] WHO_AM_I value : %d \n", who_am_i);
|
|
SpiDriver spi_driver;
|
|
|
|
uint8_t odr;
|
|
|
|
uint8_t bdu;
|
|
|
|
|
|
if (who_am_i == WHO_AM_I_DEFAULT_VALUE)
|
|
const uint8_t WHO_AM_I_DEFAULT_VALUE = 63;
|
|
{
|
|
const uint8_t TEMPERATURE_REF = 25;
|
|
TRACE("[TempSensor] WHO_AM_I ok \n");
|
|
};
|
|
return true;
|
|
```
|
|
}
|
|
In order to configure the *ODR* and *BDU* values we can define two `enum` that contain all the possible values for those two variables. All these values can be found in the sensor's datasheet.
|
|
|
|
|
|
TRACE("[TempSensor] wrong WHO_AM_I \n");
|
|
```cpp
|
|
return false;
|
|
enum OutputDataRate : uint8_t
|
|
}
|
|
{
|
|
|
|
ODR_POWER_DOWN = 0, // 0000
|
|
|
|
ODR_3_125_HZ = 1, // 0001, 3.125 Hz
|
|
|
|
ODR_6_25_HZ = 2, // 0010, 6.25 Hz
|
|
|
|
ODR_12_5_HZ = 3, // 0011, 12.5 Hz
|
|
|
|
ODR_25_HZ = 4, // 0100
|
|
|
|
ODR_50_HZ = 5, // 0101
|
|
|
|
ODR_100_HZ = 6, // 0110, default value
|
|
|
|
ODR_400_HZ = 7, // 0111
|
|
|
|
ODR_800_HZ = 8, // 1000
|
|
|
|
ODR_1600_HZ = 9 // 1001
|
|
|
|
};
|
|
|
|
|
|
|
|
enum BlockDataUpdate : uint8_t
|
|
|
|
{
|
|
|
|
CONTINUOUS_UPDATE_MODE = 0, // continuous update of accelerometer data
|
|
|
|
UPDATE_AFTER_READ_MODE = 1 // values updated only when MSB and LSB are read
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
Finally in order to read the data from the sensor and to configure it we need some variables that indicate the sensor's registers addresses.
|
|
|
|
The registers that we need are:
|
|
|
|
- The register that contains the `who_am_i` value.
|
|
|
|
- The register from which we can read the temperature values.
|
|
|
|
- The `CTRL_REG4` which is needed in order to configure the *ODR* and *BDU* values.
|
|
|
|
|
|
|
|
The registers addresses, of course, are specified in the sensor's datasheet.
|
|
|
|
```cpp
|
|
|
|
enum Registers : uint8_t
|
|
|
|
{
|
|
|
|
WHO_AM_I_REG = 0x0F,
|
|
|
|
CTRL_REG4 = 0x20, // control register to set ODR and BDU
|
|
|
|
OUT_T = 0x0C // temperature output register
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
## Cpp file
|
|
|
|
Import the header file and in the constructor simply initialized the *odr* and *bdu* members:
|
|
|
|
```cpp
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "TempSensor.h"
|
|
|
|
|
|
int8_t getTemperature()
|
|
TempSensor::TempSensor(uint8_t odr, uint8_t bdu)
|
|
|
|
: odr(odr), bdu(bdu)
|
|
{
|
|
{
|
|
// the temperature is given as a 8-bits integer (in 2-complement)
|
|
|
|
// 1 LSB/deg - 8-bit resolution
|
|
|
|
// also, reading zero means 25 °C, so add the temperature reference
|
|
|
|
return spi_driver.read(OUT_T) + TEMPERATURE_REF;
|
|
|
|
}
|
|
}
|
|
|
|
```
|
|
|
|
#### Check WHO_AM_I value
|
|
|
|
In order to check the `who_am_i` value we can simply ask the SPI driver to read the `WHO_AM_I` register. If the returned value is the correct one, than we can return `true` (otherwise `false`).
|
|
|
|
```cpp
|
|
|
|
bool TempSensor::checkWhoAmI()
|
|
|
|
{
|
|
|
|
uint8_t who_am_i = spi_driver.read(WHO_AM_I_REG);
|
|
|
|
|
|
enum OutputDataRate : uint8_t
|
|
if (who_am_i == WHO_AM_I_DEFAULT_VALUE)
|
|
{
|
|
|
|
ODR_POWER_DOWN = 0, // 0000
|
|
|
|
ODR_3_125_HZ = 1, // 0001, 3.125 Hz
|
|
|
|
ODR_6_25_HZ = 2, // 0010, 6.25 Hz
|
|
|
|
ODR_12_5_HZ = 3, // 0011, 12.5 Hz
|
|
|
|
ODR_25_HZ = 4, // 0100
|
|
|
|
ODR_50_HZ = 5, // 0101
|
|
|
|
ODR_100_HZ = 6, // 0110, default value
|
|
|
|
ODR_400_HZ = 7, // 0111
|
|
|
|
ODR_800_HZ = 8, // 1000
|
|
|
|
ODR_1600_HZ = 9 // 1001
|
|
|
|
};
|
|
|
|
|
|
|
|
enum BlockDataUpdate : uint8_t
|
|
|
|
{
|
|
{
|
|
CONTINUOUS_UPDATE_MODE = 0, // continuous update of accelerometer data
|
|
return true;
|
|
UPDATE_AFTER_READ_MODE =
|
|
}
|
|
1 // values updated only when MSB and LSB are read (recommended)
|
|
|
|
};
|
|
return false;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
private:
|
|
#### Configure the sensor
|
|
enum Registers : uint8_t
|
|
During sensor's initialization, if the *checkWhoAmI* method returns `false`, then the initialization fails.
|
|
{
|
|
|
|
WHO_AM_I_REG = 0x0F,
|
|
To configure the *ODR* and *BDU* we have to set their values in the `CTRL_REG4` register of the sensor. We have to compute the value to be written to the register's address.
|
|
CTRL_REG4 = 0x20, // control register to set ODR and BDU
|
|
The `CTRL_REG4` register has the following 8-bits structure (LSB on the right and MSB on the left):
|
|
OUT_T = 0x0C // temperature output register
|
|
|
|
};
|
|
|
|
|
|
|
|
SpiDriver spi_driver;
|
|
| **ODR3** | **ODR2** | **ODR1** | **ODR0** | **BDU** | Zen | Yen | Xen |
|
|
|
|
|----------|----------|----------|----------|----------|------|------|------|
|
|
|
|
|
|
uint8_t odr; // output data rate (25 Hz)
|
|
As we can se we have to shift the *BDU* value by 3 positions to the left and the *ODR* value by 4 position to the left. We can achieve this result through a *bitwise OR*.
|
|
uint8_t bdu; // continuous mode or not (continuous update)
|
|
|
|
|
|
Finally we can write the resulting value to the `CTRL_REG4` register's address using the SPI driver.
|
|
|
|
|
|
const uint8_t WHO_AM_I_DEFAULT_VALUE = 63; // 00111111
|
|
```cpp
|
|
const uint8_t TEMPERATURE_REF = 25;
|
|
bool TempSensor::init()
|
|
};
|
|
{
|
|
|
|
if (!checkWhoAmI()) // wrong who_am_i value
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the output data rate and the BDU in CTRL_REG4
|
|
|
|
uint8_t ctrl_reg4_value = (odr << 4) | (bdu << 3);
|
|
|
|
spi_driver.write(CTRL_REG4, ctrl_reg4_value);
|
|
|
|
|
|
|
|
return true; // correctly initialized
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
#### Read temperature
|
|
|
|
In order to read the temperature sample the only things we need is to read the `OUT_T` register through the SPI driver, rembering to add the `TEMPERATURE_REF` value to all the samples:
|
|
|
|
```cpp
|
|
|
|
int8_t TempSensor::getTemperature()
|
|
|
|
{
|
|
|
|
// the temperature is given as a 8-bits integer (in 2-complement)
|
|
|
|
// 1 LSB/deg - 8-bit resolution
|
|
|
|
// also, reading zero means 25 °C, so add the temperature reference
|
|
|
|
return spi_driver.read(OUT_T) + TEMPERATURE_REF;
|
|
|
|
}
|
|
```
|
|
```
|
|
|
|
|
|
|
|
## Create your entrypoint
|
|
|
|
In `src/entrypoints` add a file called `test-tempsensor.cpp`
|
|
|
|
|
|
|
|
A simple entrypoint creates a `TempSensor` object and periodically reads the temperature value:
|
|
```cpp
|
|
```cpp
|
|
#include <Common.h>
|
|
#include <Common.h>
|
|
#include "TempSensor.h"
|
|
#include "TempSensor.h"
|
|
|
|
|
|
int main()
|
|
int main()
|
|
{
|
|
{
|
|
TempSensor temp_driver;
|
|
TempSensor temp_driver(TempSensor::OutputDataRate::ODR_100_HZ,
|
|
|
|
TempSensor::BlockDataUpdate::UPDATE_AFTER_READ_MODE);
|
|
|
|
|
|
if (temp_driver.init())
|
|
if (temp_driver.init())
|
|
{
|
|
{
|
|
while(true)
|
|
while (true)
|
|
{
|
|
{
|
|
TRACE("Temp : %d °C \n", temp_driver.getTemperature());
|
|
TRACE("Temp : %d °C \n", temp_driver.getTemperature());
|
|
|
|
|
|
Thread::sleep(2000);
|
|
delayMs(2000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
``` |
|
```
|
|
\ No newline at end of file |
|
|
|
|
|
## Compile with SBS
|
|
|
|
Let's write our dependencies section. We need `Debug.cpp` (for the *TRACE()* function) and the source code of the SPI driver:
|
|
|
|
```sh
|
|
|
|
[spi_driver]
|
|
|
|
Type: srcfiles
|
|
|
|
Files: src/shared/SpiDriver.cpp
|
|
|
|
src/shared/Debug.cpp # for the TRACE
|
|
|
|
```
|
|
|
|
And then specify the entrypoint section, including the dependencies:
|
|
|
|
```
|
|
|
|
[test-tempsensor]
|
|
|
|
Type: board
|
|
|
|
BoardId: stm32f407vg_stm32f4discovery
|
|
|
|
BinName: test-tempsensor
|
|
|
|
Include: %spi_driver
|
|
|
|
Defines: -DDEBUG
|
|
|
|
Main: test-tempsensor
|
|
|
|
```
|
|
|
|
|
|
|
|
## Run it!
|
|
|
|
Same as before: build the entrypoint with `python sbs -b test-tempsensor` and [flash the binary on the board](Flashing-on-a-Target-Board). |
|
|
|
\ No newline at end of file |