Rename project to fantemp

This commit is contained in:
BlackMark 2020-03-30 19:45:33 +02:00
parent 094e61a7c1
commit 7175193561
41 changed files with 2498 additions and 19 deletions

16
.gitmodules vendored
View File

@ -1,12 +1,12 @@
[submodule "thermistor/uart"]
path = thermistor/uart
[submodule "fantemp/uart"]
path = fantemp/uart
url = git@git.blackmark.me:avr/uart.git
[submodule "thermistor/flash"]
path = thermistor/flash
[submodule "fantemp/flash"]
path = fantemp/flash
url = git@git.blackmark.me:avr/flash.git
[submodule "thermistor/io"]
path = thermistor/io
[submodule "fantemp/io"]
path = fantemp/io
url = git@git.blackmark.me:avr/io.git
[submodule "thermistor/adc"]
path = thermistor/adc
[submodule "fantemp/adc"]
path = fantemp/adc
url = git@git.blackmark.me:avr/adc.git

View File

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Atmel Studio Solution File, Format Version 11.00
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "thermistor", "thermistor\thermistor.cppproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "fantemp", "fantemp\fantemp.cppproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

13
fantemp/adc/.clang-format Normal file
View File

@ -0,0 +1,13 @@
---
BasedOnStyle: LLVM
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: ForIndentation
AlignEscapedNewlines: DontAlign
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
...

9
fantemp/adc/.gitattributes vendored Normal file
View File

@ -0,0 +1,9 @@
*.h eol=lf
*.hpp eol=lf
*.c eol=lf
*.cpp eol=lf
.git* eol=lf
*.vcxproj* eol=crlf
*.cppproj eol=crlf
*.sln eol=crlf
*.atsln eol=crlf

11
fantemp/adc/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.vs
Release
Debug
*.componentinfo.xml
*.elf
*.o
*.hex
*.srec
*.eeprom
*.lss
*.map

21
fantemp/adc/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 BlackMark
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 (including the next
paragraph) 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.

219
fantemp/adc/adc.hpp Normal file
View File

@ -0,0 +1,219 @@
#ifndef ADC_HPP
#define ADC_HPP
#include "config.hpp"
#include "hardware.hpp"
#include <stdint.h>
#include "../io/io.hpp"
namespace adc {
namespace detail {
extern void (*fnAdcIntHandler)(uint16_t);
using reg_ptr_t = volatile uint8_t *;
template <uintptr_t Address>
static inline reg_ptr_t getRegPtr()
{
return reinterpret_cast<reg_ptr_t>(Address);
}
template <typename Cfg>
class AdcImpl {
public:
static uint16_t read()
{
*getRegPtr<Registers::CTRL_STAT_A_ADDR>() |= 1 << ControlFlagsA::START_CONV;
while (*getRegPtr<Registers::CTRL_STAT_A_ADDR>() & (1 << ControlFlagsA::START_CONV))
;
uint16_t adcSample = *getRegPtr<Registers::DATA_L_ADDR>();
adcSample |= *getRegPtr<Registers::DATA_H_ADDR>() << 8;
return adcSample;
}
protected:
static void init(uint8_t muxVal)
{
*getRegPtr<Registers::MUX_SEL_ADDR>() = muxVal | calcRef();
auto ctrlStatA = calcCtrlStatA(detail::fnAdcIntHandler != nullptr);
*getRegPtr<Registers::CTRL_STAT_A_ADDR>() = ctrlStatA;
constexpr auto ctrlStatB = calcCtrlStatB();
*getRegPtr<Registers::CTRL_STAT_B_ADDR>() = ctrlStatB;
}
static constexpr auto calcRef()
{
uint8_t muxVal = 0;
if constexpr (Cfg::VREF == VoltageRef::AVCC) {
muxVal |= 1 << ControlFlagsMUX::REF_SEL_0;
} else if constexpr (Cfg::VREF == VoltageRef::INTERNAL) {
muxVal |= (1 << ControlFlagsMUX::REF_SEL_0) | (1 << ControlFlagsMUX::REF_SEL_1);
}
return muxVal;
}
static constexpr uint8_t calcPrescaler()
{
constexpr auto validPrescaler = Cfg::PRESCALER == 2 || Cfg::PRESCALER == 4 || Cfg::PRESCALER == 8 ||
Cfg::PRESCALER == 16 || Cfg::PRESCALER == 32 || Cfg::PRESCALER == 64 ||
Cfg::PRESCALER == 128;
static_assert(validPrescaler, "Invalid prescaler");
// clang-format off
switch (Cfg::PRESCALER) {
case 2: return 1;
case 4: return 2;
case 8: return 3;
case 16: return 4;
case 32: return 5;
case 64: return 6;
case 128: return 7;
}
// clang-format on
}
static auto calcCtrlStatA(bool interruptEnable)
{
uint8_t ctrlStatA = 1 << ControlFlagsA::ENABLE;
if constexpr (Cfg::MODE == Mode::AUTO) {
ctrlStatA |= 1 << ControlFlagsA::AUTO_TRIGGER;
} else if (interruptEnable) {
ctrlStatA |= 1 << ControlFlagsA::CONV_COMPLETE_INT_ENABLE;
}
return ctrlStatA | calcPrescaler();
}
static constexpr uint8_t calcCtrlStatB()
{
if constexpr (Cfg::MODE == Mode::AUTO) {
// clang-format off
switch (Cfg::TRIGGER_SRC) {
case TriggerSource::FREE_RUNNING: return 0;
case TriggerSource::ANALOG_COMP: return 1;
case TriggerSource::EXTERNAL_INT_0: return 2;
case TriggerSource::TIMER0_COMP_A: return 3;
case TriggerSource::TIMER0_OVERFLOW: return 4;
case TriggerSource::TIMER1_COMP_B: return 5;
case TriggerSource::TIMER1_OVERFLOW: return 6;
case TriggerSource::TIMER1_CAPTURE: return 7;
}
// clang-format on
}
return 0;
}
};
} // namespace detail
template <typename Cfg, typename Input, Input src>
class Adc {
public:
static_assert(sizeof(Input) == -1, "Invalid input source selected");
};
template <typename Cfg, io::P pin>
class Adc<Cfg, io::P, pin> : public detail::AdcImpl<Cfg> {
using callback_t = void (*)(uint16_t);
public:
static_assert(detail::supports_adc_v<pin>, "Pin does not support ADC");
static void init(callback_t callback)
{
detail::fnAdcIntHandler = callback;
init();
}
static void init()
{
constexpr auto muxVal = calcChannel();
detail::AdcImpl<Cfg>::init(muxVal);
}
private:
static constexpr auto calcChannel()
{
return static_cast<uint8_t>(pin) - static_cast<uint8_t>(io::P::C0);
}
};
template <typename Cfg, InputSource src>
class Adc<Cfg, InputSource, src> : public detail::AdcImpl<Cfg> {
using callback_t = void (*)(uint16_t);
public:
static void init(callback_t callback)
{
detail::fnAdcIntHandler = callback;
init();
}
static void init()
{
constexpr auto muxVal = calcChannel();
detail::AdcImpl<Cfg>::init(muxVal);
}
private:
static constexpr auto calcChannel()
{
using mx = detail::ControlFlagsMUX;
// clang-format off
switch (src) {
case InputSource::TEMP: return 1 << mx::CHANNEL_SEL_3;
case InputSource::VBG: return (1 << mx::CHANNEL_SEL_3) | (1 << mx::CHANNEL_SEL_2) | (1 << mx::CHANNEL_SEL_1);
case InputSource::GND: return (1 << mx::CHANNEL_SEL_3) | (1 << mx::CHANNEL_SEL_2) | (1 << mx::CHANNEL_SEL_1) | (1 << mx::CHANNEL_SEL_0);
}
// clang-format on
}
};
} // namespace adc
#endif
//////////////////////////////////////////////////////////////////////////
#ifdef ADC_INT_VECTOR
#include <stdint.h>
#include <avr/interrupt.h>
namespace adc {
namespace detail {
#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega328P__)
void (*fnAdcIntHandler)(uint16_t) = nullptr;
ISR(ADC_vect)
{
if (fnAdcIntHandler) {
const auto adcSample = *getRegPtr<Registers::DATA_L_ADDR>() | (*getRegPtr<Registers::DATA_H_ADDR>() << 8);
fnAdcIntHandler(adcSample);
}
}
#else
#error "This chip is not supported"
#endif
} // namespace detail
} // namespace adc
#undef ADC_INT_VECTORS
#endif

