From 4a75665e19e2c4a522d1055f7953b190eeb5d005 Mon Sep 17 00:00:00 2001 From: Valerio Flamminii <valerio.flamminii@skywarder.eu> Date: Sat, 25 Nov 2023 12:54:59 +0100 Subject: [PATCH] my_LIS3DSH driver --- src/entrypoints/test-tempsensor.cpp | 71 ++++ src/shared/sensors/my_LIS3DSH/my_LIS3DSH.cpp | 310 ++++++++++++++++++ src/shared/sensors/my_LIS3DSH/my_LIS3DSH.h | 211 ++++++++++++ .../sensors/my_LIS3DSH/my_LIS3DSHData.h | 51 +++ src/shared/sensors/my_LIS3DSH/spi-driver.cpp | 141 ++++++++ src/shared/sensors/my_LIS3DSH/spi-driver.h | 65 ++++ 6 files changed, 849 insertions(+) create mode 100644 src/entrypoints/test-tempsensor.cpp create mode 100644 src/shared/sensors/my_LIS3DSH/my_LIS3DSH.cpp create mode 100644 src/shared/sensors/my_LIS3DSH/my_LIS3DSH.h create mode 100644 src/shared/sensors/my_LIS3DSH/my_LIS3DSHData.h create mode 100644 src/shared/sensors/my_LIS3DSH/spi-driver.cpp create mode 100644 src/shared/sensors/my_LIS3DSH/spi-driver.h diff --git a/src/entrypoints/test-tempsensor.cpp b/src/entrypoints/test-tempsensor.cpp new file mode 100644 index 000000000..5dd788070 --- /dev/null +++ b/src/entrypoints/test-tempsensor.cpp @@ -0,0 +1,71 @@ +/* Copyright (c) <2023> Skyward Experimental Rocketry + * Author: <Valerio flamminii> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <miosix.h> +#include <utils/Debug.h> +#include <sensors/my_LIS3DSH/my_LIS3DSH.h> +#include <sensors/my_LIS3DSH/my_LIS3DSHData.h> + +using namespace miosix; +using namespace Boardcore; + + +int main() +{ + + + LIS3DSH sensore(LIS3DSH::OutputDataRate::ODR_100_HZ, + LIS3DSH::BlockDataUpdate::UPDATE_AFTER_READ_MODE, + LIS3DSH::FullScale::FULL_SCALE_2G); + + // test getlasterror + /* + TRACE("sensore self-test: %d\n", sensore.selfTest()); + TRACE("lastError: %d\n\n", sensore.getLastError()); + + TRACE("sensore.init(): %d\n\n", sensore.init()); + + TRACE("sensore self-test: %d\n", sensore.selfTest()); + TRACE("lastError: %d\n", sensore.getLastError()); + */ + + if (sensore.init()) + { + + my_LIS3DSHData accel; + + while (true) + { + sensore.sample(); + accel = sensore.getLastSample(); + printf("timeStamp: %llu\n", accel.accelerationTimestamp); + TRACE("X: %f\n", accel.accelerationX); + TRACE("Y: %f\n", accel.accelerationY); + TRACE("Z: %f\n\n", accel.accelerationZ); + Thread::sleep(200); + } + + } + else TRACE("-----sensor initialisation failed -----\n"); + + return 0; +} diff --git a/src/shared/sensors/my_LIS3DSH/my_LIS3DSH.cpp b/src/shared/sensors/my_LIS3DSH/my_LIS3DSH.cpp new file mode 100644 index 000000000..31ccf3a31 --- /dev/null +++ b/src/shared/sensors/my_LIS3DSH/my_LIS3DSH.cpp @@ -0,0 +1,310 @@ +/* Copyright (c) <2023> Skyward Experimental Rocketry + * Author: <Valerio flamminii> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +#include <miosix.h> +#include <utils/Debug.h> +#include <drivers/timer/TimestampTimer.h> +#include <math.h> +#include "my_LIS3DSH.h" + +namespace Boardcore { + + + LIS3DSH::LIS3DSH(uint8_t odr = OutputDataRate::ODR_100_HZ, + uint8_t bdu = BlockDataUpdate::UPDATE_AFTER_READ_MODE, + uint8_t fullScale = FullScale::FULL_SCALE_2G) + : odr(odr), bdu(bdu), fullScale(fullScale) {} + + + bool LIS3DSH::init() { + + // check if the sensor is already initialized + if (initialized) + { + lastError = SensorErrors::ALREADY_INIT; + return false; + } + + // config spi-driver to initialise the communication + spi_driver.config(); + + // check who_am_i value + if(checkWhoAmI() == false) return false; + + // set the output data rate and the block-data-update in CTRL_REG4 + // enable X,Y and Z axis setting the LSB three bits. + // so it's possible reading both temperature and accelaration. + uint8_t ctrl_reg4_value = (odr << 4) | (bdu << 3) | 7; // 7 = 111b + + // set the full scale range possible values: 2g, 4g, 6g, 8g, 16g + uint8_t ctrl_reg5_value = fullScale << 3; + + // write values in control registers 4 and 5 + spi_driver.write(CTRL_REG5, ctrl_reg5_value); + spi_driver.write(CTRL_REG4, ctrl_reg4_value); + + // select the correct sensitivity + // for the specified full scale range + sensitivity = selectSensitivity(); + + initialized = true; + + return true; + } + + + bool LIS3DSH::checkWhoAmI() { + + // check the WHO_AM_I_REG register + uint8_t whoAmIValue = spi_driver.read(WHO_AM_I_REG); + + if (whoAmIValue == WHO_AM_I_DEFAULT_VALUE) return true; + else lastError = SensorErrors::INVALID_WHOAMI; + + return false; + } + + + int16_t LIS3DSH::combine(uint8_t msb, uint8_t lsb) { + return (msb << 8) | lsb; + } + + + float LIS3DSH::selectSensitivity() { + + float s; + switch(fullScale) { + + case(FULL_SCALE_2G): + s = sensitivityValues[FullScale::FULL_SCALE_2G]; + break; + + case(FULL_SCALE_4G): + s = sensitivityValues[FullScale::FULL_SCALE_4G]; + break; + + case(FullScale::FULL_SCALE_6G): + s = sensitivityValues[FullScale::FULL_SCALE_6G]; + break; + + case(FullScale::FULL_SCALE_8G): + s = sensitivityValues[FullScale::FULL_SCALE_8G]; + break; + + case(FullScale::FULL_SCALE_16G): + s = sensitivityValues[FullScale::FULL_SCALE_16G]; + break; + + default: + TRACE("Invalid full scale range given, using +/-2g"); + this->fullScale = FullScale::FULL_SCALE_2G; + s = sensitivityValues[FullScale::FULL_SCALE_2G]; + break; + } + return s; + } + + + AccelerometerData LIS3DSH::readAccelData() + { + + AccelerometerData accelData; + + // read the sensor's status register + uint8_t status = spi_driver.read(STATUS); + + if (status & 0x08) + { // bit 3 of status set to 1 (new data available) + if (status & 0x80) + { // bit 7 of status set to 1 (some data overwritten) + + // getting timestamp + accelData.accelerationTimestamp = TimestampTimer::getTimestamp(); + + // read acceleration X axis + int8_t accel_L = spi_driver.read(OUT_X_L); + int8_t accel_H = spi_driver.read(OUT_X_H); + accelData.accelerationX = static_cast<float>(combine(accel_H, accel_L)) * sensitivity; + + // read acceleration on Y + accel_L = spi_driver.read(OUT_Y_L); + accel_H = spi_driver.read(OUT_Y_H); + accelData.accelerationY = static_cast<float>(combine(accel_H, accel_L)) * sensitivity; + + // read acceleration on Z + accel_L = spi_driver.read(OUT_Z_L); + accel_H = spi_driver.read(OUT_Z_H); + accelData.accelerationZ = static_cast<float>(combine(accel_H, accel_L)) * sensitivity; + + // reset last error of the sensor + lastError = SensorErrors::NO_ERRORS; + } + } + else lastError = SensorErrors::NO_NEW_DATA; + + return accelData; + } + + + my_LIS3DSHData LIS3DSH::sampleImpl() + { + // check if the sensor is initialized + if (!initialized) + { + lastError = SensorErrors::NOT_INIT; + return lastSample; + } + + AccelerometerData accelData = readAccelData(); + + if (lastError != SensorErrors::NO_ERRORS) return lastSample; + else { + return my_LIS3DSHData(accelData); + } + } + + + bool LIS3DSH::selfTest() + { + // check if the sensor is initialized + if (!initialized) + { + lastError = SensorErrors::NOT_INIT; + return false; + } + + const uint8_t numSamples = 5; // number of samples to be used + // vectors for storing samples, both + // in self-test and no-self-test modes + float X_ST[numSamples] = {0}; + float Y_ST[numSamples] = {0}; + float Z_ST[numSamples] = {0}; + float X_NO_ST[numSamples] = {0}; + float Y_NO_ST[numSamples] = {0}; + float Z_NO_ST[numSamples] = {0}; + // vectors containing avg values for each axis + float AVG_ST[3] = {0}; // one element per axis + float AVG_NO_ST[3] = {0}; // one element per axis + + // set output data rate to 50 hz + uint8_t ctrlReg4Value = (OutputDataRate::ODR_100_HZ << 4) | + (BlockDataUpdate::UPDATE_AFTER_READ_MODE << 3) | + (7 << 0); + + + spi_driver.write(CTRL_REG4, ctrlReg4Value); + + + // set full scale to default value +/-2g + // enable the self-test mode with positive sign + uint8_t ctrlReg5Value = (FullScale::FULL_SCALE_2G << 3) | (1 << 1); + + spi_driver.write(CTRL_REG5, ctrlReg5Value); + + // read samples in self-test positive sign mode + for (uint8_t i = 0; i < numSamples; i++) + { + AccelerometerData accelData = readAccelData(); + X_ST[i] = accelData.accelerationX; + Y_ST[i] = accelData.accelerationY; + Z_ST[i] = accelData.accelerationZ; + miosix::Thread::sleep(10); + } + + // reset the self-test bits + ctrlReg5Value &= ~(3 << 1); + // normal mode with full scale range +/-2g + ctrlReg5Value |= (FULL_SCALE_2G << 3); + + spi_driver.write(CTRL_REG5, ctrlReg5Value); + + // read samples in normal mode + for (uint8_t i = 0; i < numSamples; i++) + { + AccelerometerData accelData = readAccelData(); + X_NO_ST[i] = accelData.accelerationX; + Y_NO_ST[i] = accelData.accelerationY; + Z_NO_ST[i] = accelData.accelerationZ; + miosix::Thread::sleep(10); + } + // compute averages vectors: + // they contain one element for each axis + // (position 0 for x, 1 for y and 2 for z) + // AVG_ST : for self-test samples + // AVG_NO_ST : for normal mode samples + for (uint8_t i = 0; i < numSamples; i++) + { + AVG_ST[0] += X_ST[i]; + AVG_ST[1] += Y_ST[i]; + AVG_ST[2] += Z_ST[i]; + AVG_NO_ST[0] += X_NO_ST[i]; + AVG_NO_ST[1] += Y_NO_ST[i]; + AVG_NO_ST[2] += Z_NO_ST[i]; + } + for (uint8_t i = 0; i < 3; i++) + { + AVG_ST[i] /= numSamples; + AVG_NO_ST[i] /= numSamples; + } + + // Reset registers values with the ones + // specified in the constructor: + // set the output data rate value in CTRL_REG4 + ctrlReg4Value = (odr << 4) | (bdu << 3) | (7 << 0); + + spi_driver.write(CTRL_REG4, ctrlReg4Value); + + // set the full scale value in CTRL_REG5 + ctrlReg5Value = (fullScale << 3); // normal mode + + spi_driver.write(CTRL_REG5, ctrlReg5Value); + + float delta[3] = {0}; + for (uint8_t i = 0; i < 3; i++) + { + delta[i] = fabs(AVG_NO_ST[i] - AVG_ST[i]); + } + + /* + TRACE("Selftest: delta[x] = {%f}, delta[y] = {%f}, delta[z] = {%f}", + delta[0], delta[1], delta[2]); + */ + + // check that the averages differences + // do not exceed maximum tolerance + if ((delta[0] > + SELF_TEST_DIFF_X_Y + SELF_TEST_DIFF_X_Y * SELF_TEST_TOLERANCE) || + (delta[1] > + SELF_TEST_DIFF_X_Y + SELF_TEST_DIFF_X_Y * SELF_TEST_TOLERANCE) || + (delta[2] > + SELF_TEST_DIFF_Z + SELF_TEST_DIFF_Z * SELF_TEST_TOLERANCE)) + { + lastError = SensorErrors::SELF_TEST_FAIL; + return false; + } + + return true; + } + + + +} // namespace Bordcore \ No newline at end of file diff --git a/src/shared/sensors/my_LIS3DSH/my_LIS3DSH.h b/src/shared/sensors/my_LIS3DSH/my_LIS3DSH.h new file mode 100644 index 000000000..111aac96e --- /dev/null +++ b/src/shared/sensors/my_LIS3DSH/my_LIS3DSH.h @@ -0,0 +1,211 @@ +/* Copyright (c) <2023> Skyward Experimental Rocketry + * Author: <Valerio flamminii> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <sensors/Sensor.h> +#include "my_LIS3DSHData.h" +#include <drivers/timer/TimestampTimer.h> +#include "spi-driver.h" + +namespace Boardcore { + + /** + * @brief Driver class for stm32f407vg discovery on-board 3-axis + * accelerometer. + * The sensor is connected to SPI1 using the + * following GPIOs: PA5 : clock + * PA6 : miso + * PA7 : mosi + * PE3 : chip select + */ + class LIS3DSH : public Sensor<my_LIS3DSHData> { + + public: + + /** + * @brief constructor + * @param odr output data rate, default value = 100 hz + * @param bdu block data update mode. + * can be continuos or not-continuos mode, default value is update after data + * has been read (BDU=1). + * @param fullScale full scale range value (from +/-2g up to +/-16g) + */ + LIS3DSH(uint8_t odr, uint8_t bdu, uint8_t fullScale); + + /** + * @brief Initialize the sensor. + * + * @return boolean value indicating whether the operation succeded or not + */ + bool init() override; + + /** + * @brief Check if the sensor is working properly. + * + * @return boolean indicating whether the sensor is correctly working or not + */ + bool selfTest() override; + + /** + * @brief Output data rate allowed values (4 bits). + */ + enum OutputDataRate : uint8_t { + + // to make the sensor working odr must be set (don't keep the default one) + ODR_POWER_DOWN = 0, // the sensor is turned off (default value) + ODR_3_125_HZ = 1, + ODR_6_25_HZ = 2, + ODR_12_5_HZ = 3, + ODR_25_HZ = 4, + ODR_50_HZ = 5, + ODR_100_HZ = 6, + ODR_400_HZ = 7, + ODR_800_HZ = 8, + ODR_1600_HZ = 9 + + }; + + /** + * @brief Block data update allowed modes (1 bit). + */ + enum BlockDataUpdate : uint8_t { + + CONTINUOS_UPDATE_MODE = 0, // continuos sensor data updating + UPDATE_AFTER_READ_MODE = 1 // update data after reading process + }; + + /** + * @brief Full scale range allowed values (3 bits). + */ + enum FullScale + { + FULL_SCALE_2G = 0, // 000, +/- 2g + FULL_SCALE_4G = 1, // 001, +/- 4g + FULL_SCALE_6G = 2, // 010, +/- 6g + FULL_SCALE_8G = 3, // 011, +/- 8g + FULL_SCALE_16G = 4 // 100 +/- 16g + }; + + + private: + + /** + * @brief Read new data from the accelerometer. + * Accelerations are returned in g. + * @return accelerations data, if there are new data available otherwise it returns the last sample. + */ + my_LIS3DSHData sampleImpl() override; + + /** + * @brief Read accelerometer data. + * + * @return the read accelerometer sample + */ + AccelerometerData readAccelData(); + + /** + * @brief Check that the WHO_AM_I register + * contains the correct value. + * + * @return boolean value indicating whether the value read + * from the WHO_AM_I register is correct or not + */ + bool checkWhoAmI(); + + /** + * @brief Given the requested full scale range, select the correct + * sensitivity value. + * + * @return the sensitivity value corresponding to the requested full scale + * range + */ + float selectSensitivity(); + + /** + * @brief Combine low and high bits in a single number. + * + * @param msb the most significatn bits + * @param lsb the least significant bits + * @return MSB and LSB combined in one value + */ + int16_t combine(uint8_t msb, uint8_t lsb); + + /** + * @brief Registers' addresses definition. + */ + enum REG : uint8_t { + + WHO_AM_I_REG = 0x0F, // contains the who_am_i number to be checked + + CTRL_REG1 = 0x21, // state Machine 1 interrupt configuration register + CTRL_REG2 = 0x22, // state Machine 2 interrupt configuration register + CTRL_REG3 = 0x23, // interrupt handling + CTRL_REG4 = 0x20, // control register 4, to set odr and bdu parameters + CTRL_REG5 = 0x24, // control register 5, to set the full scale 2,4,6,8,16g + CTRL_REG6 = 0x25, // fifo structure handling + + // status register + STATUS = 0x27, + + // accelerometer output registers + // for x, y and z axis + // (low and high bits in separate registers) + OUT_X_L = 0x28, + OUT_X_H = 0x29, + OUT_Y_L = 0x2A, + OUT_Y_H = 0x2B, + OUT_Z_L = 0x2C, + OUT_Z_H = 0x2D, + + // temperature output data register + OUT_T = 0x0C + + }; + + SpiDriver spi_driver; + + bool initialized = false; // whether the sensor has been initialized or not + + uint8_t odr; // output data rate, default = 100hz + uint8_t bdu; // block data update mode: continuous or block after update + uint8_t fullScale; // full scale range value default +-2g + + /** + * @brief Sensitivity values corresponding to full scale range allowed + * values. + */ + const float sensitivityValues[5] = {0.06, 0.12, 0.18, 0.24, 0.73}; + + float sensitivity = sensitivityValues[FullScale::FULL_SCALE_2G]; // default sensitivity value of +-2g scale + + const uint8_t WHO_AM_I_DEFAULT_VALUE = 63; + + // constants values needed for the self-test of the sensor + const float SELF_TEST_DIFF_X_Y = 140.0f; // 140 mg + const float SELF_TEST_DIFF_Z = 590.0f; // 590 mg + const float SELF_TEST_TOLERANCE = 0.3f; + + }; + + +} // namespace boardcore \ No newline at end of file diff --git a/src/shared/sensors/my_LIS3DSH/my_LIS3DSHData.h b/src/shared/sensors/my_LIS3DSH/my_LIS3DSHData.h new file mode 100644 index 000000000..c53dd56be --- /dev/null +++ b/src/shared/sensors/my_LIS3DSH/my_LIS3DSHData.h @@ -0,0 +1,51 @@ +/* Copyright (c) <2023> Skyward Experimental Rocketry + * Author: <Valerio flamminii> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include<sensors/SensorData.h> + + +namespace Boardcore { + + /** + * @brief my_LIS3DSHData is a data struct same as struct Boardcore::AccelerometerData in SensorData.h + */ + struct my_LIS3DSHData : public AccelerometerData { + + /** + * @brief constructor + */ + my_LIS3DSHData() : AccelerometerData {0, 0.0, 0.0, 0.0} {} + + /** + * @brief constructor + * @param acc AccelerometerData struct + */ + my_LIS3DSHData(AccelerometerData acc) + : AccelerometerData{acc.accelerationTimestamp, acc.accelerationX, + acc.accelerationY, acc.accelerationZ} {} + + }; + + +} // namespace boardcore \ No newline at end of file diff --git a/src/shared/sensors/my_LIS3DSH/spi-driver.cpp b/src/shared/sensors/my_LIS3DSH/spi-driver.cpp new file mode 100644 index 000000000..0690242e1 --- /dev/null +++ b/src/shared/sensors/my_LIS3DSH/spi-driver.cpp @@ -0,0 +1,141 @@ +/* Copyright (c) <2023> Skyward Experimental Rocketry + * Author: <Valerio flamminii> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +#include <miosix.h> +#include <utils/Debug.h> +#include "spi-driver.h" + + +namespace Boardcore { + + + SpiDriver::SpiDriver() {} + + void SpiDriver::config(){ + + // slave select pin is active low during communication + + cs::mode(miosix::Mode::OUTPUT); // set the pin with a low impedance (output) + + cs::high(); // pin state: HIGH + + // sck, miso and mosi in ALTERNATE mode so they won't be affettected + // by their GPIO function and will be used just for SPI. + sck::mode(miosix::Mode::ALTERNATE); + miso::mode(miosix::Mode::ALTERNATE); + mosi::mode(miosix::Mode::ALTERNATE); + + // setting AF5 in order to use SPI1 on portA pins. + sck::alternateFunction(5); + miso::alternateFunction(5); + mosi::alternateFunction(5); + + //TRACE("[SpiDriver] GPIOs configured \n"); + + // now disable interrupts in order to prioritize the SPI communication. + // In a "local" scope so, at the end, the objects inside will be deallocated. + { + // now disable interrupts in order to prioritize the SPI communication. + miosix::FastInterruptDisableLock dLock; + + // enabling SPI peripheral by setting the corresponding bit high in + // the "clock gating register", which is gonna start the peripheral. + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; + RCC_SYNC(); + } + + /* let's set some protocol configurations (by setting high these under-written bits): + - slave select pin is controlled by the firmware. (SPI_CR1_SSM) + - internal slave select set (SPI_CR1_SSI) + - myDiscovery as an SPI master device (SPI_CR1_MSTR) + - set clock division factor to 32 (84MHz / 32 = 2.625MHz) (SPI_CR1_BR_2) + - finally, enable the entire SPI peripheral (SPI_CR1_SPE) + */ + SPI1->CR1 = SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR | SPI_CR1_BR_2 | SPI_CR1_SPE; + + + //TRACE("[SpiDriver] SPI1 configured \n"); + } + + + uint8_t SpiDriver::sendRecv(uint8_t data){ + SPI1->DR = data; // send data on the SPI bus + waitBusy(); // wait till the comunication has ended + return SPI1->DR; // return the byte received + } + + void SpiDriver::waitBusy() { + + // wait until the RX Not Empty flag in peripheral's status + // register (SPI1->SR) is no longer LOW. This means that the communication is over and + // the data received by the slave device are available in the SP1->DR (SPI data register). + while((SPI1->SR & SPI_SR_RXNE) == 0) {;} + + } + + uint8_t SpiDriver::read(uint8_t addr) { + + // to read a value in one of the slave's registers, we need to: + // 1 - pull-down the slave select pin (cs) + // 2 - send the register's address with the eigth bit HIGH (reading access) + // 3 - send a "jummy" value over the SPI bus just in order to receive our needed data + // 4 - pull-up the slave select pin (cs) to end the transaction. + + cs::low(); + + sendRecv(addr | 0x80); + + uint8_t data = sendRecv(0x00); // "jummy" value just to shift the register. + + cs::high(); + + miosix::delayUs(10); // wait 10 micros + + return data; + } + + void SpiDriver::write(uint8_t addr, uint8_t value) { + + // to write a value in one of slave's register or address, we need to: + // 1 - pull-down the slave select pin (cs) + // 2 - send the address/ or register's address that we need to write. + // 3 - send the data to be written. + // 4 - pull-up the slave select pin (cs) to end the transaction. + + // active communication with the slave + cs::low(); + + // send address + sendRecv(addr); + + // send value (data) + sendRecv(value); + + // cs HIGH to end the transaction over SPI bus. + cs::high(); + + // wait 10 microseconds + miosix::delayUs(10); + } + + +} // namespace Boardcore \ No newline at end of file diff --git a/src/shared/sensors/my_LIS3DSH/spi-driver.h b/src/shared/sensors/my_LIS3DSH/spi-driver.h new file mode 100644 index 000000000..93b881716 --- /dev/null +++ b/src/shared/sensors/my_LIS3DSH/spi-driver.h @@ -0,0 +1,65 @@ +/* Copyright (c) <2023> Skyward Experimental Rocketry + * Author: <Valerio flamminii> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + + +#include <miosix.h> + + +namespace Boardcore { + + // define GPIO pins 5,6,7,3 as "sck", "miso", "mosi" and "cs" + typedef miosix::Gpio<GPIOA_BASE, 5> sck; // serial clock line pin + typedef miosix::Gpio<GPIOA_BASE, 6> miso; // master input slave output pin + typedef miosix::Gpio<GPIOA_BASE, 7> mosi; // master output slave input pin + typedef miosix::Gpio<GPIOE_BASE, 3> cs; // slave-select pin + + /* + @brief SPI driver for dev-board: STM32F407VG discovery + */ + class SpiDriver { + + public: + + // constructor + SpiDriver(); + + // config spi-peripheral + void config(); + + // read by SPi bus + uint8_t read(uint8_t addr); + + // write on SPI bus + void write(uint8_t addr, uint8_t value); + + + private: + + // trade a byte on SPI bus + uint8_t sendRecv(uint8_t data); + + // wait until SPI bus is free + void waitBusy(); + }; + + +} // namespace boardcore \ No newline at end of file -- GitLab