Skip to content

Commit

Permalink
[driver] Add MAX31865 driver with STM32F469 Discovery example
Browse files Browse the repository at this point in the history
  • Loading branch information
hshose committed Apr 6, 2023
1 parent d1938eb commit 78b309e
Show file tree
Hide file tree
Showing 5 changed files with 475 additions and 0 deletions.
86 changes: 86 additions & 0 deletions examples/stm32f469_discovery/max31855/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// coding: utf-8
/*
* Copyright (c) 2022, Sarah Vilete
* Copyright (c) 2022, Rasmus Kleist Hørlyck Sørensen
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include <modm/board.hpp>
#include <modm/processing.hpp>
#include <modm/driver/temperature/max31865.hpp>

using namespace Board;

using SpiMaster = modm::platform::SpiMaster2;

using Cs = D9;
using Mosi = D11;
using Miso = D12;
using Sck = D13;


class ThermocoupleThread : public modm::pt::Protothread
{
public:
ThermocoupleThread() : data{}, pt100(data) {}

bool
run()
{
PT_BEGIN();
pt100.initialize();

while (true)
{
MODM_LOG_INFO << "\nNew readout:" << modm::endl;
RF_CALL_BLOCKING(pt100.readout());

MODM_LOG_INFO << " resistance : " << data.getResistance() << " Ohm" << modm::endl;
MODM_LOG_INFO << " temperature fast: " << data.getTemperatureFast() << " degrees" << modm::endl;
MODM_LOG_INFO << " temperature precise: " << data.getTemperaturePrecise() << " degrees" << modm::endl;

timeout.restart(std::chrono::milliseconds(1000));
PT_WAIT_UNTIL(timeout.isExpired());
}

PT_END();
}

private:
modm::max31865pt100::Data data;
modm::Max31865<SpiMaster, Cs> pt100;

modm::ShortTimeout timeout;
};

ThermocoupleThread pt100Thread{};

int
main()
{
Board::initialize();
Cs::setOutput(modm::Gpio::High);

SpiMaster::connect<Miso::Miso, Mosi::Mosi, Sck::Sck>();
SpiMaster::initialize<Board::SystemClock, 351_kHz>();

MODM_LOG_INFO << "==========MAX 31865 Test==========" << modm::endl;
MODM_LOG_DEBUG << "Debug logging here" << modm::endl;
MODM_LOG_INFO << "Info logging here" << modm::endl;
MODM_LOG_WARNING << "Warning logging here" << modm::endl;
MODM_LOG_ERROR << "Error logging here" << modm::endl;
MODM_LOG_INFO << "===============================" << modm::endl;

while (true)
{
pt100Thread.run();
Board::LedOrange::toggle();
}

return 0;
}
14 changes: 14 additions & 0 deletions examples/stm32f469_discovery/max31855/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<library>
<extends>modm:disco-f469ni</extends>
<options>
<option name="modm:build:build.path">../../../build/stm32f469_discovery/max31855</option>
</options>
<modules>
<module>modm:driver:max31865</module>
<module>modm:platform:gpio</module>
<module>modm:platform:spi:2</module>
<module>modm:processing:protothread</module>
<module>modm:processing:timer</module>
<module>modm:build:scons</module>
</modules>
</library>
219 changes: 219 additions & 0 deletions src/modm/driver/temperature/max31865.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// coding: utf-8
// ----------------------------------------------------------------------------
/*
* Copyright (c) 2023, Henrik Hose
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#ifndef MODM_MAX31865_HPP
#define MODM_MAX31865_HPP

#include <array>
#include <modm/architecture/interface/register.hpp>
#include <modm/architecture/interface/spi_device.hpp>
#include <modm/processing/resumable.hpp>

namespace modm
{

/// @ingroup modm_driver_max31865
// Standard values for IEC 751 (PT100) with 400 Ohm reference
template<float R0 = 100.f, float Rref = 430.f, float alpha = 3.85055e-3f,
double a = double{3.90830e-3}, double b = double{-5.77500e-7},
double c = double{-4.18301e-12}>
struct max31865
{

enum class Fault : uint8_t
{
RtdHighThreshold = Bit7,
RtdLowThreshold = Bit6,
RefinMGreater85Percentbias = Bit5,
RefinMLess85PercentVbias = Bit4,
RtdinMLess85PercentVbias = Bit3,
OverUnderVoltage = Bit2
};
MODM_FLAGS8(Fault)

enum class Config : uint8_t
{
VBias = Bit7,
ConversionModeAuto = Bit6,
OneShot = Bit5,
ThreeWire = Bit4,
FaultDetectionCycle1 = Bit3,
FaultDetectionCycle0 = Bit2,
FaultStatusClear = Bit1,
Rejection = Bit0
};
MODM_FLAGS8(Config)

enum class FaultDetectionWrite : uint8_t
{
NoAction = 0b00,
FaultDetectionAutomaticDelay = 0b01,
RunFaultDetectionWithManualDelayCycle1 = 0b10,
FinishFaultDetectionWithManualDelayCycle2 = 0b11,
};
typedef Configuration<Config_t, FaultDetectionWrite, 0b11, 2>
FaultDetectionWrite_t; // Bit 8..10

enum class FaultDetectionRead : uint8_t
{
FaultDetectionFinished = 0b00,
AutomaticDetectionStillRunning = 0b01,
ManualCycle1Running =
0b10, // waiting for user to write FinishFaultDetectionWithManualDelayCycle2
ManualCycle2StillRunning = 0b11,
};
typedef Configuration<Config_t, FaultDetectionRead, 0b11, 2> FaultDetectionRead_t; // Bit 8..10

enum class Rejection : uint8_t
{
Rejection60Hz = 0b0,
Rejection50Hz = 0b1,
};
typedef Configuration<Config_t, Rejection, 0b1> Rejection_t; // Bit 8..10

struct FaultThreshold
{
// Resistance has 15 bits
typedef uint16_t RtdResistance_t;
};

static constexpr uint16_t
rtdfaultthresh(const max31865::FaultThreshold::RtdResistance_t res)
{
return static_cast<uint16_t>(res << 1);
};

enum class Register : uint8_t
{
ReadConfiguration = 0x00,
ReadRtdMsb = 0x01,
ReadRtdLsb = 0x02,
ReadHighFaultThresholdMsb = 0x03,
ReadHighFaultThresholdLsb = 0x04,
ReadLowFaultThresholdMsb = 0x05,
ReadLowFaultThresholdLsb = 0x06,
ReadFaultStatus = 0x07,

WriteConfiguration = 0x80,
WriteHighFaultThresholdMsb = 0x83,
WriteHighFaultThresholdLsb = 0x84,
WriteLowFaultThresholdMsb = 0x85,
WriteLowFaultThresholdLsb = 0x86,
};

struct modm_packed Data
{
/// @return docs
constexpr float
getResistance() const
{
const uint16_t adccode = data >> 1;
return adccode / 32768.f * Rref;
}

constexpr float
getTemperatureFast() const
{
return (getResistance() - R0) / alpha / 100.f;
}

/// @return docs
constexpr double
getTemperaturePrecise() const
{
const double res = getResistance();
double T = getTemperatureFast();
const double c0 = T <= 0 ? c : 0;

// Do some fixed number of newton steps for root finding
// on Callendar Van Dusen equation:
// R = R0*(1+a*T+b*T*T+c*(T-100)*T*T*T)
// Newton seems to need double precision to achieve 1.e-10 residual?!
for (int i = 0; i < 10; i++)
{
const double R =
double{R0} * (1 + (a * T) + (b * T * T) + (c0 * (T - 100) * T * T * T)) - res;
const double Rdash =
double{R0} * (a + (2 * b * T) + c0 * (((4 * T) - 300) * T * T));
T -= R / Rdash;
if (std::abs(R) <= 1.e-10) { break; }
}

return T;
}

uint16_t data;
};
}; // struct max31865

using max31865pt100 = max31865<>;

/**
* @tparam SpiMaster
* @tparam Cs
*
* @author Henrik Hose
* @ingroup modm_driver_max31865
*/
template<typename SpiMaster, typename Cs>
class Max31865 : public max31865pt100,
public modm::SpiDevice<SpiMaster>,
protected modm::NestedResumable<3>
{
public:
/**
* @param data pointer to buffer of the internal data of type Data
*/
Max31865(Data &data);

/// Call this function once before using the device
void
initialize();

/// Read the raw data from the sensor
modm::ResumableResult<void>
readout();

// modm::ResumableResult<void>
// setFaultThreshold(FaultThreshold lower, FaultThreshold upper);

public:
/// Get the data object for this sensor
inline Data &
getData()
{
return data;
}

private:
Data &data;
std::array<uint8_t, 2> buffer;
Config_t config;

modm::ShortTimeout timeout;

modm::ResumableResult<void>
writeSingleRegister(Register address, uint8_t data);

modm::ResumableResult<uint8_t>
readSingleRegister(Register address);

modm::ResumableResult<uint16_t>
readTwoRegisters(Register address);
};

} // namespace modm

#include "max31865_impl.hpp"

#endif // MODM_MAX31865_HPP
32 changes: 32 additions & 0 deletions src/modm/driver/temperature/max31865.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2023, Henrik Hose
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------


def init(module):
module.name = ":driver:max31865"
module.description = """\
# MAX31865 RTD-to-Digital Converter
[Datasheet](https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf)
"""

def prepare(module, options):
module.depends(
":architecture:gpio",
":architecture:spi.device",
":processing:resumable")
return True

def build(env):
env.outbasepath = "modm/src/modm/driver/temperature"
env.copy("max31865.hpp")
env.copy("max31865_impl.hpp")
Loading

0 comments on commit 78b309e

Please sign in to comment.