73
fantemp/adc/config.hpp Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include <stdint.h>
namespace adc {
namespace detail {
enum class Mode {
SINGLE,
AUTO,
FREE_RUNNING,
};
} // namespace detail
enum class TriggerSource {
FREE_RUNNING,
ANALOG_COMP,
EXTERNAL_INT_0,
TIMER0_COMP_A,
TIMER0_OVERFLOW,
TIMER1_COMP_B,
TIMER1_OVERFLOW,
TIMER1_CAPTURE,
};
template <TriggerSource src = TriggerSource::FREE_RUNNING>
struct AutoMode {
static constexpr auto SRC = src;
};
struct FreeRunningMode {
};
struct SingleMode {
};
enum class VoltageRef {
EXTERNAL,
AVCC,
INTERNAL,
};
enum class InputSource {
TEMP,
VBG,
GND,
};
template <class Mode, VoltageRef vref = VoltageRef::AVCC, uint8_t prescaler = 128>
struct Config {
static constexpr auto MODE = detail::Mode::AUTO;
static constexpr auto TRIGGER_SRC = Mode::SRC;
static constexpr auto VREF = vref;
static constexpr auto PRESCALER = prescaler;
};
template <VoltageRef vref, uint8_t prescaler>
struct Config<FreeRunningMode, vref, prescaler> {
static constexpr auto MODE = detail::Mode::FREE_RUNNING;
static constexpr auto VREF = vref;
static constexpr auto PRESCALER = prescaler;
};
template <VoltageRef vref, uint8_t prescaler>
struct Config<SingleMode, vref, prescaler> {
static constexpr auto MODE = detail::Mode::SINGLE;
static constexpr auto VREF = vref;
static constexpr auto PRESCALER = prescaler;
};
} // namespace adc

