Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • avn/swd/skyward-boardcore
  • emilio.corigliano/skyward-boardcore
  • ettore.pane/skyward-boardcore
  • giulia.facchi/skyward-boardcore
  • valerio.flamminii/skyward-boardcore
  • nicolo.caruso/skyward-boardcore
6 results
Select Git revision
Show changes
Showing
with 97 additions and 70196 deletions
# Copyright (c) 2025 Skyward Experimental Rocketry
# Author: Niccolò Betto
#
# 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.
# --- Version Information File Generation ---
# Find Git executable
find_package(Git REQUIRED QUIET)
set(GIT_WORKING_DIR ${CMAKE_SOURCE_DIR})
# Determine dirty status
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --dirty --always --broken
WORKING_DIRECTORY ${GIT_WORKING_DIR}
OUTPUT_VARIABLE GIT_DESCRIBE_OUTPUT # We don't use the output directly, just for the dirty check
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE GIT_DESCRIBE_RESULT
ERROR_QUIET # Ignore errors
)
set(GIT_DIRTY_SUFFIX "")
# Check if the command succeeded AND if its output ends with "-dirty"
if(GIT_DESCRIBE_RESULT EQUAL 0 AND GIT_DESCRIBE_OUTPUT MATCHES "-dirty$")
set(GIT_DIRTY_SUFFIX "-dirty")
endif()
# Get *annotated* tag name
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --exact-match HEAD
WORKING_DIRECTORY ${GIT_WORKING_DIR}
OUTPUT_VARIABLE GIT_TAG # Captures the annotated tag name if successful
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE GIT_EXACT_TAG_RESULT # Will be 0 on success, non-zero on failure
ERROR_QUIET # Ignore errors, we expect this to fail when not on an annotated tag
)
# Get branch name
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${GIT_WORKING_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
# No ERROR_QUIET/RESULT_VARIABLE -> CMake halts on git command failure
)
# Get short commit hash
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
WORKING_DIRECTORY ${GIT_WORKING_DIR}
OUTPUT_VARIABLE GIT_REV
OUTPUT_STRIP_TRAILING_WHITESPACE
# No ERROR_QUIET/RESULT_VARIABLE -> CMake halts on git command failure
)
# Determine the version info based on tag match result
set(GIT_VERSION_INFO "")
if(GIT_EXACT_TAG_RESULT EQUAL 0)
# Use the tag name when exactly on an annotated tag
set(GIT_VERSION_INFO "${GIT_TAG}")
else()
# Use branch-rev when not on a tag
set(GIT_VERSION_INFO "${GIT_BRANCH}-${GIT_REV}")
endif()
# Construct the final version string
# Format: <annotated_tag>[-dirty] OR <branch>-<hash>[-dirty]
set(GIT_VERSION_STRING "${GIT_VERSION_INFO}${GIT_DIRTY_SUFFIX}")
# Additional variables set by the called of this script via command line args:
# - TARGET_NAME
# - CMAKE_BUILD_TYPE
configure_file(
"${BOARDCORE_PATH}/version/version.cpp.in"
"${OUT_DIR}/version.cpp"
)
"Category","Name (letters,numbers,underscores,NO SPACES, all caps!)","Description","Generated Name(autogenerated column!)"
"ANAKIN","TEST_FAULT","This fault is for testing","F_ANAKIN_TEST_FAULT"
# This is an STM32F4 discovery board with a single STM32F407VGT6 chip.
# http://www.st.com/internet/evalboard/product/252419.jsp
source [find interface/stlink-v2-1.cfg]
source [find target/stm32f4x.cfg]
# use hardware reset, connect under reset
reset_config srst_only srst_nogate
# This is an STM32F7 discovery board with a single STM32F407VGT6 chip.
# http://www.st.com/internet/evalboard/product/252419.jsp
source [find interface/stlink-v2-1.cfg]
source [find target/stm32f7x.cfg]
# use hardware reset, connect under reset
reset_config srst_only srst_nogate
This diff is collapsed.
Subproject commit 05993b2739364c7d4cb510ad6840a62cca6c69cd
Subproject commit ac64dd5294f23f78a89f0f7c335c7a4f7f5394fd
Subproject commit ed8a4d8c24e59ce7513b1cebc9b6427fde55c7ce
Subproject commit 011ff9ede6fab86ccba5de8eb08f4bd02d7d7522
Subproject commit d9c27d87e35ec11cc3eb72ebf2b22b59d2104ce1
Subproject commit da07e825bc6c02b86cef8e1ec6e62b4d1978b762
Subproject commit 85c4cfb0cddab47ceb14ed158559ffd4fa7af288
Subproject commit e300a6c2587d29f4795b67ee0eaebf18aeebb5cb
/* Copyright (c) 2015-2016 Skyward Experimental Rocketry
* Authors: Alain Carlucci, Matteo Piazzolla
*
* 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 <Common.h>
#include <drivers/BusTemplate.h>
#include <sensors/MAX21105.h>
#include <sensors/MPU9250.h>
#include <sensors/iNemo.h>
using namespace miosix;
#define SENSOR(NAME, CSPORT, CSPIN) \
typedef Gpio<GPIO##CSPORT##_BASE, CSPIN> CS_##NAME; \
typedef ProtocolSPI<busSPI1, CS_##NAME> spi##NAME
#define READ(NAME, ID) spi##NAME::read(ID)
#define TEST(NAME, REG, REF) test_and_print(#NAME, REG, READ(NAME, REG), REF)
#define ACCEL_ENDLESS_TEST
// SPI1
typedef Gpio<GPIOA_BASE, 5> GpioSck;
typedef Gpio<GPIOA_BASE, 6> GpioMiso;
typedef Gpio<GPIOA_BASE, 7> GpioMosi;
typedef BusSPI<1, GpioMosi, GpioMiso, GpioSck> busSPI1;
// clang-format off
// Here's the list of SPI sensors that should be tested
// | NAME | CSPORT | CSPIN |
SENSOR(MPU9250, D, 13);
SENSOR(FXAS21002, G, 10);
SENSOR(MAX21105, E, 2);
SENSOR(MAX31856, B, 11);
SENSOR(LSM9DS0_G, G, 9);
SENSOR(LSM9DS0_A, G, 11);
SENSOR(LPS331AP, E, 4);
SENSOR(MS580301BA07, B, 10);
// clang-format on
static const char mOK[] = "OK";
static const char mKO[] = "FAIL";
int num_ok = 0, num_fail = 0;
typedef MAX21105<spiMAX21105> max_t;
typedef iNEMOLSM9DS0<spiLSM9DS0_G, spiLSM9DS0_A> lsm_t;
typedef MPU9250<spiMPU9250> mpu_t;
typedef struct
{
Vec3 v[3];
} vec_t;
Vec3 calcMid(vec_t data[5], int j)
{
Vec3 ret;
for (int i = 0; i < 5; i++)
ret += Vec3(data[i].v[j]);
ret *= 1.0 / 5.0;
return ret;
}
void calibrate(const vec_t &data[10])
{
for (const auto &i : data)
{
for (int j = 0; j < 3; j++)
{
i->v[j].normalize();
i->v[j] *= EARTH_GRAVITY;
}
}
}
int main()
{
vec_t data[5];
vector<AccelSensor *> accels;
accels.push_back(
new lsm_t(lsm_t::ACC_FS_2G, lsm_t::GYRO_FS_245, lsm_t::COMPASS_FS_2));
accels.push_back(new mpu_t(mpu_t::ACC_FS_2G, mpu_t::GYRO_FS_250));
accels.push_back(new max_t(max_t::ACC_FS_2G, max_t::GYRO_FS_250));
uint16_t cnt = 0;
for (auto jj : accels)
jj->init();
printf("Wait..\n");
Thread::sleep(2000);
printf("Calibrating..\n");
vec_t caa[10];
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 3; j++)
{
AccelSensor *a = accels[j];
a->updateParams();
caa[i].v[j] = a->getAccel();
}
printf("%02d/10...\r", i + 1);
Thread::sleep(20);
}
calibrate(caa);
while (true)
{
printf("%05d ", cnt);
for (int j = 0; j < 3; j++)
{
AccelSensor *i = accels[j];
i->updateParams();
data[cnt % 5].v[j] = i->getAccel();
Vec3 mid = adjust(data, j);
printf("[%+5.2f,%+5.2f,%+5.2f] ", mid.getX(), mid.getY(),
mid.getZ());
}
printf(" \r");
cnt++;
Thread::sleep(10);
}
}
/* Copyright (c) 2015-2016 Skyward Experimental Rocketry
* Author: Alain Carlucci
*
* 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 <Common.h>
#include <drivers/BusTemplate.h>
#include <drivers/Leds.h>
#include <drivers/ethernet/UdpManager.h>
#include <drivers/stm32f2_f4_i2c.h>
#include <math/Matrix.h>
#include <math/Vec3.h>
#include <sensors/FXAS21002.h>
#include <sensors/LPS331AP.h>
#include <sensors/MAX21105.h>
#include <sensors/MAX31856.h>
#include <sensors/MPL3115.h>
#include <sensors/MPU9250.h>
#include <sensors/MS580301BA07.h>
#include <sensors/Si7021.h>
#include <sensors/iNemo.h>
using namespace miosix;
using std::vector;
//#define ENABLE_ETHERNET
constexpr uint32_t lp_alpha = 250;
constexpr uint32_t hp_beta = 700;
#define SENSOR(NAME, CSPORT, CSPIN) \
typedef Gpio<GPIO##CSPORT##_BASE, CSPIN> CS_##NAME; \
typedef ProtocolSPI<busSPI1, CS_##NAME> spi##NAME
// SPI1
typedef Gpio<GPIOA_BASE, 5> GpioSck;
typedef Gpio<GPIOA_BASE, 6> GpioMiso;
typedef Gpio<GPIOA_BASE, 7> GpioMosi;
typedef BusSPI<1, GpioMosi, GpioMiso, GpioSck> busSPI1;
// clang-format off
// Here's the list of SPI sensors that should be tested
// | NAME | CSPORT | CSPIN |
SENSOR(MPU9250, D, 13);
SENSOR(FXAS21002, G, 10);
SENSOR(MAX21105, E, 2);
SENSOR(MAX31856, B, 11);
SENSOR(LSM9DS0_G, G, 9);
SENSOR(LSM9DS0_A, G, 11);
SENSOR(LPS331AP, E, 4);
SENSOR(MS580301BA07, B, 10);
// clang-format on
#pragma pack(1)
struct pkt_t
{
float accel[3];
float gyro[3];
float compass[3];
float temperature;
float humidity;
float pressure;
float orientation[3];
};
#pragma pack()
// Please don't judge me for these lines
#define PUSH(a, b, x) \
do \
{ \
sensor_t zz = a, b; \
sensors.push_back(zz); \
x.push_back(zz); \
} while (0)
#define PUSH2(a, b, x, y) \
do \
{ \
sensor_t zz = a, b; \
PUSH(a, b, x); \
y.push_back(zz); \
} while (0)
#define PUSH3(a, b, x, y, z) \
do \
{ \
sensor_t zz = a, b; \
PUSH2(a, b, x, y); \
z.push_back(zz); \
} while (0)
#define PP(name, func) \
do \
{ \
printf("[Sensor] " name " started\n"); \
func; \
} while (0)
class DemoBoard : public Singleton<DemoBoard>
{
friend class Singleton<DemoBoard>;
public:
void init() {}
void update()
{
for (const sensor_t& s : sensors)
(s.sensor)->onSimpleUpdate();
}
// Read a float value
#define RFLO(x, y, k, z, w) \
vector<x> y() const \
{ \
vector<x> j; \
for (const sensor_t& i : w) \
{ \
j.push_back(*((dynamic_cast<z*>(i.sensor))->k())); \
} \
return j; \
}
// Read a Vec3 value and multiply it by the sensor transform matrix
#define RVEC(x, y, k, z, w) \
vector<x> y() const \
{ \
vector<x> j; \
for (const sensor_t& i : w) \
{ \
x vec = *(i.transform) * *((dynamic_cast<z*>(i.sensor))->k()); \
j.push_back(vec); \
} \
return j; \
}
// clang-format off
RVEC(Vec3, getAccel, accelDataPtr, AccelSensor, accel)
RVEC(Vec3, getRotation, gyroDataPtr, GyroSensor, gyro)
RVEC(Vec3, getCompass, compassDataPtr, CompassSensor, compass)
RFLO(float, getTemperature, tempDataPtr, TemperatureSensor, temps)
RFLO(float, getHumidity, humidityDataPtr,HumiditySensor, humidity)
RFLO(float, getPressure, pressureDataPtr,PressureSensor, pressure)
// clang-format on
#undef RFLO
#undef RVEC
private:
DemoBoard()
{
static const float v_inemo[16] = {0, 1, 0, 0, -1, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 1};
static const float v_mpu[16] = {-1, 0, 0, 0, 0, -1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 1};
static const Mat4 idMat = Mat4();
static const Mat4 iNemoMat = Mat4(v_inemo);
static const Mat4 mpuMat = Mat4(v_mpu);
// ----- FXAS21102 -----
typedef FXAS21002<spiFXAS21002> fx_t;
fx_t* fx = new fx_t(fx_t::DPS500);
if (fx->init())
PP("FXAS21102", PUSH({fx, &idMat}, gyro));
else
printf("Cannot start FXAS21102\n");
// ----- iNEMO -----
typedef iNEMOLSM9DS0<spiLSM9DS0_G, spiLSM9DS0_A> pd_inemo_t;
pd_inemo_t* nemo =
new pd_inemo_t(pd_inemo_t::ACC_FS_2G, pd_inemo_t::GYRO_FS_245,
pd_inemo_t::COMPASS_FS_2);
if (nemo->init())
PP("iNemo", PUSH3({nemo, &iNemoMat}, accel, gyro, compass));
else
printf("Cannot start iNemo\n");
// ----- LPS331AP -----
typedef LPS331AP<spiLPS331AP> lps_t;
lps_t* lps = new lps_t(lps_t::SS_25HZ);
if (lps->init())
PP("LPS331AP", PUSH2({lps, &idMat}, pressure, temps));
else
printf("Cannot start LPS\n");
// ----- MAX21105 -----
typedef MAX21105<spiMAX21105> max21_t;
max21_t* max21 = new max21_t(max21_t::ACC_FS_2G, max21_t::GYRO_FS_500);
if (max21->init())
PP("MAX21105", PUSH3({max21, &idMat}, accel, gyro, temps));
else
printf("Cannot start MAX21105\n");
// ----- MPL3115 -----
sensors::sda::mode(Mode::ALTERNATE_OD);
sensors::sda::alternateFunction(4);
sensors::scl::mode(Mode::ALTERNATE_OD);
sensors::scl::alternateFunction(4);
typedef MPL3115<ProtocolI2C<miosix::I2C1Driver> > mpl_t;
mpl_t* mpl = new mpl_t();
if (mpl->init())
PP("MPL3115", PUSH2({mpl, &idMat}, pressure, temps));
else
printf("Cannot start MPL3115\n");
// ----- MPU9250 -----
typedef MPU9250<spiMPU9250> mpu_t;
mpu_t* mpu = new mpu_t(mpu_t::ACC_FS_2G, mpu_t::GYRO_FS_250);
if (mpu->init())
PP("MPU9250", PUSH3({mpu, &mpuMat}, accel, compass, temps));
else
printf("Cannot start MPU9250\n");
// ----- MS580301BA07 -----
typedef MS580301BA07<spiMS580301BA07> ms5_t;
ms5_t* ms5 = new ms5_t();
if (ms5->init())
PP("MS580301BA07", PUSH2({ms5, &idMat}, pressure, temps));
else
printf("Cannot start MS580\n");
// ----- Si7021 -----
typedef Si7021<ProtocolI2C<miosix::I2C1Driver> > si_t;
si_t* si = new si_t();
if (si->init())
PP("Si7021", PUSH2({si, &idMat}, humidity, temps));
else
printf("Cannot start Si7021\n");
printf("Loaded %d sensors\n", sensors.size());
printf(" + %d gyro sensors\n", gyro.size());
printf(" + %d accel sensors\n", accel.size());
printf(" + %d compass sensors\n", compass.size());
printf(" + %d temperature sensors\n", temps.size());
printf(" + %d humidity sensors\n", humidity.size());
printf(" + %d pressure sensors\n", pressure.size());
}
typedef struct
{
Sensor* sensor;
const Mat4* transform;
} sensor_t;
vector<sensor_t> sensors;
vector<sensor_t> accel;
vector<sensor_t> gyro;
vector<sensor_t> compass;
vector<sensor_t> temps;
vector<sensor_t> humidity;
vector<sensor_t> pressure;
#define sDemoBoard DemoBoard::getInstance()
};
Vec3 averageVec(const vector<Vec3>& in)
{
Vec3 out;
for (const Vec3& i : in)
out += i;
if (in.size() > 0)
out *= 1.0f / (float)in.size();
return out;
}
float averageFloat(const vector<float>& in)
{
float out = 0;
for (const float& i : in)
out += i;
if (in.size() > 0)
out *= 1.0f / (float)in.size();
return out;
}
template <uint32_t alpha>
class LowPassPolicy
{
protected:
static float process(uint16_t id, float in)
{
if (id >= storage.size())
storage.resize(id + 1, 0.0);
storage[id] += (alpha / 1000.0f) * (in - storage[id]);
return storage[id];
}
static vector<float> storage;
};
template <uint32_t beta>
class HighPassPolicy
{
protected:
static float process(uint16_t id, float in)
{
if (id >= storage.size())
{
storage.resize(id + 1, 0.0);
unfiltered.resize(id + 1, 0.0);
}
storage[id] = (beta / 1000.0f) * (storage[id] + in - unfiltered[id]);
unfiltered[id] = in;
return storage[id];
}
static vector<float> unfiltered;
static vector<float> storage;
};
template <typename FilterPolicy>
class BandFilter : private FilterPolicy
{
public:
/* For each call, increment id of 3 * data.size() to avoid overlapping */
static void process(uint16_t id, vector<Vec3>& data)
{
for (size_t i = 0; i < data.size(); i++)
process(id + i * 3, data[i]);
}
/* For each call, increment id of data.size() to avoid overlapping */
static void process(uint16_t id, vector<float>& data)
{
for (size_t i = 0; i < data.size(); i++)
FilterPolicy::process(id + i, data[i]);
}
static void printStorage()
{
printf("Lowpass: [");
for (const float& f : FilterPolicy::storage)
printf("%5.2f ", f);
printf("]\n");
}
static void process(uint16_t id, Vec3& data)
{
data.setX(FilterPolicy::process(id + 0, data.getX()));
data.setY(FilterPolicy::process(id + 1, data.getY()));
data.setZ(FilterPolicy::process(id + 2, data.getZ()));
}
};
template <uint32_t alpha>
vector<float> LowPassPolicy<alpha>::storage;
template <uint32_t beta>
vector<float> HighPassPolicy<beta>::storage;
template <uint32_t beta>
vector<float> HighPassPolicy<beta>::unfiltered;
#define VECARRAY_FILTER(id, array) \
do \
{ \
BandFilter<LowPassPolicy<lp_alpha> >::process(id, array); \
id += array.size() * 3; \
} while (0)
#define FLOATARRAY_FILTER(id, array) \
do \
{ \
BandFilter<LowPassPolicy<lp_alpha> >::process(id, array); \
id += array.size(); \
} while (0)
#define COPYVEC(arr, v) \
do \
{ \
arr[0] = v.getX(); \
arr[1] = v.getY(); \
arr[2] = v.getZ(); \
} while (0)
int main()
{
Thread::sleep(100);
sDemoBoard->init();
// Ethernet setup
#ifdef ENABLE_ETHERNET
const uint8_t ipAddr[] = {192, 168, 1, 30}; // Device's IP address
const uint8_t mask[] = {255, 255, 255, 0}; // Subnet mask
const uint8_t destIp[] = {192, 168, 1, 4}; // Destination IP address
const uint16_t destPort = 1234; // Destination port
printf("[Ethernet] Starting...\n");
W5200& eth = W5200::instance();
eth.setIpAddress(ipAddr);
eth.setSubnetMask(mask);
printf("[Ethernet] Got addr\n");
// This socket listens on UDP port 2020
// UdpManager::getInstance().setTxPort(2020);
printf("[Ethernet] Socket ready\n");
#endif
Vec3 orientation;
Vec3 angularvel;
int ignore_ctr = 100;
int led_status = 0x200;
while (1)
{
sDemoBoard->update();
Thread::sleep(20);
vector<Vec3> accels = sDemoBoard->getAccel();
vector<Vec3> rots = sDemoBoard->getRotation();
vector<Vec3> compsv = sDemoBoard->getCompass();
vector<float> temps = sDemoBoard->getTemperature();
vector<float> humid = sDemoBoard->getHumidity();
vector<float> press = sDemoBoard->getPressure();
uint16_t id = 0;
VECARRAY_FILTER(id, accels);
VECARRAY_FILTER(id, rots);
BandFilter<HighPassPolicy<hp_beta> >::process(0, rots);
VECARRAY_FILTER(id, compsv);
FLOATARRAY_FILTER(id, temps);
FLOATARRAY_FILTER(id, humid);
FLOATARRAY_FILTER(id, press);
Vec3 acc = averageVec(accels);
Vec3 rot = averageVec(rots);
Vec3 comps = averageVec(compsv);
float tempm = averageFloat(temps);
float humim = averageFloat(humid);
float presm = averageFloat(press);
pkt_t packet;
COPYVEC(packet.accel, acc);
COPYVEC(packet.gyro, rot);
COPYVEC(packet.compass, comps);
COPYVEC(packet.orientation, orientation);
packet.temperature = tempm;
packet.humidity = humim;
packet.pressure = presm;
Leds::set(led_status);
if (ignore_ctr == 0)
{
led_status <<= 1;
if (led_status == 0x400)
led_status = 0x04;
angularvel += rot * 0.2f;
orientation += angularvel * 0.2f;
BandFilter<HighPassPolicy<999> >::process(4, orientation);
}
else
{
led_status ^= 0x200;
--ignore_ctr;
if (ignore_ctr == 0)
led_status = 0x04;
}
#ifdef ENABLE_ETHERNET
sock.sendPacketTo(destIp, destPort, &packet, sizeof(packet));
#endif
printf(
"[%5.2f %5.2f %5.2f] "
"[%5.2f %5.2f %5.2f] "
"[%d %5.2f %5.2f %5.2f] "
"[%5.2f %5.2f %5.2f] "
"[%5.2f %5.2f %5.2f] \r",
acc.getX(), acc.getY(), acc.getZ(), rot.getX(), rot.getY(),
rot.getZ(), ignore_ctr, orientation.getX(), orientation.getY(),
orientation.getZ(), comps.getX(), comps.getY(), comps.getZ(), tempm,
humim, presm);
fflush(stdout);
}
// Bye.
while (1)
{
Thread::sleep(1000);
}
}
/* Copyright (c) 2015-2017 Skyward Experimental Rocketry
* Author: Alain Carlucci
*
* 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 <Common.h>
#include <boards/AnakinBoard.h>
#include <diagnostic/CpuMeter.h>
#include <diagnostic/Log.h>
#include <drivers/Leds.h>
using namespace miosix;
void fifoQueueSz(void* arg)
{
const SPIDriver& spi = SPIDriver::instance();
int tx_accum = 0, rx_accum = 0, sz_ctr = 0;
int qsize_accum = 0;
sLog->logString("Thread started");
while (1)
{
DMAFIFOStatus tx = spi.getTxFIFOStatus();
DMAFIFOStatus rx = spi.getRxFIFOStatus();
if (tx > -1 && rx > -1)
{
tx_accum += tx;
rx_accum += rx;
qsize_accum += sLog->getLogQueueSize();
if (++sz_ctr == 100)
{
float tx1 = tx_accum / (float)(DFS_100 - DFS_EE + 1) * 255.0f;
float rx1 = rx_accum / (float)(DFS_100 - DFS_EE + 1) * 255.0f;
float qsz = qsize_accum / 100.0f * 255.0f;
tx_accum = rx_accum = sz_ctr = qsize_accum = 0;
sLog->logLimitedInt(17, 0, 255, tx1);
sLog->logLimitedInt(18, 0, 255, rx1);
sLog->logUInt32(19, spi.getFIFOFaultCtr());
sLog->logUInt32(20, averageCpuUtilization());
sLog->logLimitedInt(21, 0, 255, qsz);
}
}
Thread::sleep(1);
}
}
int main()
{
sLog->start();
printf("\n");
Leds::set(0);
Log::getInstance();
sBoard->init();
const std::vector<SingleSensor>& data = sBoard->debugGetSensors();
int ctr = 0;
Thread::create(fifoQueueSz, 1024);
while (1)
{
for (const auto& s : data)
{
switch (s.data)
{
case DATA_VEC3:
sLog->logSensorVec3(s.sensor, *(Vec3*)s.value);
break;
case DATA_FLOAT:
sLog->logSensorFloat(s.sensor, *(float*)s.value);
break;
default:
break;
}
}
Thread::sleep(100);
}
return 0;
}
/* Copyright (c) 2015-2016 Skyward Experimental Rocketry
* Authors: Alain Carlucci, Matteo Piazzolla
*
* 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 <Common.h>
#include <drivers/BusTemplate.h>
#include <sensors/FXAS21002.h>
#include <sensors/MAX21105.h>
#include <sensors/MPU9250.h>
#include <sensors/MS580301BA07.h>
//#include <sensors/Si7021.h>
using namespace miosix;
#define SENSOR(NAME, CSPORT, CSPIN) \
typedef Gpio<GPIO##CSPORT##_BASE, CSPIN> CS_##NAME; \
typedef ProtocolSPI<busSPI1, CS_##NAME> spi##NAME
#define READ(NAME, ID) spi##NAME::read(ID)
#define TEST(NAME, REG, REF) test_and_print(#NAME, REG, READ(NAME, REG), REF)
#define ACCEL_ENDLESS_TEST
// SPI1
typedef Gpio<GPIOA_BASE, 5> GpioSck;
typedef Gpio<GPIOA_BASE, 6> GpioMiso;
typedef Gpio<GPIOA_BASE, 7> GpioMosi;
typedef BusSPI<1, GpioMosi, GpioMiso, GpioSck> busSPI1;
// clang-format off
// Here's the list of SPI sensors that should be tested
// | NAME | CSPORT | CSPIN |
SENSOR(MPU9250, D, 13);
SENSOR(FXAS21002, G, 10);
SENSOR(MAX21105, E, 2);
SENSOR(MAX31856, B, 11);
SENSOR(LSM9DS0_G, G, 9);
SENSOR(LSM9DS0_A, G, 11);
SENSOR(LPS331AP, E, 4);
SENSOR(MS580301BA07, B, 10);
// clang-format on
static const char mOK[] = "OK";
static const char mKO[] = "FAIL";
int num_ok = 0, num_fail = 0;
void banner()
{
printf(
"----------------------------------------------------------------------"
"------\n"
"d888888b d88888b .d8888. d888888b .d8888. db db d888888b d888888b "
"d88888b\n"
"`~~88~~' 88' 88' YP `~~88~~' 88' YP 88 88 `88' `~~88~~' "
"88' \n"
" 88 88ooooo `8bo. 88 `8bo. 88 88 88 88 "
"88ooooo\n"
" 88 88~~~~~ `Y8b. 88 `Y8b. 88 88 88 88 "
"88~~~~~\n"
" 88 88. db 8D 88 db 8D 88b d88 .88. 88 "
"88. \n"
" YP Y88888P `8888Y' YP `8888Y' ~Y8888P' Y888888P YP "
"Y88888P\n"
"----------------------------------------------------------------------"
"------\n"
"\n");
}
void test_and_print(const char* name, uint8_t reg, uint8_t test,
uint8_t reference)
{
if (test == reference)
++num_ok;
else
++num_fail;
printf("[%13s] [REG 0x%02x]: 0x%02x -> [%s]\n", name, reg, test,
(test == reference) ? mOK : mKO);
}
void assert_true(const char* name, const char* func, bool value)
{
if (value)
++num_ok;
else
++num_fail;
printf("[%13s] [FUNC %9s] -> [%s]\n", name, func, (value) ? mOK : mKO);
}
void test_max31856()
{
// Check if a bunch of register have the reference value
TEST(MAX31856, 0x00, 0x00); // config 0
TEST(MAX31856, 0x01, 0x03); // config 1
TEST(MAX31856, 0x02, 0xFF); // mask
TEST(MAX31856, 0x03, 0x7F); // hi fault
TEST(MAX31856, 0x04, 0xc0); // lo fault
TEST(MAX31856, 0x05, 0x7f); // lol
TEST(MAX31856, 0x0F, 0x00); // fault status reg
}
void test_altimeter()
{
MS580301BA07<spiMS580301BA07> altiMS580;
// Read and check different const registers
TEST(MS580301BA07, 0xA4, READ(MS580301BA07, 0xA4));
TEST(MS580301BA07, 0xAA, READ(MS580301BA07, 0xAA));
TEST(MS580301BA07, 0xAC, READ(MS580301BA07, 0xAC));
assert_true("MS580301BA07", "init()", altiMS580.init());
}
int main()
{
MAX21105<spiMAX21105> max21(MAX21105<spiMAX21105>::ACC_FS_2G,
MAX21105<spiMAX21105>::GYRO_FS_500);
banner();
// Pausa per lasciare il tempo ai sensori di accendersi
Thread::sleep(100);
printf("Polling sensors...\n");
// clang-format off
// First check: compare sensors WHO_AM_I against the reference value
TEST(MPU9250, 0x75, 0x71);
TEST(FXAS21002, 0x0c, 0xd7);
TEST(MAX21105, 0x20, 0xb4);
TEST(LSM9DS0_G, 0x0f, 0xd4);
TEST(LSM9DS0_A, 0x0f, 0x49);
TEST(LPS331AP, 0x0f, 0xBB);
// clang-format on
test_max31856();
test_altimeter();
assert_true("MAX21105", "init()", max21.init());
printf("---------------------\n");
printf(
"NUM TEST OK: %5d\n"
"NUM TEST FAIL:%5d\n\n",
num_ok, num_fail);
#ifdef ACCEL_ENDLESS_TEST
uint16_t cnt = 0;
const Vec3* a = max21.accelDataPtr();
const Vec3* g = max21.gyroDataPtr();
const float* temp = max21.tempDataPtr();
while (true)
{
max21.onSimpleUpdate();
printf(
"%05u[ACC %+5.2f,%+5.2f,%+5.2f] "
"[GYR %+5.2f,%+5.2f,%+5.2f] "
"[TMP %+5.2fC] \r",
(uint16_t)(++cnt), a->getX(), a->getY(), a->getZ(), g->getX(),
g->getY(), g->getZ(), *temp);
Thread::sleep(10);
}
#endif
// Bye.
while (1)
{
Thread::sleep(1000);
}
}
/* Copyright (c) 2015-2016 Skyward Experimental Rocketry
* Author: Silvano Seva
*
* 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 <Common.h>
#include <drivers/ethernet/UdpManager.h>
#include <string>
using namespace miosix;
using namespace std;
int main()
{
uint8_t ipAddr[] = {192, 168, 1, 30}; // Device's IP address
uint8_t mask[] = {255, 255, 255, 0}; // Subnet mask
uint8_t destIp[] = {192, 168, 1, 180}; // Destination IP address
char message[] = "message from the hell...\n";
W5200& eth = W5200::instance();
eth.setIpAddress(ipAddr);
eth.setSubnetMask(mask);
auto udp = Singleton<UdpManager>::getInstance();
udp->setTxPort(1234);
udp->setRxPort(1235);
int counter = 0;
while (1)
{
int ncar = sprintf(message, "Hello gumby n° %d!\n", counter);
udp->sendPacketTo(destIp, 2020, (void*)message, ncar);
counter++;
if (udp->newReceivedPackets())
{
size_t pktSiz = udp->recvPacketSize() + 1;
uint8_t buffer[pktSiz];
memset(buffer, 0x00, pktSiz);
uint8_t sip[4] = {0};
uint16_t sport = 0;
udp->readPacket(sip, sport, buffer);
printf("received %s from %d.%d.%d.%d:%d\n\n", buffer, sip[0],
sip[1], sip[2], sip[3], sport);
}
Thread::sleep(200);
}
return 0;
}
/* Copyright (c) 2018-2019 Skyward Experimental Rocketry
* Author: 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 <sensors/Sensor.h>
#include <stdint.h>
#include <utils/Debug.h>
#include "AD7994Data.h"
/**
* Driver for the AD7994 Analog Digital Converter
* CLASS INVARIANT: The address pointer register points to the conversion result
* register.
* This is done for performance reasons: we don't want to write to the
* address pointer register each time we perform a conversion (eg multiple times
* per second)
*/
template <typename BusI2C, typename BusyPin, typename CONVST>
class AD7994 : public Sensor
{
public:
enum class Channel : uint8_t
{
CH1 = 1,
CH2,
CH3,
CH4
};
/**
* @brief AD7994 constructor. Invariant is respected as the sensor powers up
* pointing at the conversion result register.
*
* @param i2c_address I2C address of the AD7994
*/
AD7994(uint8_t i2c_address)
: i2c_address(i2c_address), enabled_channels{0, 0, 0, 0}
{
}
~AD7994() {}
/**
* @brief Initialize the sensor writing the configuration register
* This driver is designed to work in Mode 1
* The configuration register is set to enable the BUSY pin, I2C filtering
* and no enabled channels.
* @return true If the sensor was configured successfully
*/
bool init() override
{
uint8_t config_reg_value = 0x0A; // 0b00001010
// Write the configuration register
BusI2C::write(i2c_address, REG_CONFIG, &config_reg_value, 1);
uint8_t read_config_reg_value;
// Read back the value
BusI2C::directRead(i2c_address, &read_config_reg_value, 1);
// BusI2C::read(i2c_address, REG_CONFIG, &read_config_reg_value, 1);
pointToConversionResult();
return read_config_reg_value == config_reg_value;
}
void enableChannel(Channel channel)
{
uint8_t ch = static_cast<uint8_t>(channel);
enabled_channels[ch - 1] = true;
uint8_t config_reg_value;
BusI2C::read(i2c_address, REG_CONFIG, &config_reg_value, 1);
// Update the config register value
uint8_t channel_reg = 0;
for (int i = 0; i < 4; i++)
{
if (enabled_channels[i])
channel_reg |= 1 << i;
}
config_reg_value = (config_reg_value & 0x0F) | channel_reg << 4;
BusI2C::write(i2c_address, REG_CONFIG, &config_reg_value, 1);
pointToConversionResult();
}
void disableChannel(Channel channel)
{
uint8_t ch = static_cast<uint8_t>(channel);
enabled_channels[ch - 1] = false;
uint8_t channel_reg = 0;
for (int i = 0; i < 4; i++)
{
if (enabled_channels[i])
channel_reg |= 1 << i;
}
uint8_t config_reg_value;
BusI2C::read(i2c_address, REG_CONFIG, &config_reg_value, 1);
// Update the config register value
config_reg_value = (config_reg_value & 0x0F) | channel_reg << 4;
BusI2C::write(i2c_address, REG_CONFIG, &config_reg_value, 1);
pointToConversionResult();
}
/**
* @brief Triggers a new ADC conversion on the enabled channels and reads
* the value of the conversion register.
* TODO: Check how to sample multiple channels.
* @return true If the conversion was successful on all enabled channels
*/
bool onSimpleUpdate() override
{
uint8_t data[2];
for (int i = 0; i < 4; i++)
{
if (enabled_channels[i])
{
// Trigger a conversion
CONVST::high();
miosix::delayUs(3);
CONVST::low();
// Wait for the conversion to complete
miosix::delayUs(2);
BusI2C::directRead(i2c_address, data, 2);
samples[i] = decodeConversion(data);
samples[i].timestamp = miosix::getTick();
}
}
return true;
}
bool selfTest() { return true; }
AD7994Sample getLastSample(Channel channel)
{
uint8_t ch = static_cast<uint8_t>(channel);
return samples[ch - 1];
}
private:
// Address of the AD7994 on the I2C bus
const uint8_t i2c_address;
// The 4 LSBs indicate where the corresponding channel is enabled or not.
bool enabled_channels[4];
/**
* @brief Writes the conversion register address to the address pointer
* register in order to restore the class invariant.
*/
void pointToConversionResult()
{
uint8_t reg_addr = REG_CONVERSION_RESULT;
BusI2C::directWrite(i2c_address, &reg_addr, 1);
}
/**
* @brief Decodes the 2 bytes of the conversion register into an
* AD7994Sample structure
*
* @param data_ptr Pointer to the first of the 2 bytes of the conversione
* register
* @return AD7994Sample
*/
AD7994Sample decodeConversion(uint8_t* data_ptr)
{
uint16_t conv_reg =
static_cast<uint16_t>((data_ptr[0]) << 8) + data_ptr[1];
AD7994Sample out;
out.value = conv_reg & 0x0FFF;
out.channel_id = (static_cast<uint8_t>(conv_reg >> 12) & 0x03) + 1;
out.alert_flag = static_cast<bool>(conv_reg >> 15);
return out;
}
enum Registers : uint8_t
{
REG_CONVERSION_RESULT = 0x00,
REG_ALERT_STATUS = 0x01,
REG_CONFIG = 0x02,
};
AD7994Sample samples[4];
};
/* Copyright (c) 2018 Skyward Experimental Rocketry
* Author: Silvano Seva
*
* 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 "IsbProtocol_serial2.h"
using namespace std;
using namespace miosix;
typedef Gpio<GPIOA_BASE, 2> u2tx;
typedef Gpio<GPIOA_BASE, 3> u2rx;
typedef Gpio<GPIOA_BASE, 1> u2rts;
void __attribute__((weak)) USART2_IRQHandler()
{
saveContext();
asm volatile("bl _Z11Serial2_Irqv");
restoreContext();
}
void __attribute__((used)) Serial2_Irq()
{
auto inst = IsbProtocol_serial2::instance();
inst.IRQHandler();
}
IsbProtocol_serial2::IsbProtocol_serial2()
{
{
FastInterruptDisableLock dLock;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
RCC_SYNC();
u2tx::mode(Mode::ALTERNATE);
u2tx::alternateFunction(7);
#ifdef _ARCH_CORTEXM3_STM32F1
u2rx::mode(Mode::INPUT);
#else
u2rx::mode(Mode::ALTERNATE);
u2rx::alternateFunction(7);
#endif
u2rts::mode(Mode::OUTPUT);
u2rts::low();
}
/*** USART 2 configuration ***/
// Enable parity, 9 bit frame length, no parity,
// generate interrupt in case a new byte is received
USART2->CR1 |= USART_CR1_M | USART_CR1_RXNEIE | USART_CR1_TE // enable tx
| USART_CR1_RE; // enable rx
// CR2 and CR3 registers are left untouched since their default values are
// OK
NVIC_SetPriority(USART2_IRQn, 15); // Lowest priority for serial
NVIC_ClearPendingIRQ(USART2_IRQn);
NVIC_EnableIRQ(USART2_IRQn);
}
IsbProtocol_serial2::~IsbProtocol_serial2()
{
FastInterruptDisableLock dLock;
RCC->APB1ENR &= ~RCC_APB1ENR_USART2EN;
RCC_SYNC();
}
void IsbProtocol_serial2::IRQHandler()
{
if (USART2->SR & USART_SR_RXNE)
{
/* This protocol uses 9 bit long usart frames.
* The STM32's data register is 9 bit long, so the
* lower 8 bits are used to transport the data byte and
* the MSB - the 9th bit - is used to select between
* data or address byte
*/
uint16_t byte = USART->DR;
uint16_t checkMask = 0x100 | nodeAddress;
if (byte == checkMask)
{
rxStatus.rxIndex = 0;
rxStatus.rxInProgress = true;
rxStatus.dataLenPending = true;
rxBuf[rxStatus.rxIndex] = byte & 0xFF; // strip away the 9th byte
rxStatus.rxIndex++;
}
else if (rxStatus.rxInProgress)
{
if (rxStatus.dataLenPending)
{
// Second byte in the packet is the data len field. To this
// value we have to add 4 bytes: address, data len and two bytes
// of CRC
rxStatus.packetSize = (byte & 0xFF) + 4;
rxStatus.dataLenPending = false;
}
rxBuf[rxStatus.rxIndex] = byte & 0xFF;
rxStatus.rxIndex++;
if (rxStatus.rxIndex >= rxStatus.packetSize)
{
rxStatus.rxInProgress = false;
}
}
}
}
size_t IsbProtocol_serial2::newDataAvailable()
{
if ((rxStatus.rxInProgress == true) || (rxStatus.packetSize = 0))
{
return 0;
}
// we pass packetSize - 2 in order to avoid including in the CRC calculation
// the CRC value inserted from the sender
uint16_t crc = CRC16(rxBuf, rxStatus.packetSize - 2);
uint16_t pktCrc =
(rxBuf[rxStatus.packetSize - 2] << 8) | rxBuf[rxStatus.packetSize - 1];
// Packet length check: the value contained in the second byte must be
// equal to the number of bytes received minus 4
bool checkLen = (rxBuf[1] == (rxStatus.packetSize - 4)) ? true : false;
if ((crc != pktCrc) || (!checkLen))
{
rxStatus.packetSize = 0; // packet is corrupt, discard it
return 0;
}
// Return length of the data field
return rxBuf[1];
}
void IsbProtocol_serial2::getData(uint8_t* buffer)
{
std::memcpy(buffer, rxBuf[2], rxBuf[1]);
}
void IsbProtocol_serial2::sendData(uint8_t dstAddr, uint8_t* data, size_t len)
{
// packet too long
if (len > 0xFF)
{
return;
}
txBuf[0] = dstAddr;
txBuf[1] = len;
std::memcpy(txBuf[2], data, len);
// Include in CRC also address and data len field
uint16_t crc = CRC16(txBuf, len + 2);
size_t crcBegin = len + 2;
txBuf[crcBegin] = crc >> 8;
txBuf[crcBegin + 1] = crc & 0xFF;
u2rts::high();
for (size_t i = 0; i < len + 4; i++)
{
// the first byte is the address, so it has to be sent with the 9th bit
// set to 1
if (i == 0)
{
USART2->DR = txBuf[i] | 0x100;
}
else
{
USART2->DR = txBuf[i];
}
// Wait until tx buffer is empty again
while ((USART2->SR & USART_SR_TXE) == 0)
;
}
u2rts::low();
}
void IsbProtocol_serial2::setBaud(uint32_t baud)
{
uint32_t busFreq = SystemCoreClock;
#ifdef _ARCH_CORTEXM3_STM32F1
if (RCC->CFGR & RCC_CFGR_PPRE1_2)
{
busFreq /= 1 << (((RCC->CFGR >> 8) & 0x3) + 1);
}
#else
if (RCC->CFGR & RCC_CFGR_PPRE1_2)
{
busFreq /= 1 << (((RCC->CFGR >> 10) & 0x3) + 1);
}
#endif
uint32_t quot = 2 * busFreq / baud; // 2*freq for round to nearest
USART2->BRR = quot / 2 + (quot & 1);
}
void IsbProtocol_serial2::setNodeAddress(uint8_t address)
{
nodeAddress = address;
}
uint16_t IsbProtocol_serial2::CRC16(uint8_t* data, size_t len)
{
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < len; i++)
{
uint16_t x = ((crc >> 8) ^ data[i]) & 0xff;
x ^= x >> 4;
crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x;
}
return crc;
}
IsbProtocol_serial2& IsbProtocol_serial2::instance()
{
static IsbProtocol_serial2 inst;
return inst;
}
/* Copyright (c) 2017 Skyward Experimental Rocketry
* Author: Silvano Seva
*
* 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 <stddef.h>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include "miosix.h"
/* This class is a kind of driver to be used to excange data between the
* stormtrooper master and the other stormtrooper slave boards through the
* RS485 lines. The communication model is this: all the boards are connected
* on the same bus and each board has a unique node address. To avoid conflicts,
* the master is the only device on the bus that can initiate a communication;
* thus, to communicate with a specific slave, the master sends a packet
* containing the targeted slave address and the data and then waits for the
* response (if expected). Owing to this model, if two stormtrooper slaves need
* to exchange data they have to rely on the stormtrooper master acting as a
* router between them.
*
* The packet structure is this:
*
* +------+-----+------+-----+
* | addr | len | data | CRC |
* +------+-----+------+-----+
*
* -> addr: target node address, 8 bit
* -> len: lenght of the data field, 8 bit
* -> data: data bytes, up to 255
* -> CRC: 16 bit field calculated on addr, len and data using the ccitt CRC16
* formula
*
* On the bus data is sent using 9 bit long frames with one stop bit. If the 9th
* bit is set to 1 means that the byte received is an address byte and so a
* packet is incoming
*
*
* Here is a sample code about this class' usage:
*
* auto comm = IsbProtocol_serial2::instance();
*
* comm.setBaud(9600); //set baud to 9600 bps
* comm.setNodeAddress(0xAB); //set node ID
*
* // this to check if new data arrived and to copy the bytes into a local
* buffer
*
* uint8_t rx_buf[256];
*
* if(comm.newDataAvailable() > 0)
* {
* comm.getData(rx_buf);
* }
*
* // this to send data
*
* uint8_t tx_buf[] = "Some data for you! :)";
*
* comm.sendData(0xE0, tx_buf, sizeof(tx_buf));
*
*/
class IsbProtocol_serial2
{
public:
/**
* @return singleton intance of the class
*/
static IsbProtocol_serial2& instance();
~IsbProtocol_serial2();
/**
* Set the USART baud rate.
* @param baud baud rate to be set
*/
void setBaud(uint32_t baud);
/**
* Set the node's address in the network
*/
void setNodeAddress(uint8_t address);
/**
* @return number of payload data bytes received or 0 if none received
*/
size_t newDataAvailable();
/**
* Get the latest payload data received.
* @param buffer pointer to buffer in which copy the new data, it must have
* dimension greater than or equal to the value returned by
* newDataAvailable()
*/
void getData(uint8_t* buffer);
/**
* Send a packet, this function blocks until all data is sent
*
* @param dstAddr destination node address
* @param data pointer to a buffer containing the payload data
* @param len payload size in bytes
*/
void sendData(uint8_t dstAddr, uint8_t* data, size_t len);
/**
* Interrupt handler. DON'T CALL IT ANYWHERE!!
*/
void IRQHandler();
private:
uint8_t rxBuf[300];
struct R
{
uint16_t packetSize;
uint16_t rxIndex;
bool rxInProgress;
bool dataLenPending;
} rxStatus;
uint8_t txBuf[300];
uint8_t nodeAddress;
uint16_t CRC16(uint8_t* data, size_t len);
IsbProtocol_serial2();
IsbProtocol_serial2(const IsbProtocol_serial2& other);
IsbProtocol_serial2& operator=(const IsbProtocol_serial2& other);
bool operator==(const IsbProtocol_serial2& other);
};
/*
* Inter Stormtrooper Boards communication protocol through STM32's
* USART3 interface
*
* Copyright (c) 2017 Skyward Experimental Rocketry
* Author: Silvano Seva
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* Copyright (c) 2017 Skyward Experimental Rocketry
* Author: Silvano Seva
*
* 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 "IsbProtocol_serial3.h"
using namespace std;
using namespace miosix;
typedef Gpio<GPIOB_BASE, 10> u3tx;
typedef Gpio<GPIOB_BASE, 11> u3rx;
typedef Gpio<GPIOB_BASE, 14> u3rts;
void __attribute__((weak)) USART3_IRQHandler()
{
saveContext();
asm volatile("bl _Z11Serial3_Irqv");
restoreContext();
}
void __attribute__((used)) Serial3_Irq()
{
auto inst = IsbProtocol_serial3::instance();
inst.IRQHandler();
}
IsbProtocol_serial3::IsbProtocol_serial3()
{
{
FastInterruptDisableLock dLock;
RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
RCC_SYNC();
u3tx::mode(Mode::ALTERNATE);
u3tx::alternateFunction(7);
#ifdef _ARCH_CORTEXM3_STM32F1
u3rx::mode(Mode::INPUT);
#else
u3rx::mode(Mode::ALTERNATE);
u3rx::alternateFunction(7);
#endif
u3rts::mode(Mode::OUTPUT);
u3rts::low();
}
/*** USART 3 configuration ***/
// Enable parity, 9 bit frame length, no parity,
// generate interrupt in case a new byte is received
USART3->CR1 |= USART_CR1_M | USART_CR1_RXNEIE | USART_CR1_TE // enable tx
| USART_CR1_RE; // enable rx
// CR2 and CR3 registers are left untouched since their default values are
// OK
NVIC_SetPriority(USART3_IRQn, 15); // Lowest priority for serial
NVIC_ClearPendingIRQ(USART3_IRQn);
NVIC_EnableIRQ(USART3_IRQn);
}
IsbProtocol_serial3::~IsbProtocol_serial3()
{
FastInterruptDisableLock dLock;
RCC->APB1ENR &= ~RCC_APB1ENR_USART3EN;
RCC_SYNC();
}
void IsbProtocol_serial3::IRQHandler()
{
if (USART3->SR & USART_SR_RXNE)
{
/* This protocol uses 9 bit long usart frames.
* The STM32's data register is 9 bit long, so the
* lower 8 bits are used to transport the data byte and
* the MSB - the 9th bit - is used to select between
* data or address byte
*/
uint16_t byte = USART->DR;
uint16_t checkMask = 0x100 | nodeAddress;
if (byte == checkMask)
{
rxStatus.rxIndex = 0;
rxStatus.rxInProgress = true;
rxStatus.dataLenPending = true;
rxBuf[rxStatus.rxIndex] = byte & 0xFF; // strip away the 9th byte
rxStatus.rxIndex++;
}
else if (rxStatus.rxInProgress)
{
if (rxStatus.dataLenPending)
{
// Second byte in the packet is the data len field. To this
// value we have to add 4 bytes: address, data len and two bytes
// of CRC
rxStatus.packetSize = (byte & 0xFF) + 4;
rxStatus.dataLenPending = false;
}
rxBuf[rxStatus.rxIndex] = byte & 0xFF;
rxStatus.rxIndex++;
if (rxStatus.rxIndex >= rxStatus.packetSize)
{
rxStatus.rxInProgress = false;
}
}
}
}
size_t IsbProtocol_serial3::newDataAvailable()
{
if ((rxStatus.rxInProgress == true) || (rxStatus.packetSize = 0))
{
return 0;
}
// we pass packetSize - 2 in order to avoid including in the CRC calculation
// the CRC value inserted from the sender
uint16_t crc = CRC16(rxBuf, rxStatus.packetSize - 2);
uint16_t pktCrc =
(rxBuf[rxStatus.packetSize - 2] << 8) | rxBuf[rxStatus.packetSize - 1];
// Packet length check: the value contained in the second byte must be
// equal to the number of bytes received minus 4
bool checkLen = (rxBuf[1] == (rxStatus.packetSize - 4)) ? true : false;
if ((crc != pktCrc) || (!checkLen))
{
rxStatus.packetSize = 0; // packet is corrupt, discard it
return 0;
}
// Return length of the data field
return rxBuf[1];
}
void IsbProtocol_serial3::getData(uint8_t* buffer)
{
std::memcpy(buffer, rxBuf[2], rxBuf[1]);
}
void IsbProtocol_serial3::sendData(uint8_t dstAddr, uint8_t* data, size_t len)
{
// packet too long
if (len > 0xFF)
{
return;
}
txBuf[0] = dstAddr;
txBuf[1] = len;
std::memcpy(txBuf[2], data, len);
// Include in CRC also address and data len field
uint16_t crc = CRC16(txBuf, len + 2);
size_t crcBegin = len + 2;
txBuf[crcBegin] = crc >> 8;
txBuf[crcBegin + 1] = crc & 0xFF;
u3rts::high();
for (size_t i = 0; i < len + 4; i++)
{
// the first byte is the address, so it has to be sent with the 9th bit
// set to 1
if (i == 0)
{
USART3->DR = txBuf[i] | 0x100;
}
else
{
USART3->DR = txBuf[i];
}
// Wait until tx buffer is empty again
while ((USART3->SR & USART_SR_TXE) == 0)
;
}
u3rts::low();
}
void IsbProtocol_serial3::setBaud(uint32_t baud)
{
uint32_t busFreq = SystemCoreClock;
#ifdef _ARCH_CORTEXM3_STM32F1
if (RCC->CFGR & RCC_CFGR_PPRE1_2)
{
busFreq /= 1 << (((RCC->CFGR >> 8) & 0x3) + 1);
}
#else
if (RCC->CFGR & RCC_CFGR_PPRE1_2)
{
busFreq /= 1 << (((RCC->CFGR >> 10) & 0x3) + 1);
}
#endif
uint32_t quot = 2 * busFreq / baud; // 2*freq for round to nearest
USART3->BRR = quot / 2 + (quot & 1);
}
void IsbProtocol_serial3::setNodeAddress(uint8_t address)
{
nodeAddress = address;
}
uint16_t IsbProtocol_serial3::CRC16(uint8_t* data, size_t len)
{
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < len; i++)
{
uint16_t x = ((crc >> 8) ^ data[i]) & 0xff;
x ^= x >> 4;
crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x;
}
return crc;
}
IsbProtocol_serial3& IsbProtocol_serial3::instance()
{
static IsbProtocol_serial3 inst;
return inst;
}
/* Copyright (c) 2017 Skyward Experimental Rocketry
* Author: Silvano Seva
*
* 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 <stddef.h>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include "miosix.h"
/* This class is a kind of driver to be used to excange data between the
* stormtrooper master and the other stormtrooper slave boards through the
* RS485 lines. The communication model is this: all the boards are connected
* on the same bus and each board has a unique node address. To avoid conflicts,
* the master is the only device on the bus that can initiate a communication;
* thus, to communicate with a specific slave, the master sends a packet
* containing the targeted slave address and the data and then waits for the
* response (if expected). Owing to this model, if two stormtrooper slaves need
* to exchange data they have to rely on the stormtrooper master acting as a
* router between them.
*
* The packet structure is this:
*
* +------+-----+------+-----+
* | addr | len | data | CRC |
* +------+-----+------+-----+
*
* -> addr: target node address, 8 bit
* -> len: lenght of the data field, 8 bit
* -> data: data bytes, up to 255
* -> CRC: 16 bit field calculated on addr, len and data using the ccitt CRC16
* formula
*
* On the bus data is sent using 9 bit long frames with one stop bit. If the 9th
* bit is set to 1 means that the byte received is an address byte and so a
* packet is incoming
*
*
* Here is a sample code about this class' usage:
*
* auto comm = IsbProtocol_serial2::instance();
*
* comm.setBaud(9600); //set baud to 9600 bps
* comm.setNodeAddress(0xAB); //set node ID
*
* // this to check if new data arrived and to copy the bytes into a local
* buffer
*
* uint8_t rx_buf[256];
*
* if(comm.newDataAvailable() > 0)
* {
* comm.getData(rx_buf);
* }
*
* // this to send data
*
* uint8_t tx_buf[] = "Some data for you! :)";
*
* comm.sendData(0xE0, tx_buf, sizeof(tx_buf));
*
*/
class IsbProtocol_serial3
{
public:
/**
* @return singleton intance of the class
*/
static IsbProtocol_serial3& instance();
~IsbProtocol_serial3();
/**
* Set the USART baud rate.
* @param baud baud rate to be set
*/
void setBaud(uint32_t baud);
/**
* Set the node's address in the network
*/
void setNodeAddress(uint8_t address);
/**
* @return number of payload data bytes received or 0 if none received
*/
size_t newDataAvailable();
/**
* Get the latest payload data received.
* @param buffer pointer to buffer in which copy the new data, it must have
* dimension greater than or equal to the value returned by
* newDataAvailable()
*/
void getData(uint8_t* buffer);
/**
* Send a packet, this function blocks until all data is sent
*
* @param dstAddr destination node address
* @param data pointer to a buffer containing the payload data
* @param len payload size in bytes
*/
void sendData(uint8_t dstAddr, uint8_t* data, size_t len);
/**
* Interrupt handler. DON'T CALL IT ANYWHERE!!
*/
void IRQHandler();
private:
uint8_t rxBuf[300];
struct R
{
uint16_t packetSize;
uint16_t rxIndex;
bool rxInProgress;
bool dataLenPending;
} rxStatus;
uint8_t txBuf[300];
uint8_t nodeAddress;
uint16_t CRC16(uint8_t* data, size_t len);
IsbProtocol_serial3();
IsbProtocol_serial3(const IsbProtocol_serial3& other);
IsbProtocol_serial3& operator=(const IsbProtocol_serial3& other);
bool operator==(const IsbProtocol_serial3& other);
};
/* Copyright (c) 2016-2017 Skyward Experimental Rocketry
* Authors: Alain Carlucci, Federico Terraneo
*
* 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 "Leds.h"
// Relying on the BSP to provide leds and configure them as output
#define LED(x) miosix::leds::led##x::getPin()
std::array<miosix::GpioPin, Leds::numLeds> Leds::pins{
{LED(0), LED(1), LED(2), LED(3), LED(4), LED(5), LED(6), LED(7), LED(8),
LED(9)}};
#undef LED