Compare commits

...

37 Commits

Author SHA1 Message Date
5407e94337 Make use of C++ standard library 2022-05-29 16:14:22 +02:00
bf94ebaac1 Fix volatile compound assignments being deprecated in C++20 2022-05-29 14:45:05 +02:00
d89322bdaa Implement empty/unused pin and port 2022-05-28 15:41:26 +02:00
37b8f77fd3 Replace FORCE_INLINE macro with C++17 attribute syntax 2022-05-28 14:51:40 +02:00
80de36ee7e Improved grammar 2020-02-01 22:28:44 +01:00
a30b78fb81 Added static assert for number of pins on virtual port 2020-02-01 22:13:27 +01:00
cde4f9beda Implemented virtual port convenience object 2020-02-01 22:07:52 +01:00
986ceef65d Added MIT license file 2020-02-01 15:25:30 +01:00
571f28ab05 Undid accidental regression 2019-08-15 18:49:09 +02:00
d991959bff Fixed workaround with correct macro 2019-08-15 17:37:13 +02:00
1265702325 Changed git ignore to ignore make output 2019-08-11 10:03:47 +02:00
86d1db21f7 Fixed nullptr not being a valid uintptr_t 2019-08-10 13:58:42 +02:00
f1634a4dd7 Improved formatting 2019-08-10 13:39:04 +02:00
661bbfea7e Added reg_ptr_t type for cleaner code 2019-08-10 13:36:50 +02:00
a47e9a1a66 Refactored pegister pointer code 2019-08-10 13:32:35 +02:00
1bc8a38988 Fixed non-compliant code storing a ptr as a constexpr 2019-08-10 13:16:02 +02:00
4f40aa70dc Renamed getPin function to clearly differentiate it from getPIN 2019-08-10 12:55:53 +02:00
25bd873f94 Fixed non-portable preprocessor defines 2019-08-10 12:54:23 +02:00
387bc5f110 Removed unnecessary const qualifiers in template 2019-07-28 14:00:12 +02:00
d64793770c Added hardware invert for ports as well 2019-07-26 19:48:49 +02:00
c1820f62f1 Added support for hardware toggle 2019-07-26 19:44:20 +02:00
468368692e Finished library implementation 2019-07-26 18:49:53 +02:00
7396831828 Changed header to C++ header 2019-07-26 14:01:14 +02:00
4585ef89f6 Renamed library 2019-07-26 13:58:22 +02:00
82a6b179df Added clang-format 2019-07-26 13:46:30 +02:00
cf3e0664b5 Implemented toggle function for pins 2019-01-02 20:54:29 +01:00
42bc1147b8 Shortened function names and removed useless constructor 2018-04-26 15:58:15 +02:00
a69465fca0 Rewrote interface to make sure port and pin lookup are done at compile time 2018-04-26 15:23:42 +02:00
093c0a6a02 Rewrote inout library to allow compile time port lookup and inline optimization 2018-04-24 18:59:12 +02:00
b4fca1e7ce Added ATmega8A 2017-04-17 00:40:43 +02:00
c73fbeea85 Added destructor that tri-states pins and ports 2016-06-19 12:46:54 +02:00
098fc43e98 Added git ignore and attribute files 2016-06-19 12:39:34 +02:00
7363565130 Added Attiny 85 support 2016-06-12 22:53:35 +02:00
c8bef36032 Removed trailing new lines 2016-05-24 20:11:24 +02:00
89c0e2ca40 Renamed to lowercase 2016-05-24 19:41:59 +02:00
2cf8cbb8d6 Removed attributes file from submodule 2016-02-25 22:45:38 +01:00
7c12961633 Created submodule branch 2016-02-25 22:28:28 +01:00
11 changed files with 622 additions and 791 deletions

13
.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
...

1
.gitattributes vendored
View File

@ -2,6 +2,7 @@
*.hpp eol=lf
*.c eol=lf
*.cpp eol=lf
.git* eol=lf
*.vcxproj* eol=crlf
*.cppproj eol=crlf
*.sln eol=crlf

11
.gitignore vendored Normal file
View File

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

View File