95
fantemp/adc/hardware.hpp Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include <stdint.h>
#include "../io/io.hpp"
namespace adc {
namespace detail {
#if defined(__AVR_ATmega328P__)
/*
The following works in avr-gcc 5.4.0, but is not legal C++, because ptr's are not legal constexpr's:
constexpr auto *foo = ptr;
Workaround is to store the address of the ptr in a uintptr_t and reinterpret_cast it at call site.
The _SFR_ADDR macro in sfr_defs.h would give the address, but it does that by taking the address of the dereferenced
pointer and casts it to uint16_t, which is still not a legal constexpr.
The workaround therefore is to disable the pointer-cast-and-dereference macro _MMIO_BYTE temporarily.
*/
#pragma push_macro("_MMIO_BYTE")
#undef _MMIO_BYTE
#define _MMIO_BYTE
struct Registers {
static constexpr uintptr_t MUX_SEL_ADDR = ADMUX;
static constexpr uintptr_t CTRL_STAT_A_ADDR = ADCSRA;
static constexpr uintptr_t DATA_L_ADDR = ADCL;
static constexpr uintptr_t DATA_H_ADDR = ADCH;
static constexpr uintptr_t CTRL_STAT_B_ADDR = ADCSRB;
static constexpr uintptr_t DIG_IN_DIS_ADDR = DIDR0;
};
enum class ControlFlagsMUX {
CHANNEL_SEL_0 = MUX0,
CHANNEL_SEL_1 = MUX1,
CHANNEL_SEL_2 = MUX2,
CHANNEL_SEL_3 = MUX3,
LEFT_ADJ_RES = ADLAR,
REF_SEL_0 = REFS0,
REF_SEL_1 = REFS1,
};
enum class ControlFlagsA {
PRESCALER_SEL_0 = ADPS0,
PRESCALER_SEL_1 = ADPS1,
PRESCALER_SEL_2 = ADPS2,
CONV_COMPLETE_INT_ENABLE = ADIE,
CONV_COMPLETE = ADIF,
AUTO_TRIGGER = ADATE,
START_CONV = ADSC,
ENABLE = ADEN,
};
enum class ControlFlagsB {
TRIGGER_SRC_0 = ADTS0,
TRIGGER_SRC_1 = ADTS1,
TRIGGER_SRC_2 = ADTS2,
ANALOG_COMP_MUX_ENABLE = ACME,
};
enum class ControlFlagsDigInDis {
DIGITAL_INPUT_DISABLE_0 = ADC0D,
DIGITAL_INPUT_DISABLE_1 = ADC1D,
DIGITAL_INPUT_DISABLE_2 = ADC2D,
DIGITAL_INPUT_DISABLE_3 = ADC3D,
DIGITAL_INPUT_DISABLE_4 = ADC4D,
DIGITAL_INPUT_DISABLE_5 = ADC5D,
};
// clang-format off
constexpr int operator<<(const int &lhs, const ControlFlagsMUX &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsA &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsB &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
template <io::P pin>
struct supports_adc {
static constexpr auto value = (io::detail::getBus(pin) == io::Bus::C) ? true : false;
};
template <io::P pin>
constexpr auto supports_adc_v = supports_adc<pin>::value;
#pragma pop_macro("_MMIO_BYTE")
#else
#error "This chip is not supported"
#endif
} // namespace detail
} // namespace adc

View File

@ -12,9 +12,9 @@
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
<AssemblyName>thermistor</AssemblyName>
<Name>thermistor</Name>
<RootNamespace>thermistor</RootNamespace>
<AssemblyName>fantemp</AssemblyName>
<Name>fantemp</Name>
<RootNamespace>fantemp</RootNamespace>
<ToolchainFlavour>avr-g++-9.1.0</ToolchainFlavour>
<KeepTimersRunning>true</KeepTimersRunning>
<OverrideVtor>false</OverrideVtor>

View File

@ -0,0 +1,13 @@
---
BasedOnStyle: LLVM
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: ForIndentation
AlignEscapedNewlines: DontAlign
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
...

9
fantemp/flash/.gitattributes vendored Normal file
View File

@ -0,0 +1,9 @@
*.h eol=lf
*.hpp eol=lf
*.c eol=lf
*.cpp eol=lf
.git* eol=lf
*.vcxproj* eol=crlf
*.cppproj eol=crlf
*.sln eol=crlf
*.atsln eol=crlf

11
fantemp/flash/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.vs
Release
Debug
*.componentinfo.xml
*.elf
*.o
*.hex
*.srec
*.eeprom
*.lss
*.map

21
fantemp/flash/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 BlackMark
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 (including the next
paragraph) 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.

12
fantemp/flash/flash.hpp Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <avr/pgmspace.h>
#define F(str) (reinterpret_cast<const ::detail::FlashString *>(PSTR(str)))
namespace detail {
// Only used for C++ type safety, because otherwise a PSTR would be indistinguishable from a normal c-string
struct FlashString;
} // namespace detail

13
fantemp/io/.clang-format Normal file
View File

@ -0,0 +1,13 @@
---
BasedOnStyle: LLVM
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: ForIndentation
AlignEscapedNewlines: DontAlign
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
...

9
fantemp/io/.gitattributes vendored Normal file
View File

@ -0,0 +1,9 @@
*.h eol=lf
*.hpp eol=lf
*.c eol=lf
*.cpp eol=lf
.git* eol=lf
*.vcxproj* eol=crlf
*.cppproj eol=crlf
*.sln eol=crlf
*.atsln eol=crlf

11
fantemp/io/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.vs
Release
Debug
*.componentinfo.xml
*.elf
*.o
*.hex
*.srec
*.eeprom
*.lss
*.map

21
fantemp/io/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 BlackMark
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 (including the next
paragraph) 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.

557
fantemp/io/io.hpp Normal file
View File

@ -0,0 +1,557 @@
#pragma once
#include <stdint.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
//////////////////////////////////////////////////////////////////////////
// Preprocessor defines
#if defined(__AVR_ATmega32__) || defined(__AVR_ATmega32A__) || defined(__AVR_ATmega644P__) || \
defined(__AVR_ATmega1284P__)
#define GPIO_32
#endif
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8A__) || defined(__AVR_ATmega168A__) || defined(__AVR_ATmega328P__)
#define GPIO_23
#endif
#if defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__)
#define GPIO_6
#endif
#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega168A__) || \
defined(__AVR_ATmega328P__) || defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__)
#define HARDWARE_TOGGLE
#endif
#ifdef GPIO_32
#define PORT_A_AVAILABLE
#endif
#if defined(GPIO_32) || defined(GPIO_23) || defined(GPIO_6)
#define PORT_B_AVAILABLE
#endif
#if defined(GPIO_32) || defined(GPIO_23)
#define PORT_C_AVAILABLE
#define PORT_D_AVAILABLE
#define PIN_B6_AVAILABLE
#define PIN_B7_AVAILABLE
#endif
#if defined(GPIO_32)
#define PIN_C7_AVAILABLE
#endif
#define FORCE_INLINE __attribute__((always_inline))
//////////////////////////////////////////////////////////////////////////
// Library implementation
namespace io {
enum class Dir { IN, OUT };
enum class P {
#ifdef PORT_A_AVAILABLE
A0 = 0x00,
A1 = 0x01,
A2 = 0x02,
A3 = 0x03,
A4 = 0x04,
A5 = 0x05,
A6 = 0x06,
A7 = 0x07,
#endif
#ifdef PORT_B_AVAILABLE
B0 = 0x10,
B1 = 0x11,
B2 = 0x12,
B3 = 0x13,
B4 = 0x14,
B5 = 0x15,
#ifdef PIN_B6_AVAILABLE
B6 = 0x16,
#endif
#ifdef PIN_B7_AVAILABLE
B7 = 0x17,
#endif
#endif
#ifdef PORT_C_AVAILABLE
C0 = 0x20,
C1 = 0x21,
C2 = 0x22,
C3 = 0x23,
C4 = 0x24,
C5 = 0x25,
C6 = 0x26,
#ifdef PIN_C7_AVAILABLE
C7 = 0x27,
#endif
#endif
#ifdef PORT_D_AVAILABLE
D0 = 0x30,
D1 = 0x31,
D2 = 0x32,
D3 = 0x33,
D4 = 0x34,
D5 = 0x35,
D6 = 0x36,
D7 = 0x37,
#endif
};
enum class Bus {
#ifdef PORT_A_AVAILABLE
A = 0x00,
#endif
#ifdef PORT_B_AVAILABLE
B = 0x01,
#endif
#ifdef PORT_C_AVAILABLE
C = 0x02,
#endif
#ifdef PORT_D_AVAILABLE
D = 0x03,
#endif
};
//////////////////////////////////////////////////////////////////////////
// Implementation details
namespace detail {
/*
The following works in avr-gcc 5.4.0, but is not legal C++, because ptr's are not legal constexpr's:
constexpr auto *foo = ptr;
Workaround is to store the address of the ptr in a uintptr_t and reinterpret_cast it at call site.
The _SFR_ADDR macro in sfr_defs.h would give the address, but it does that by taking the address of the dereferenced
pointer and casts it to uint16_t, which is still not a legal constexpr.
The workaround therefore is to disable the pointer-cast-and-dereference macro _MMIO_BYTE temporarily.
*/
#pragma push_macro("_MMIO_BYTE")
#undef _MMIO_BYTE
#define _MMIO_BYTE
#ifdef PORT_A_AVAILABLE
static constexpr uintptr_t PORT_A_DIR_REG_ADDR = DDRA;
static constexpr uintptr_t PORT_A_OUTPUT_REG_ADDR = PORTA;
static constexpr uintptr_t PORT_A_INPUT_REG_ADDR = PINA;
#else
static constexpr uintptr_t PORT_A_DIR_REG_ADDR = 0;
static constexpr uintptr_t PORT_A_OUTPUT_REG_ADDR = 0;
static constexpr uintptr_t PORT_A_INPUT_REG_ADDR = 0;
#endif
#ifdef PORT_B_AVAILABLE
static constexpr uintptr_t PORT_B_DIR_REG_ADDR = DDRB;
static constexpr uintptr_t PORT_B_OUTPUT_REG_ADDR = PORTB;
static constexpr uintptr_t PORT_B_INPUT_REG_ADDR = PINB;
#else
static constexpr uintptr_t PORT_B_DIR_REG_ADDR = 0;
static constexpr uintptr_t PORT_B_OUTPUT_REG_ADDR = 0;
static constexpr uintptr_t PORT_B_INPUT_REG_ADDR = 0;
#endif
#ifdef PORT_C_AVAILABLE
static constexpr uintptr_t PORT_C_DIR_REG_ADDR = DDRC;
static constexpr uintptr_t PORT_C_OUTPUT_REG_ADDR = PORTC;
static constexpr uintptr_t PORT_C_INPUT_REG_ADDR = PINC;
#else
static constexpr uintptr_t PORT_C_DIR_REG_ADDR = 0;
static constexpr uintptr_t PORT_C_OUTPUT_REG_ADDR = 0;
static constexpr uintptr_t PORT_C_INPUT_REG_ADDR = 0;
#endif
#ifdef PORT_D_AVAILABLE
static constexpr uintptr_t PORT_D_DIR_REG_ADDR = DDRD;
static constexpr uintptr_t PORT_D_OUTPUT_REG_ADDR = PORTD;
static constexpr uintptr_t PORT_D_INPUT_REG_ADDR = PIND;
#else
static constexpr uintptr_t PORT_D_DIR_REG_ADDR = 0;
static constexpr uintptr_t PORT_D_OUTPUT_REG_ADDR = 0;
static constexpr uintptr_t PORT_D_INPUT_REG_ADDR = 0;
#endif
#pragma pop_macro("_MMIO_BYTE")
static constexpr auto getBus(const P pin)
{
// Upper 4 bits of pin encode which port this pin is on
uint8_t port = static_cast<uint8_t>(pin) >> 4 & 0x0F;
return static_cast<Bus>(port);
}
static constexpr auto getPinBit(const P pin)
{
// Lower 4 bits of pin encode which pin bit it is
uint8_t pinBit = static_cast<uint8_t>(pin) & 0x0F;
return pinBit;
}
static constexpr auto getDDR(const Bus bus)
{
switch (static_cast<uint8_t>(bus)) {
case 0: // Bus::A
return PORT_A_DIR_REG_ADDR;
case 1: // Bus::B
return PORT_B_DIR_REG_ADDR;
case 2: // Bus::C
return PORT_C_DIR_REG_ADDR;
case 3: // Bus::D
return PORT_D_DIR_REG_ADDR;
}
}
static constexpr auto getPORT(const Bus bus)
{
switch (static_cast<uint8_t>(bus)) {
case 0: // Bus::A
return PORT_A_OUTPUT_REG_ADDR;
case 1: // Bus::B
return PORT_B_OUTPUT_REG_ADDR;
case 2: // Bus::C
return PORT_C_OUTPUT_REG_ADDR;
case 3: // Bus::D
return PORT_D_OUTPUT_REG_ADDR;
}
}
static constexpr auto getPIN(const Bus bus)
{
switch (static_cast<uint8_t>(bus)) {
case 0: // Bus::A
return PORT_A_INPUT_REG_ADDR;
case 1: // Bus::B
return PORT_B_INPUT_REG_ADDR;
case 2: // Bus::C
return PORT_C_INPUT_REG_ADDR;
case 3: // Bus::D
return PORT_D_INPUT_REG_ADDR;
}
}
using reg_ptr_t = volatile uint8_t *;
template <uintptr_t Address>
static inline reg_ptr_t getRegPtr()
{
return reinterpret_cast<reg_ptr_t>(Address);
}
template <template <P, uint8_t> typename Func, P pin, P... pins>
struct CallHelper {
template <typename... Args>
FORCE_INLINE static inline void call(Args... args)
{
Func<pin, sizeof...(pins)>::call(args...);
CallHelper<Func, pins...>::call(args...);
}
};
template <template <P, uint8_t> typename Func, P pin>
struct CallHelper<Func, pin> {
template <typename... Args>
FORCE_INLINE static inline void call(Args... args)
{
Func<pin, 0>::call(args...);
}
};
template <template <P> typename Func, P pin, P... pins>
struct ReadCallHelper {
static inline uint8_t call() FORCE_INLINE
{
return Func<pin>::call() << sizeof...(pins) | ReadCallHelper<Func, pins...>::call();
}
};
template <template <P> typename Func, P pin>
struct ReadCallHelper<Func, pin> {
static inline uint8_t call() FORCE_INLINE
{
return Func<pin>::call();
}
};
} // namespace detail
//////////////////////////////////////////////////////////////////////////
// Zero overhead Pin object for pretty code without losing performance
template <P pin>
class Pin {
public:
// Pin objects cannot be moved or copied
Pin(const Pin &) = delete;
Pin(Pin &&) = delete;
Pin &operator=(const Pin &) = delete;
Pin &operator=(Pin &&) = delete;
// The only valid way to create a Pin object is with the default constructor
Pin() = default;
static inline void dir(const Dir dir) FORCE_INLINE
{
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
detail::reg_ptr_t dirRegPtr = detail::getRegPtr<detail::getDDR(bus)>();
if (dir == Dir::IN)
*dirRegPtr &= ~(1 << pinBit);
else if (dir == Dir::OUT)
*dirRegPtr |= (1 << pinBit);
}
static inline void pullup(const bool enable) FORCE_INLINE
{
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
if (enable)
*portRegPtr |= (1 << pinBit);
else
*portRegPtr &= ~(1 << pinBit);
}
static inline void write(const bool value) FORCE_INLINE
{
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
if (value)
*portRegPtr |= (1 << pinBit);
else
*portRegPtr &= ~(1 << pinBit);
}
static inline void toggle() FORCE_INLINE
{
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
#ifdef HARDWARE_TOGGLE
detail::reg_ptr_t pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
*pinRegPtr |= (1 << pinBit);
#else
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
*portRegPtr ^= (1 << pinBit);
#endif
}
static inline bool read() FORCE_INLINE
{
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
detail::reg_ptr_t pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
if (*pinRegPtr >> pinBit & 1)
return true;
return false;
}
Pin &operator=(const bool value) FORCE_INLINE
{
write(value);
return *this;
}
operator bool() const FORCE_INLINE
{
return read();
}
};
//////////////////////////////////////////////////////////////////////////
// Zero overhead Port object for pretty code without losing performance
template <Bus port>
class Port {
public:
// Port objects cannot be moved or copied
Port(const Port &) = delete;
Port(Port &&) = delete;
Port &operator=(const Port &) = delete;
Port &operator=(Port &&) = delete;
// The only valid way to create a Port object is with the default constructor
Port() = default;
static inline void dir(const Dir dir) FORCE_INLINE
{
detail::reg_ptr_t dirRegPtr = detail::getRegPtr<detail::getDDR(port)>();
if (dir == Dir::IN)
*dirRegPtr = 0x00;
else if (dir == Dir::OUT)
*dirRegPtr = 0xFF;
}
static inline void pullup(const bool enable) FORCE_INLINE
{
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
if (enable)
*portRegPtr = 0xFF;
else
*portRegPtr = 0x00;
}
static inline void write(const uint8_t value) FORCE_INLINE
{
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
*portRegPtr = value;
}
static inline void invert() FORCE_INLINE
{
#ifdef HARDWARE_TOGGLE
detail::reg_ptr_t pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
*pinRegPtr = 0xFF;
#else
detail::reg_ptr_t portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
*portRegPtr = ~(*portRegPtr);
#endif
}
static inline uint8_t read() FORCE_INLINE
{
detail::reg_ptr_t pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
return *pinRegPtr;
}
inline Port &operator=(const uint8_t value) FORCE_INLINE
{
write(value);
return *this;
}
inline operator uint8_t() const FORCE_INLINE
{
return read();
}
};
//////////////////////////////////////////////////////////////////////////
// Zero overhead Virtual Port object for pretty code without losing performance
namespace detail {
template <P pin, uint8_t offset>
struct Callers {
static void call(const Dir dir) FORCE_INLINE
{
Pin<pin>::dir(dir);
};
static void call(const bool enable) FORCE_INLINE
{
Pin<pin>::pullup(enable);
};
static void call(const uint8_t value) FORCE_INLINE
{
Pin<pin>::write(value >> offset & 1);
};
static void call() FORCE_INLINE
{
Pin<pin>::toggle();
};
};
template <P pin>
struct readCaller {
static bool call() FORCE_INLINE
{
return Pin<pin>::read();
};
};
} // namespace detail
template <P... pins>
class VirtPort {
public:
static_assert(sizeof...(pins) <= 8, "A virtual port cannot have more than 8 pins");
// VirtPort objects cannot be moved or copied
VirtPort(const VirtPort &) = delete;
VirtPort(VirtPort &&) = delete;
VirtPort &operator=(const VirtPort &) = delete;
VirtPort &operator=(VirtPort &&) = delete;
// The only valid way to create a VirtPort object is with the default constructor
VirtPort() = default;
static inline void dir(const Dir dir) FORCE_INLINE
{
detail::CallHelper<detail::Callers, pins...>::call(dir);
}
static inline void pullup(const bool enable) FORCE_INLINE
{
detail::CallHelper<detail::Callers, pins...>::call(enable);
}
static inline void write(const uint8_t value) FORCE_INLINE
{
detail::CallHelper<detail::Callers, pins...>::call(value);
}
static inline void invert() FORCE_INLINE
{
detail::CallHelper<detail::Callers, pins...>::call();
}
static inline uint8_t read() FORCE_INLINE
{
return detail::ReadCallHelper<detail::readCaller, pins...>::call();
}
inline VirtPort &operator=(const uint8_t value) FORCE_INLINE
{
write(value);
return *this;
}
inline operator uint8_t() const FORCE_INLINE
{
return read();
}
};
} // namespace io
//////////////////////////////////////////////////////////////////////////
#undef GPIO_32
#undef GPIO_23
#undef GPIO_6
#undef PORT_A_AVAILABLE
#undef PORT_B_AVAILABLE
#undef PORT_C_AVAILABLE
#undef PORT_D_AVAILABLE
#undef PIN_B6_AVAILABLE
#undef PIN_B7_AVAILABLE
#undef PIN_C7_AVAILABLE
#undef FORCE_INLINE

