diff --git a/.gitignore b/.gitignore index 1f62fb87aeac8e9a7e9cfb4572dc49a2a4bee886..be91b21147e8842750043ac7982987bb86bfb2d4 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ build store.json scripts/homeone/event_header_generator/generated/ +core diff --git a/sbs.conf b/sbs.conf index 58769fb570789f0dd4485b02f5f62889560abdaf..1bc18d5bd1b64c225b4da538fc7a34a931d2e05d 100644 --- a/sbs.conf +++ b/sbs.conf @@ -114,6 +114,11 @@ Files: src/shared/scheduler/TaskScheduler.cpp Type: srcfiles Files: src/shared/drivers/pwm/pwm.cpp +[spi] +Type: srcfiles +Files: src/shared/drivers/spi/SPIBus.cpp + src/shared/drivers/spi/SPITransaction.cpp + [i2c] Type: srcfiles Files: src/shared/drivers/i2c/stm32f2_f4_i2c.cpp @@ -159,12 +164,16 @@ Files: src/shared/utils/testutils/TestHelper.cpp [tests] Type: srcfiles -Files: src/tests/catch/test-eventbroker.cpp - src/tests/catch/test-circularbuffer.cpp - src/tests/catch/test-aero.cpp +Files: src/tests/catch/test-aero.cpp src/tests/catch/test-buttonhandler.cpp + src/tests/catch/test-circularbuffer.cpp + src/tests/catch/test-eventbroker.cpp + src/tests/catch/test-hardwaretimer.cpp + #src/tests/catch/test-kalman.cpp + src/tests/catch/test-matrix.cpp src/tests/catch/test-packetqueue.cpp - + src/tests/catch/spidriver/test-spidriver + #-------------------------------# # Entrypoints # @@ -207,9 +216,9 @@ Main: kernel-testsuite [tests-catch] Type: test -BoardId: stm32f429zi_skyward_homeone +BoardId: stm32f429zi_stm32f4discovery BinName: tests-catch -Include: %tests %shared %test-utils +Include: %tests %shared %test-utils %spi Defines: Main: catch/catch-tests-entry @@ -271,6 +280,7 @@ Main: test-pinobserver ## Drivers + [test-dsgamma] Type: test BoardId: stm32f429zi_stm32f4discovery @@ -303,14 +313,6 @@ Include: %shared Defines: Main: drivers/test-lsm -[test-timer] -Type: test -BoardId: stm32f429zi_stm32f4discovery -BinName: test-timer -Include: %shared -Defines: -DDEBUG -Main: drivers/test-timer - [test-canbus] Type: test BoardId: stm32f429zi_stm32f4discovery @@ -363,7 +365,7 @@ Main: drivers/test-mavlink Type: test BoardId: stm32f429zi_skyward_death_stack BinName: xbee-bitrate -Include: %shared %xbee +Include: %shared %xbee %spi Defines: -DDEBUG -DSDRAM_ISSI Main: misc/xbee-bitrate @@ -371,7 +373,7 @@ Main: misc/xbee-bitrate Type: test BoardId: stm32f429zi_skyward_death_stack BinName: xbee-send-rcv -Include: %shared %xbee +Include: %shared %xbee %spi Defines: -DDEBUG Main: misc/xbee-send-rcv @@ -379,7 +381,7 @@ Main: misc/xbee-send-rcv Type: test BoardId: stm32f429zi_skyward_death_stack BinName: xbee-time-to-send -Include: %shared %xbee +Include: %shared %xbee %spi Defines: -DDEBUG Main: misc/xbee-time-to-send @@ -391,21 +393,14 @@ Include: %shared Defines: Main: drivers/test-ad7994 -[test-matrix] -Type: test -BoardId: stm32f429zi_stm32f4discovery -BinName: test-matrix -Include: %shared -Defines: -DSTANDALONE_CATCH1_TEST -Main: catch/test-matrix -[test-kalman] -Type: test -BoardId: stm32f429zi_stm32f4discovery -BinName: test-kalman -Include: %shared -Defines: -DSTANDALONE_CATCH1_TEST -Main: catch/test-kalman +# [test-circularbuffer] +# Type: test +# BoardId: stm32f429zi_stm32f4discovery +# BinName: test-circularbuffer +# Include: %shared %test-utils +# Defines: -DSTANDALONE_CATCH1_TEST +# Main: catch/test-circularbuffer #[test-eventbroker] #Type: test @@ -415,13 +410,6 @@ Main: catch/test-kalman #Defines: -DSTANDALONE_CATCH1_TEST #Main: catch/test-eventbroker -[test-circularbuffer] -Type: test -BoardId: stm32f429zi_stm32f4discovery -BinName: test-circularbuffer -Include: %shared -Defines: -DSTANDALONE_CATCH1_TEST -Main: catch/test-circularbuffer [test-tempSensor] Type: test @@ -475,10 +463,18 @@ Main: drivers/calibrate-mpu9250 Type: test BoardId: stm32f429zi_skyward_death_stack BinName: test-ms5803 -Include: %shared +Include: %shared %spi Defines: Main: drivers/test-ms5803 +[test-l3gd20] +Type: test +BoardId: stm32f429zi_stm32f4discovery +BinName: test-l3gd20 +Include: %shared %spi +Defines: +Main: drivers/test-l3gd20 + [test-rls] Type: test BoardId: stm32f429zi_skyward_death_stack diff --git a/src/shared/drivers/HardwareTimer.h b/src/shared/drivers/HardwareTimer.h index f983b78a4d7245b062aa6281a5f497bbd4c72d63..cb82f77b63815471b8cd60cb673be7ec53b65afa 100644 --- a/src/shared/drivers/HardwareTimer.h +++ b/src/shared/drivers/HardwareTimer.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2018 Skyward Experimental Rocketry +/** + * Copyright (c) 2018-2019 Skyward Experimental Rocketry * Authors: Luca Erbetta * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -14,26 +14,18 @@ * * 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 + * 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. */ -#ifndef SRC_SHARED_DRIVERS_HARDWARETIMER_H -#define SRC_SHARED_DRIVERS_HARDWARETIMER_H +#pragma once -#include <kernel/scheduler/scheduler.h> #include <miosix.h> -#include <cassert> -#include "Debug.h" -#if !defined _ARCH_CORTEXM4_STM32F4 -#error "Unsupported architecture!" -#endif - -using miosix::FastInterruptDisableLock; +#include <type_traits> class TimerUtils { @@ -91,57 +83,50 @@ public: } }; -template <typename T, unsigned Tim> +/** + * @brief Class for handling Hardware Timers and perform time unit conversions + * + * @tparam Type type to use to access timer register (uint32_t if 32 bit timer, + * uint16_t if 16 bit timer) + */ +template <typename Type> class HardwareTimer { - using TimerType = HardwareTimer<T, Tim>; - - static_assert(std::is_same<T, uint32_t>::value || - std::is_same<T, uint16_t>::value, - "Timer output type must be either uint16_t or uint32_t"); - - static_assert(!((Tim == 2 || Tim == 5) && std::is_same<T, uint16_t>::value), - "Tim2 and Tim5 are 32 bit timers!"); + static_assert(std::is_same<Type, uint32_t>::value || + std::is_same<Type, uint16_t>::value, + "Type must be either uint32_t or uint16_t."); public: - inline static TimerType& instance() - { - static TimerType timer; - return timer; - } - - inline T start() + /** + * @brief Creates a new HardwareTimer instance + * + * @param timer The timer to use (pointer to timer registers struct) + * @param psc_input_freq Input frequency of the timer's prescaler, see + * TimerUtils::getPrescalerInputFrequency() + */ + HardwareTimer(TIM_TypeDef* timer, unsigned int psc_input_freq) + : tim(timer), prescaler_freq(psc_input_freq) { - if (!ticking) - { - ticking = true; - TIM->CNT = 0; // Reset the counter - - TIM->CR1 |= TIM_CR1_CEN; - return 0; - } - else - { - return tick(); - } } - inline T tick() { return TIM->CNT; } - - inline T stop() - { - if (ticking) - { - T tick = TIM->CNT; - TIM->CR1 &= ~TIM_CR1_CEN; - ticking = false; - return tick; - } + /** + * @brief Starts the timer returns the current tick + * + * @return Current tick + */ + Type start(); - return 0; - } + /** + * @brief Stops the timer if already started + * + * @return Current tick or 0 if already stopped + */ + Type stop(); - bool isTicking() { return ticking; } + /** + * @brief Returns current tick + */ + Type tick(); /** * @brief Converts from ticks to microseconds, using the current prescaler @@ -150,11 +135,7 @@ public: * @param ticks * @return float */ - float toMicroSeconds(T ticks) - { - return (1.0f * ticks * 1000000 * (1 + prescaler)) / - (float)TimerUtils::getPrescalerInputFrequency(clk); - } + float toMicroSeconds(Type ticks); /** * @brief Converts from ticks to milliseconds, using the current prescaler @@ -163,11 +144,7 @@ public: * @param ticks * @return float */ - float toMilliSeconds(T ticks) - { - return (1.0f * ticks * 1000 * (1 + prescaler)) / - (float)TimerUtils::getPrescalerInputFrequency(clk); - } + float toMilliSeconds(Type ticks); /** * @brief Converts from ticks to seconds, using the current prescaler @@ -176,154 +153,129 @@ public: * @param ticks * @return float */ - float toSeconds(T ticks) - { - return (1.0f * ticks * (1 + prescaler)) / - (float)TimerUtils::getPrescalerInputFrequency(clk); - } + float toSeconds(Type ticks); - /*static float toSeconds(T tick) {} - static float toMilliseconds(T tick) {} - static float toMicroSeconds(T tick) {}*/ + /** + * @brief Returns the current resolution of the timer in microseconds. + * @return Resolution in microseconds + */ + float getResolution(); + + /** + * @brief Returns the maximum time the timer can measure before restarting + * from 0, in seconds. + * + * @return Maximum timer duration in seconds. + */ + float getMaxDuration(); /** * @brief Sets the prescaler value. * The tick frequency is defined as the clock frequency value of the timer * divided by the prescaler. See the datasheet for further information. */ - void setPrescaler(uint16_t prescaler) - { - // reset(); - TIM->PSC = prescaler; - TIM->EGR = - TIM_EGR_UG; // Send an update event to load the new prescaler - // value - this->prescaler = prescaler; - } + void setPrescaler(uint16_t prescaler); /** * @brief Set the auto reload value. * The auto reload value is the maximum value the timer can count to. * After this value is reached, the timers counts back from 0. */ - void setAutoReload(T auto_reload) - { - if (ticking) - { - stop(); - } - this->auto_reload = auto_reload; - TIM->ARR = auto_reload; - - TIM->EGR = - TIM_EGR_UG; // Send an update event to load the new auto-reload - } + void setAutoReload(Type auto_reload); private: - HardwareTimer() + TIM_TypeDef* tim; + unsigned prescaler_freq; + + bool ticking = false; + uint16_t prescaler = 0; + Type auto_reload = + static_cast<Type>(-1); // Max value of Type (Type is unsigned) +}; + +template <typename Type> +inline Type HardwareTimer<Type>::start() +{ + if (!ticking) { - switch (Tim) - { - case 1: - TIM_EN = RCC_APB2ENR_TIM1EN; - TIM = TIM1; - break; - default: // Use TIM2 as default - TRACE("Wrong timer selected. Using TIM2.\n"); - case 2: - TIM_EN = RCC_APB1ENR_TIM2EN; - TIM = TIM2; - break; - case 3: - TIM_EN = RCC_APB1ENR_TIM3EN; - TIM = TIM3; - break; - case 4: - TIM_EN = RCC_APB1ENR_TIM4EN; - TIM = TIM4; - break; - case 5: - TIM_EN = RCC_APB1ENR_TIM5EN; - TIM = TIM5; - break; - case 6: - TIM_EN = RCC_APB1ENR_TIM6EN; - TIM = TIM6; - break; - case 7: - TIM_EN = RCC_APB1ENR_TIM7EN; - TIM = TIM7; - break; - case 8: - TIM_EN = RCC_APB2ENR_TIM8EN; - TIM = TIM8; - break; - case 9: - TIM_EN = RCC_APB2ENR_TIM9EN; - TIM = TIM9; - break; - case 10: - TIM_EN = RCC_APB2ENR_TIM10EN; - TIM = TIM10; - break; - case 11: - TIM_EN = RCC_APB2ENR_TIM11EN; - TIM = TIM11; - break; - case 12: - TIM_EN = RCC_APB1ENR_TIM12EN; - TIM = TIM12; - break; - case 13: - TIM_EN = RCC_APB1ENR_TIM13EN; - TIM = TIM13; - break; - case 14: - TIM_EN = RCC_APB1ENR_TIM14EN; - TIM = TIM14; - break; - } + ticking = true; + tim->CNT = 0; // Reset the counter - if (Tim == 1 || (Tim >= 8 && Tim <= 11)) - { - clk = TimerUtils::InputClock::APB2; + tim->CR1 |= TIM_CR1_CEN; + return 0; + } + else + { + return tick(); + } +} - FastInterruptDisableLock dLock; - RCC->APB2ENR |= TIM_EN; - RCC_SYNC(); - } - else - { - clk = TimerUtils::InputClock::APB1; +template <typename Type> +inline Type HardwareTimer<Type>::tick() +{ + return tim->CNT; +} - FastInterruptDisableLock dLock; - RCC->APB1ENR |= TIM_EN; - RCC_SYNC(); - } +template <typename Type> +inline Type HardwareTimer<Type>::stop() +{ + if (ticking) + { + tim->CR1 &= ~TIM_CR1_CEN; + ticking = false; + return tim->CNT; + } - // Reset control registers - TIM->CR1 = 0; - TIM->CR2 = 0; + return 0; +} - auto_reload = static_cast<T>(-1); // Max value of T (unsigned int) - TIM->ARR = auto_reload; +template <typename Type> +void HardwareTimer<Type>::setPrescaler(uint16_t prescaler) +{ + this->prescaler = prescaler; + tim->PSC = prescaler; + tim->EGR = TIM_EGR_UG; // Send an update event to load the new prescaler + // value +} + +template <typename Type> +void HardwareTimer<Type>::setAutoReload(Type auto_reload) +{ + this->auto_reload = auto_reload; + tim->ARR = auto_reload; - prescaler = 0; - TIM->PSC = prescaler; + tim->EGR = TIM_EGR_UG; // Send an update event to load the new auto-reload +} - TIM->EGR = - TIM_EGR_UG; // Send an update event to load the new prescaler and - // auto reload values - } +template <typename Type> +float HardwareTimer<Type>::toMicroSeconds(Type ticks) +{ + return (1.0f * ticks * 1000000 * (1 + prescaler)) / prescaler_freq; +} - TIM_TypeDef* TIM; - uint32_t TIM_EN; - TimerUtils::InputClock clk; +template <typename Type> +float HardwareTimer<Type>::toMilliSeconds(Type ticks) +{ + return (1.0f * ticks * 1000 * (1 + prescaler)) / prescaler_freq; +} - T auto_reload; - uint16_t prescaler; +template <typename Type> +float HardwareTimer<Type>::toSeconds(Type ticks) +{ + return (1.0f * ticks * (1 + prescaler)) / prescaler_freq; +} - bool ticking = false; -}; +template <typename Type> +float HardwareTimer<Type>::getResolution() +{ + // Resolution in us = number microseconds in one tick + return toMicroSeconds(1); +} -#endif /* SRC_SHARED_DRIVERS_HARDWARETIMER_H */ +template <typename Type> +float HardwareTimer<Type>::getMaxDuration() +{ + // Maximum duration = number of seconds to count to the auto_reload (reset) + // value. + return toSeconds(auto_reload); +} \ No newline at end of file diff --git a/src/shared/drivers/Xbee/Xbee.h b/src/shared/drivers/Xbee/Xbee.h index 163089b0b7745512d289c034ce17ecca519c0f5e..d269b1e27052a71a807ac8571cc865dd792cad03 100644 --- a/src/shared/drivers/Xbee/Xbee.h +++ b/src/shared/drivers/Xbee/Xbee.h @@ -23,12 +23,13 @@ #pragma once #include <Common.h> -#include <drivers/BusTemplate.h> #include <drivers/Transceiver.h> +#include <drivers/spi/SPIDriver.h> +#include <miosix.h> + #include <algorithm> #include <vector> -#include <miosix.h> #include "ActiveObject.h" #include "XbeeStatus.h" #include "diagnostic/StackLogger.h" @@ -106,25 +107,51 @@ static void __attribute__((used)) handleATTNInterrupt() } /** - * WARNING: An IRQ linked with the ATTN pin of the Xbee module must be enabled + * @warning: An IRQ linked with the ATTN pin of the Xbee module must be enabled * before using this class. See test/misc/xbee-bitrate for an example. + * @warning: Due to the way this driver is written, the SPI bus must be reserved + * only for the XBee device. No other devices can communicate on the same bus. */ -template <typename Bus, class CS, class ATTN, class RST> class Xbee : public Transceiver, public ActiveObject { public: - Xbee(unsigned int send_timeout) : send_timeout(send_timeout) + Xbee(SPIBusInterface& bus, GpioPin cs, GpioPin attn, GpioPin rst, + unsigned int send_timeout = 1000) + : send_timeout(send_timeout), spi_xbee(bus, cs), attn(attn), rst(rst) { + spi_xbee.config.br = SPIBaudRate::DIV_128; + + // No need to configure before each transaction, we are the only device + // on the bus. + spi_xbee.bus.configure(spi_xbee.config); + reset(); miosix::Thread::sleep(10); - CS::low(); + spi_xbee.cs.low(); miosix::Thread::sleep(1); - CS::high(); + spi_xbee.cs.high(); + miosix::Thread::sleep(1); + } + + Xbee(SPIBusInterface& bus, GpioPin cs, GpioPin attn, GpioPin rst, + SPIBusConfig spi_config, unsigned int send_timeout = 1000) + : send_timeout(send_timeout), spi_xbee(bus, cs, spi_config), attn(attn), + rst(rst) + { + // No need to configure before each transaction, we are the only device + // on the bus. + spi_xbee.bus.configure(spi_xbee.config); + + reset(); + miosix::Thread::sleep(10); + + spi_xbee.cs.low(); + miosix::Thread::sleep(1); + spi_xbee.cs.high(); miosix::Thread::sleep(1); } - Xbee() : Xbee(1000) {} /* * Send a message through the XBee * Blocks until the message is sent (successfully or not) @@ -231,7 +258,7 @@ protected: // Check if we have data to send (tx_buf.size > 0) or receive // (attn == 0) with disabled interrupts to avoid race conditions - if (ATTN::value() != 0 && tx_buf.size() == 0) + if (attn.value() != 0 && tx_buf.size() == 0) { // If we have nothing to receive or send, wait. waiting = miosix::Thread::getCurrentThread(); @@ -292,10 +319,10 @@ private: void reset() { - RST::mode(miosix::Mode::OPEN_DRAIN); - RST::low(); + rst.mode(miosix::Mode::OPEN_DRAIN); + rst.low(); miosix::delayUs(500); - RST::high(); + rst.high(); } /** @@ -319,9 +346,12 @@ private: */ void transferData() { + SPIBusInterface& bus = spi_xbee.bus; + ParseResult result = ParseResult::IDLE; - CS::low(); + bus.select(spi_xbee.cs); + vector<uint8_t> data; { @@ -338,7 +368,7 @@ private: { // Full duplex transfer, the data vector is replaced with received // data, if any. - Bus::transfer(data.data(), data.size()); + bus.transfer(data.data(), data.size()); // Parse the received data for (uint8_t rx : data) @@ -355,7 +385,7 @@ private: // If there's nothing more to parse, return if (result != ParseResult::PARSING) { - CS::high(); + bus.deselect(spi_xbee.cs); return; } @@ -365,7 +395,7 @@ private: // Read until we have received a packet (or no packet is found) do { - result = parse(Bus::read()); + result = parse(bus.read()); } while (result == ParseResult::PARSING); if (result == ParseResult::SUCCESS) @@ -377,7 +407,7 @@ private: TRACE("[Xbee] Read failed. Parser result: %d\n", (int)result); } - CS::high(); + bus.deselect(spi_xbee.cs); } /** @@ -610,6 +640,10 @@ private: vector<uint8_t> parser_buf; XbeeStatus status; + + SPISlave spi_xbee; + GpioPin attn; + GpioPin rst; }; // namespace Xbee } // namespace Xbee \ No newline at end of file diff --git a/src/shared/drivers/spi/MockSPIBus.h b/src/shared/drivers/spi/MockSPIBus.h new file mode 100644 index 0000000000000000000000000000000000000000..5bbcb07737e5ea0b15cd99d06ecf0c4b528a2f40 --- /dev/null +++ b/src/shared/drivers/spi/MockSPIBus.h @@ -0,0 +1,239 @@ +/** + * Copyright (c) 2020 Skyward Experimental Rocketry + * Authors: Luca Erbetta (luca.erbetta@skywarder.eu) + * + * 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 <cstdint> +#include <vector> + +#include "SPIInterface.h" + +using std::vector; + +/** + * @brief Mock SPI Bus to be used for testing communication to a single slave: + * data are read and written to two buffers on the memory that can then be + * checked. Operations on the mock bus will not be successfull if the + * configuration is not correct or the "slave" is not selected. + * + * Usage: + * 1. Set the expected config (data wont be written / read if the bus current + * configuration of the bus is different from the expected one) + * 2. Set the data to be read from the bus (in_buf). + * 3. Perform operations. write() will write bytes in out_buf, read() will + * return data from in_buf. + * 4. Check if out_buf contains the expected data. Check if data returned from + * read() is as expected from in_buf. + * 5. ??? + * 6. Profit. + */ +class MockSPIBus : public SPIBusInterface +{ +public: + MockSPIBus() {} + ~MockSPIBus() {} + + // Delete copy/move contructors/operators + MockSPIBus(const MockSPIBus&) = delete; + MockSPIBus& operator=(const MockSPIBus&) = delete; + + MockSPIBus(MockSPIBus&&) = delete; + MockSPIBus& operator=(MockSPIBus&&) = delete; + + /** + * @brief See SPIBusInterface::write() + */ + void write(uint8_t byte) override; + + /** + * @brief See SPIBusInterface::write() + */ + void write(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::read() + */ + uint8_t read() override; + + /** + * @brief See SPIBusInterface::read() + */ + void read(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::transfer() + */ + uint8_t transfer(uint8_t data) override; + + /** + * @brief See SPIBusInterface::transfer() + */ + void transfer(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::select() + * + * @param cs Not used, pass any GpioPin + */ + void select(GpioPin& cs) override; + + /** + * @brief See SPIBusInterface::deselect() + * + * @param cs Not used, pass any GpioPin + */ + void deselect(GpioPin& cs) override; + + /** + * @brief See SPIBusInterface::configure() + */ + void configure(SPIBusConfig config) override; + + /** + * @brief Wether the chip select is asserted or not + */ + bool isSelected() { return selected; } + + vector<uint8_t> out_buf; // Data written on the bus are stored here + vector<uint8_t> in_buf; // Store here data to be read from the bus + + unsigned int in_buf_pos = 0; // Read data iterator + + SPIBusConfig expected_config; // Expected configuration of the bus + +private: + bool canCommunicate(); + + SPIBusConfig current_config; + bool selected = false; +}; + +bool MockSPIBus::canCommunicate() +{ + return selected && current_config == expected_config; +} + +void MockSPIBus::write(uint8_t byte) +{ + if (canCommunicate()) + { + out_buf.push_back(byte); + } + else + { + out_buf.push_back(0); + } +} + +void MockSPIBus::write(uint8_t* data, size_t size) +{ + if (canCommunicate()) + { + out_buf.insert(out_buf.end(), data, data + size); + } + else + { + out_buf.insert(out_buf.end(), size, 0); + } +} + +uint8_t MockSPIBus::read() +{ + if (canCommunicate()) + { + return in_buf[in_buf_pos++]; + } + return 0; +} + +void MockSPIBus::read(uint8_t* data, size_t size) +{ + if (canCommunicate()) + { + for (size_t i = 0; i < size; i++) + { + *data = in_buf[in_buf_pos++]; + data++; + } + } + else + { + for (size_t i = 0; i < size; i++) + { + *data = 0; + data++; + } + } +} + +uint8_t MockSPIBus::transfer(uint8_t data) +{ + if (canCommunicate()) + { + + out_buf.push_back(data); + return in_buf[in_buf_pos++]; + } + else + { + + out_buf.push_back(0); + return 0; + } +} + +void MockSPIBus::transfer(uint8_t* data, size_t size) +{ + if (canCommunicate()) + { + for (size_t i = 0; i < size; i++) + { + out_buf.push_back(*data); + *data = in_buf[in_buf_pos++]; + data++; + } + } + else + { + for (size_t i = 0; i < size; i++) + { + out_buf.push_back(0); + *data = 0; + data++; + } + } +} + +void MockSPIBus::select(GpioPin& cs) +{ + (void)cs; + selected = true; +} + +void MockSPIBus::deselect(GpioPin& cs) +{ + (void)cs; + selected = false; +} + +void MockSPIBus::configure(SPIBusConfig config) { current_config = config; } \ No newline at end of file diff --git a/src/shared/drivers/spi/SPIBus.cpp b/src/shared/drivers/spi/SPIBus.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7dc7c803a4972a0150f1b9ac6d916f63ec6af67 --- /dev/null +++ b/src/shared/drivers/spi/SPIBus.cpp @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2020 Skyward Experimental Rocketry + * Authors: Luca Erbetta (luca.erbetta@skywarder.eu) + * + * 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 "SPIBus.h" + +SPIBus::SPIBus(SPI_TypeDef* spi) : spi(spi) {} + +void SPIBus::configure(SPIBusConfig new_config) +{ + // Reconfigure the bus only if config enabled. Do not reconfigure if already + // in the correct configuration. + if (config_enabled && (!first_config_applied || new_config != config)) + { + first_config_applied = true; + config = new_config; + + // Clean CR1 + spi->CR1 = 0; + + // Configure clock division (BR bits) + spi->CR1 |= (static_cast<uint16_t>(config.br) & 0x0007) << 3; + // Configure CPOL & CPHA bits + spi->CR1 |= ((uint16_t)config.cpol & 0x0001) << 1 | + ((uint16_t)config.cpha & 0x0001); + + // Configure LSBFIRST bit + spi->CR1 |= (uint16_t)config.lsb_first << 7; + + spi->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM // Use software chip-select + | SPI_CR1_MSTR // Master mode + | SPI_CR1_SPE; // Enable SPI + } +} \ No newline at end of file diff --git a/src/shared/drivers/spi/SPIBus.h b/src/shared/drivers/spi/SPIBus.h new file mode 100644 index 0000000000000000000000000000000000000000..eb02b313e4aa7fb938287342ecc0d12f2e39085b --- /dev/null +++ b/src/shared/drivers/spi/SPIBus.h @@ -0,0 +1,244 @@ +/** + * Copyright (c) 2020 Skyward Experimental Rocketry + * Authors: Luca Erbetta (luca.erbetta@skywarder.eu) + * + * 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 "SPIInterface.h" + +#pragma once + +/** + * @brief Low level driver for communicating on a SPI Bus, provides + * SPIBusInterface. + */ +class SPIBus : public SPIBusInterface +{ +public: + /** + * @brief Instantiates a new SPIBus + * + * @param spi Pointer to the SPI peripheral to be used + */ + SPIBus(SPI_TypeDef* spi); + ~SPIBus() {} + + // Delete copy/move contructors/operators + SPIBus(const SPIBus&) = delete; + SPIBus& operator=(const SPIBus&) = delete; + + SPIBus(SPIBus&&) = delete; + SPIBus& operator=(SPIBus&&) = delete; + + /** + * @brief Wether to apply slave-specific bus configuration before each + * transaction (BusTemplate compatibility mode). + * Only set to false to use SPIDriver alongside BusTemplate.h. + * Default value is true. + * + * @param value True: The slave configuration is applied to the SPI + * peripheral before each transaction. False: No configuration is ever + * applied to the SPI peripheral. The SPI peripheral retains the + * configuration set by BusTemplate.h + */ + void enableSlaveConfiguration(bool value) { config_enabled = value; } + + /** + * @brief See SPIBusInterface::write() + */ + void write(uint8_t byte) override; + + /** + * @brief See SPIBusInterface::write() + */ + void write(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::read() + */ + uint8_t read() override; + + /** + * @brief See SPIBusInterface::read() + */ + void read(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::transfer() + */ + uint8_t transfer(uint8_t data) override; + + /** + * @brief See SPIBusInterface::transfer() + */ + void transfer(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::select() + */ + void select(GpioPin& cs) override; + + /** + * @brief See SPIBusInterface::deselect() + */ + void deselect(GpioPin& cs) override; + + /** + * @brief See SPIBusInterface::configure() + */ + void configure(SPIBusConfig config) override; + +private: + /** + * Writes a single byte on the SPI bus. + * + * @param byte Pointer to the byte to be written + */ + void write(uint8_t* byte); + + /** + * Reads a single byte from the SPI bus. + * + * @param byte Pointer to the byte where the read data will be stored + */ + void read(uint8_t* byte); + + /** + * Full duplex transfer. Writes a single byte on the SPI bus and replaces + * its content with the received data + * + * @param byte Pointer to the byte to be transfered + */ + void transfer(uint8_t* byte); + + SPI_TypeDef* spi; + + SPIBusConfig config; + bool config_enabled = true; + bool first_config_applied = false; +}; + +// Defined here and not in the .cpp to make them inline + +inline void SPIBus::write(uint8_t data) { write(&data); } + +inline void SPIBus::write(uint8_t* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + write(data + i); + } +} + +inline uint8_t SPIBus::read() +{ + uint8_t data; + read(&data); + + return data; +} + +inline void SPIBus::read(uint8_t* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + read(data + i); + } +} + +inline uint8_t SPIBus::transfer(uint8_t data) +{ + transfer(&data); + return data; +} + +inline void SPIBus::transfer(uint8_t* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + transfer(data + i); + } +} + +inline void SPIBus::select(GpioPin& cs) +{ + cs.low(); + if (config.cs_setup_time_us > 0) + { + delayUs(config.cs_setup_time_us); + } +} + +inline void SPIBus::deselect(GpioPin& cs) +{ + if (config.cs_hold_time_us > 0) + { + delayUs(config.cs_hold_time_us); + } + cs.high(); +} + +inline void SPIBus::write(uint8_t* byte) +{ + // Wait until the peripheral is ready to transmit + while ((spi->SR & SPI_SR_TXE) == 0) + ; + // Write the byte in the transmit buffer + spi->DR = *byte; + + // Wait until byte is transmitted + while ((spi->SR & SPI_SR_RXNE) == 0) + ; + + // Clear the RX buffer by accessing the DR register + spi->DR; +} + +inline void SPIBus::transfer(uint8_t* byte) +{ + // Wait until the peripheral is ready to transmit + while ((spi->SR & SPI_SR_TXE) == 0) + ; + // Write the byte in the transmit buffer + spi->DR = *byte; + + // Wait until byte is transmitted + while ((spi->SR & SPI_SR_RXNE) == 0) + ; + + // Store the received data in the byte + *byte = (uint8_t)spi->DR; +} + +inline void SPIBus::read(uint8_t* byte) +{ + // Wait until the peripheral is ready to transmit + while ((spi->SR & SPI_SR_TXE) == 0) + ; + // Write the byte in the transmit buffer + spi->DR = 0; + + // Wait until byte is transmitted + while ((spi->SR & SPI_SR_RXNE) == 0) + ; + + // Store the received data in the byte + *byte = (uint8_t)spi->DR; +} \ No newline at end of file diff --git a/src/tests/drivers/test-timer.cpp b/src/shared/drivers/spi/SPIDriver.h similarity index 64% rename from src/tests/drivers/test-timer.cpp rename to src/shared/drivers/spi/SPIDriver.h index 5ef955e78440eba187000bb61ad56d60a4b22de8..c90ba4743742cfef611f55a0217c44fcd81d46ed 100644 --- a/src/tests/drivers/test-timer.cpp +++ b/src/shared/drivers/spi/SPIDriver.h @@ -1,44 +1,28 @@ -/* - * Copyright (c) 2018 Skyward Experimental Rocketry - * Authors: Luca Erbetta - * +/** + * Copyright (c) 2020 Skyward Experimental Rocketry + * Authors: Luca Erbetta (luca.erbetta@skywarder.eu) + * * 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 + * 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 <cstdio> -#include "drivers/HardwareTimer.h" +#pragma once -using miosix::Thread; - -int main() -{ - HardwareTimer<uint32_t, 2>& timer2 = HardwareTimer<uint32_t, 2>::instance(); - - timer2.start(); - - while (true) - { - uint32_t tick = timer2.tick(); - printf("%lu\t\t(%.3f)\n", tick, timer2.toMilliSeconds(tick)); - - usleep(100000); - } - return 0; -} \ No newline at end of file +#include "SPIInterface.h" +#include "SPITransaction.h" +#include "SPIBus.h" \ No newline at end of file diff --git a/src/shared/drivers/spi/SPIInterface.h b/src/shared/drivers/spi/SPIInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..e7d9955e9a5c516e250aa65fdfcd9be740b8fd29 --- /dev/null +++ b/src/shared/drivers/spi/SPIInterface.h @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2019 Skyward Experimental Rocketry + * Authors: Luca Erbetta + * + * 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 <miosix.h> + +#include <cstdint> +#include <cstdio> + +using miosix::delayUs; +using miosix::GpioPin; + +/** + * @brief SPI baud rate selection parameter. + * SPI clock frequency will be equal to the SPI peripheral bus clock speed + * divided by the value specified in this enum. + * + * Eg: DIV_2 --> spi clock freq = f_PCLK / 2 + * + * See SPI->CR1 on the datasheet for further information. + */ +enum class SPIBaudRate +{ + DIV_2 = 0, + DIV_4 = 1, + DIV_8 = 2, + DIV_16 = 3, + DIV_32 = 4, + DIV_64 = 5, + DIV_128 = 6, + DIV_256 = 7, +}; + +/** + * @brief SPI Bus configuration for a specific slave. + * See slave datasheet for information on how to populate this struct + */ +struct SPIBusConfig +{ + SPIBaudRate br = SPIBaudRate::DIV_2; ///> Peripheral clock division + uint8_t cpol = 0; ///> Clock polarity (0 - 1) + uint8_t cpha = 0; ///> Clock phase (0 - 1) + bool lsb_first = false; ///> MSB or LSB first + + unsigned int cs_setup_time_us = 0; ///> How long to wait before starting a + ///> a trasmission after CS is set (us) + unsigned int cs_hold_time_us = 0; ///> How long to hold cs after the end + ///> of a trasmission (us) + + // Custom comparison operator + bool operator==(const SPIBusConfig& other) const + { + // Compare member-by-member + // clang-format off + return br == other.br + && cpol == other.cpol + && cpha == other.cpha + && lsb_first == other.lsb_first + && cs_setup_time_us == other.cs_setup_time_us + && cs_setup_time_us == other.cs_hold_time_us; + // clang-format on + } + + bool operator!=(const SPIBusConfig& other) const + { + return !(*this == other); + } +}; + +/** + * @brief Interface for low level access of a SPI bus + */ +class SPIBusInterface +{ +public: + SPIBusInterface() {} + + ~SPIBusInterface() {} + + // Delete copy/move contructors/operators + SPIBusInterface(const SPIBusInterface&) = delete; + SPIBusInterface& operator=(const SPIBusInterface&) = delete; + + SPIBusInterface(SPIBusInterface&&) = delete; + SPIBusInterface& operator=(SPIBusInterface&&) = delete; + + /** + * @brief Writes a single \p byte to the bus. + * + * @param byte Byte to write + */ + virtual void write(uint8_t byte) = 0; + + /** + * @brief Writes \p data to the bus. + * + * @param data Buffer containing data to write + * @param size Number of bytes to write + */ + virtual void write(uint8_t* data, size_t size) = 0; + + /** + * @brief Reads a single byte from the bus. + * @return Byte read from the bus + */ + virtual uint8_t read() = 0; + + /** + * @brief Reads \p size bytes from the SPI bus, putting them in \p data. + * + * @param data Buffer to be filled with received data + * @param size Number of bytes to receive + */ + virtual void read(uint8_t* data, size_t size) = 0; + + /** + * @brief Full duplex transmission on the SPI bus. + * A \p byte is written on the bus and a byte is read and returned + * + * @param byte Byte to write + * @return Data read from the bus + */ + virtual uint8_t transfer(uint8_t byte) = 0; + + /** + * @brief Full duplex transmission on the SPI bus. + * \p data is written on the bus and its contents are then replaced with the + * received bytes. + * + * @param data Buffer containing data to transfer + * @param size Number of bytes to transfer + */ + virtual void transfer(uint8_t* data, size_t size) = 0; + + /** + * @brief Selects the slave + * + * @param cs Chip select pin for the slave + */ + virtual void select(GpioPin& cs) = 0; + + /** + * @brief Deselects the slave + * + * @param cs Chip select pin for the slave + * @return + */ + virtual void deselect(GpioPin& cs) = 0; + + /** + * @brief Configures the bus with the provided configuration parameters. + * + * @param config Configuration parameters + * @return + */ + virtual void configure(SPIBusConfig config) = 0; +}; + +/** + * @brief Contains information about a single SPI slave device. + */ +struct SPISlave +{ + SPIBusInterface& bus; ///> Bus on which the slave is connected + + SPIBusConfig config; ///> How the bus should be configured to communicate + ///> with the slave. + GpioPin cs; ///> Chip select pin + + SPISlave(SPIBusInterface& bus, GpioPin cs) : bus(bus), cs(cs) {} + + SPISlave(SPIBusInterface& bus, GpioPin cs, SPIBusConfig config) + : bus(bus), config(config), cs(cs) + { + } +}; diff --git a/src/shared/drivers/spi/SPITransaction.cpp b/src/shared/drivers/spi/SPITransaction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8e5746907547e82f25cd8deeaf7f3e9b17a16ca4 --- /dev/null +++ b/src/shared/drivers/spi/SPITransaction.cpp @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2019 Skyward Experimental Rocketry + * Authors: Luca Erbetta + * + * 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 "SPITransaction.h" + +SPITransaction::SPITransaction(SPIBusInterface& bus, GpioPin cs, + SPIBusConfig config) + : bus(bus), cs(cs) +{ + bus.configure(config); +} + +SPITransaction::SPITransaction(SPISlave slave) + : SPITransaction(slave.bus, slave.cs, slave.config) +{ +} + +void SPITransaction::write(uint8_t cmd) +{ + bus.select(cs); + bus.write(&cmd, 1); + bus.deselect(cs); +} + +void SPITransaction::write(uint8_t reg, uint8_t val) +{ + bus.select(cs); + bus.write(®, 1); + bus.write(&val, 1); + bus.deselect(cs); +} + +void SPITransaction::write(uint8_t reg, uint8_t* data, size_t size) +{ + bus.select(cs); + bus.write(®, 1); + bus.write(data, size); + bus.deselect(cs); +} + +void SPITransaction::write(uint8_t* data, size_t size) +{ + bus.select(cs); + bus.write(data, size); + bus.deselect(cs); +} + +void SPITransaction::transfer(uint8_t* data, size_t size) +{ + bus.select(cs); + bus.transfer(data, size); + bus.deselect(cs); +} + +uint8_t SPITransaction::read(uint8_t reg, bool set_read_bit) +{ + if (set_read_bit) + reg = reg | 0x80; + + uint8_t out; + bus.select(cs); + bus.write(®, 1); + bus.read(&out, 1); + bus.deselect(cs); + return out; +} + +void SPITransaction::read(uint8_t reg, uint8_t* data, size_t size, + bool set_read_bit) +{ + if (set_read_bit) + reg = reg | 0x80; + + bus.select(cs); + bus.write(®, 1); + bus.read(data, size); + bus.deselect(cs); +} + +void SPITransaction::read(uint8_t* data, size_t size) +{ + bus.select(cs); + bus.read(data, size); + bus.deselect(cs); +} \ No newline at end of file diff --git a/src/shared/drivers/spi/SPITransaction.h b/src/shared/drivers/spi/SPITransaction.h new file mode 100644 index 0000000000000000000000000000000000000000..2aed6c404cdbae94736c72ed2e86c9ff94fed219 --- /dev/null +++ b/src/shared/drivers/spi/SPITransaction.h @@ -0,0 +1,162 @@ +/** + * + * + * Copyright (c) 2020 Skyward Experimental Rocketry + * Authors: Luca Erbetta (luca.erbetta@skywarder.eu) + * + * 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 "SPIInterface.h" + +/** + * @brief Provides high-level access to the SPI Bus for a single transaction. + * To make sure the bus is properly configured for the provided slave, you have + * to create a new instance of this class for every transaction, as the bus is + * configured upon instantiation. + * + * @warning DO NOT store an instance of this class for later use, as the bus may + * be incorrectly configured by then. + * + * Example transaction: + * + * { + * // Transaction begin: + * SPITransaction spi(bus, cs, config); // Configures the bus with the + * // provided parameters + * + * spi.write(REG_EX, 0x56); // writes data to REG_EX + * uint8_t reg2 = spi.read(REG_EX_2); // reads from REG_EX_2 + * + * // ...As many read/writes as you wish... + * + * // transaction end. SPITransaction object is destructed + * } + */ +class SPITransaction +{ +public: + /** + * @brief Instatiates a new SPITransaction, configuring the bus with the + * provided parameters + * + * @param slave Slave to communicate with + */ + SPITransaction(SPISlave slave); + + /** + * @brief Instatiates a new SPITransaction, configuring the bus with the + * provided parameters + * + * @param bus Bus to communicate on + * @param cs Chip select of the slave to communicate to + * @param config Configuration of the bus for the selected slave + */ + SPITransaction(SPIBusInterface& bus, GpioPin cs, SPIBusConfig config); + + // Delete copy/move contructors/operators + SPITransaction(const SPITransaction&) = delete; + SPITransaction& operator=(const SPITransaction&) = delete; + + SPITransaction(SPITransaction&&) = delete; + SPITransaction& operator=(SPITransaction&&) = delete; + + /** + * @brief Writes a command \p cmd to the bus + * + * @param cmd Command to write on the bus + */ + void write(uint8_t cmd); + + /** + * @brief Writes \p val into the \p reg register + * + * @param reg Slave device register + * @param val Value to be written in the register + */ + void write(uint8_t reg, uint8_t val); + + /** + * @brief Writes \p size bytes into the \p reg register + * + * @param reg Slave device register + * @param data Data to be written + * @param size Number of bytes to be written + */ + void write(uint8_t reg, uint8_t* data, size_t size); + + /** + * @brief Writes \p bytes on the bus + * + * @param data Bytes to be written + * @param size Number of bytes to be written + */ + void write(uint8_t* data, size_t size); + + /** + * @brief Read the contents of the \p reg register + * + * @param reg Slave device register + * @param set_read_bit Wether to set the read bit to 1 (MSB of reg) + * (default = true). + */ + uint8_t read(uint8_t reg, bool set_read_bit = true); + + /** + * @brief Reads \p size bytes from the \p reg register + * + * @param reg Slave device register + * @param data Buffer where read bytes will be stored + * @param size Number of bytes to read + * @param set_read_bit Wether to set the read bit to 1 (MSB of reg) + * (default = true). + */ + void read(uint8_t reg, uint8_t* data, size_t size, + bool set_read_bit = true); + + /** + * @brief Reads \p size bytes from the bus + * + * @param data Buffer where read bytes will be stored + * @param size Number of bytes to read + */ + void read(uint8_t* data, size_t size); + + /** + * @brief Full duplex transfer: \p data is written on the bus and its + * contents are replaced with the received bytes. + * + * @param data Buffer containign data to be transfered + * @param size Number of bytes to be transfer + */ + void transfer(uint8_t* data, size_t size); + + /** + * @brief Returns the underlying bus for low level access + * + * @return SPIBusInterface associated with this transaction + */ + SPIBusInterface& getBus() { return bus; } + +private: + SPIBusInterface& bus; + GpioPin cs; +}; \ No newline at end of file diff --git a/src/shared/sensors/L3GD20.h b/src/shared/sensors/L3GD20.h new file mode 100644 index 0000000000000000000000000000000000000000..9133d5b5090e3fd47ba9257ef8c295c0f5afd1f3 --- /dev/null +++ b/src/shared/sensors/L3GD20.h @@ -0,0 +1,238 @@ +/** + * Copyright (c) 2019 Skyward Experimental Rocketry + * Authors: Luca Erbetta + * + * 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 <miosix.h> + +#include <array> + +#include "Sensor.h" +#include "drivers/spi/SPIDriver.h" + +using miosix::GpioPin; +using std::array; + +class L3GD20 : public GyroSensor +{ +public: + enum class FullScaleRange + { + FS_250 = 250, + FS_500 = 500, + FS_2000 = 2000 + }; + + enum class OutPutDataRate + { + ODR_95 = 0x00, + ODR_190 = 0x01, + ODR_380 = 0x02, + ODR_760 = 0x03 + }; + /** + * @brief Creates an instance of an L3GD20 sensor + * + * @param bus SPI bus the sensor is connected to + * @param cs Chip Select GPIO + * @param range Full Scale Range (See datasheet) + * @param odr Output Data Rate (See datasheet) + * @param cutoff_freq Low pass filter cutoff frequency (See datasheet) + * @param fifo_enabled Fifo enabled + * @param fifo_watermark FIFO watermark level in range [1,32] (used for + * interrupt generation, see datasheet). + */ + L3GD20(SPIBusInterface& bus, GpioPin cs, + FullScaleRange range = FullScaleRange::FS_250, + OutPutDataRate odr = OutPutDataRate::ODR_95, + uint8_t cutoff_freq = 0x03, bool fifo_enabled = false, + unsigned int fifo_watermark = 24) + : fifo_enabled(fifo_enabled), fifo_watermark(fifo_watermark), + spislave(bus, cs), fs(range), odr(odr), cutoff_freq(cutoff_freq) + { + // Configure SPI + spislave.config.br = SPIBaudRate::DIV_64; + // memset(last_fifo, 0, sizeof(Vec3) * 32); + } + + bool init() + { + SPITransaction spi(spislave); + + uint8_t whoami = spi.read(REG_WHO_AM_I); + + if (whoami != WHO_AM_I_VAL) + { + printf("WAMI: %d\n", whoami); + last_error = ERR_NOT_ME; + return false; + } + + // uint8_t ctrl4 = spi.read(REG_CTRL4); + + switch (fs) + { + case FullScaleRange::FS_250: + spi.write(REG_CTRL4, 0); + break; + case FullScaleRange::FS_500: + spi.write(REG_CTRL4, (uint8_t)(1 << 4)); + break; + case FullScaleRange::FS_2000: + spi.write(REG_CTRL4, (uint8_t)(2 << 4)); + break; + default: + break; + } + if (fifo_enabled) + { + // Enable fifo + spi.write(REG_CTRL5, 1 << 6); + + // Set watermark level to fifo_watermark samples + uint8_t fifo_ctrl = fifo_watermark; + + // Set fifo to FIFO mode + fifo_ctrl |= 0x02 << 5; + + spi.write(REG_FIFO_CTRL, fifo_ctrl); + + // Enable FIFO watermark interrupt + spi.write(REG_CTRL3, 0x04); + } + // Enter normal mode, enable output + uint8_t ctrl1 = 0x0F; + + // Configure ODR + ctrl1 |= static_cast<uint8_t>(odr) << 6; + + // Configure cutoff frequency + ctrl1 |= (cutoff_freq & 0x03) << 4; + + spi.write(REG_CTRL1, ctrl1); + + return true; + } + + bool selfTest() { return true; } + + bool onSimpleUpdate() + { + float scale = static_cast<int>(fs); + + if (!fifo_enabled) + { + uint8_t data[6]; + // Read output data registers (X, Y, Z) + { + SPITransaction spi(spislave); + spi.read(REG_OUT_X_L | 0x40, data, 6); + } + + int16_t x = data[0] | data[1] << 8; + int16_t y = data[2] | data[3] << 8; + int16_t z = data[4] | data[5] << 8; + // printf("%02X,%02X,%02X\n", x, y, z); + + mLastGyro = + Vec3(x * scale / 65535, y * scale / 65535, z * scale / 65535); + } + else // FIFO is enabled + { + uint8_t buf[192]; + + SPITransaction spi(spislave); + // Read last fifo level + uint8_t fifo_src = spi.read(REG_FIFO_SRC); + uint8_t ovr = (fifo_src & 0x40); + last_fifo_level = fifo_src & 0x1F; + + // if ovr --> fifo is full --> level = level + 1 + if (ovr) + { + ++last_fifo_level; + } + + // Read fifo + spi.read(REG_OUT_X_L | 0x40, buf, last_fifo_level * 6); + + // Convert units & store the FIFO content + for (uint8_t i = 0; i < last_fifo_level; i++) + { + int16_t x = buf[i * 6] | buf[i * 6 + 1] << 8; + int16_t y = buf[i * 6 + 2] | buf[i * 6 + 3] << 8; + int16_t z = buf[i * 6 + 4] | buf[i * 6 + 5] << 8; + + Vec3 t = Vec3(x * scale / 65535, y * scale / 65535, + z * scale / 65535); + + last_fifo[i] = t; + } + } + + return true; + } + + const array<Vec3, 32>& getLastFifo() const { return last_fifo; } + uint8_t getLastFifoSize() const { return last_fifo_level; } + +private: + bool fifo_enabled = false; + unsigned int fifo_watermark; + + array<Vec3, 32> last_fifo; + uint8_t last_fifo_level = 0; + + SPISlave spislave; + FullScaleRange fs; + OutPutDataRate odr; + uint8_t cutoff_freq; + + constexpr static uint8_t WHO_AM_I_VAL = 212; + + enum RegMap + { + REG_WHO_AM_I = 0x0F, + + REG_CTRL1 = 0x20, + REG_CTRL2 = 0x21, + REG_CTRL3 = 0x22, + REG_CTRL4 = 0x23, + REG_CTRL5 = 0x24, + + REG_REFERENCE = 0x25, + REG_OUT_TEMP = 0x26, + REG_STATUS = 0x27, + + REG_OUT_X_L = 0x28, + REG_OUT_X_H = 0x29, + + REG_OUT_Y_L = 0x2A, + REG_OUT_Y_H = 0x2B, + + REG_OUT_Z_L = 0x2C, + REG_OUT_Z_H = 0x2D, + + REG_FIFO_CTRL = 0x2E, + REG_FIFO_SRC = 0x2F + }; +}; \ No newline at end of file diff --git a/src/shared/sensors/MS580301BA07/MS580301BA07.h b/src/shared/sensors/MS580301BA07/MS580301BA07.h index b52b1474f8b0e74093602ea0b6517bbf3a3d30ac..5131e32496e1b7e79068ffd5118a72957949daab 100644 --- a/src/shared/sensors/MS580301BA07/MS580301BA07.h +++ b/src/shared/sensors/MS580301BA07/MS580301BA07.h @@ -24,19 +24,24 @@ #pragma once -#include <drivers/BusTemplate.h> #include "../Sensor.h" -#include "MS580301BA07Data.h" #include "Debug.h" +#include "MS580301BA07Data.h" +#include "drivers/spi/SPIDriver.h" -// TODO second order temperature compensation -template <class Bus> class MS580301BA07 : public PressureSensor, public TemperatureSensor { public: /* Class constructor. Reset lastPressure and lastTemperature */ - MS580301BA07() + MS580301BA07(SPIBusInterface& bus, GpioPin cs) + : MS580301BA07(bus, cs, SPIBusConfig{}) + { + spi_ms5803.config.br = SPIBaudRate::DIV_128; + } + + MS580301BA07(SPIBusInterface& bus, GpioPin cs, SPIBusConfig config) + : spi_ms5803(bus, cs, config) { memset(&cd, 0, sizeof(calibration_data)); mLastTemp = 0; @@ -47,15 +52,18 @@ public: bool init() { + SPITransaction spi{spi_ms5803}; + int timeout = 10; do { - Bus::read(RESET_DEV); + spi.read(RESET_DEV); miosix::Thread::sleep(3); - cd.sens = readReg(PROM_READ_MASK | PROM_SENS_MASK); - if (cd.sens == 0){ + cd.sens = readReg(spi, PROM_READ_MASK | PROM_SENS_MASK); + if (cd.sens == 0) + { miosix::Thread::sleep(1); TRACE("Could not read cd.sens\n"); } @@ -67,14 +75,14 @@ public: return false; } - cd.off = readReg(PROM_READ_MASK | PROM_OFF_MASK); - cd.tcs = readReg(PROM_READ_MASK | PROM_TCS_MASK); - cd.tco = readReg(PROM_READ_MASK | PROM_TCO_MASK); - cd.tref = readReg(PROM_READ_MASK | PROM_TREF_MASK); - cd.tempsens = readReg(PROM_READ_MASK | PROM_TEMPSENS_MASK); + cd.off = readReg(spi, PROM_READ_MASK | PROM_OFF_MASK); + cd.tcs = readReg(spi, PROM_READ_MASK | PROM_TCS_MASK); + cd.tco = readReg(spi, PROM_READ_MASK | PROM_TCO_MASK); + cd.tref = readReg(spi, PROM_READ_MASK | PROM_TREF_MASK); + cd.tempsens = readReg(spi, PROM_READ_MASK | PROM_TEMPSENS_MASK); TRACE("off: %d, tcs: %d, tco: %d, tref: %d, tsens: %d\n", (int)cd.off, - (int)cd.tcs, (int)cd.tco, (int)cd.tref, (int)cd.tempsens); + (int)cd.tcs, (int)cd.tco, (int)cd.tref, (int)cd.tempsens); mStatus = 0; @@ -97,34 +105,36 @@ public: */ bool onSimpleUpdate() { + SPITransaction spi{spi_ms5803}; // Begin an SPI transaction + uint8_t rcvbuf[3]; uint32_t temperature = 0; switch (mStatus) { case STATE_INIT: - Bus::write(CONVERT_D1_4096); + spi.write(CONVERT_D1_4096); mStatus = STATE_SAMPLED_PRESSURE; case STATE_SAMPLED_PRESSURE: - Bus::read_low(ADC_READ, rcvbuf, 3); + spi.read(ADC_READ, rcvbuf, 3, false); mInternalPressure = rcvbuf[2] | ((uint32_t)rcvbuf[1] << 8) | ((uint32_t)rcvbuf[0] << 16); - Bus::write(CONVERT_D2_4096); // Begin temperature sampling + spi.write(CONVERT_D2_4096); // Begin temperature sampling mStatus = STATE_SAMPLED_TEMPERATURE; break; case STATE_SAMPLED_TEMPERATURE: - Bus::read_low(ADC_READ, rcvbuf, 3); + spi.read(ADC_READ, rcvbuf, 3, false); // TODO use swapBytes temperature = (uint32_t)rcvbuf[2] | ((uint32_t)rcvbuf[1] << 8) | ((uint32_t)rcvbuf[0] << 16); updateData(mInternalPressure, temperature); - Bus::write(CONVERT_D1_4096); // Begin pressure sampling + spi.write(CONVERT_D1_4096); // Begin pressure sampling mStatus = STATE_SAMPLED_PRESSURE; - + break; } @@ -143,6 +153,8 @@ public: uint8_t getState() { return mStatus; } private: + SPISlave spi_ms5803; + static constexpr uint8_t TIMEOUT = 5; uint8_t mStatus; uint32_t mInternalPressure; @@ -205,10 +217,10 @@ private: calibration_data cd; - uint16_t readReg(uint8_t reg) + uint16_t readReg(SPITransaction& spi, uint8_t reg) { uint8_t rcv[2]; - Bus::read(reg, rcv, 2); + spi.read(reg, rcv, 2); uint16_t data = (rcv[0] << 8) | rcv[1]; return data; } diff --git a/src/shared/utils/collections/CircularBuffer.h b/src/shared/utils/collections/CircularBuffer.h index 49b88bf6de8cb535a05fe79fc96ef8ad594d11af..2a4cae1dad4ac949f9ba4edc8dfca51e0efd7b52 100644 --- a/src/shared/utils/collections/CircularBuffer.h +++ b/src/shared/utils/collections/CircularBuffer.h @@ -47,7 +47,7 @@ public: virtual T& put(const T& elem) { buffer[write_ptr] = elem; - T& added = buffer[write_ptr]; + T& added = buffer[write_ptr]; if (!empty && write_ptr == read_ptr) { @@ -62,12 +62,15 @@ public: /** * Gets an element from the buffer, without removing it - * Index starts at the element returned by get() or pop(): get(0) is - * the same as get() + * Index starts from the oldest element in the buffer: get(0) returns the + * same element as get() * * @warning Remember to catch the exception! + * @throw range_error if index >= count() + * + * @param i Index of the elemnt to get, starting from the oldest + * * @return the element - * @throws range_error if buffer is empty */ virtual T& get(unsigned int i) { @@ -81,21 +84,19 @@ public: } /** - * @brief Returns the last element added in the buffer + * @brief Returns the last element added in the buffer. + * + * @throw range_error if buffer is empty * @warning Remember to catch the exception! * @return the element - * @throws range_error if buffer is empty */ - virtual T& last() - { - return get(count() - 1); - } + virtual T& last() { return get(count() - 1); } /** * Gets the first element from the buffer, without removing it + * @throw range_error if buffer is empty * @warning Remember to catch the exception! * @return the element - * @throws range_error if buffer is empty */ virtual T& get() { @@ -109,9 +110,9 @@ public: /** * Pops the first element in the buffer. + * @throw range_error if buffer is empty * @warning Remember to catch the exception! * @return the element that has been popped - * @throws range_error if buffer is empty */ virtual const T& pop() { @@ -148,7 +149,10 @@ public: virtual bool isEmpty() const { return empty; } - virtual bool isFull() const { return count() == Size; } + virtual bool isFull() const + { + return CircularBuffer<T, Size>::count() == Size; + } /** * Returns the maximum number of elements that can be stored in the buffer * @return buffer size diff --git a/src/tests/catch/spidriver/FakeSPIBus.h b/src/tests/catch/spidriver/FakeSPIBus.h new file mode 100644 index 0000000000000000000000000000000000000000..2a395552b1fe4d9698a7854154b3d543a270b623 --- /dev/null +++ b/src/tests/catch/spidriver/FakeSPIBus.h @@ -0,0 +1,277 @@ +/** + * Copyright (c) 2020 Skyward Experimental Rocketry + * Authors: Luca Erbetta (luca.erbetta@skywarder.eu) + * + * 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 "FakeSpiTypedef.h" +#include "drivers/spi/SPIInterface.h" + +#pragma once + +/** + * @brief SPIBus modified to accept a fake Chip select & spi peripheral struct. + * Not ideal as modifications to SPIBus have to be manually applied here too, + * but that's the only way I found to test it effectively + */ +class FakeSPIBus : public SPIBusInterface +{ +public: + /** + * @brief Instantiates a new FakeSPIBus + * + * @param spi Pointer to the SPI peripheral to be used + */ + FakeSPIBus(FakeSpiTypedef* spi); + ~FakeSPIBus() {} + + // Delete copy/move contructors/operators + FakeSPIBus(const FakeSPIBus&) = delete; + FakeSPIBus& operator=(const FakeSPIBus&) = delete; + + FakeSPIBus(FakeSPIBus&&) = delete; + FakeSPIBus& operator=(FakeSPIBus&&) = delete; + + /** + * @brief Wether to apply slave-specific bus configuration before each + * transaction (BusTemplate compatibility mode). + * Only set to false to use SPIDriver alongside BusTemplate.h. + * Default value is true. + * + * @param value True: The slave configuration is applied to the SPI + * peripheral before each transaction. False: No configuration is ever + * applied to the SPI peripheral. The SPI peripheral retains the + * configuration set by BusTemplate.h + */ + void enableSlaveConfiguration(bool value) { config_enabled = value; } + + /** + * @brief See SPIBusInterface::write() + */ + void write(uint8_t byte) override; + + /** + * @brief See SPIBusInterface::write() + */ + void write(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::read() + */ + uint8_t read() override; + + /** + * @brief See SPIBusInterface::read() + */ + void read(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::transfer() + */ + uint8_t transfer(uint8_t data) override; + + /** + * @brief See SPIBusInterface::transfer() + */ + void transfer(uint8_t* data, size_t size) override; + + /** + * @brief See SPIBusInterface::select() + */ + void select(GpioPin& cs) override; + + /** + * @brief See SPIBusInterface::deselect() + */ + void deselect(GpioPin& cs) override; + + /** + * @brief See SPIBusInterface::configure() + */ + void configure(SPIBusConfig config) override; + +private: + /** + * Writes a single byte on the SPI bus. + * + * @param byte Pointer to the byte to be written + */ + void write(uint8_t* byte); + + /** + * Reads a single byte from the SPI bus. + * + * @param byte Pointer to the byte where the read data will be stored + */ + void read(uint8_t* byte); + + /** + * Full duplex transfer. Writes a single byte on the SPI bus and replaces + * its content with the received data + * + * @param byte Pointer to the byte to be transfered + */ + void transfer(uint8_t* byte); + + FakeSpiTypedef* spi; + + SPIBusConfig config; + bool config_enabled = true; + bool first_config_applied = false; +}; + +FakeSPIBus::FakeSPIBus(FakeSpiTypedef* spi) : spi(spi) {} + +void FakeSPIBus::configure(SPIBusConfig new_config) +{ + // Reconfigure the bus only if config enabled. Do not reconfigure if already + // in the correct configuration. + if (config_enabled && (!first_config_applied || new_config != config)) + { + first_config_applied = true; + config = new_config; + + // Clean CR1 + spi->CR1 = 0; + + // Configure clock division (BR bits) + spi->CR1 |= (static_cast<uint16_t>(config.br) & 0x0007) << 3; + // Configure CPOL & CPHA bits + spi->CR1 |= ((uint16_t)config.cpol & 0x0001) << 1 | + ((uint16_t)config.cpha & 0x0001); + + // Configure LSBFIRST bit + spi->CR1 |= (uint16_t)config.lsb_first << 7; + + spi->CR1 |= SPI_CR1_SSI | SPI_CR1_SSM // Use software chip-select + | SPI_CR1_MSTR // Master mode + | SPI_CR1_SPE; // Enable SPI + } +} + +inline void FakeSPIBus::write(uint8_t data) { write(&data); } + +inline void FakeSPIBus::write(uint8_t* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + write(data + i); + } +} + +inline uint8_t FakeSPIBus::read() +{ + uint8_t data; + read(&data); + + return data; +} + +inline void FakeSPIBus::read(uint8_t* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + read(data + i); + } +} + +inline uint8_t FakeSPIBus::transfer(uint8_t data) +{ + transfer(&data); + return data; +} + +inline void FakeSPIBus::transfer(uint8_t* data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + transfer(data + i); + } +} + +inline void FakeSPIBus::select(GpioPin& rcs) +{ + FakeGpioPin& cs = static_cast<FakeGpioPin&>(rcs); + + cs.low(); + if (config.cs_setup_time_us > 0) + { + delayUs(config.cs_setup_time_us); + } +} + +inline void FakeSPIBus::deselect(GpioPin& rcs) +{ + FakeGpioPin& cs = static_cast<FakeGpioPin&>(rcs); + + if (config.cs_hold_time_us > 0) + { + delayUs(config.cs_hold_time_us); + } + cs.high(); +} + +inline void FakeSPIBus::write(uint8_t* byte) +{ + // Wait until the peripheral is ready to transmit + while ((spi->SR & SPI_SR_TXE) == 0) + ; + // Write the byte in the transmit buffer + spi->DR = *byte; + + // Wait until byte is transmitted + while ((spi->SR & SPI_SR_RXNE) == 0) + ; + + // Clear the RX buffer by accessing the DR register + (void)spi->DR; +} + +inline void FakeSPIBus::transfer(uint8_t* byte) +{ + // Wait until the peripheral is ready to transmit + while ((spi->SR & SPI_SR_TXE) == 0) + ; + // Write the byte in the transmit buffer + spi->DR = *byte; + + // Wait until byte is transmitted + while ((spi->SR & SPI_SR_RXNE) == 0) + ; + + // Store the received data in the byte + *byte = (uint8_t)spi->DR; +} + +inline void FakeSPIBus::read(uint8_t* byte) +{ + // Wait until the peripheral is ready to transmit + while ((spi->SR & SPI_SR_TXE) == 0) + ; + // Write the byte in the transmit buffer + spi->DR = 0; + + // Wait until byte is transmitted + while ((spi->SR & SPI_SR_RXNE) == 0) + ; + + // Store the received data in the byte + *byte = (uint8_t)spi->DR; +} \ No newline at end of file diff --git a/src/tests/catch/spidriver/FakeSpiTypedef.h b/src/tests/catch/spidriver/FakeSpiTypedef.h new file mode 100644 index 0000000000000000000000000000000000000000..dab7a26fa77879e52f1316d44550f0acac447f2c --- /dev/null +++ b/src/tests/catch/spidriver/FakeSpiTypedef.h @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2020 Skyward Experimental Rocketry + * Authors: Luca Erbetta (luca.erbetta@skywarder.eu) + * + * 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 <miosix.h> + +#include <cstdint> +#include <vector> + +using std::vector; + +class FakeGpioPin : public miosix::GpioPin +{ +public: + FakeGpioPin() : GpioPin(GPIOA_BASE,1) {} + + void high() { val = 1; } + + void low() { val = 0; } + + int value() { return val; } + +private: + int val = 1; +}; + +/** + * @brief Mock STM32F4 SPI peripheral: intercepts register value changes to + * emulate a real SPI peripheral / slave. + */ +struct FakeSpiTypedef +{ + uint32_t CR1 = 0; + uint32_t CR2 = 0; + uint32_t SR = 3; + + struct RegDR + { + // Intercept uint32_t assignements + void operator=(uint32_t DR) + { + // If slave is selected & bus configured properly + if (parent.cs.value() == 0 && parent.CR1 == parent.CR1_expected && + parent.CR2 == parent.CR2_expected) + { + out_buf.push_back(DR); + } + } + + operator uint32_t() + { + // If slave is selected + if (parent.cs.value() == 0 && parent.CR1 == parent.CR1_expected && + parent.CR2 == parent.CR2_expected) + { + return in_buf[in_it++]; + } + + return 0; + } + + RegDR(FakeSpiTypedef& parent) : parent(parent) {} + + unsigned int in_it = 0; + vector<uint32_t> in_buf; + vector<uint32_t> out_buf; + + private: + FakeSpiTypedef& parent; + }; + + uint32_t CR1_expected = 0; + uint32_t CR2_expected = 0; + + RegDR DR; + FakeGpioPin cs; + + FakeSpiTypedef() : DR(*this) {} +}; \ No newline at end of file diff --git a/src/tests/catch/spidriver/test-spidriver.cpp b/src/tests/catch/spidriver/test-spidriver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5438d812fa9bbff845e426a997a30dfe08c9e993 --- /dev/null +++ b/src/tests/catch/spidriver/test-spidriver.cpp @@ -0,0 +1,477 @@ +/** + * Copyright (c) 2020 Skyward Experimental Rocketry + * Authors: Luca Erbetta (luca.erbetta@skywarder.eu) + * + * 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. + */ + +#ifdef STANDALONE_CATCH1_TEST +#include "../catch-tests-entry.cpp" +#endif + +#include <utils/testutils/catch.hpp> + +#include "FakeSPIBus.h" +#include "drivers/spi/MockSPIBus.h" +#include "drivers/spi/SPIDriver.h" + +template <typename T1, typename T2> +bool bufcmp(T1* buf1, T2* buf2, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + if (*buf1 != *buf2) + return false; + + buf1++; + buf2++; + } + return true; +} + +TEST_CASE("SPIBus - Bus Configuration") +{ + FakeSpiTypedef spi; + + FakeSPIBus bus{&spi}; + + REQUIRE(spi.CR1 == 0); + + SECTION("Configure & check CR1") + { + SPIBusConfig config; + config.br = SPIBaudRate::DIV_16; + + config.cpha = 1; + config.cpol = 1; + config.lsb_first = true; + + uint32_t expected_CR1 = 0x03DF; + + bus.configure(config); + REQUIRE(spi.CR1 == expected_CR1); + + // Change config + config.br = SPIBaudRate::DIV_256; + + config.cpha = 0; + config.cpol = 0; + config.lsb_first = false; + + expected_CR1 = 0x037C; + + bus.configure(config); + REQUIRE(spi.CR1 == expected_CR1); + + config.cpha = 0; + config.cpol = 1; + config.lsb_first = false; + + expected_CR1 = 0x037E; + + bus.configure(config); + REQUIRE(spi.CR1 == expected_CR1); + + config.cpha = 1; + config.cpol = 0; + config.lsb_first = false; + + expected_CR1 = 0x037D; + + bus.configure(config); + REQUIRE(spi.CR1 == expected_CR1); + + // Reapply same config + bus.configure(config); + REQUIRE(spi.CR1 == expected_CR1); + } + + SECTION("Disable configuration") + { + SPIBusConfig config; + config.br = SPIBaudRate::DIV_16; + + config.cpha = 1; + config.cpol = 1; + config.lsb_first = true; + + bus.enableSlaveConfiguration(false); + bus.configure(config); + REQUIRE(spi.CR1 == 0); + } + + SECTION("Wrong parameters") + { + SPIBusConfig config; + config.br = SPIBaudRate::DIV_16; + + config.cpha = 8; + config.cpol = 9; + config.lsb_first = true; + + uint32_t expected_CR1 = 0x03DE; + + bus.configure(config); + REQUIRE(spi.CR1 == expected_CR1); + } +} + +TEST_CASE("SPIBus - Chip select") +{ + FakeSpiTypedef spi; + + FakeSPIBus bus{&spi}; + + REQUIRE(spi.cs.value() == 1); + + bus.select(spi.cs); + REQUIRE(spi.cs.value() == 0); + + bus.deselect(spi.cs); + REQUIRE(spi.cs.value() == 1); +} + +TEST_CASE("SPIBus - One byte operations") +{ + FakeSpiTypedef spi; + + spi.DR.in_buf = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + spi.CR1_expected = 0x03DF; + + FakeSPIBus bus{&spi}; + + SPIBusConfig config; + config.br = SPIBaudRate::DIV_16; + + config.cpha = 1; + config.cpol = 1; + config.lsb_first = true; + + bus.configure(config); + bus.select(spi.cs); + + SECTION("Write") + { + bus.write(1); + REQUIRE(spi.DR.out_buf.back() == 1); + REQUIRE(spi.DR.out_buf.size() == 1); + + bus.write(2); + REQUIRE(spi.DR.out_buf.back() == 2); + REQUIRE(spi.DR.out_buf.size() == 2); + } + SECTION("Read") + { + REQUIRE(bus.read() == spi.DR.in_buf[0]); + REQUIRE(spi.DR.out_buf.size() == 1); + REQUIRE(spi.DR.out_buf.back() == 0); + + REQUIRE(bus.read() == spi.DR.in_buf[1]); + REQUIRE(spi.DR.out_buf.size() == 2); + REQUIRE(spi.DR.out_buf.back() == 0); + } + + SECTION("Transfer") + { + REQUIRE(bus.transfer(55) == spi.DR.in_buf[0]); + REQUIRE(spi.DR.out_buf.back() == 55); + REQUIRE(spi.DR.out_buf.size() == 1); + + REQUIRE(bus.transfer(255) == spi.DR.in_buf[1]); + REQUIRE(spi.DR.out_buf.back() == 255); + REQUIRE(spi.DR.out_buf.size() == 2); + } +} + +TEST_CASE("SPIBus - Multi byte operations") +{ + FakeSpiTypedef spi; + + spi.DR.in_buf = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + spi.CR1_expected = 0x03DF; + + FakeSPIBus bus{&spi}; + + SPIBusConfig config; + config.br = SPIBaudRate::DIV_16; + + config.cpha = 1; + config.cpol = 1; + config.lsb_first = true; + + bus.configure(config); + bus.select(spi.cs); + + // 2 identical buffers + uint8_t buf[] = {4, 3, 2, 1}; + uint8_t bufc[] = {4, 3, 2, 1}; + + SECTION("Write") + { + bus.write(buf, 0); + REQUIRE(spi.DR.out_buf.size() == 0); + + bus.write(buf, 4); + REQUIRE(spi.DR.out_buf.size() == 4); + REQUIRE(bufcmp(bufc, spi.DR.out_buf.data(), 4)); + } + + SECTION("Read") + { + bus.read(buf, 0); + // Nothing read + REQUIRE(bufcmp(bufc, buf, 4)); + + bus.read(buf, 4); + + REQUIRE(bufcmp(buf, spi.DR.in_buf.data(), 4)); + } + + SECTION("Transfer") + { + bus.transfer(buf, 0); + // Nothing read + REQUIRE(bufcmp(bufc, buf, 4)); + // Nothing written + REQUIRE(spi.DR.out_buf.size() == 0); + + bus.transfer(buf, 4); + REQUIRE(spi.DR.out_buf.size() == 4); + + REQUIRE(bufcmp(bufc, spi.DR.out_buf.data(), 4)); + REQUIRE(bufcmp(buf, spi.DR.in_buf.data(), 4)); + } +} + +TEST_CASE("SPITransaction - writes") +{ + MockSPIBus bus{}; + SPIBusConfig config1{}; + + config1.cpha = 1; + config1.br = SPIBaudRate::DIV_32; + + bus.expected_config = config1; + + SECTION("Transaction") + { + SPITransaction spi(bus, GpioPin(GPIOA_BASE, 1), config1); + + REQUIRE(bus.out_buf.size() == 0); + + SECTION("cmd write") + { + spi.write(9); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 1); + REQUIRE(bus.out_buf.back() == 9); + } + + SECTION("1 byte reg write") + { + spi.write(10, 77); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 2); + REQUIRE(bus.out_buf[0] == 10); + REQUIRE(bus.out_buf[1] == 77); + } + + SECTION("multi byte reg write") + { + uint8_t buf[] = {1, 2, 3, 4, 5, 6}; + + SECTION("0 size write") + { + spi.write(10, buf, 0); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 1); + REQUIRE(bus.out_buf[0] == 10); + } + + SECTION("2 writes") + { + spi.write(10, buf, 4); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 5); + + REQUIRE(bus.out_buf[0] == 10); + REQUIRE(bufcmp(buf, bus.out_buf.data() + 1, 4)); + + spi.write(99, buf, 6); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 12); + + REQUIRE(bus.out_buf[5] == 99); + REQUIRE(bufcmp(buf, bus.out_buf.data() + 6, 6)); + } + } + + SECTION("raw write") + { + uint8_t buf[] = {1, 2, 3, 4, 5, 6}; + + spi.write(buf, 0); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 0); + + spi.write(buf, 4); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 4); + + REQUIRE(bufcmp(buf, bus.out_buf.data(), 4)); + + spi.write(buf, 6); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 10); + + REQUIRE(bufcmp(buf, bus.out_buf.data() + 4, 6)); + } + } +} + +TEST_CASE("SPITransaction - reads") +{ + MockSPIBus bus; + + bus.in_buf = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + SPIBusConfig config1; + + config1.cpha = 1; + config1.br = SPIBaudRate::DIV_32; + + bus.expected_config = config1; + + SECTION("Transaction") + { + SPISlave slave(bus, GpioPin(GPIOA_BASE, 1), config1); + SPITransaction spi(slave); + + REQUIRE(bus.out_buf.size() == 0); + + SECTION("1 byte reg read") + { + + REQUIRE(spi.read(0x05) == 1); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 1); + REQUIRE(bus.out_buf.back() == 0x85); + + REQUIRE(spi.read(0x05, true) == 2); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 2); + REQUIRE(bus.out_buf.back() == 0x85); + + REQUIRE(spi.read(0x05, false) == 3); + REQUIRE_FALSE(bus.isSelected()); + + REQUIRE(bus.out_buf.size() == 3); + REQUIRE(bus.out_buf.back() == 0x05); + } + + SECTION("multi byte reg read") + { + uint8_t read[4] = {0, 0, 0, 0}; + uint8_t zero[4] = {0, 0, 0, 0}; + + spi.read(0x05, read, 0); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 1); + REQUIRE(bus.out_buf.back() == 0x85); + REQUIRE(bufcmp(read, zero, 4)); + + spi.read(0x05, read, 3); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 2); + REQUIRE(bus.out_buf.back() == 0x85); + REQUIRE(bufcmp(read, bus.in_buf.data(), 3)); + + spi.read(0x05, read, 3, true); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 3); + REQUIRE(bus.out_buf.back() == 0x85); + REQUIRE(bufcmp(read, bus.in_buf.data() + 3, 3)); + + spi.read(0x05, read, 4, false); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 4); + REQUIRE(bus.out_buf.back() == 0x05); + REQUIRE(bufcmp(read, bus.in_buf.data() + 6, 4)); + } + + SECTION("multi byte raw read") + { + uint8_t read[4] = {0, 0, 0, 0}; + uint8_t zero[4] = {0, 0, 0, 0}; + + spi.read(read, 0); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 0); + REQUIRE(bufcmp(read, zero, 4)); + + spi.read(read, 3); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 0); + REQUIRE(bufcmp(read, bus.in_buf.data(), 3)); + } + } +} + +TEST_CASE("SPITransaction - transfer") +{ + MockSPIBus bus; + + bus.in_buf = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + SPIBusConfig config1; + + config1.cpha = 1; + config1.br = SPIBaudRate::DIV_32; + + bus.expected_config = config1; + + SECTION("Transaction") + { + SPISlave slave(bus, GpioPin(GPIOA_BASE, 1), config1); + SPITransaction spi(slave); + + uint8_t buf[4] = {4, 3, 2, 1}; + uint8_t cmp[4] = {4, 3, 2, 1}; + + spi.transfer(buf, 0); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 0); + REQUIRE(bufcmp(buf, cmp, 4)); + + spi.transfer(buf, 4); + REQUIRE_FALSE(bus.isSelected()); + REQUIRE(bus.out_buf.size() == 4); + REQUIRE(bufcmp(buf, bus.in_buf.data(), 4)); + REQUIRE(bufcmp(cmp, bus.out_buf.data(), 4)); + } +} \ No newline at end of file diff --git a/src/tests/catch/test-hardwaretimer.cpp b/src/tests/catch/test-hardwaretimer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f954fdbe3ced5a579afc37a26aaecee851396ad6 --- /dev/null +++ b/src/tests/catch/test-hardwaretimer.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019 Skyward Experimental Rocketry + * Authors: Luca Erbetta + * + * 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. + */ + +#ifdef STANDALONE_CATCH1_TEST +#include "catch-tests-entry.cpp" +#endif + +#include <drivers/HardwareTimer.h> +#include <miosix.h> + +#include <utils/testutils/catch.hpp> + +using namespace miosix; + +class TimerTestFixture +{ +public: + TimerTestFixture() + : timer32(TIM5, TimerUtils::getPrescalerInputFrequency( + TimerUtils::InputClock::APB1)), + timer16(TIM10, TimerUtils::getPrescalerInputFrequency( + TimerUtils::InputClock::APB2)) + { + RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; + RCC->APB2ENR |= RCC_APB2ENR_TIM10EN; + } + + ~TimerTestFixture() + { + timer16.stop(); + timer32.stop(); + + RCC->APB1ENR &= ~RCC_APB1ENR_TIM5EN; + RCC->APB2ENR &= ~RCC_APB2ENR_TIM10EN; + } + + HardwareTimer<uint32_t> timer32; + HardwareTimer<uint16_t> timer16; +}; + +TEST_CASE_METHOD(TimerTestFixture, "Test basic functionality") +{ + timer32.setPrescaler(63); + timer16.setPrescaler(127); + + REQUIRE(timer32.getMaxDuration() == Approx(3272.356).margin(0.001)); + REQUIRE(timer16.getMaxDuration() == Approx(0.049).margin(0.001)); + + REQUIRE(timer32.getResolution() == Approx(0.761).margin(0.001)); + REQUIRE(timer16.getResolution() == Approx(0.761).margin(0.001)); + + REQUIRE(timer32.tick() == 0); + REQUIRE(timer16.tick() == 0); + + REQUIRE(timer32.start() == 0); + REQUIRE(timer16.start() == 0); + + Thread::sleep(10); + + uint32_t tick32 = timer32.tick(); + uint32_t tick16 = timer16.tick(); + + REQUIRE(timer32.toMilliSeconds(tick32) == Approx(10).margin(1)); + REQUIRE(timer16.toMilliSeconds(tick16) == Approx(10).margin(1)); + + Thread::sleep(30); + + REQUIRE(timer32.toMilliSeconds(timer32.tick() - tick32) == + Approx(30).margin(1)); + REQUIRE(timer16.toMilliSeconds(timer16.tick() - tick16) == + Approx(30).margin(1)); + + tick32 = timer32.stop(); + tick16 = timer16.stop(); + + Thread::sleep(20); + + REQUIRE(timer32.tick() == tick32); + REQUIRE(timer16.tick() == tick16); +} + +TEST_CASE_METHOD(TimerTestFixture, "Test long term precision") +{ + timer32.setPrescaler(63); + timer16.setPrescaler(65535); // Max prescaler + + REQUIRE(timer32.getMaxDuration() == Approx(3272.356).margin(0.001)); + REQUIRE(timer16.getMaxDuration() == Approx(25.565).margin(0.001)); + + REQUIRE(timer32.getResolution() == Approx(0.761).margin(0.001)); + REQUIRE(timer16.getResolution() == Approx(390.095).margin(0.001)); + + REQUIRE(timer32.start() == 0); + REQUIRE(timer16.start() == 0); + + Thread::sleep(24000); + + uint32_t tick32 = timer32.tick(); + uint32_t tick16 = timer16.tick(); + + REQUIRE(timer32.toMilliSeconds(timer32.tick()) == Approx(24000).margin(1)); + REQUIRE(timer16.toMilliSeconds(timer16.tick()) == Approx(24000).margin(1)); + + Thread::sleep(36000); + + REQUIRE(timer32.toMilliSeconds(timer32.tick()) == Approx(60000).margin(1)); +} \ No newline at end of file diff --git a/src/tests/catch/test-matrix.cpp b/src/tests/catch/test-matrix.cpp index 32ee05424d6897230642bc8afe077bdc30bb82a7..8a5ef3dbf9f2dfa995c4b32dd2c0b32dde170b3e 100644 --- a/src/tests/catch/test-matrix.cpp +++ b/src/tests/catch/test-matrix.cpp @@ -34,6 +34,8 @@ TEST_CASE("Multiply test") MatrixBase<float, 3, 1> B{1.0f, 2.0f, 3.7f}; MatrixBase<float, 3, 1> C{}; + C = A * B; + REQUIRE( C(0,0) == Approx( 8.1f) ); REQUIRE( C(1,0) == Approx(-7.7f) ); REQUIRE( C(2,0) == Approx(-10.3f) ); @@ -46,6 +48,8 @@ TEST_CASE("Sum test") MatrixBase<float, 3, 3> C{}; + C = A + B; + REQUIRE( C(0,0) == Approx(2.0f )); REQUIRE( C(0,1) == Approx(0.0f )); REQUIRE( C(0,2) == Approx(6.0f )); @@ -64,6 +68,8 @@ TEST_CASE("Subtract test") MatrixBase<float, 3, 3> C{}; + C = A - B; + REQUIRE( C(0,0) == Approx(0.0f )); REQUIRE( C(0,1) == Approx(-4.0f )); REQUIRE( C(0,2) == Approx(0.0f )); diff --git a/src/tests/drivers/test-dsgamma.cpp b/src/tests/drivers/test-dsgamma.cpp index 5ff13c1baaaf45e57e67a4eb0b1f19bfada0b0dd..43bd00f67db139ddf5942af7f0469f30f259a391 100644 --- a/src/tests/drivers/test-dsgamma.cpp +++ b/src/tests/drivers/test-dsgamma.cpp @@ -16,7 +16,6 @@ using miosix::Gpio; /* DISCOVERY F429I*/ typedef Gpio<GPIOA_BASE, 0> button; -typedef HardwareTimer<uint32_t, 4> Clock; // RTT calculation // long long sendTime = 0; enum State diff --git a/src/tests/drivers/test-l3gd20.cpp b/src/tests/drivers/test-l3gd20.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5a63718ccb9427fc4f07f0d0f18afe5fc78ddd0 --- /dev/null +++ b/src/tests/drivers/test-l3gd20.cpp @@ -0,0 +1,240 @@ +/** + * Copyright (c) 2019 Skyward Experimental Rocketry + * Authors: Luca Erbetta + * + * 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 <array> + +#include "diagnostic/CpuMeter.h" +#include "drivers/HardwareTimer.h" +#include "drivers/spi/SPIDriver.h" +#include "sensors/L3GD20.h" + +using namespace miosix; +using std::array; + +typedef Gpio<GPIOF_BASE, 7> GpioSck; +typedef Gpio<GPIOF_BASE, 8> GpioMiso; +typedef Gpio<GPIOF_BASE, 9> GpioMosi; + +typedef Gpio<GPIOA_BASE, 2> GpioINT2; + +static constexpr bool FIFO_ENABLED = true; +static constexpr unsigned int FIFO_WATERMARK = 24; +// Expected frequency from the datasheet is 760 Hz, but due to clock +// misalignment / temperature errors and other factors, the observed clock (and +// output data rate) is the following: +static constexpr float SAMPLE_FREQUENCY = 782.3f; +static constexpr int NUM_SAMPLES = 10000; + +void enableInterrupt(); + +struct GyroSample +{ + int fifo_num; + float timestamp; + Vec3 data; + int level; + float wtm_delta; + float cpu; +}; + +// 364 KB buffer to store up to 30 seconds of data @ 760 Hz +GyroSample data[22800]; +int data_counter = 0; + +// High resolution hardware timer using TIM5 +HardwareTimer<uint32_t> hrclock{ + TIM5, TimerUtils::getPrescalerInputFrequency(TimerUtils::InputClock::APB1)}; + +// Last interrupt received timer tick +volatile uint32_t last_watermark_tick; // Stores the high-res tick of the last + // interrupt (L3GD20 watermark event) +volatile uint32_t watermark_delta; // Tick delta between the last 2 watermark + // events + +// L3GD20 SPI +SPIBus bus(SPI5); +GpioPin cs(GPIOC_BASE, 1); +SPIBusConfig cfg; +uint8_t buf[192]; + +// Interrupt handlers +void __attribute__((naked)) EXTI2_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20EXTI2_IRQHandlerImplv"); + restoreContext(); +} + +void __attribute__((used)) EXTI2_IRQHandlerImpl() +{ + uint32_t tick = hrclock.tick(); + watermark_delta = tick - last_watermark_tick; + last_watermark_tick = tick; + + EXTI->PR |= EXTI_PR_PR2; // Reset pending register +} + +int main() +{ + cfg.br = SPIBaudRate::DIV_64; + + { + FastInterruptDisableLock dLock; + + RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; + RCC->APB2ENR |= RCC_APB2ENR_SPI5EN; + + GpioSck::mode(Mode::ALTERNATE); + GpioMiso::mode(Mode::ALTERNATE); + GpioMosi::mode(Mode::ALTERNATE); + + GpioSck::alternateFunction(5); + GpioMiso::alternateFunction(5); + GpioMosi::alternateFunction(5); + + // Interrupt + GpioINT2::mode(Mode::INPUT_PULL_DOWN); + + cs.mode(Mode::OUTPUT); + } + // High resolution clock configuration + // 1.8 hours run time, 1.5 us resolution + hrclock.setPrescaler(127); + hrclock.start(); + + enableInterrupt(); + cs.high(); + + L3GD20 gyro(bus, cs, L3GD20::FullScaleRange::FS_250, + L3GD20::OutPutDataRate::ODR_760, 0x03, FIFO_ENABLED, + FIFO_WATERMARK); + + while (!gyro.init()) + { + } + + Thread::sleep(500); + + long long first_tick = miosix::getTick(); + + // Sample NUM_SAMPLES data + int fifo_num = 0; + while (data_counter < NUM_SAMPLES) + { + long last_tick = miosix::getTick(); + + if (FIFO_ENABLED) + { + // Precise timestamp of the last sample in the FIFO + float wtm_timestamp = hrclock.toSeconds(last_watermark_tick); + float wtm_delta = hrclock.toMilliSeconds(watermark_delta); + + // Read the fifo + gyro.onSimpleUpdate(); + + uint8_t level = + gyro.getLastFifoSize(); // Number of samples in the FIFO + + const array<Vec3, 32>& fifo = gyro.getLastFifo(); + + for (int i = 0; i < level; i++) + { + // Assign a timestamp to each sample in the FIFO + // Samples before the watermark are older, after the watermark + // are younger. Time delta between samples is + // (1 / SAMPLE_FREQUENCY) seconds + float ts = wtm_timestamp + + (i - (int)FIFO_WATERMARK) / SAMPLE_FREQUENCY; + + data[data_counter++] = {fifo_num, ts, + fifo[i], level, + wtm_delta, averageCpuUtilization()}; + if (data_counter >= NUM_SAMPLES) + { + break; + } + } + ++fifo_num; + + // Wait until fifo has about 25 samples + Thread::sleepUntil(last_tick + 34); + } + else + { + // Sample sensor @ 500 Hz + gyro.onSimpleUpdate(); + + data[data_counter++] = {0, + (last_tick - first_tick) / 1000.0f, + *(gyro.gyroDataPtr()), + 0, + 0, + averageCpuUtilization()}; + Thread::sleepUntil(last_tick + 2); + } + } + // Dump buffer content as CSV on the serial (might take a while) + printf("FIFO_num,timestamp,watermark_delta,sample_delta,x,y,z,level,cpu"); + for (int i = 1; i < data_counter; i++) + { + printf("%d,%.3f,%.3f,%.3f,%f,%f,%f,%d,%.2f\n", data[i].fifo_num, + data[i].timestamp * 1000, data[i].wtm_delta, + (data[i].timestamp - data[i - 1].timestamp) * 1000, + data[i].data.getX(), data[i].data.getY(), data[i].data.getZ(), + data[i].level, data[i].cpu); + } + + printf("\n\n\nend.\n"); + for (;;) + { + Thread::sleep(1000); + } +} + +void enableInterrupt() +{ + { + FastInterruptDisableLock l; + RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; + } + // Refer to the datasheet for a detailed description on the procedure and + // interrupt registers + + // Clear the mask on the wanted line + EXTI->IMR |= EXTI_IMR_MR2; + + // Trigger the interrupt on a falling edge + // EXTI->FTSR |= EXTI_FTSR_TR2; + + // Trigger the interrupt on a rising edge + EXTI->RTSR |= EXTI_RTSR_TR2; + + EXTI->PR |= EXTI_PR_PR2; // Reset pending register + + // Enable interrupt on PA2 in SYSCFG + SYSCFG->EXTICR[0] &= 0xFFFFF0FF; + + // // Enable the interrput in the interrupt controller + NVIC_EnableIRQ(EXTI2_IRQn); + NVIC_SetPriority(EXTI2_IRQn, 15); +} \ No newline at end of file diff --git a/src/tests/drivers/test-ms5803.cpp b/src/tests/drivers/test-ms5803.cpp index e35b80540ef18b2850160b9ba149b7592d233d2d..1a339433d914d7b5005246c6bf62f038f9b8a18e 100644 --- a/src/tests/drivers/test-ms5803.cpp +++ b/src/tests/drivers/test-ms5803.cpp @@ -21,27 +21,32 @@ */ #include <Common.h> -#include <drivers/BusTemplate.h> -#include <interfaces-impl/hwmapping.h> -#include "sensors/MS580301BA07/MS580301BA07.h" - +#include <drivers/spi/SPIDriver.h> #include <drivers/spi/SensorSpi.h> +#include <interfaces-impl/hwmapping.h> #include <sensors/SensorSampling.h> +#include "sensors/MS580301BA07/MS580301BA07.h" + using namespace miosix; using namespace miosix::interfaces; -typedef Gpio<GPIOD_BASE, 7> cs_ms58; -typedef BusSPI<1, spi1::mosi, spi1::miso, spi1::sck> busSPI1; -typedef ProtocolSPI<busSPI1, cs_ms58> spiMS58; -typedef MS580301BA07<spiMS58> ms58_t; +SPIBus bus{SPI1}; +GpioPin chip_select{GPIOD_BASE, 7}; int main() { + // Activate the SPI bus + { + FastInterruptDisableLock dLock; + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; + + // SCK, MISO, MOSI already initialized in the bsp + } + SimpleSensorSampler sampler; - spiMS58::init(); - ms58_t* ms58 = new ms58_t(); + MS580301BA07* ms58 = new MS580301BA07(bus, chip_select); Thread::sleep(100); @@ -70,8 +75,8 @@ int main() const float* last_pressure = ms58->pressureDataPtr(); const float* last_temp = ms58->tempDataPtr(); MS5803Data md = ms58->getData(); - printf("%d,%f,%d,%f\n", (int)md.raw_press, - *last_pressure, (int)md.raw_temp, *last_temp); + printf("%d,%f,%d,%f\n", (int)md.raw_press, *last_pressure, + (int)md.raw_temp, *last_temp); Thread::sleep(100); } diff --git a/src/tests/kalman/test-kalman-benchmark.cpp b/src/tests/kalman/test-kalman-benchmark.cpp index bd9ba8ad8b949e9187c3700e48ceff2e0becb5dc..bbcd86bcda1eec3cff0b1f9fd7f94e46874f6e19 100644 --- a/src/tests/kalman/test-kalman-benchmark.cpp +++ b/src/tests/kalman/test-kalman-benchmark.cpp @@ -23,14 +23,15 @@ // This prgram runs through a simulated flight and reports the apogee detection, // while measuring the time elapsed - // RESULT: Update operation 0.0319 on average #include <Common.h> #include <drivers/HardwareTimer.h> #include <kalman/Kalman.h> + #include <iostream> -#include "util/util.h" + #include "test-kalman-data.h" +#include "util/util.h" using namespace miosix; @@ -52,17 +53,18 @@ int main(int argc, char const* argv[]) } // Timer for benchmarking purposes - HardwareTimer<uint32_t, 2>& timer = HardwareTimer<uint32_t, 2>::instance(); + HardwareTimer<uint32_t> timer{TIM5, TimerUtils::getPrescalerInputFrequency( + TimerUtils::InputClock::APB1)}; // Instanciate matrices - MatrixBase<float,3,3> P{0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1}; - MatrixBase<float,1,1> V2{10}; - MatrixBase<float,3,3> V1{0.01, 0, 0, 0, 0.01, 0, 0, 0, 0.01}; - MatrixBase<float,1,3> C{1, 0, 0}; - MatrixBase<float,3,3> A{1, 0, 0, 0, 1, 0, 0, 0, 1}; + MatrixBase<float, 3, 3> P{0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1}; + MatrixBase<float, 1, 1> V2{10}; + MatrixBase<float, 3, 3> V1{0.01, 0, 0, 0, 0.01, 0, 0, 0, 0.01}; + MatrixBase<float, 1, 3> C{1, 0, 0}; + MatrixBase<float, 3, 3> A{1, 0, 0, 0, 1, 0, 0, 0, 1}; // Instanciate filter object - Kalman<3,1> filter = Kalman<3,1>(A, C, V1, V2, P); + Kalman<3, 1> filter = Kalman<3, 1>(A, C, V1, V2, P); float last_time = 0.0; // Variable to save the time of the last sample float time; // Current time as read from csv file @@ -78,7 +80,7 @@ int main(int argc, char const* argv[]) { if (i == 0) { - filter.X(0,0) = INPUT[0]; + filter.X(0, 0) = INPUT[0]; continue; } time = TIME[i]; @@ -88,8 +90,8 @@ int main(int argc, char const* argv[]) filter.A(0, 2) = 0.5 * T * T; filter.A(1, 2) = T; - MatrixBase<float,1,1> y{}; - y(0,0) = INPUT[i]; + MatrixBase<float, 1, 1> y{}; + y(0, 0) = INPUT[i]; tick1 = timer.tick(); filter.update(y); @@ -98,7 +100,7 @@ int main(int argc, char const* argv[]) // printf("%f, %f, %f;\n", filter.X(0,0), filter.X(1,0), filter.X(2,0)); // std::cout << MemoryProfiling::getCurrentFreeStack() << "\n"; last_time = time; - if (filter.X(1,0) < 0) + if (filter.X(1, 0) < 0) { greenLed::high(); redLed::low(); diff --git a/src/tests/misc/xbee-bitrate.cpp b/src/tests/misc/xbee-bitrate.cpp index 229fa358f1f54b48fc16f47d6aeee8b5aaf8b239..c4b4f5f8ae287d4188130e707f22104e57d5832b 100644 --- a/src/tests/misc/xbee-bitrate.cpp +++ b/src/tests/misc/xbee-bitrate.cpp @@ -31,7 +31,7 @@ #include "drivers/Xbee/Xbee.h" #include "math/Stats.h" -#include <drivers/BusTemplate.h> +#include <drivers/spi/SPIDriver.h> using std::cin; using std::cout; @@ -43,20 +43,24 @@ static constexpr int PKT_NUM = 100; using namespace miosix; using namespace interfaces; -// SPI1 binding al sensore - // WARNING: If flashing on stm32f49 discovery board (with screen removed) use // SPI1 as the 2nd isnt working. -typedef BusSPI<1, spi1::mosi, spi1::miso, spi1::sck> busSPI2; // Creo la SPI2 -// typedef BusSPI<1, spi2::mosi, spi2::miso, spi2::sck> busSPI2; // Creo la -// SPI2 +// Discovery +SPIBus bus{SPI1}; +GpioPin cs = sensors::lsm6ds3h::cs::getPin(); +GpioPin attn = xbee::attn::getPin(); +GpioPin rst = xbee::reset::getPin(); + +// Death stack +// SPIBus bus{SPI2}; +// GpioPin cs = xbee::cs::getPin(); +// GpioPin attn = xbee::attn::getPin(); +// GpioPin rst = xbee::reset::getPin(); -// WARNING: Don't use xbee::cs on discovery board as it isn't working -typedef Xbee::Xbee<busSPI2, sensors::lsm6ds3h::cs, xbee::attn, xbee::reset> - Xbee_t; -Xbee_t xbee_transceiver; +Xbee::Xbee* xbee_transceiver; + void __attribute__((used)) EXTI10_IRQHandlerImpl() { Xbee::handleATTNInterrupt(); } void enableXbeeInterrupt() @@ -102,7 +106,7 @@ bool sendPacket(uint8_t size) } ++snd_cntr; - if (!xbee_transceiver.send(snd_buf, size)) + if (!xbee_transceiver->send(snd_buf, size)) { return false; } @@ -119,8 +123,9 @@ void resetXBee() int main() { enableXbeeInterrupt(); - busSPI2::init(); - xbee_transceiver.start(); + xbee_transceiver = new Xbee::Xbee(bus, cs, attn, rst); + + xbee_transceiver->start(); resetXBee(); printf("XBee bitrate measurement\n"); diff --git a/src/tests/misc/xbee-send-rcv.cpp b/src/tests/misc/xbee-send-rcv.cpp index a5fced8963c8e89aa73735b3e7a93b8a918813af..6365eae4adccde76e97b29f9a472b6011a8210c4 100644 --- a/src/tests/misc/xbee-send-rcv.cpp +++ b/src/tests/misc/xbee-send-rcv.cpp @@ -21,8 +21,10 @@ * THE SOFTWARE. */ +#include <drivers/spi/SPIDriver.h> #include <interfaces-impl/hwmapping.h> #include <miosix.h> + #include <cstdio> #include <iostream> #include <string> @@ -31,33 +33,36 @@ #include "drivers/Xbee/Xbee.h" #include "math/Stats.h" -#include <drivers/BusTemplate.h> - using std::cin; using std::cout; using std::string; -using HwTimer = HardwareTimer<uint32_t, 2>; static const unsigned int PKT_SIZE = 128; using namespace miosix; using namespace interfaces; -// SPI1 binding al sensore - // WARNING: If flashing on stm32f49 discovery board (with screen removed) use // SPI1 as the 2nd isnt working. -// typedef BusSPI<1, spi1::mosi, spi1::miso, spi1::sck> busSPI2; // Creo la SPI2 -typedef BusSPI<2, spi2::mosi, spi2::miso, spi2::sck> busSPI2; // Creo la -// SPI2 +// Discovery +SPIBus bus{SPI1}; +GpioPin cs = sensors::lsm6ds3h::cs::getPin(); +GpioPin attn = xbee::attn::getPin(); +GpioPin rst = xbee::reset::getPin(); -// WARNING: Don't use xbee::cs on discovery board as it isn't working -typedef Xbee::Xbee<busSPI2, xbee::cs, xbee::attn, xbee::reset> - Xbee_t; +// Death stack +// SPIBus bus{SPI2}; +// GpioPin cs = xbee::cs::getPin(); +// GpioPin attn = xbee::attn::getPin(); +// GpioPin rst = xbee::reset::getPin(); -Xbee_t xbee_transceiver; -void __attribute__((used)) EXTI10_IRQHandlerImpl() { Xbee::handleATTNInterrupt(); } +Xbee::Xbee* xbee_transceiver; + +void __attribute__((used)) EXTI10_IRQHandlerImpl() +{ + Xbee::handleATTNInterrupt(); +} void enableXbeeInterrupt() { @@ -103,7 +108,7 @@ void send() c = 48; } - if (!xbee_transceiver.send(buf, PKT_SIZE)) + if (!xbee_transceiver->send(buf, PKT_SIZE)) { printf("[%d] Send error %d\n", (int)getTick(), ++fail); } @@ -124,7 +129,7 @@ void receive(void*) uint8_t buf[512]; for (;;) { - ssize_t len = xbee_transceiver.receive(buf, 512); + ssize_t len = xbee_transceiver->receive(buf, 512); if (len <= 0) { printf("Receive failed.\n"); @@ -168,12 +173,9 @@ int main() // reset(); - HwTimer& t = HwTimer::instance(); - t.setPrescaler(1024); - - busSPI2::init(); + xbee_transceiver = new Xbee::Xbee(bus, cs, attn, rst); - xbee_transceiver.start(); + xbee_transceiver->start(); // Send & receive Thread::create(receive, 2048); diff --git a/src/tests/misc/xbee-time-to-send.cpp b/src/tests/misc/xbee-time-to-send.cpp index 4e2d8dc7a45cc7eedb5f3395c67f6cfa2a169a21..1418e671bffbcd03b7ae12717a974b650af10297 100644 --- a/src/tests/misc/xbee-time-to-send.cpp +++ b/src/tests/misc/xbee-time-to-send.cpp @@ -44,19 +44,24 @@ static constexpr int PKTS_PER_SECOND = 4; using namespace miosix; using namespace interfaces; -// SPI1 binding al sensore - // WARNING: If flashing on stm32f49 discovery board (with screen removed) use // SPI1 as the 2nd isnt working. -// typedef BusSPI<1, spi1::mosi, spi1::miso, spi1::sck> busSPI2; // Creo la SPI2 -typedef BusSPI<2, spi2::mosi, spi2::miso, spi2::sck> busSPI2; +// Discovery +SPIBus bus{SPI1}; +GpioPin cs = sensors::lsm6ds3h::cs::getPin(); +GpioPin attn = xbee::attn::getPin(); +GpioPin rst = xbee::reset::getPin(); + +// Death stack +// SPIBus bus{SPI2}; +// GpioPin cs = xbee::cs::getPin(); +// GpioPin attn = xbee::attn::getPin(); +// GpioPin rst = xbee::reset::getPin(); + +Xbee::Xbee* xbee_transceiver; -// WARNING: Don't use xbee::cs on discovery board as it isn't working -typedef Xbee::Xbee<busSPI2, xbee::cs, xbee::attn, xbee::reset> - Xbee_t; -Xbee_t xbee_transceiver; void __attribute__((used)) EXTI10_IRQHandlerImpl() { Xbee::handleATTNInterrupt(); } void enableXbeeInterrupt() @@ -101,7 +106,7 @@ bool sendPacket(uint8_t size) } ++snd_cntr; - if (!xbee_transceiver.send(snd_buf, size)) + if (!xbee_transceiver->send(snd_buf, size)) { return false; } @@ -112,8 +117,10 @@ bool sendPacket(uint8_t size) int main() { enableXbeeInterrupt(); - busSPI2::init(); - xbee_transceiver.start(); + + xbee_transceiver = new Xbee::Xbee(bus, cs, attn, rst); + + xbee_transceiver->start(); printf("XBee time-to-send-measurement\n"); printf(