@ -1,22 +0,0 @@

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}") = "InOut", "InOut\InOut.cppproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AVR = Debug|AVR
Release|AVR = Release|AVR
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.ActiveCfg = Debug|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Debug|AVR.Build.0 = Debug|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.ActiveCfg = Release|AVR
{DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.Build.0 = Release|AVR
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -1,14 +0,0 @@
/*
* Copyright (c) by BlackMark 2015
* Date 24/11/2015
* Version 1.1
*/
#ifndef CLOCK_H
#define CLOCK_H
#define F_CPU 20000000
#include <util/delay.h>
#endif

View File

@ -1,221 +0,0 @@
#include "InOut.h"
//////////////////////////////////////////////////////////////////////////
volatile uint8_t* InOut::getPort( Pin enmPin, Type enmType )
{
volatile uint8_t *vpui8Port = nullptr;
if( enmPin == Pin::P_NONE )
{
return vpui8Port;
}
uint8_t ui8Port = ( static_cast<uint16_t>( enmPin ) >> 4 ) & 0x0F;
switch( ui8Port )
{
case 0:
{
if( enmType == Type::T_PIN )
{
vpui8Port = PORT_PINA;
}
else if( enmType == Type::T_DDR )
{
vpui8Port = PORT_DDRA;
}
else if( enmType == Type::T_PORT )
{
vpui8Port = PORT_PORTA;
}
break;
}
case 1:
{
if( enmType == Type::T_PIN )
{
vpui8Port = PORT_PINB;
}
else if( enmType == Type::T_DDR )
{
vpui8Port = PORT_DDRB;
}
else if( enmType == Type::T_PORT )
{
vpui8Port = PORT_PORTB;
}
break;
}
case 2:
{
if( enmType == Type::T_PIN )
{
vpui8Port = PORT_PINC;
}
else if( enmType == Type::T_DDR )
{
vpui8Port = PORT_DDRC;
}
else if( enmType == Type::T_PORT )
{
vpui8Port = PORT_PORTC;
}
break;
}
case 3:
{
if( enmType == Type::T_PIN )
{
vpui8Port = PORT_PIND;
}
else if( enmType == Type::T_DDR )
{
vpui8Port = PORT_DDRD;
}
else if( enmType == Type::T_PORT )
{
vpui8Port = PORT_PORTD;
}
break;
}
}
return vpui8Port;
}
//////////////////////////////////////////////////////////////////////////
uint8_t InOut::getPin( Pin enmPin )
{
return static_cast<uint16_t>( enmPin ) & 0x0F;
}
//////////////////////////////////////////////////////////////////////////
void InOut::setPinDirection( Pin enmPin, Dir enmDir, bool bPullup )
{
if( enmPin == Pin::P_NONE )
{
return;
}
volatile uint8_t *vpui8PortDir = getPort( enmPin, Type::T_DDR );
volatile uint8_t *vpui8PortOut = getPort( enmPin, Type::T_PORT );
uint8_t ui8Pin = getPin( enmPin );
setPinDirection( vpui8PortDir, ui8Pin, enmDir );
if( enmDir == Dir::D_IN )
{
writePin( vpui8PortOut, ui8Pin, bPullup );
}
}
//////////////////////////////////////////////////////////////////////////
bool InOut::readPin( Pin enmPin )
{
if( enmPin == Pin::P_NONE )
{
return false;
}
volatile uint8_t *vpui8Port = getPort( enmPin, Type::T_PIN );
uint8_t ui8Pin = getPin( enmPin );
return readPin( vpui8Port, ui8Pin );
}
//////////////////////////////////////////////////////////////////////////
void InOut::writePin( Pin enmPin, bool bValue )
{
if( enmPin == Pin::P_NONE )
{
return;
}
volatile uint8_t *vpui8Port = getPort( enmPin, Type::T_PORT );
uint8_t ui8Pin = getPin( enmPin );
writePin( vpui8Port, ui8Pin, bValue );
}
//////////////////////////////////////////////////////////////////////////
uint8_t InOut::readPort( Pin enmPortPin )
{
if( enmPortPin == Pin::P_NONE )
{
return 0;
}
volatile uint8_t *vpui8Port = getPort( enmPortPin, Type::T_PIN );
return readPort( vpui8Port );
}
//////////////////////////////////////////////////////////////////////////
void InOut::writePort( Pin enmPortPin, uint8_t ui8Value )
{
if( enmPortPin == Pin::P_NONE )
{
return;
}
volatile uint8_t *vpui8Port = getPort( enmPortPin, Type::T_PORT );
writePort( vpui8Port, ui8Value );
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
InOutPin::InOutPin()
{
setPin( InOut::Pin::P_NONE );
}
//////////////////////////////////////////////////////////////////////////
InOutPin::InOutPin( InOut::Pin enmPin )
{
setPin( enmPin );
}
//////////////////////////////////////////////////////////////////////////
InOutPin::~InOutPin()
{}
//////////////////////////////////////////////////////////////////////////
void InOutPin::setPin( InOut::Pin enmPin )
{
m_vpui8Input = InOut::getPort( enmPin, InOut::Type::T_PIN );
m_vpui8Dir = InOut::getPort( enmPin, InOut::Type::T_DDR );
m_vpui8Output = InOut::getPort( enmPin, InOut::Type::T_PORT );
m_ui8Pin = InOut::getPin( enmPin );
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
InOutPort::InOutPort()
{
setPort( InOut::Pin::P_NONE );
}
//////////////////////////////////////////////////////////////////////////
InOutPort::InOutPort( InOut::Pin enmPin )
{
setPort( enmPin );
}
//////////////////////////////////////////////////////////////////////////
InOutPort::~InOutPort()
{}
//////////////////////////////////////////////////////////////////////////
void InOutPort::setPort( InOut::Pin enmPortPin )
{
m_vpui8Input = InOut::getPort( enmPortPin, InOut::Type::T_PIN );
m_vpui8Dir = InOut::getPort( enmPortPin, InOut::Type::T_DDR );
m_vpui8Output = InOut::getPort( enmPortPin, InOut::Type::T_PORT );
}

View File

@ -1,184 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>7.0</ProjectVersion>
<ToolchainName>com.Atmel.AVRGCC8.CPP</ToolchainName>
<ProjectGuid>dce6c7e3-ee26-4d79-826b-08594b9ad897</ProjectGuid>
<avrdevice>ATmega328P</avrdevice>
<avrdeviceseries>none</avrdeviceseries>
<OutputType>Executable</OutputType>
<Language>CPP</Language>
<OutputFileName>$(MSBuildProjectName)</OutputFileName>
<OutputFileExtension>.elf</OutputFileExtension>
<OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory>
<AssemblyName>InOut</AssemblyName>
<Name>InOut</Name>
<RootNamespace>InOut</RootNamespace>
<ToolchainFlavour>Native</ToolchainFlavour>
<KeepTimersRunning>true</KeepTimersRunning>
<OverrideVtor>false</OverrideVtor>
<CacheFlash>true</CacheFlash>
<ProgFlashFromRam>true</ProgFlashFromRam>
<RamSnippetAddress>0x20000000</RamSnippetAddress>
<UncachedRange />
<preserveEEPROM>true</preserveEEPROM>
<OverrideVtorValue>exception_table</OverrideVtorValue>
<BootSegment>2</BootSegment>
<eraseonlaunchrule>0</eraseonlaunchrule>
<AsfFrameworkConfig>
<framework-data xmlns="">
<options />
<configurations />
<files />
<documentation help="" />
<offline-documentation help="" />
<dependencies>
<content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.29.0" />
</dependencies>
</framework-data>
</AsfFrameworkConfig>
<avrtool>
</avrtool>
<avrtoolserialnumber />
<avrdeviceexpectedsignature>0x1E9705</avrdeviceexpectedsignature>
<com_atmel_avrdbg_tool_stk500>
<ToolOptions>
<InterfaceProperties>
<IspClock>125000</IspClock>
</InterfaceProperties>
<InterfaceName>ISP</InterfaceName>
</ToolOptions>
<ToolType>com.atmel.avrdbg.tool.stk500</ToolType>
<ToolNumber>
</ToolNumber>
<ToolName>STK500</ToolName>
</com_atmel_avrdbg_tool_stk500>
<avrtoolinterface>ISP</avrtoolinterface>
<avrtoolinterfaceclock>125000</avrtoolinterfaceclock>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<ToolchainSettings>
<AvrGccCpp>
<avrgcc.common.Device>-mmcu=atmega328p -B "%24(PackRepoDir)\Atmel\ATmega_DFP\1.0.91\gcc\dev\atmega328p"</avrgcc.common.Device>
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>NDEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.0.91\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcccpp.compiler.symbols.DefSymbols>
<ListValues>
<Value>NDEBUG</Value>
</ListValues>
</avrgcccpp.compiler.symbols.DefSymbols>
<avrgcccpp.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.0.91\include</Value>
</ListValues>
</avrgcccpp.compiler.directories.IncludePaths>
<avrgcccpp.compiler.optimization.level>Optimize for size (-Os)</avrgcccpp.compiler.optimization.level>
<avrgcccpp.compiler.optimization.PackStructureMembers>True</avrgcccpp.compiler.optimization.PackStructureMembers>
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.warnings.Pedantic>True</avrgcccpp.compiler.warnings.Pedantic>
<avrgcccpp.compiler.miscellaneous.OtherFlags>-Wextra -std=c++11</avrgcccpp.compiler.miscellaneous.OtherFlags>
<avrgcccpp.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcccpp.linker.libraries.Libraries>
</AvrGccCpp>
</ToolchainSettings>
<PreBuildEvent>echo "C:\avrdude-6.2\avrdude.exe" -v -p$(avrdevice) %%* -Uflash:w:"$(OutputDirectory)\$(Name).hex":i &gt; "$(MSBuildProjectDirectory)\avrdude.bat"</PreBuildEvent>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<ToolchainSettings>
<AvrGccCpp>
<avrgcc.common.Device>-mmcu=atmega328p -B "%24(PackRepoDir)\Atmel\ATmega_DFP\1.0.91\gcc\dev\atmega328p"</avrgcc.common.Device>
<avrgcc.common.optimization.RelaxBranches>True</avrgcc.common.optimization.RelaxBranches>
<avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex>
<avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss>
<avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep>
<avrgcc.common.outputfiles.srec>True</avrgcc.common.outputfiles.srec>
<avrgcc.common.outputfiles.usersignatures>False</avrgcc.common.outputfiles.usersignatures>
<avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcc.compiler.symbols.DefSymbols>
<ListValues>
<Value>DEBUG</Value>
</ListValues>
</avrgcc.compiler.symbols.DefSymbols>
<avrgcc.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.0.91\include</Value>
</ListValues>
</avrgcc.compiler.directories.IncludePaths>
<avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level>
<avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers>
<avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel>
<avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultCharTypeUnsigned>
<avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcccpp.compiler.general.ChangeDefaultBitFieldUnsigned>
<avrgcccpp.compiler.symbols.DefSymbols>
<ListValues>
<Value>DEBUG</Value>
</ListValues>
</avrgcccpp.compiler.symbols.DefSymbols>
<avrgcccpp.compiler.directories.IncludePaths>
<ListValues>
<Value>%24(PackRepoDir)\Atmel\ATmega_DFP\1.0.91\include</Value>
</ListValues>
</avrgcccpp.compiler.directories.IncludePaths>
<avrgcccpp.compiler.optimization.level>Optimize (-O1)</avrgcccpp.compiler.optimization.level>
<avrgcccpp.compiler.optimization.PackStructureMembers>True</avrgcccpp.compiler.optimization.PackStructureMembers>
<avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcccpp.compiler.optimization.AllocateBytesNeededForEnum>
<avrgcccpp.compiler.optimization.DebugLevel>Default (-g2)</avrgcccpp.compiler.optimization.DebugLevel>
<avrgcccpp.compiler.warnings.AllWarnings>True</avrgcccpp.compiler.warnings.AllWarnings>
<avrgcccpp.compiler.warnings.Pedantic>True</avrgcccpp.compiler.warnings.Pedantic>
<avrgcccpp.compiler.miscellaneous.OtherFlags>-Wextra -std=c++11</avrgcccpp.compiler.miscellaneous.OtherFlags>
<avrgcccpp.linker.libraries.Libraries>
<ListValues>
<Value>libm</Value>
</ListValues>
</avrgcccpp.linker.libraries.Libraries>
<avrgcccpp.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcccpp.assembler.debugging.DebugLevel>
</AvrGccCpp>
</ToolchainSettings>
<PreBuildEvent>echo "C:\avrdude-6.2\avrdude.exe" -v -p$(avrdevice) %%* -Uflash:w:"$(OutputDirectory)\$(Name).hex":i &gt; "$(MSBuildProjectDirectory)\avrdude.bat"</PreBuildEvent>
</PropertyGroup>
<ItemGroup>
<Compile Include="Clock.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="InOut.cpp">
<SubType>compile</SubType>
</Compile>
<Compile Include="InOut.h">
<SubType>compile</SubType>
</Compile>
<Compile Include="main.cpp">
<SubType>compile</SubType>
</Compile>
</ItemGroup>
<Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" />
</Project>

View File

@ -1,298 +0,0 @@
/*
* Copyright (c) by BlackMark 2015-2016
* Date 25/02/2016
* Version 2.4
*/
#ifndef INOUT_H
#define INOUT_H
#include <stdint.h>
#include <avr/io.h>
#define AVR_DIP40 defined (__AVR_ATmega32A__) || defined (__AVR_ATmega644P__) || defined (__AVR_ATmega1284P__)
#define AVR_DIP28 defined (__AVR_ATmega8__) || defined (__AVR_ATmega168A__) || defined (__AVR_ATmega328P__)
#define AVR_DIP8 defined (__AVR_ATtiny13A__)
#if AVR_DIP40
#define PORT_PINA &PINA
#define PORT_DDRA &DDRA
#define PORT_PORTA &PORTA
#else
#define PORT_PINA nullptr
#define PORT_DDRA nullptr
#define PORT_PORTA nullptr
#endif
#if AVR_DIP40 || AVR_DIP28 || AVR_DIP8
#define PORT_PINB &PINB
#define PORT_DDRB &DDRB
#define PORT_PORTB &PORTB
#else
#define PORT_PINB nullptr
#define PORT_DDRB nullptr
#define PORT_PORTB nullptr
#endif
#if AVR_DIP40 || AVR_DIP28
#define PORT_PINC &PINC
#define PORT_DDRC &DDRC
#define PORT_PORTC &PORTC
#else
#define PORT_PINC nullptr
#define PORT_DDRC nullptr
#define PORT_PORTC nullptr
#endif
#if AVR_DIP40 || AVR_DIP28
#define PORT_PIND &PIND
#define PORT_DDRD &DDRD
#define PORT_PORTD &PORTD
#else
#define PORT_PIND nullptr
#define PORT_DDRD nullptr
#define PORT_PORTD nullptr
#endif
class InOut
{
public:
enum class Pin
{
P_NONE = -1,
#if AVR_DIP40
P_A0 = 0x00,
P_A1 = 0x01,
P_A2 = 0x02,
P_A3 = 0x03,
P_A4 = 0x04,
P_A5 = 0x05,
P_A6 = 0x06,
P_A7 = 0x07,
#endif
#if AVR_DIP40 || AVR_DIP28 || AVR_DIP8
P_B0 = 0x10,
P_B1 = 0x11,
P_B2 = 0x12,
P_B3 = 0x13,
P_B4 = 0x14,
P_B5 = 0x15,
#endif
#if AVR_DIP40 || AVR_DIP28
P_B6 = 0x16,
P_B7 = 0x17,
P_C0 = 0x20,
P_C1 = 0x21,
P_C2 = 0x22,
P_C3 = 0x23,
P_C4 = 0x24,
P_C5 = 0x25,
P_C6 = 0x26,
#endif
#if AVR_DIP40
P_C7 = 0x27,
#endif
#if AVR_DIP40 || AVR_DIP28
P_D0 = 0x30,
P_D1 = 0x31,
P_D2 = 0x32,
P_D3 = 0x33,
P_D4 = 0x34,
P_D5 = 0x35,
P_D6 = 0x36,
P_D7 = 0x37,
#endif
};
enum class Dir
{
D_IN = 0,
D_OUT = 1
};
enum class Type
{
T_PIN = 0,
T_DDR = 1,
T_PORT = 2
};
static volatile uint8_t* getPort( Pin enmPin, Type enmType );
static uint8_t getPin( Pin enmPin );
static void setPinDirection( Pin enmPin, Dir enmDir, bool bPullup );
static bool readPin( Pin enmPin );
static void writePin( Pin enmPin, bool bValue );
static uint8_t readPort( Pin enmPortPin );
static void writePort( Pin enmPortPin, uint8_t ui8Value );
//////////////////////////////////////////////////////////////////////////
static inline void setPinDirection( volatile uint8_t *vpui8Port, uint8_t ui8Pin, Dir enmDir )
{
if( enmDir == Dir::D_OUT )
{
*vpui8Port |= ( 1 << ui8Pin );
}
else
{
*vpui8Port &= ~( 1 << ui8Pin );
}
}
//////////////////////////////////////////////////////////////////////////
static inline bool readPin( volatile uint8_t *vpui8Port, uint8_t ui8Pin )
{
if( ( ( *vpui8Port ) >> ui8Pin ) & 1 )
{
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
static inline void writePin( volatile uint8_t *vpui8Port, uint8_t ui8Pin, bool bValue )
{
if( bValue )
{
*vpui8Port |= ( 1 << ui8Pin );
}
else
{
*vpui8Port &= ~( 1 << ui8Pin );
}
}
//////////////////////////////////////////////////////////////////////////
static inline void setPortDirection( volatile uint8_t *vpui8Port, Dir enmDir )
{
*vpui8Port = ( ( enmDir == InOut::Dir::D_OUT ) ? ( 0xFF ) : ( 0x00 ) );
}
//////////////////////////////////////////////////////////////////////////
static inline uint8_t readPort( volatile uint8_t *vpui8Port )
{
return *vpui8Port;
}
//////////////////////////////////////////////////////////////////////////
static inline void writePort( volatile uint8_t *vpui8Port, uint8_t ui8Value )
{
*vpui8Port = ui8Value;
}
};
//////////////////////////////////////////////////////////////////////////
class InOutPin
{
private:
volatile uint8_t *m_vpui8Input;
volatile uint8_t *m_vpui8Dir;
volatile uint8_t *m_vpui8Output;
uint8_t m_ui8Pin;
public:
InOutPin();
InOutPin( InOut::Pin enmPin );
~InOutPin();
void setPin( InOut::Pin enmPin );
//////////////////////////////////////////////////////////////////////////
inline void setDirection( InOut::Dir enmDir, bool bPullup )
{
if( !m_vpui8Dir || !m_vpui8Output )
{
return;
}
InOut::setPinDirection( m_vpui8Dir, m_ui8Pin, enmDir );
if( enmDir == InOut::Dir::D_IN )
{
InOut::writePin( m_vpui8Output, m_ui8Pin, bPullup );
}
}
//////////////////////////////////////////////////////////////////////////
inline bool read()
{
if( !m_vpui8Input )
{
return false;
}
return InOut::readPin( m_vpui8Input, m_ui8Pin );
}
//////////////////////////////////////////////////////////////////////////
inline void write( bool bValue )
{
if( !m_vpui8Output )
{
return;
}
InOut::writePin( m_vpui8Output, m_ui8Pin, bValue );
}
};
//////////////////////////////////////////////////////////////////////////
class InOutPort
{
private:
volatile uint8_t *m_vpui8Input;
volatile uint8_t *m_vpui8Dir;
volatile uint8_t *m_vpui8Output;
public:
InOutPort();
InOutPort( InOut::Pin enmPortPin );
~InOutPort();
void setPort( InOut::Pin enmPortPin );
//////////////////////////////////////////////////////////////////////////
inline void setDirection( InOut::Dir enmDir, bool bPullup )
{
if( !m_vpui8Dir || !m_vpui8Output )
{
return;
}
InOut::setPortDirection( m_vpui8Dir, enmDir );
if( enmDir == InOut::Dir::D_IN )
{
InOut::writePort( m_vpui8Output, ( ( bPullup ) ? ( 0xFF ) : ( 0x00 ) ) );
}
}
//////////////////////////////////////////////////////////////////////////
inline uint8_t read()
{
if( !m_vpui8Input )
{
return 0;
}
return InOut::readPort( m_vpui8Input );
}
//////////////////////////////////////////////////////////////////////////
inline void write( uint8_t ui8Value )
{
if( !m_vpui8Output )
{
return;
}
InOut::writePort( m_vpui8Output, ui8Value );
}
};
#endif

View File

@ -1,52 +0,0 @@
/*
* Copyright (c) by BlackMark 2015-2016
* Date 25/02/2016
* Version 1.3
*/
#include "Clock.h"
#include "InOut.h"
int main()
{
InOutPin cLED( InOut::Pin::P_D7 );
cLED.setDirection( InOut::Dir::D_OUT, false );
cLED.write( false );
while( true )
{
for( uint8_t i = 0; i < 3; ++i )
{
cLED.write( true );
_delay_ms( 100 );
cLED.write( false );
_delay_ms( 100 );
}
_delay_ms( 300 );
for( uint8_t i = 0; i < 3; ++i )
{
cLED.write( true );
_delay_ms( 300 );
cLED.write( false );
_delay_ms( 300 );
}
_delay_ms( 100 );
for( uint8_t i = 0; i < 3; ++i )
{
cLED.write( true );
_delay_ms( 100 );
cLED.write( false );
_delay_ms( 100 );
}
_delay_ms( 1000 );
}
return 0;
}

21
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.

576
io.hpp Normal file
View File

@ -0,0 +1,576 @@
#pragma once
#include <cstdint>
#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
//////////////////////////////////////////////////////////////////////////
// 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
NONE,
};
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
NONE,
};
//////////////////////////////////////////////////////////////////////////
// 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
const auto port = static_cast<std::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
const auto pinBit = static_cast<std::uint8_t>(pin) & 0x0F;
return pinBit;
}
static constexpr auto getDDR(const Bus bus)
{
switch (static_cast<std::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<std::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<std::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 std::uint8_t *;
template <uintptr_t Address>
static inline reg_ptr_t getRegPtr()
{
return reinterpret_cast<reg_ptr_t>(Address);
}
template <template <P, std::uint8_t> typename Func, P pin, P... pins>
struct CallHelper {
template <typename... Args>
[[gnu::always_inline]] static inline void call(Args... args)
{
Func<pin, sizeof...(pins)>::call(args...);
CallHelper<Func, pins...>::call(args...);
}
};
template <template <P, std::uint8_t> typename Func, P pin>
struct CallHelper<Func, pin> {
template <typename... Args>
[[gnu::always_inline]] static inline void call(Args... args)
{
Func<pin, 0>::call(args...);
}
};
template <template <P> typename Func, P pin, P... pins>
struct ReadCallHelper {
[[gnu::always_inline]] static inline std::uint8_t call()
{
return Func<pin>::call() << sizeof...(pins) | ReadCallHelper<Func, pins...>::call();
}
};
template <template <P> typename Func, P pin>
struct ReadCallHelper<Func, pin> {
[[gnu::always_inline]] static inline std::uint8_t call()
{
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;
[[gnu::always_inline]] static inline void dir(const Dir dir)
{
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
auto dirRegPtr = detail::getRegPtr<detail::getDDR(bus)>();
if (dir == Dir::IN)
*dirRegPtr = *dirRegPtr & ~(1 << pinBit);
else if (dir == Dir::OUT)
*dirRegPtr = *dirRegPtr | (1 << pinBit);
}
}
[[gnu::always_inline]] static inline void pullup(const bool enable)
{
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
auto portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
if (enable)
*portRegPtr = *portRegPtr | (1 << pinBit);
else
*portRegPtr = *portRegPtr & ~(1 << pinBit);
}
}
[[gnu::always_inline]] static inline void write(const bool value)
{
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
auto portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
if (value)
*portRegPtr = *portRegPtr | (1 << pinBit);
else
*portRegPtr = *portRegPtr & ~(1 << pinBit);
}
}
[[gnu::always_inline]] static inline void toggle()
{
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
#ifdef HARDWARE_TOGGLE
auto pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
*pinRegPtr = *pinRegPtr | (1 << pinBit);
#else
auto portRegPtr = detail::getRegPtr<detail::getPORT(bus)>();
*portRegPtr = *portRegPtr ^ (1 << pinBit);
#endif
}
}
[[gnu::always_inline]] static inline bool read()
{
if constexpr (pin != P::NONE) {
constexpr auto bus = detail::getBus(pin);
constexpr auto pinBit = detail::getPinBit(pin);
auto pinRegPtr = detail::getRegPtr<detail::getPIN(bus)>();
if (*pinRegPtr >> pinBit & 1)
return true;
}
return false;
}
[[gnu::always_inline]] Pin &operator=(const bool value)
{
write(value);
return *this;
}
[[gnu::always_inline]] operator bool() const
{
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;
[[gnu::always_inline]] static inline void dir(const Dir dir)
{
if constexpr (port != Bus::NONE) {
auto dirRegPtr = detail::getRegPtr<detail::getDDR(port)>();
if (dir == Dir::IN)
*dirRegPtr = 0x00;
else if (dir == Dir::OUT)
*dirRegPtr = 0xFF;
}
}
[[gnu::always_inline]] static inline void pullup(const bool enable)
{
if constexpr (port != Bus::NONE) {
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
if (enable)
*portRegPtr = 0xFF;
else
*portRegPtr = 0x00;
}
}
[[gnu::always_inline]] static inline void write([[maybe_unused]] const std::uint8_t value)
{
if constexpr (port != Bus::NONE) {
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
*portRegPtr = value;
}
}
[[gnu::always_inline]] static inline void invert()
{
if constexpr (port != Bus::NONE) {
#ifdef HARDWARE_TOGGLE
auto pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
*pinRegPtr = 0xFF;
#else
auto portRegPtr = detail::getRegPtr<detail::getPORT(port)>();
*portRegPtr = ~(*portRegPtr);
#endif
}
}
[[gnu::always_inline]] static inline std::uint8_t read()
{
if constexpr (port != Bus::NONE) {
auto pinRegPtr = detail::getRegPtr<detail::getPIN(port)>();
return *pinRegPtr;
}
return 0x00;
}
[[gnu::always_inline]] inline Port &operator=(const std::uint8_t value)
{
write(value);
return *this;
}
[[gnu::always_inline]] inline operator std::uint8_t() const
{
return read();
}
};
//////////////////////////////////////////////////////////////////////////
// Zero overhead Virtual Port object for pretty code without losing performance
namespace detail {
template <P pin, std::uint8_t offset>
struct Callers {
[[gnu::always_inline]] static void call(const Dir dir)
{
Pin<pin>::dir(dir);
};
[[gnu::always_inline]] static void call(const bool enable)
{
Pin<pin>::pullup(enable);
};
[[gnu::always_inline]] static void call(const std::uint8_t value)
{
Pin<pin>::write(value >> offset & 1);
};
[[gnu::always_inline]] static void call()
{
Pin<pin>::toggle();
};
};
template <P pin>
struct readCaller {
[[gnu::always_inline]] static bool call()
{
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;
[[gnu::always_inline]] static inline void dir(const Dir dir)
{
detail::CallHelper<detail::Callers, pins...>::call(dir);
}
[[gnu::always_inline]] static inline void pullup(const bool enable)
{
detail::CallHelper<detail::Callers, pins...>::call(enable);
}
[[gnu::always_inline]] static inline void write(const std::uint8_t value)
{
detail::CallHelper<detail::Callers, pins...>::call(value);
}
[[gnu::always_inline]] static inline void invert()
{
detail::CallHelper<detail::Callers, pins...>::call();
}
[[gnu::always_inline]] static inline std::uint8_t read()
{
return detail::ReadCallHelper<detail::readCaller, pins...>::call();
}
[[gnu::always_inline]] inline VirtPort &operator=(const std::uint8_t value)
{
write(value);
return *this;
}
[[gnu::always_inline]] inline operator std::uint8_t() const
{
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