View File

@ -50,15 +50,15 @@ int main()
while (true) {
const auto adcSample = temp.sampleAdc(adcPin, 1000);
const auto thermistorResistance = temp.getResistance(adcSample);
const auto temperature = temp.getTemperature(thermistorResistance);
const auto fantempResistance = temp.getResistance(adcSample);
const auto temperature = temp.getTemperature(fantempResistance);
const auto fanSpeed = temperatureCurve(temperature);
char floatBuffer[16];
sprintf(floatBuffer, "%.2f", adcSample);
serial << F("Read ADC: ") << floatBuffer << F("\r\n");
sprintf(floatBuffer, "%.2f", thermistorResistance);
sprintf(floatBuffer, "%.2f", fantempResistance);
serial << F("Resistance: ") << floatBuffer << F("\r\n");
sprintf(floatBuffer, "%.2f", temperature);
serial << F("Temperature: ") << floatBuffer << F(" C \r\n");

View File

@ -0,0 +1,13 @@
---
BasedOnStyle: LLVM
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: ForIndentation
AlignEscapedNewlines: DontAlign
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
...

9
fantemp/uart/.gitattributes vendored Normal file
View File

@ -0,0 +1,9 @@
*.h eol=lf
*.hpp eol=lf
*.c eol=lf
*.cpp eol=lf
.git* eol=lf
*.vcxproj* eol=crlf
*.cppproj eol=crlf
*.sln eol=crlf
*.atsln eol=crlf

11
fantemp/uart/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.vs
Release
Debug
*.componentinfo.xml
*.elf
*.o
*.hex
*.srec
*.eeprom
*.lss
*.map

21
fantemp/uart/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 BlackMark
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 (including the next
paragraph) 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.

50
fantemp/uart/config.hpp Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include <stdint.h>
namespace uart {
enum class DataBits {
FIVE,
SIX,
SEVEN,
EIGHT,
NINE,
};
enum class StopBits {
ONE,
TWO,
};
enum class Parity {
NONE,
ODD,
EVEN,
};
namespace detail {
template <DataBits dataBits>
struct choose_data_type {
using type = uint8_t;
};
template <>
struct choose_data_type<DataBits::NINE> {
using type = uint16_t;
};
} // namespace detail
template <uint32_t baudRate = 9600, DataBits dataBits = DataBits::EIGHT, Parity parity = Parity::NONE,
StopBits stopBits = StopBits::ONE>
struct Config {
static constexpr auto BAUD_RATE = baudRate;
static constexpr auto DATA_BITS = dataBits;
static constexpr auto PARITY = parity;
static constexpr auto STOP_BITS = stopBits;
using data_t = typename detail::choose_data_type<DATA_BITS>::type;
};
} // namespace uart

389
fantemp/uart/hardware.hpp Normal file
View File

@ -0,0 +1,389 @@
#pragma once
#include "../clock.hpp"
#include <stdint.h>
#include "utils.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
enum class Mode {
ASYNCHRONOUS,
ASYNCHRONOUS_2X,
SYNCHRONOUS_MASTER,
SYNCHRONOUS_SLAVE,
SPI,
};
enum class Driven {
INTERRUPT,
BLOCKING,
};
namespace detail {
using reg_ptr_t = volatile uint8_t *;
template <uintptr_t Address>
static inline reg_ptr_t getRegPtr()
{
return reinterpret_cast<reg_ptr_t>(Address);
}
template <typename data_t, uint8_t Size>
struct RingBuffer {
uint8_t head;
uint8_t tail;
data_t buf[Size];
};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Driven driven,
Mode mode>
class Hardware {
public:
static void init() FORCE_INLINE
{
constexpr auto baudVal = calcBaud();
*getRegPtr<Registers::BAUD_REG_H_ADDR>() = static_cast<uint8_t>(baudVal >> 8);
*getRegPtr<Registers::BAUD_REG_L_ADDR>() = static_cast<uint8_t>(baudVal);
constexpr auto dataBitsVal = calcDataBits();
constexpr auto parityVal = calcParity();
constexpr auto stopBitsVal = calcStopBits();
constexpr auto modeVal = calcMode();
constexpr auto enableRx = calcRxState<true>();
constexpr auto enableTx = calcTxState<true>();
constexpr auto interruptVal = calcInterrupt();
constexpr uint8_t controlRegB = dataBitsVal.regBVal | enableRx | enableTx | interruptVal;
constexpr uint8_t controlRegC = dataBitsVal.regCVal | parityVal | stopBitsVal | modeVal;
*getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>() = controlRegB;
*getRegPtr<Registers::CTRL_STAT_REG_C_ADDR>() = controlRegC;
}
static bool rxByteBlocking(typename cfg::data_t &byte) FORCE_INLINE
{
if (*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
byte = *getRegPtr<Registers::IO_REG_ADDR>();
return true;
}
return false;
}
static typename cfg::data_t rxByteInterrupt() FORCE_INLINE
{
return *getRegPtr<Registers::IO_REG_ADDR>();
}
static bool txEmpty() FORCE_INLINE
{
return *getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::DATA_REG_EMPTY);
}
static bool txComplete() FORCE_INLINE
{
return *getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::TRANSMIT_COMPLETE);
}
static void clearTxComplete() FORCE_INLINE
{
*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() |= (1 << CtrlFlagsA::TRANSMIT_COMPLETE);
}
static void txByteBlocking(const typename cfg::data_t &byte) FORCE_INLINE
{
while (!txEmpty())
;
*getRegPtr<Registers::IO_REG_ADDR>() = byte;
}
static void txByteInterrupt(volatile const typename cfg::data_t &byte) FORCE_INLINE
{
*getRegPtr<Registers::IO_REG_ADDR>() = byte;
}
static bool peekBlocking() FORCE_INLINE
{
if (*getRegPtr<Registers::CTRL_STAT_REG_A_ADDR>() & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
return true;
}
return false;
}
static void enableDataRegEmptyInt() FORCE_INLINE
{
*getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>() |= (1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
}
static void disableDataRegEmptyInt() FORCE_INLINE
{
*getRegPtr<Registers::CTRL_STAT_REG_B_ADDR>() &= ~(1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
}
private:
struct DataBitsVal {
uint8_t regCVal = 0;
uint8_t regBVal = 0;
};
static constexpr auto calcBaud()
{
// The actual formula is (F_CPU / (16 * baudRate)) - 1, but this one has the advantage of rounding correctly
constexpr auto baudVal = (F_CPU + 8 * cfg::BAUD_RATE) / (16 * cfg::BAUD_RATE) - 1;
return baudVal;
}
static constexpr auto calcDataBits()
{
DataBitsVal dataBitsVal;
switch (cfg::DATA_BITS) {
case DataBits::FIVE:
dataBitsVal.regCVal = 0;
break;
case DataBits::SIX:
dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_0);
break;
case DataBits::SEVEN:
dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1);
break;
case DataBits::EIGHT:
dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1) | (1 << CtrlFlagsC::CHAR_SIZE_0);
break;
case DataBits::NINE:
dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1) | (1 << CtrlFlagsC::CHAR_SIZE_0);
dataBitsVal.regBVal = (1 << CtrlFlagsB::CHAR_SIZE_2);
break;
}
return dataBitsVal;
}
static constexpr auto calcParity()
{
uint8_t parityVal = 0;
if (cfg::PARITY == Parity::EVEN)
parityVal = (1 << CtrlFlagsC::PARITY_MODE_1);
else if (cfg::PARITY == Parity::ODD)
parityVal = (1 << CtrlFlagsC::PARITY_MODE_1) | (1 << CtrlFlagsC::PARITY_MODE_0);
return parityVal;
}
static constexpr auto calcStopBits()
{
uint8_t stopBitsVal = 0;
if (cfg::STOP_BITS == StopBits::TWO)
stopBitsVal = (1 << CtrlFlagsC::STOP_BIT_SEL);
return stopBitsVal;
}
static constexpr auto calcMode()
{
static_assert(mode != Mode::SPI, "SPI mode can not be used with uart");
uint8_t modeVal = 0;
if (mode == Mode::SYNCHRONOUS_MASTER || mode == Mode::SYNCHRONOUS_SLAVE) {
modeVal = (1 << CtrlFlagsC::MODE_SEL_0);
}
return modeVal;
}
template <bool enable>
static constexpr auto calcRxState()
{
uint8_t enableVal = 0;
if (enable)
enableVal = (1 << CtrlFlagsB::RX_ENABLE);
return enableVal;
}
template <bool enable>
static constexpr auto calcTxState()
{
uint8_t enableVal = 0;
if (enable)
enableVal = (1 << CtrlFlagsB::TX_ENABLE);
return enableVal;
}
static constexpr auto calcInterrupt()
{
uint8_t interruptVal = 0;
if (driven == Driven::INTERRUPT)
interruptVal = (1 << CtrlFlagsB::RX_INT_ENABLE);
return interruptVal;
}
};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode>
class BlockingHardware {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
static void init() FORCE_INLINE
{
HardwareImpl::init();
}
static void txByte(const data_t &byte) FORCE_INLINE
{
HardwareImpl::txByteBlocking(byte);
}
static bool rxByte(data_t &byte) FORCE_INLINE
{
return HardwareImpl::rxByteBlocking(byte);
}
static bool peek(data_t &) FORCE_INLINE
{
static_assert(util::always_false_v<data_t>, "Peek with data is not supported in blocking mode");
return false;
}
static bool peek() FORCE_INLINE
{
return HardwareImpl::peekBlocking();
}
static void flushTx() FORCE_INLINE
{
while (!HardwareImpl::txEmpty())
;
while (!HardwareImpl::txComplete())
;
HardwareImpl::clearTxComplete();
}
private:
using HardwareImpl = Hardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, Driven::BLOCKING, mode>;
};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode>
class InterruptHardware {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
static void txByte(const data_t &byte) FORCE_INLINE
{
uint8_t tmpHead = (sm_txBuf.head + 1) % TX_BUFFER_SIZE;
while (tmpHead == sm_txBuf.tail)
;
sm_txBuf.buf[tmpHead] = byte;
sm_txBuf.head = tmpHead;
HardwareImpl::enableDataRegEmptyInt();
}
static bool rxByte(data_t &byte) FORCE_INLINE
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
sm_rxBuf.tail = tmpTail;
return true;
}
static bool peek(data_t &byte) FORCE_INLINE
{
if (sm_rxBuf.head == sm_rxBuf.tail)
return false;
uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE;
byte = sm_rxBuf.buf[tmpTail];
return true;
}
static bool peek() FORCE_INLINE
{
return (sm_rxBuf.head != sm_rxBuf.tail);
}
static void flushTx() FORCE_INLINE
{
while (sm_txBuf.head != sm_txBuf.tail)
;
while (!HardwareImpl::txEmpty())
;
while (!HardwareImpl::txComplete())
;
HardwareImpl::clearTxComplete();
}
protected:
static void rxIntHandler() FORCE_INLINE
{
auto data = HardwareImpl::rxByteInterrupt();
uint8_t tmpHead = (sm_rxBuf.head + 1) % RX_BUFFER_SIZE;
if (tmpHead != sm_rxBuf.tail) {
sm_rxBuf.head = tmpHead;
sm_rxBuf.buf[tmpHead] = data;
} else {
// TODO: Handle overflow
}
}
static void dataRegEmptyIntHandler() FORCE_INLINE
{
if (sm_txBuf.head != sm_txBuf.tail) {
uint8_t tmpTail = (sm_txBuf.tail + 1) % TX_BUFFER_SIZE;
sm_txBuf.tail = tmpTail;
HardwareImpl::txByteInterrupt(sm_txBuf.buf[tmpTail]);
} else
HardwareImpl::disableDataRegEmptyInt();
}
private:
using HardwareImpl = Hardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, Driven::INTERRUPT, mode>;
static constexpr auto TX_BUFFER_SIZE = 16;
static constexpr auto RX_BUFFER_SIZE = 16;
static volatile RingBuffer<data_t, TX_BUFFER_SIZE> sm_txBuf;
static volatile RingBuffer<data_t, RX_BUFFER_SIZE> sm_rxBuf;
};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode>
volatile RingBuffer<typename InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::data_t,
InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::TX_BUFFER_SIZE>
InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::sm_txBuf = {0, 0, {0}};
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode>
volatile RingBuffer<typename InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::data_t,
InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::RX_BUFFER_SIZE>
InterruptHardware<Registers, CtrlFlagsA, CtrlFlagsB, CtrlFlagsC, cfg, mode>::sm_rxBuf = {0, 0, {0}};
} // namespace detail
} // namespace uart
#undef FORCE_INLINE

169
fantemp/uart/hardware0.hpp Normal file
View File

@ -0,0 +1,169 @@
#ifndef UART_HARDWARE_0_HPP
#define UART_HARDWARE_0_HPP
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include "config.hpp"
#include "hardware.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega328P__)
/*
The following works in avr-gcc 5.4.0, but is not legal C++, because ptr's are not legal constexpr's:
constexpr auto *foo = ptr;
Workaround is to store the the address of the ptr in a uintptr_t and reinterpret_cast it at call site.
The _SFR_ADDR macro in sfr_defs.h would give the address, but it does that by taking the address of the dereferenced
pointer and casts it to uint16_t, which is still not a legal constexpr.
The workaround therefore is to disable the pointer cast and dereference macro _MMIO_BYTE temporarily.
*/
#pragma push_macro("_MMIO_BYTE")
#undef _MMIO_BYTE
#define _MMIO_BYTE
struct Registers0 {
static constexpr uintptr_t IO_REG_ADDR = UDR0;
static constexpr uintptr_t CTRL_STAT_REG_A_ADDR = UCSR0A;
static constexpr uintptr_t CTRL_STAT_REG_B_ADDR = UCSR0B;
static constexpr uintptr_t CTRL_STAT_REG_C_ADDR = UCSR0C;
static constexpr uintptr_t BAUD_REG_L_ADDR = UBRR0L;
static constexpr uintptr_t BAUD_REG_H_ADDR = UBRR0H;
};
#pragma pop_macro("_MMIO_BYTE")
enum class ControlFlagsA0 {
MULTI_PROC_COMM_MODE = MPCM0,
SPEED_2X = U2X0,
PARITY_ERROR = UPE0,
DATA_OVER_RUN = DOR0,
FRAME_ERROR = FE0,
DATA_REG_EMPTY = UDRE0,
TRANSMIT_COMPLETE = TXC0,
RECEIVE_COMPLETE = RXC0,
};
enum class ControlFlagsB0 {
TX_DATA_BIT_8 = TXB80,
RX_DATA_BIT_8 = RXB80,
CHAR_SIZE_2 = UCSZ02,
TX_ENABLE = TXEN0,
RX_ENABLE = RXEN0,
DATA_REG_EMPTY_INT_ENABLE = UDRIE0,
TX_INT_ENABLE = TXCIE0,
RX_INT_ENABLE = RXCIE0,
};
enum class ControlFlagsC0 {
CLK_POLARITY = UCPOL0,
CHAR_SIZE_0 = UCSZ00,
CHAR_SIZE_1 = UCSZ01,
STOP_BIT_SEL = USBS0,
PARITY_MODE_0 = UPM00,
PARITY_MODE_1 = UPM01,
MODE_SEL_0 = UMSEL00,
MODE_SEL_1 = UMSEL01,
};
// clang-format off
constexpr int operator<<(const int &lhs, const ControlFlagsA0 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsB0 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsC0 &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
extern void (*fnRx0IntHandler)();
extern void (*fnDataReg0EmptyIntHandler)();
#else
#error "This chip is not supported"
#endif
} // namespace detail
template <class cfg = Config<>, Driven driven = Driven::INTERRUPT, Mode mode = Mode::ASYNCHRONOUS>
class Hardware0 : public detail::BlockingHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode> {
};
template <class cfg, Mode mode>
class Hardware0<cfg, Driven::INTERRUPT, mode>
: public detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode> {
using detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode>::rxIntHandler;
using detail::InterruptHardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, mode>::dataRegEmptyIntHandler;
using HardwareImpl = detail::Hardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
detail::ControlFlagsC0, cfg, Driven::INTERRUPT, mode>;
public:
static void init() FORCE_INLINE
{
detail::fnRx0IntHandler = rxIntHandler;
detail::fnDataReg0EmptyIntHandler = dataRegEmptyIntHandler;
HardwareImpl::init();
sei();
}
};
} // namespace uart
#undef FORCE_INLINE
#endif
//////////////////////////////////////////////////////////////////////////
#ifdef UART0_INT_VECTORS
#include <avr/interrupt.h>
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega328P__)
#if defined(__AVR_ATmega328P__)
#define USART0_RX_vect USART_RX_vect
#define USART0_UDRE_vect USART_UDRE_vect
#endif
void (*fnRx0IntHandler)() = nullptr;
void (*fnDataReg0EmptyIntHandler)() = nullptr;
ISR(USART0_RX_vect)
{
if (fnRx0IntHandler)
fnRx0IntHandler();
}
ISR(USART0_UDRE_vect)
{
if (fnDataReg0EmptyIntHandler)
fnDataReg0EmptyIntHandler();
}
#else
#error "This chip is not supported"
#endif
} // namespace detail
} // namespace uart
#undef UART0_INT_VECTORS
#endif

166
fantemp/uart/hardware1.hpp Normal file
View File

@ -0,0 +1,166 @@
#ifndef UART_HARDWARE_1_HPP
#define UART_HARDWARE_1_HPP
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include "config.hpp"
#include "hardware.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__)
/*
The following works in avr-gcc 5.4.0, but is not legal C++, because ptr's are not legal constexpr's:
constexpr auto *foo = ptr;
Workaround is to store the the address of the ptr in a uintptr_t and reinterpret_cast it at call site.
The _SFR_ADDR macro in sfr_defs.h would give the address, but it does that by taking the address of the dereferenced
pointer and casts it to uint16_t, which is still not a legal constexpr.
The workaround therefore is to disable the pointer cast and dereference macro _MMIO_BYTE temporarily.
*/
#pragma push_macro("_MMIO_BYTE")
#undef _MMIO_BYTE
#define _MMIO_BYTE
struct Registers1 {
static constexpr uintptr_t IO_REG_ADDR = UDR1;
static constexpr uintptr_t CTRL_STAT_REG_A_ADDR = UCSR1A;
static constexpr uintptr_t CTRL_STAT_REG_B_ADDR = UCSR1B;
static constexpr uintptr_t CTRL_STAT_REG_C_ADDR = UCSR1C;
static constexpr uintptr_t BAUD_REG_L_ADDR = UBRR1L;
static constexpr uintptr_t BAUD_REG_H_ADDR = UBRR1H;
};
#pragma pop_macro("_MMIO_BYTE")
enum class ControlFlagsA1 {
MULTI_PROC_COMM_MODE = MPCM1,
SPEED_2X = U2X1,
PARITY_ERROR = UPE1,
DATA_OVER_RUN = DOR1,
FRAME_ERROR = FE1,
DATA_REG_EMPTY = UDRE1,
TRANSMIT_COMPLETE = TXC1,
RECEIVE_COMPLETE = RXC1,
};
enum class ControlFlagsB1 {
TX_DATA_BIT_8 = TXB81,
RX_DATA_BIT_8 = RXB81,
CHAR_SIZE_2 = UCSZ12,
TX_ENABLE = TXEN1,
RX_ENABLE = RXEN1,
DATA_REG_EMPTY_INT_ENABLE = UDRIE1,
TX_INT_ENABLE = TXCIE1,
RX_INT_ENABLE = RXCIE1,
};
enum class ControlFlagsC1 {
CLK_POLARITY = UCPOL1,
CHAR_SIZE_0 = UCSZ10,
CHAR_SIZE_1 = UCSZ11,
STOP_BIT_SEL = USBS1,
PARITY_MODE_0 = UPM10,
PARITY_MODE_1 = UPM11,
MODE_SEL_0 = UMSEL10,
MODE_SEL_1 = UMSEL11,
};
// clang-format off
constexpr int operator<<(const int &lhs, const ControlFlagsA1 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsB1 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsC1 &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
extern void (*fnRx1IntHandler)();
extern void (*fnDataReg1EmptyIntHandler)();
#define HAS_UART1
#endif
} // namespace detail
#ifdef HAS_UART1
template <class cfg = Config<>, Driven driven = Driven::INTERRUPT, Mode mode = Mode::ASYNCHRONOUS>
class Hardware1 : public detail::BlockingHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode> {
};
template <class cfg, Mode mode>
class Hardware1<cfg, Driven::INTERRUPT, mode>
: public detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode> {
using detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode>::rxIntHandler;
using detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode>::dataRegEmptyIntHandler;
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, Driven::INTERRUPT, mode>;
public:
static void init() FORCE_INLINE
{
detail::fnRx1IntHandler = rxIntHandler;
detail::fnDataReg1EmptyIntHandler = dataRegEmptyIntHandler;
HardwareImpl::init();
sei();
}
};
#endif
} // namespace uart
#undef FORCE_INLINE
#endif
//////////////////////////////////////////////////////////////////////////
#ifdef UART1_INT_VECTORS
#include <avr/interrupt.h>
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__)
void (*fnRx1IntHandler)() = nullptr;
void (*fnDataReg1EmptyIntHandler)() = nullptr;
ISR(USART1_RX_vect)
{
if (fnRx1IntHandler)
fnRx1IntHandler();
}
ISR(USART1_UDRE_vect)
{
if (fnDataReg1EmptyIntHandler)
fnDataReg1EmptyIntHandler();
}
#endif
} // namespace detail
} // namespace uart
#undef UART1_INT_VECTORS
#endif

21
fantemp/uart/software.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "config.hpp"
#include "utils.hpp"
#include "../io/io.hpp"
namespace uart {
template <io::P rxPin, io::P txPin, class cfg = Config<>>
class Software {
static_assert(util::always_false_v<cfg>, "Not implemented");
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
static void init() {}
};
} // namespace uart

373
fantemp/uart/uart.hpp Normal file
View File

@ -0,0 +1,373 @@
#pragma once
#include <stdint.h>
#include "config.hpp"
#include "software.hpp"
#include "utils.hpp"
#undef UART0_INT_VECTORS
#include "hardware0.hpp"
#undef UART1_INT_VECTORS
#include "hardware1.hpp"
#include "../flash/flash.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
namespace detail {
template <typename T, T limit, size_t Base>
static constexpr size_t cntDigits()
{
T num = limit;
size_t cnt = 0;
do {
num /= Base;
++cnt;
} while (num > 0);
return cnt;
}
template <typename T, size_t Base>
static constexpr size_t maxNumDigits()
{
constexpr T minVal = util::NumericLimits<T>::min();
constexpr T maxVal = util::NumericLimits<T>::max();
T minDigits = cntDigits<T, minVal, Base>();
T maxDigits = cntDigits<T, maxVal, Base>();
return (minDigits < maxDigits) ? maxDigits : minDigits;
}
} // namespace detail
template <class Driver>
class Uart {
public:
using data_t = typename Driver::data_t;
// Constructing a uart object does not initialize the driver to allow different specializations with the same
// back-end to exists at the same time
// Note that init must be called every time when switching specializations with the same back-end
Uart() = default;
// Moving and copying uart objects is not supported
Uart(const Uart &) = delete;
Uart(Uart &&) = delete;
Uart &operator=(const Uart &) = delete;
Uart &operator=(Uart &&) = delete;
// Before using the uart init must be called
static void init()
{
Driver::init();
}
static void txByte(const data_t &byte)
{
Driver::txByte(byte);
}
static bool rxByte(data_t &byte)
{
return Driver::rxByte(byte);
}
static bool peek(data_t &byte)
{
return Driver::peek(byte);
}
static bool peek()
{
return Driver::peek();
}
static void flushTx()
{
Driver::flushTx();
}
static void txString(const char *str)
{
static_assert(Driver::DATA_BITS == DataBits::EIGHT, "Strings are only supported with 8 data bits");
while (char ch = *str++)
txByte(ch);
}
static void txString(const ::detail::FlashString *str)
{
static_assert(Driver::DATA_BITS == DataBits::EIGHT, "Strings are only supported with 8 data bits");
const char *strIt = reinterpret_cast<const char *>(str);
while (char ch = pgm_read_byte(strIt++))
txByte(ch);
}
template <typename T, size_t Base = 10, size_t Padding = 0, char PadChar = '0', bool LowerCase = true>
static void txNumber(const T &val)
{
static_assert(util::is_integral_v<T>, "Only supported on integral types");
static_assert(Base >= 2, "Numbers with base less than 2 make no sense");
static_assert(Base <= 16, "Numbers with base higher than 16 are not supported");
static_assert(Padding <= detail::maxNumDigits<T, Base>(), "Cannot pad more than maximum length of number");
constexpr char alphaChar = (LowerCase) ? 'a' : 'A';
constexpr size_t numDigits = detail::maxNumDigits<T, Base>();
data_t buffer[numDigits];
data_t *bufEnd = buffer + numDigits - 1;
T digits = val;
if (digits < 0) {
digits = -digits;
txByte('-');
}
do {
data_t lastDigit = digits % Base;
*bufEnd-- = (lastDigit < 10) ? ('0' + lastDigit) : (alphaChar + lastDigit - 10);
digits /= Base;
} while (digits > 0);
if (Padding > 0) {
size_t strLen = buffer + numDigits - (bufEnd + 1);
if (Padding > strLen) {
for (size_t i = Padding; i > strLen && bufEnd >= buffer; --i) {
*bufEnd-- = PadChar;
}
}
}
for (data_t *buf = bufEnd + 1; buf < buffer + numDigits; ++buf)
txByte(*buf);
}
//////////////////////////////////////////////////////////////////////////
// Output stream overloads
Uart &operator<<(const char *str)
{
txString(str);
return *this;
}
Uart &operator<<(const ::detail::FlashString *str)
{
txString(str);
return *this;
}
Uart &operator<<(const char &val)
{
txByte(val);
return *this;
}
Uart &operator<<(const signed char &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const unsigned char &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const short &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(unsigned short &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const int &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const unsigned int &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(const long &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(unsigned long &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(long long &val)
{
txNumber(val);
return *this;
}
Uart &operator<<(unsigned long long &val)
{
txNumber(val);
return *this;
}
template <typename... Ts>
Uart &operator<<(float) const
{
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator<<(double) const
{
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator<<(long double) const
{
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
Uart &operator<<(const bool &val)
{
txString(val ? F("true") : F("false"));
return *this;
}
Uart &operator<<(const void *val)
{
txString(F("0x"));
txNumber<uint16_t, 16, 4, '0', false>(reinterpret_cast<uint16_t>(val));
return *this;
}
//////////////////////////////////////////////////////////////////////////
// Input stream overloads
template <typename... Ts>
Uart &operator>>(char &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned char &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(short &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned short &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(int &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned int &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(long &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned long &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(long long &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(unsigned long long &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(float &) const
{
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator>>(double &) const
{
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator>>(long double &) const
{
static_assert(util::always_false_v<Ts...>, "Not supported by hardware");
}
template <typename... Ts>
Uart &operator>>(bool &) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
template <typename... Ts>
Uart &operator>>(const void *&) const
{
static_assert(util::always_false_v<Ts...>, "Not implemented");
}
};
template <typename cfg = Config<>>
using Uart0 = Uart<Hardware0<cfg, Driven::INTERRUPT, Mode::ASYNCHRONOUS>>;
#ifdef HAS_UART1
template <typename cfg = Config<>>
using Uart1 = Uart<Hardware1<cfg, Driven::INTERRUPT, Mode::ASYNCHRONOUS>>;
#endif
} // namespace uart
#undef FORCE_INLINE
#undef HAS_UART1

143
fantemp/uart/utils.hpp Normal file
View File

@ -0,0 +1,143 @@
#pragma once
// Fix for limits.h not exposing LLONG_MIN, LLONG_MIN, and ULLONG_MAX to C++ context
#ifdef __cplusplus
#define __STDC_VERSION__ 201112L
#endif
#include <float.h>
#include <limits.h>
namespace uart {
namespace util {
// clang-format off
template <bool Val> struct set_bool { static constexpr auto value = Val; };
struct true_type : set_bool<true> {};
struct false_type : set_bool<false> {};
template <typename...> struct always_false : false_type {};
template <typename... Ts> static constexpr auto always_false_v = always_false<Ts...>::value;
template <typename T> struct is_integral : false_type {};
template <> struct is_integral<bool> : true_type {};
template <> struct is_integral<char> : true_type {};
template <> struct is_integral<signed char> : true_type {};
template <> struct is_integral<unsigned char> : true_type {};
template <> struct is_integral<short> : true_type {};
template <> struct is_integral<int> : true_type {};
template <> struct is_integral<long int> : true_type {};
template <> struct is_integral<long long int> : true_type {};
template <> struct is_integral<unsigned short> : true_type {};
template <> struct is_integral<unsigned int> : true_type {};
template <> struct is_integral<unsigned long int> : true_type {};
template <> struct is_integral<unsigned long long int> : true_type {};
template <typename T> static constexpr auto is_integral_v = is_integral<T>::value;
template <typename T, typename U> struct is_same : false_type {};
template <typename T> struct is_same<T, T> : true_type {};
template <typename T, typename U> static constexpr auto is_same_v = is_same<T, U>::value;
template <typename T>
struct NumericLimits {
static constexpr T min() { return T(); }
static constexpr T max() { return T(); }
};
template <>
struct NumericLimits<bool> {
static constexpr bool min() { return false; }
static constexpr bool max() { return true; }
};
template <>
struct NumericLimits<char> {
static constexpr char min() { return CHAR_MIN; }
static constexpr char max() { return CHAR_MAX; }
};
template <>
struct NumericLimits<signed char> {
static constexpr signed char min() { return SCHAR_MIN; }
static constexpr signed char max() { return SCHAR_MAX; }
};
template <>
struct NumericLimits<unsigned char> {
static constexpr unsigned char min() { return 0; }
static constexpr unsigned char max() { return UCHAR_MAX; }
};
template <>
struct NumericLimits<short> {
static constexpr short min() { return SHRT_MIN; }
static constexpr short max() { return SHRT_MAX; }
};
template <>
struct NumericLimits<int> {
static constexpr int min() { return INT_MIN; }
static constexpr int max() { return INT_MAX; }
};
template <>
struct NumericLimits<long> {
static constexpr long int min() { return LONG_MIN; }
static constexpr long int max() { return LONG_MAX; }
};
template <>
struct NumericLimits<long long int> {
static constexpr long long int min() { return LLONG_MIN; }
static constexpr long long int max() { return LLONG_MAX; }
};
template <>
struct NumericLimits<unsigned short> {
static constexpr unsigned short min() { return 0; }
static constexpr unsigned short max() { return USHRT_MAX; }
};
template <>
struct NumericLimits<unsigned int> {
static constexpr unsigned int min() { return 0; }
static constexpr unsigned int max() { return UINT_MAX; }
};
template <>
struct NumericLimits<unsigned long int> {
static constexpr unsigned long int min() { return 0; }
static constexpr unsigned long int max() { return ULONG_MAX; }
};
template <>
struct NumericLimits<unsigned long long int> {
static constexpr unsigned long long int min() { return 0; }
static constexpr unsigned long long int max() { return ULLONG_MAX; }
};
template <>
struct NumericLimits<float> {
template <typename... Ts> static constexpr float min() { return FLT_MIN; }
template <typename... Ts> static constexpr float max() { return FLT_MAX; }
};
template <>
struct NumericLimits<double> {
template <typename... Ts> static constexpr double min() { return DBL_MIN; }
template <typename... Ts> static constexpr double max() { return DBL_MAX; }
};
template <>
struct NumericLimits<long double> {
template <typename... Ts> static constexpr long double min() { return LDBL_MIN; }
template <typename... Ts> static constexpr long double max() { return LDBL_MAX; }
};
// clang-format on
} // namespace util
} // namespace uart

@ -1 +0,0 @@
Subproject commit 5d38b97254e271e3e0fb48739a927be6eae26a7f

@ -1 +0,0 @@
Subproject commit 9245b49e4c981098ca966343f396f5064f762fb5

@ -1 +0,0 @@
Subproject commit 80de36ee7ee3e6b0842d5eaee81d54062cb496b2

@ -1 +0,0 @@
Subproject commit cb436b11a8428c2c40a11b51fedb4d2c7d38ece7